diff --git a/Example/PBLiveLoader/NSInputStream+Reader.h b/Example/PBLiveLoader/NSInputStream+Reader.h new file mode 100644 index 0000000..b6b17d6 --- /dev/null +++ b/Example/PBLiveLoader/NSInputStream+Reader.h @@ -0,0 +1,25 @@ +// +// NSInputStream+Reader.h +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// Copyright © 2017 galen. All rights reserved. +// + +#include + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import + +@interface NSInputStream (Reader) + +- (int)readInt; + +- (NSString *)readString; + +- (NSData *)readData; + +@end + +#endif diff --git a/Example/PBLiveLoader/NSInputStream+Reader.m b/Example/PBLiveLoader/NSInputStream+Reader.m new file mode 100644 index 0000000..fd0ec91 --- /dev/null +++ b/Example/PBLiveLoader/NSInputStream+Reader.m @@ -0,0 +1,36 @@ +// +// NSInputStream+Reader.m +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// Copyright © 2017 galen. All rights reserved. +// + +#import "NSInputStream+Reader.h" + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +@implementation NSInputStream (Reader) + +- (int)readInt { + uint8_t bytes[4]; + [self read:bytes maxLength:4]; + int intData = *((int *)bytes); + return NSSwapInt(intData); +} + +- (NSData *)readData { + int length = [self readInt]; + uint8_t *bytes = malloc(length); + NSInteger len = [self read:bytes maxLength:length]; + return [NSData dataWithBytes:bytes length:len]; +} + +- (NSString *)readString { + NSData *data = [self readData]; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +} + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLInspector.h b/Example/PBLiveLoader/PBLLInspector.h new file mode 100644 index 0000000..4596ab5 --- /dev/null +++ b/Example/PBLiveLoader/PBLLInspector.h @@ -0,0 +1,23 @@ +// +// PBLLInspector.h +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// Copyright © 2017 galen. All rights reserved. +// + +#include + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import + +@interface PBLLInspector : UIButton + ++ (instancetype)sharedInspector; + ++ (void)addToWindow; + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLInspector.m b/Example/PBLiveLoader/PBLLInspector.m new file mode 100644 index 0000000..db70982 --- /dev/null +++ b/Example/PBLiveLoader/PBLLInspector.m @@ -0,0 +1,52 @@ +// +// PBLLInspector.m +// Pbind +// +// Created by Galen Lin on 15/03/2017. +// + +#import "PBLLInspector.h" + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import "PBLLInspectorController.h" +#import + +@interface PBLLInspector () + +@end + +@implementation PBLLInspector + ++ (instancetype)sharedInspector { + static PBLLInspector *o; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + o = [PBLLInspector buttonWithType:UIButtonTypeCustom]; + }); + return o; +} + ++ (void)addToWindow { + PBLLInspector *inspector = [self sharedInspector]; + CGRect frame = [UIScreen mainScreen].bounds; + frame.origin.x = frame.size.width - 68.f; + frame.origin.y = frame.size.height - 100.f; + frame.size.width = 60.f; + frame.size.height = 44.f; + inspector.frame = frame; + inspector.backgroundColor = [UIColor greenColor]; + [inspector setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [inspector setTitle:@"Pbind" forState:UIControlStateNormal]; + [inspector addTarget:inspector action:@selector(didClick:) forControlEvents:UIControlEventTouchUpInside]; + [[[UIApplication sharedApplication].delegate window] addSubview:inspector]; +} + +- (void)didClick:(id)sender { + PBLLInspectorController *controller = [[PBLLInspectorController alloc] init]; + [PBTopController().navigationController pushViewController:controller animated:YES]; +} + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLInspectorController.h b/Example/PBLiveLoader/PBLLInspectorController.h new file mode 100644 index 0000000..0f97046 --- /dev/null +++ b/Example/PBLiveLoader/PBLLInspectorController.h @@ -0,0 +1,19 @@ +// +// PBLLInspectorController.h +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// Copyright © 2017 galen. All rights reserved. +// + +#include + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import + +@interface PBLLInspectorController : UIViewController + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLInspectorController.m b/Example/PBLiveLoader/PBLLInspectorController.m new file mode 100644 index 0000000..70684e0 --- /dev/null +++ b/Example/PBLiveLoader/PBLLInspectorController.m @@ -0,0 +1,96 @@ +// +// PBLLInspectorController.m +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// + +#import "PBLLInspectorController.h" + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import "PBLLInspector.h" +#import "PBLLRemoteWatcher.h" +#import + +@interface PBLLInspectorController () +{ + UISearchBar *_searchBar; +} + +@end + +@implementation PBLLInspectorController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. + self.edgesForExtendedLayout = UIRectEdgeNone; + self.view.backgroundColor = [UIColor whiteColor]; + CGRect frame = self.view.bounds; + frame.size.height = 44.f; + frame.origin.y = 16.f; + UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:frame]; + searchBar.text = [self defaultIP]; + searchBar.delegate = self; + searchBar.returnKeyType = UIReturnKeyJoin; + [self.view addSubview:searchBar]; + _searchBar = searchBar; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)]; + [self.view addGestureRecognizer:tap]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [PBLLInspector sharedInspector].hidden = YES; +} + +- (void)viewDidDisappear:(BOOL)animated { + [super viewDidDisappear:animated]; + [PBLLInspector sharedInspector].hidden = NO; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (NSString *)defaultIP { + NSString *ip = [[NSUserDefaults standardUserDefaults] objectForKey:@"pbind.server.ip"]; + if (ip == nil) { + ip = @"192.168.1.10"; + } + return ip; +} + +- (void)setDefaultIP:(NSString *)ip { + [[NSUserDefaults standardUserDefaults] setObject:ip forKey:@"pbind.server.ip"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +#pragma mark - UISearchBarDelegate + +- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { + NSString *ip = searchBar.text; + if (ip.length == 0) { + return; + } + + [self setDefaultIP:ip]; + [[PBLLRemoteWatcher globalWatcher] connect:ip]; + [self.navigationController popViewControllerAnimated:YES]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [PBTopController().view pb_reloadClient]; + }); +} + +#pragma mark - Gesture + +- (void)didTap:(id)sender { + [_searchBar resignFirstResponder]; +} + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLRemoteWatcher.h b/Example/PBLiveLoader/PBLLRemoteWatcher.h new file mode 100644 index 0000000..79a68eb --- /dev/null +++ b/Example/PBLiveLoader/PBLLRemoteWatcher.h @@ -0,0 +1,41 @@ +// +// PBLLRemoteWatcher.h +// Pchat +// +// Created by Galen Lin on 15/03/2017. +// Copyright © 2017 galen. All rights reserved. +// + +#include + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import + +@class PBLLRemoteWatcher; + +@protocol PBLLRemoteWatcherDelegate + +- (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didUpdateFile:(NSString *)fileName withData:(NSData *)data; + +@optional + +- (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didCreateFile:(NSString *)fileName; +- (void)remoteWatcher:(PBLLRemoteWatcher *)watcher didDeleteFile:(NSString *)fileName; + +@end + +@interface PBLLRemoteWatcher : NSObject + ++ (instancetype)globalWatcher; + +- (void)connect:(NSString *)ip; +- (void)connectDefaultIP; + +- (void)requestAPI:(NSString *)api success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure; + +@property (nonatomic, assign) id delegate; + +@end + +#endif diff --git a/Example/PBLiveLoader/PBLLRemoteWatcher.m b/Example/PBLiveLoader/PBLLRemoteWatcher.m new file mode 100644 index 0000000..c76a0c3 --- /dev/null +++ b/Example/PBLiveLoader/PBLLRemoteWatcher.m @@ -0,0 +1,163 @@ +// +// PBLLRemoteWatcher.m +// Pbind +// +// Created by Galen Lin on 15/03/2017. +// + +#import "PBLLRemoteWatcher.h" + +#if (DEBUG && !(TARGET_IPHONE_SIMULATOR)) + +#import "NSInputStream+Reader.h" + +@interface PBLLRemoteWatcher () +{ + NSInputStream *inputStream; + NSOutputStream *outputStream; + void (^responseBlock)(NSData *); + BOOL streamOpened; + NSData *apiReqData; + NSString *tempResourcesPath; + NSString *connectedIP; +} + +@end + +@implementation PBLLRemoteWatcher + ++ (instancetype)globalWatcher { + static PBLLRemoteWatcher *o; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + o = [[PBLLRemoteWatcher alloc] init]; + }); + return o; +} + +- (void)connect:(NSString *)ip { + [self close]; + + connectedIP = ip; + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)ip, 8082, &readStream, &writeStream); + inputStream = (__bridge NSInputStream *)readStream; + outputStream = (__bridge NSOutputStream *)writeStream; + inputStream.delegate = self; + outputStream.delegate = self; + [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [inputStream open]; + [outputStream open]; +} + +- (void)connectDefaultIP { + NSString *ip = [[NSUserDefaults standardUserDefaults] objectForKey:@"pbind.server.ip"]; + if (ip == nil) { + ip = @"192.168.1.2"; + } + [self connect:ip]; +} + +- (void)requestAPI:(NSString *)api success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure { + if (inputStream == nil) { + failure([NSError errorWithDomain:@"PBLLRemoteWatcher" + code:1 + userInfo:@{NSLocalizedDescriptionKey: @"Watcher wasn't online!"}]); + return; + } + + responseBlock = success; + NSData *data = [api dataUsingEncoding:NSUTF8StringEncoding]; + if (streamOpened) { + [outputStream write:[data bytes] maxLength:[data length]]; + } else { + apiReqData = data; + } +} + +- (void)close { + if (inputStream != nil) { + [inputStream close]; + inputStream = nil; + } + if (outputStream != nil) { + [outputStream close]; + outputStream = nil; + } + streamOpened = NO; +} + +#pragma mark - NSStreamDelegate + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { + NSLog(@"socket: %i, %@", (int)eventCode, aStream); + switch (eventCode) { + case NSStreamEventOpenCompleted: + + break; + case NSStreamEventHasSpaceAvailable: + if (aStream == outputStream) { + streamOpened = YES; + if (apiReqData != nil) { + [outputStream write:[apiReqData bytes] maxLength:[apiReqData length]]; + apiReqData = nil; + } + } + break; + case NSStreamEventHasBytesAvailable: + if (aStream == inputStream) { + NSLog(@"inputStream is ready."); + + uint8_t type[1]; + [inputStream read:type maxLength:1]; + switch (*type) { + case 0xE0: { // Got json + NSData *jsonData = [inputStream readData]; + if (responseBlock != nil) { + responseBlock(jsonData); + } + break; + } + case 0xF0: { // Create file + NSString *fileName = [inputStream readString]; + [self.delegate remoteWatcher:self didCreateFile:fileName]; + break; + } + case 0xF1: { // Update file + NSString *fileName = [inputStream readString]; + NSData *fileData = [inputStream readData]; + [self.delegate remoteWatcher:self didUpdateFile:fileName withData:fileData]; + break; + } + case 0xF2: { // Delete file + NSString *fileName = [inputStream readString]; + [self.delegate remoteWatcher:self didDeleteFile:fileName]; + break; + } + default: + break; + } + } + break; + case NSStreamEventErrorOccurred: + if (aStream == outputStream) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self connect:connectedIP]; + }); + } + break; + case NSStreamEventEndEncountered: + if (aStream == inputStream) { + [self close]; + } + break; + default: + break; + } +} + +@end + +#endif diff --git a/Example/PBLiveLoader/PBSimulatorEnviroment.h b/Example/PBLiveLoader/PBSimulatorEnviroment.h new file mode 100644 index 0000000..53f275c --- /dev/null +++ b/Example/PBLiveLoader/PBSimulatorEnviroment.h @@ -0,0 +1,43 @@ +// +// PBSimulatorEnviroment.h +// Pbind +// +// Created by Galen Lin on 13/03/2017. +// + +#if (DEBUG) + +#include +#import + +#if (TARGET_IPHONE_SIMULATOR) + +FOUNDATION_STATIC_INLINE NSString *PBLLProjectPath() { + return [[@(__FILE__) stringByDeletingLastPathComponent] stringByDeletingLastPathComponent]; +} + +FOUNDATION_STATIC_INLINE NSString *PBLLMainBundlePath() { + NSString *projectPath = PBLLProjectPath(); + NSString *bundlePath = [projectPath stringByAppendingPathComponent:[projectPath lastPathComponent]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) { + return bundlePath; + } + + NSArray *subdirs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:projectPath error:nil]; + for (NSString *subdir in subdirs) { + bundlePath = [projectPath stringByAppendingPathComponent:subdir]; + NSString *mainFile = [bundlePath stringByAppendingPathComponent:@"main.m"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:mainFile]) { + return bundlePath; + } + } + return nil; +} + +FOUNDATION_STATIC_INLINE NSString *PBLLMockingAPIPath() { + return [PBLLProjectPath() stringByAppendingPathComponent:@"PBLocalhost"]; +} + +#endif + +#endif