pbind 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c07d34ce4ff3b24370c314f9d036a3a7cec91b0f
4
- data.tar.gz: 04e7b6199d0c44ef14f99927325355f517a67eb7
3
+ metadata.gz: 2d28ab25efd022a0182268eb102046401ddb3800
4
+ data.tar.gz: 112719797adf95c13e034efd3bd70eab5f511983
5
5
  SHA512:
6
- metadata.gz: b8010b2ebce635a3c9927a2f557729eed3b2c2db0b4d43857c3f77cb0cfabb6c0bc586220c32973efdf90ad475fee4fedf019c040c614ed4c09d41948b8ac746
7
- data.tar.gz: c6bd623fd5a67ae102f68dfe2eb2bb75ef2aa006b298143345deffd0db3a6b7ae4d01c44a4764893512612d8c399564f91bdf66c3d59a823d027e759391a42a0
6
+ metadata.gz: 0d6ea7d843182955111dbab91c1fc64427ee05c49a10109669a4d81543e80dd69b2537f9d372e4a9a902cefe5431a8b5aec9811687d187b47f0d65ead3ee567a
7
+ data.tar.gz: fd041cc082b10871b081adf1d09d96fe6d1195d9690c4c6c53e54f372a05699b72b2d3a05afc2034a1750d1bd36958405546a4d47fb5fa4d9e7a2e141305cf4f
@@ -9,6 +9,7 @@ module Pbind
9
9
 
10
10
  require_relative 'command/watch'
11
11
  require_relative 'command/mock'
12
+ require_relative 'command/serv'
12
13
 
13
14
  self.abstract_command = true
14
15
  self.command = 'pbind'
@@ -0,0 +1,175 @@
1
+ require 'xcodeproj'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require 'listen'
5
+
6
+ require 'webrick'
7
+ include WEBrick
8
+
9
+ module Pbind
10
+ class Command
11
+ class Serv < Command
12
+ self.abstract_command = false
13
+ self.summary = 'Start a mock server for device.'
14
+ self.description = <<-DESC
15
+ This will start a HTTP server provides the APIs in PBLocalhost and
16
+ also a Socket server to send file changes to the device.
17
+ DESC
18
+
19
+ def validate!
20
+ verify_project_exists
21
+ end
22
+
23
+ def run
24
+ @src_name = 'PBLiveLoader'
25
+ @api_name = 'PBLocalhost'
26
+ @src_key = 'PBResourcesPath'
27
+ @project_root = File.dirname(@project_path)
28
+ @src_install_dir = File.absolute_path(File.join(@project_root, @src_name))
29
+ @api_install_dir = File.absolute_path(File.join(@project_root, @api_name))
30
+ @project = Xcodeproj::Project.open(@project_path)
31
+ @changed = false
32
+
33
+ listen_file_changes
34
+ open_tcp_server
35
+
36
+ trap("SIGINT") {
37
+ @server.close
38
+ @listener.stop
39
+ exit
40
+ }
41
+
42
+ super
43
+ # sleep
44
+ end
45
+
46
+ private
47
+
48
+ #----------------------------------------#
49
+
50
+ # !@group Private helpers
51
+
52
+ # Create a HTTP server and open it
53
+ #
54
+ # @return [void]
55
+ #
56
+ def open_tcp_server
57
+ require 'socket'
58
+
59
+ server = TCPServer.new 8082 # Server bind to port
60
+ @server = server
61
+ @clients = []
62
+
63
+ addr = server.addr
64
+ addr.shift
65
+ puts "server is on #{addr.join(':')}"
66
+
67
+ loop do
68
+ Thread.start(server.accept) do |client|
69
+ @clients.push client
70
+ loop do
71
+ line = client.readpartial(1024)
72
+ if line != nil and line.end_with?('.json')
73
+ send_json(@clients, line)
74
+ end
75
+ end
76
+ client.close
77
+ end
78
+ end
79
+
80
+ # loop do
81
+ # client = server.accept # Wait for a client to connect
82
+ # @client = client
83
+
84
+ # line = client.readpartial(1024)
85
+ # puts "get #{line}"
86
+ # if line != nil and line.end_with?('.json')
87
+ # puts "send #{line}"
88
+ # send_json(client, line)
89
+ # end
90
+
91
+ # client.close
92
+ # end
93
+ end
94
+
95
+ def listen_file_changes
96
+ @listener = Listen.to(@project_root) do |modified, added, removed|
97
+ # puts "modified absolute path: #{modified}"
98
+ # puts "added absolute path: #{added}"
99
+ # puts "removed absolute path: #{removed}"
100
+
101
+ modified.each { |m|
102
+ if m.end_with?('.plist')
103
+ if @clients != nil
104
+ send_plist @clients, m
105
+ end
106
+ elsif m.end_with?('.json')
107
+ if @clients != nil
108
+ send_file_update @clients, m, nil
109
+ end
110
+ end
111
+ }
112
+ end
113
+
114
+ @listener.start # not blocking
115
+ end
116
+
117
+ def send_json(clients, json_file)
118
+ file = File.open(File.join(@api_install_dir, json_file), "r")
119
+ content = file.read
120
+ file.close
121
+
122
+ UI.section("Send API \"/#{File.basename(json_file, '.json')}\"") {
123
+ clients.each { |client|
124
+ write_byte client, 0xE0
125
+ write_string client, content
126
+ }
127
+ }
128
+ end
129
+
130
+ def send_plist(clients, plist_path)
131
+ # Create a binary plist
132
+ require 'tempfile'
133
+ plist_name = File.basename(plist_path)
134
+ temp = Tempfile.new(plist_name)
135
+ `plutil -convert binary1 #{plist_path} -o #{temp.path}`
136
+
137
+ send_file_update clients, temp.path, plist_name
138
+ end
139
+
140
+ def send_file_update(clients, file_path, file_name)
141
+ File.open(file_path, "r") { |file|
142
+ file_content = file.read
143
+ if file_name == nil
144
+ file_name = File.basename(file_path)
145
+ end
146
+ UI.section("Update file \"#{file_name}\"") {
147
+ clients.each { |client|
148
+ write_byte(client, 0xF1)
149
+ write_string(client, file_name)
150
+ write_string(client, file_content)
151
+ }
152
+ }
153
+ }
154
+ end
155
+
156
+ def write_byte(client, b)
157
+ write_any client, [b].pack("C")
158
+ end
159
+
160
+ def write_string(client, text)
161
+ write_any client, [text.bytesize].pack("N")
162
+ write_any client, text
163
+ end
164
+
165
+ def write_any(client, obj)
166
+ begin
167
+ client.write obj
168
+ rescue Exception => e
169
+ @clients.delete client
170
+ end
171
+ end
172
+
173
+ end
174
+ end
175
+ end
@@ -36,7 +36,7 @@ module Pbind
36
36
  @changed = false
37
37
 
38
38
  install_sources
39
- add_plist_entries
39
+ # add_plist_entries
40
40
  add_group_references
41
41
 
42
42
  super
@@ -0,0 +1,25 @@
1
+ //
2
+ // NSInputStream+Reader.h
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ // Copyright © 2017 galen. All rights reserved.
7
+ //
8
+
9
+ #include <targetconditionals.h>
10
+
11
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
12
+
13
+ #import <Foundation/Foundation.h>
14
+
15
+ @interface NSInputStream (Reader)
16
+
17
+ - (int)readInt;
18
+
19
+ - (NSString *)readString;
20
+
21
+ - (NSData *)readData;
22
+
23
+ @end
24
+
25
+ #endif
@@ -0,0 +1,36 @@
1
+ //
2
+ // NSInputStream+Reader.m
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ // Copyright © 2017 galen. All rights reserved.
7
+ //
8
+
9
+ #import "NSInputStream+Reader.h"
10
+
11
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
12
+
13
+ @implementation NSInputStream (Reader)
14
+
15
+ - (int)readInt {
16
+ uint8_t bytes[4];
17
+ [self read:bytes maxLength:4];
18
+ int intData = *((int *)bytes);
19
+ return NSSwapInt(intData);
20
+ }
21
+
22
+ - (NSData *)readData {
23
+ int length = [self readInt];
24
+ uint8_t *bytes = malloc(length);
25
+ NSInteger len = [self read:bytes maxLength:length];
26
+ return [NSData dataWithBytes:bytes length:len];
27
+ }
28
+
29
+ - (NSString *)readString {
30
+ NSData *data = [self readData];
31
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
32
+ }
33
+
34
+ @end
35
+
36
+ #endif
@@ -0,0 +1,23 @@
1
+ //
2
+ // PBLLInspector.h
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ // Copyright © 2017 galen. All rights reserved.
7
+ //
8
+
9
+ #include <targetconditionals.h>
10
+
11
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
12
+
13
+ #import <UIKit/UIKit.h>
14
+
15
+ @interface PBLLInspector : UIButton
16
+
17
+ + (instancetype)sharedInspector;
18
+
19
+ + (void)addToWindow;
20
+
21
+ @end
22
+
23
+ #endif
@@ -0,0 +1,52 @@
1
+ //
2
+ // PBLLInspector.m
3
+ // Pbind
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ //
7
+
8
+ #import "PBLLInspector.h"
9
+
10
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
11
+
12
+ #import "PBLLInspectorController.h"
13
+ #import <Pbind/Pbind.h>
14
+
15
+ @interface PBLLInspector () <UISearchBarDelegate>
16
+
17
+ @end
18
+
19
+ @implementation PBLLInspector
20
+
21
+ + (instancetype)sharedInspector {
22
+ static PBLLInspector *o;
23
+ static dispatch_once_t onceToken;
24
+ dispatch_once(&onceToken, ^{
25
+ o = [PBLLInspector buttonWithType:UIButtonTypeCustom];
26
+ });
27
+ return o;
28
+ }
29
+
30
+ + (void)addToWindow {
31
+ PBLLInspector *inspector = [self sharedInspector];
32
+ CGRect frame = [UIScreen mainScreen].bounds;
33
+ frame.origin.x = frame.size.width - 68.f;
34
+ frame.origin.y = frame.size.height - 100.f;
35
+ frame.size.width = 60.f;
36
+ frame.size.height = 44.f;
37
+ inspector.frame = frame;
38
+ inspector.backgroundColor = [UIColor greenColor];
39
+ [inspector setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
40
+ [inspector setTitle:@"Pbind" forState:UIControlStateNormal];
41
+ [inspector addTarget:inspector action:@selector(didClick:) forControlEvents:UIControlEventTouchUpInside];
42
+ [[[UIApplication sharedApplication].delegate window] addSubview:inspector];
43
+ }
44
+
45
+ - (void)didClick:(id)sender {
46
+ PBLLInspectorController *controller = [[PBLLInspectorController alloc] init];
47
+ [PBTopController().navigationController pushViewController:controller animated:YES];
48
+ }
49
+
50
+ @end
51
+
52
+ #endif
@@ -0,0 +1,19 @@
1
+ //
2
+ // PBLLInspectorController.h
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ // Copyright © 2017 galen. All rights reserved.
7
+ //
8
+
9
+ #include <targetconditionals.h>
10
+
11
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
12
+
13
+ #import <UIKit/UIKit.h>
14
+
15
+ @interface PBLLInspectorController : UIViewController
16
+
17
+ @end
18
+
19
+ #endif
@@ -0,0 +1,96 @@
1
+ //
2
+ // PBLLInspectorController.m
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ //
7
+
8
+ #import "PBLLInspectorController.h"
9
+
10
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
11
+
12
+ #import "PBLLInspector.h"
13
+ #import "PBLLRemoteWatcher.h"
14
+ #import <Pbind/Pbind.h>
15
+
16
+ @interface PBLLInspectorController () <UISearchBarDelegate>
17
+ {
18
+ UISearchBar *_searchBar;
19
+ }
20
+
21
+ @end
22
+
23
+ @implementation PBLLInspectorController
24
+
25
+ - (void)viewDidLoad {
26
+ [super viewDidLoad];
27
+ // Do any additional setup after loading the view.
28
+ self.edgesForExtendedLayout = UIRectEdgeNone;
29
+ self.view.backgroundColor = [UIColor whiteColor];
30
+ CGRect frame = self.view.bounds;
31
+ frame.size.height = 44.f;
32
+ frame.origin.y = 16.f;
33
+ UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:frame];
34
+ searchBar.text = [self defaultIP];
35
+ searchBar.delegate = self;
36
+ searchBar.returnKeyType = UIReturnKeyJoin;
37
+ [self.view addSubview:searchBar];
38
+ _searchBar = searchBar;
39
+
40
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
41
+ [self.view addGestureRecognizer:tap];
42
+ }
43
+
44
+ - (void)viewWillAppear:(BOOL)animated {
45
+ [super viewWillAppear:animated];
46
+ [PBLLInspector sharedInspector].hidden = YES;
47
+ }
48
+
49
+ - (void)viewDidDisappear:(BOOL)animated {
50
+ [super viewDidDisappear:animated];
51
+ [PBLLInspector sharedInspector].hidden = NO;
52
+ }
53
+
54
+ - (void)didReceiveMemoryWarning {
55
+ [super didReceiveMemoryWarning];
56
+ // Dispose of any resources that can be recreated.
57
+ }
58
+
59
+ - (NSString *)defaultIP {
60
+ NSString *ip = [[NSUserDefaults standardUserDefaults] objectForKey:@"pbind.server.ip"];
61
+ if (ip == nil) {
62
+ ip = @"192.168.1.10";
63
+ }
64
+ return ip;
65
+ }
66
+
67
+ - (void)setDefaultIP:(NSString *)ip {
68
+ [[NSUserDefaults standardUserDefaults] setObject:ip forKey:@"pbind.server.ip"];
69
+ [[NSUserDefaults standardUserDefaults] synchronize];
70
+ }
71
+
72
+ #pragma mark - UISearchBarDelegate
73
+
74
+ - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
75
+ NSString *ip = searchBar.text;
76
+ if (ip.length == 0) {
77
+ return;
78
+ }
79
+
80
+ [self setDefaultIP:ip];
81
+ [[PBLLRemoteWatcher globalWatcher] connect:ip];
82
+ [self.navigationController popViewControllerAnimated:YES];
83
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
84
+ [PBTopController().view pb_reloadClient];
85
+ });
86
+ }
87
+
88
+ #pragma mark - Gesture
89
+
90
+ - (void)didTap:(id)sender {
91
+ [_searchBar resignFirstResponder];
92
+ }
93
+
94
+ @end
95
+
96
+ #endif
@@ -0,0 +1,41 @@
1
+ //
2
+ // PBLLRemoteWatcher.h
3
+ // Pchat
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ // Copyright © 2017 galen. All rights reserved.
7
+ //
8
+
9
+ #include <targetconditionals.h>
10
+
11
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
12
+
13
+ #import <Foundation/Foundation.h>
14
+
15
+ @class PBLLRemoteWatcher;
16
+
17
+ @protocol PBLLRemoteWatcherDelegate <NSObject>
18
+
19
+ - (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didUpdateFile:(NSString *)fileName withData:(NSData *)data;
20
+
21
+ @optional
22
+
23
+ - (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didCreateFile:(NSString *)fileName;
24
+ - (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didDeleteFile:(NSString *)fileName;
25
+
26
+ @end
27
+
28
+ @interface PBLLRemoteWatcher : NSObject
29
+
30
+ + (instancetype)globalWatcher;
31
+
32
+ - (void)connect:(NSString *)ip;
33
+ - (void)connectDefaultIP;
34
+
35
+ - (void)requestAPI:(NSString *)api success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure;
36
+
37
+ @property (nonatomic, assign) id<PBLLRemoteWatcherDelegate> delegate;
38
+
39
+ @end
40
+
41
+ #endif
@@ -0,0 +1,163 @@
1
+ //
2
+ // PBLLRemoteWatcher.m
3
+ // Pbind
4
+ //
5
+ // Created by Galen Lin on 15/03/2017.
6
+ //
7
+
8
+ #import "PBLLRemoteWatcher.h"
9
+
10
+ #if (DEBUG && !(TARGET_IPHONE_SIMULATOR))
11
+
12
+ #import "NSInputStream+Reader.h"
13
+
14
+ @interface PBLLRemoteWatcher () <NSStreamDelegate>
15
+ {
16
+ NSInputStream *inputStream;
17
+ NSOutputStream *outputStream;
18
+ void (^responseBlock)(NSData *);
19
+ BOOL streamOpened;
20
+ NSData *apiReqData;
21
+ NSString *tempResourcesPath;
22
+ NSString *connectedIP;
23
+ }
24
+
25
+ @end
26
+
27
+ @implementation PBLLRemoteWatcher
28
+
29
+ + (instancetype)globalWatcher {
30
+ static PBLLRemoteWatcher *o;
31
+ static dispatch_once_t onceToken;
32
+ dispatch_once(&onceToken, ^{
33
+ o = [[PBLLRemoteWatcher alloc] init];
34
+ });
35
+ return o;
36
+ }
37
+
38
+ - (void)connect:(NSString *)ip {
39
+ [self close];
40
+
41
+ connectedIP = ip;
42
+ CFReadStreamRef readStream;
43
+ CFWriteStreamRef writeStream;
44
+ CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)ip, 8082, &readStream, &writeStream);
45
+ inputStream = (__bridge NSInputStream *)readStream;
46
+ outputStream = (__bridge NSOutputStream *)writeStream;
47
+ inputStream.delegate = self;
48
+ outputStream.delegate = self;
49
+ [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
50
+ [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
51
+ [inputStream open];
52
+ [outputStream open];
53
+ }
54
+
55
+ - (void)connectDefaultIP {
56
+ NSString *ip = [[NSUserDefaults standardUserDefaults] objectForKey:@"pbind.server.ip"];
57
+ if (ip == nil) {
58
+ ip = @"192.168.1.2";
59
+ }
60
+ [self connect:ip];
61
+ }
62
+
63
+ - (void)requestAPI:(NSString *)api success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure {
64
+ if (inputStream == nil) {
65
+ failure([NSError errorWithDomain:@"PBLLRemoteWatcher"
66
+ code:1
67
+ userInfo:@{NSLocalizedDescriptionKey: @"Watcher wasn't online!"}]);
68
+ return;
69
+ }
70
+
71
+ responseBlock = success;
72
+ NSData *data = [api dataUsingEncoding:NSUTF8StringEncoding];
73
+ if (streamOpened) {
74
+ [outputStream write:[data bytes] maxLength:[data length]];
75
+ } else {
76
+ apiReqData = data;
77
+ }
78
+ }
79
+
80
+ - (void)close {
81
+ if (inputStream != nil) {
82
+ [inputStream close];
83
+ inputStream = nil;
84
+ }
85
+ if (outputStream != nil) {
86
+ [outputStream close];
87
+ outputStream = nil;
88
+ }
89
+ streamOpened = NO;
90
+ }
91
+
92
+ #pragma mark - NSStreamDelegate
93
+
94
+ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
95
+ NSLog(@"socket: %i, %@", (int)eventCode, aStream);
96
+ switch (eventCode) {
97
+ case NSStreamEventOpenCompleted:
98
+
99
+ break;
100
+ case NSStreamEventHasSpaceAvailable:
101
+ if (aStream == outputStream) {
102
+ streamOpened = YES;
103
+ if (apiReqData != nil) {
104
+ [outputStream write:[apiReqData bytes] maxLength:[apiReqData length]];
105
+ apiReqData = nil;
106
+ }
107
+ }
108
+ break;
109
+ case NSStreamEventHasBytesAvailable:
110
+ if (aStream == inputStream) {
111
+ NSLog(@"inputStream is ready.");
112
+
113
+ uint8_t type[1];
114
+ [inputStream read:type maxLength:1];
115
+ switch (*type) {
116
+ case 0xE0: { // Got json
117
+ NSData *jsonData = [inputStream readData];
118
+ if (responseBlock != nil) {
119
+ responseBlock(jsonData);
120
+ }
121
+ break;
122
+ }
123
+ case 0xF0: { // Create file
124
+ NSString *fileName = [inputStream readString];
125
+ [self.delegate remoteWatcher:self didCreateFile:fileName];
126
+ break;
127
+ }
128
+ case 0xF1: { // Update file
129
+ NSString *fileName = [inputStream readString];
130
+ NSData *fileData = [inputStream readData];
131
+ [self.delegate remoteWatcher:self didUpdateFile:fileName withData:fileData];
132
+ break;
133
+ }
134
+ case 0xF2: { // Delete file
135
+ NSString *fileName = [inputStream readString];
136
+ [self.delegate remoteWatcher:self didDeleteFile:fileName];
137
+ break;
138
+ }
139
+ default:
140
+ break;
141
+ }
142
+ }
143
+ break;
144
+ case NSStreamEventErrorOccurred:
145
+ if (aStream == outputStream) {
146
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
147
+ [self connect:connectedIP];
148
+ });
149
+ }
150
+ break;
151
+ case NSStreamEventEndEncountered:
152
+ if (aStream == inputStream) {
153
+ [self close];
154
+ }
155
+ break;
156
+ default:
157
+ break;
158
+ }
159
+ }
160
+
161
+ @end
162
+
163
+ #endif
@@ -5,9 +5,7 @@
5
5
  // Created by Galen Lin on 2016/12/9.
6
6
  //
7
7
 
8
- #include <targetconditionals.h>
9
-
10
- #if (DEBUG && TARGET_IPHONE_SIMULATOR)
8
+ #if (DEBUG)
11
9
 
12
10
  #import <Foundation/Foundation.h>
13
11
 
@@ -6,12 +6,32 @@
6
6
  //
7
7
 
8
8
  #import "PBLiveLoader.h"
9
+ #include <targetconditionals.h>
9
10
 
10
- #if (DEBUG && TARGET_IPHONE_SIMULATOR)
11
+ #if (DEBUG)
11
12
 
12
13
  #import "PBDirectoryWatcher.h"
13
14
  #import <Pbind/Pbind.h>
14
15
 
16
+ #import "PBSimulatorEnviroment.h"
17
+
18
+ #if !(TARGET_IPHONE_SIMULATOR)
19
+
20
+ #import "PBLLRemoteWatcher.h"
21
+ #import "PBLLInspector.h"
22
+
23
+ @interface PBLiveLoader () <PBLLRemoteWatcherDelegate>
24
+ {
25
+ void (^apiComplection)(PBResponse *);
26
+ NSData *apiReqData;
27
+ NSString *tempResourcesPath;
28
+ NSMutableDictionary *cacheResponseData;
29
+ }
30
+
31
+ @end
32
+
33
+ #endif
34
+
15
35
  @implementation PBLiveLoader
16
36
 
17
37
  static NSString *const kPlistSuffix = @".plist";
@@ -22,8 +42,11 @@ static NSString *const kDebugJSONRedirectKey = @"$redirect";
22
42
  static NSString *const kDebugJSONStatusKey = @"$status";
23
43
 
24
44
  static NSArray<NSString *> *kIgnoreAPIs;
45
+
46
+ #if (TARGET_IPHONE_SIMULATOR)
25
47
  static PBDirectoryWatcher *kResWatcher;
26
48
  static PBDirectoryWatcher *kAPIWatcher;
49
+ #endif
27
50
 
28
51
  static BOOL HasSuffix(NSString *src, NSString *tail)
29
52
  {
@@ -42,13 +65,9 @@ static BOOL HasSuffix(NSString *src, NSString *tail)
42
65
  }
43
66
 
44
67
  + (void)watchPlist {
45
- NSString *resPath = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"PBResourcesPath"];
46
- if (resPath == nil) {
47
- NSLog(@"PBLiveLoader: Please define PBResourcesPath in Info.plist with value '$(SRCROOT)/[path-to-resources]'!");
48
- return;
49
- }
50
-
51
- if (![[NSFileManager defaultManager] fileExistsAtPath:resPath]) {
68
+ #if (TARGET_IPHONE_SIMULATOR)
69
+ NSString *resPath = PBLLMainBundlePath();
70
+ if (resPath == nil || ![[NSFileManager defaultManager] fileExistsAtPath:resPath]) {
52
71
  NSLog(@"PBLiveLoader: PBResourcesPath is not exists! (%@)", resPath);
53
72
  return;
54
73
  }
@@ -79,15 +98,21 @@ static BOOL HasSuffix(NSString *src, NSString *tail)
79
98
  break;
80
99
  }
81
100
  }];
101
+ #else
102
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
103
+ NSString *documentPath = paths.firstObject;
104
+ NSString *tempBundlePath = [documentPath stringByAppendingPathComponent:@".pb_liveload"];
105
+ if (![[NSFileManager defaultManager] fileExistsAtPath:tempBundlePath]) {
106
+ [[NSFileManager defaultManager] createDirectoryAtPath:tempBundlePath withIntermediateDirectories:NO attributes:nil error:nil];
107
+ }
108
+ [self defaultLoader]->tempResourcesPath = tempBundlePath;
109
+ [Pbind addResourcesBundle:[NSBundle bundleWithPath:tempBundlePath]];
110
+ #endif
82
111
  }
83
112
 
84
113
  + (void)watchAPI {
85
- NSString *serverPath = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"PBLocalhost"];
86
- if (serverPath == nil) {
87
- NSLog(@"PBLiveLoader: Please define PBLocalhost in Info.plist with value '$(SRCROOT)/[path-to-api]'!");
88
- return;
89
- }
90
-
114
+ #if (TARGET_IPHONE_SIMULATOR)
115
+ NSString *serverPath = PBLLMockingAPIPath();
91
116
  if (![[NSFileManager defaultManager] fileExistsAtPath:serverPath]) {
92
117
  NSLog(@"PBLiveLoader: PBLocalhost is not exists! (%@)", serverPath);
93
118
  return;
@@ -122,8 +147,10 @@ static BOOL HasSuffix(NSString *src, NSString *tail)
122
147
  break;
123
148
  }
124
149
  }];
150
+ #endif
125
151
 
126
- [PBClient registerDebugServer:^id(PBClient *client, PBRequest *request) {
152
+ [PBClient registerDebugServer:^(PBClient *client, PBRequest *request, void (^complection)(PBResponse *response)) {
153
+
127
154
  NSString *action = request.action;
128
155
  if ([action characterAtIndex:0] == '/') {
129
156
  action = [action substringFromIndex:1]; // bypass '/'
@@ -135,49 +162,60 @@ static BOOL HasSuffix(NSString *src, NSString *tail)
135
162
  }
136
163
  action = [action stringByReplacingOccurrencesOfString:@"/" withString:@"-"];
137
164
  if (kIgnoreAPIs != nil && [kIgnoreAPIs containsObject:action]) {
138
- return nil;
165
+ complection(nil);
166
+ return;
139
167
  }
140
168
 
141
169
  NSString *jsonName = [NSString stringWithFormat:@"%@/%@.json", [[client class] description], action];
170
+ #if (TARGET_IPHONE_SIMULATOR)
142
171
  NSString *jsonPath = [serverPath stringByAppendingPathComponent:jsonName];
143
172
  if (![[NSFileManager defaultManager] fileExistsAtPath:jsonPath]) {
144
173
  NSLog(@"PBLiveLoader: Missing '%@', ignores!", jsonName);
145
- return nil;
174
+ complection(nil);
175
+ return;
146
176
  }
147
177
  NSData *jsonData = [NSData dataWithContentsOfFile:jsonPath];
148
- NSError *error = nil;
149
-
150
- PBResponse *response = [[PBResponse alloc] init];
151
- response.data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
152
- if (error != nil) {
153
- NSLog(@"PBLiveLoader: Invalid '%@', ignores! The file format should be pure JSON style.", jsonName);
154
- return nil;
155
- }
156
-
157
- if ([response.data isKindOfClass:[NSDictionary class]]) {
158
- NSString *redirect = [response.data objectForKey:kDebugJSONRedirectKey];
159
- if (redirect != nil) {
160
- PBExpression *expression = [PBExpression expressionWithString:redirect];
161
- if (expression != nil) {
162
- response.data = [expression valueWithData:nil];
163
- }
178
+ [self receiveJsonData:jsonData withFile:jsonName complection:complection];
179
+ #else
180
+ [[self defaultLoader] requestAPI:jsonName complection:complection];
181
+ #endif
182
+ }];
183
+ }
184
+
185
+ + (void)receiveJsonData:(NSData *)jsonData withFile:(NSString *)jsonName complection:(void (^)(PBResponse *))complection {
186
+ NSError *error = nil;
187
+ id data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
188
+ if (data == nil) {
189
+ NSLog(@"PBLiveLoader: Invalid '%@', ignores! The file format should be pure JSON style.", jsonName);
190
+ complection(nil);
191
+ return;
192
+ }
193
+
194
+ PBResponse *response = [[PBResponse alloc] init];
195
+ if ([data isKindOfClass:[NSDictionary class]]) {
196
+ NSString *redirect = [data objectForKey:kDebugJSONRedirectKey];
197
+ if (redirect != nil) {
198
+ PBExpression *expression = [PBExpression expressionWithString:redirect];
199
+ if (expression != nil) {
200
+ data = [expression valueWithData:nil];
201
+ }
202
+ } else {
203
+ NSString *statusString = [data objectForKey:kDebugJSONStatusKey];
204
+ if (statusString != nil) {
205
+ response.status = [statusString intValue];
206
+ }
207
+ NSMutableDictionary *filteredDict = [NSMutableDictionary dictionaryWithDictionary:data];
208
+ [filteredDict removeObjectForKey:kDebugJSONStatusKey];
209
+ if (filteredDict.count == 0) {
210
+ data = nil;
164
211
  } else {
165
- NSString *statusString = [response.data objectForKey:kDebugJSONStatusKey];
166
- if (statusString != nil) {
167
- response.status = [statusString intValue];
168
- }
169
- NSMutableDictionary *filteredDict = [NSMutableDictionary dictionaryWithDictionary:response.data];
170
- [filteredDict removeObjectForKey:kDebugJSONStatusKey];
171
- if (filteredDict.count == 0) {
172
- response.data = nil;
173
- } else {
174
- response.data = filteredDict;
175
- }
212
+ data = filteredDict;
176
213
  }
177
214
  }
178
-
179
- return response;
180
- }];
215
+ }
216
+
217
+ response.data = data;
218
+ complection(response);
181
219
  }
182
220
 
183
221
  + (NSArray *)ignoreAPIsWithContentsOfFile:(NSString *)path {
@@ -225,6 +263,63 @@ static BOOL HasSuffix(NSString *src, NSString *tail)
225
263
  }
226
264
  }
227
265
 
266
+ #if !(TARGET_IPHONE_SIMULATOR)
267
+
268
+ + (PBLiveLoader *)defaultLoader {
269
+ static PBLiveLoader *loader = nil;
270
+ static dispatch_once_t onceToken;
271
+ dispatch_once(&onceToken, ^{
272
+ loader = [[self alloc] init];
273
+ [PBLLRemoteWatcher globalWatcher].delegate = loader;
274
+ });
275
+ return loader;
276
+ }
277
+
278
+ - (void)requestAPI:(NSString *)api complection:(void (^)(PBResponse *))complection {
279
+ static dispatch_once_t onceToken;
280
+ dispatch_once(&onceToken, ^{
281
+ [PBLLInspector addToWindow];
282
+ [[PBLLRemoteWatcher globalWatcher] connectDefaultIP];
283
+ });
284
+
285
+ NSString *key = [api lastPathComponent];
286
+ NSData *cacheData = cacheResponseData[key];
287
+ if (cacheData != nil) {
288
+ [[self class] receiveJsonData:cacheData withFile:nil complection:complection];
289
+ [cacheResponseData removeObjectForKey:key];
290
+ return;
291
+ }
292
+
293
+ [[PBLLRemoteWatcher globalWatcher] requestAPI:api success:^(NSData *data) {
294
+ [[self class] receiveJsonData:data withFile:nil complection:complection];
295
+ } failure:^(NSError *error) {
296
+ complection(nil);
297
+ }];
298
+ }
299
+
300
+ #pragma mark - PBLLRemoteWatcherDelegate
301
+
302
+ - (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didReceiveResponse:(NSData *)jsonData {
303
+ [[self class] receiveJsonData:jsonData withFile:nil complection:apiComplection];
304
+ }
305
+
306
+ - (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didUpdateFile:(NSString *)fileName withData:(NSData *)data {
307
+ if (HasSuffix(fileName, @".plist")) {
308
+ NSString *plist = [tempResourcesPath stringByAppendingPathComponent:fileName];
309
+ [data writeToFile:plist atomically:NO];
310
+
311
+ [Pbind reloadViewsOnPlistUpdate:fileName];
312
+ } else if (HasSuffix(fileName, @".json")) {
313
+ if (cacheResponseData == nil) {
314
+ cacheResponseData = [[NSMutableDictionary alloc] init];
315
+ }
316
+ cacheResponseData[fileName] = data;
317
+ [Pbind reloadViewsOnAPIUpdate:fileName];
318
+ }
319
+ }
320
+
321
+ #endif
322
+
228
323
  @end
229
324
 
230
325
  #endif
@@ -0,0 +1,43 @@
1
+ //
2
+ // PBSimulatorEnviroment.h
3
+ // Pbind
4
+ //
5
+ // Created by Galen Lin on 13/03/2017.
6
+ //
7
+
8
+ #if (DEBUG)
9
+
10
+ #include <targetconditionals.h>
11
+ #import <Foundation/Foundation.h>
12
+
13
+ #if (TARGET_IPHONE_SIMULATOR)
14
+
15
+ FOUNDATION_STATIC_INLINE NSString *PBLLProjectPath() {
16
+ return [[@(__FILE__) stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
17
+ }
18
+
19
+ FOUNDATION_STATIC_INLINE NSString *PBLLMainBundlePath() {
20
+ NSString *projectPath = PBLLProjectPath();
21
+ NSString *bundlePath = [projectPath stringByAppendingPathComponent:[projectPath lastPathComponent]];
22
+ if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
23
+ return bundlePath;
24
+ }
25
+
26
+ NSArray *subdirs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:projectPath error:nil];
27
+ for (NSString *subdir in subdirs) {
28
+ bundlePath = [projectPath stringByAppendingPathComponent:subdir];
29
+ NSString *mainFile = [bundlePath stringByAppendingPathComponent:@"main.m"];
30
+ if ([[NSFileManager defaultManager] fileExistsAtPath:mainFile]) {
31
+ return bundlePath;
32
+ }
33
+ }
34
+ return nil;
35
+ }
36
+
37
+ FOUNDATION_STATIC_INLINE NSString *PBLLMockingAPIPath() {
38
+ return [PBLLProjectPath() stringByAppendingPathComponent:@"PBLocalhost"];
39
+ }
40
+
41
+ #endif
42
+
43
+ #endif
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pbind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Galen Lin
@@ -50,6 +50,20 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '2.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: listen
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '='
58
+ - !ruby/object:Gem::Version
59
+ version: 3.1.5
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '='
65
+ - !ruby/object:Gem::Version
66
+ version: 3.1.5
53
67
  description: A toolkit to create a xcode project with Pbind.
54
68
  email: oolgloo.2012@gmail.com
55
69
  executables:
@@ -61,13 +75,23 @@ files:
61
75
  - lib/pbind.rb
62
76
  - lib/pbind/command.rb
63
77
  - lib/pbind/command/mock.rb
78
+ - lib/pbind/command/serv.rb
64
79
  - lib/pbind/command/watch.rb
65
80
  - lib/pbind/gem_version.rb
66
81
  - lib/pbind/user_interface.rb
82
+ - source/PBLiveLoader/NSInputStream+Reader.h
83
+ - source/PBLiveLoader/NSInputStream+Reader.m
67
84
  - source/PBLiveLoader/PBDirectoryWatcher.h
68
85
  - source/PBLiveLoader/PBDirectoryWatcher.m
86
+ - source/PBLiveLoader/PBLLInspector.h
87
+ - source/PBLiveLoader/PBLLInspector.m
88
+ - source/PBLiveLoader/PBLLInspectorController.h
89
+ - source/PBLiveLoader/PBLLInspectorController.m
90
+ - source/PBLiveLoader/PBLLRemoteWatcher.h
91
+ - source/PBLiveLoader/PBLLRemoteWatcher.m
69
92
  - source/PBLiveLoader/PBLiveLoader.h
70
93
  - source/PBLiveLoader/PBLiveLoader.m
94
+ - source/PBLiveLoader/PBSimulatorEnviroment.h
71
95
  - source/PBLocalhost/ignore.h
72
96
  homepage: http://rubygems.org/gems/pbind
73
97
  licenses:
@@ -89,9 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
113
  version: '0'
90
114
  requirements: []
91
115
  rubyforge_project:
92
- rubygems_version: 2.6.6
116
+ rubygems_version: 2.6.8
93
117
  signing_key:
94
118
  specification_version: 4
95
119
  summary: Pbind xcodeproj helper
96
120
  test_files: []
97
- has_rdoc: