Slide 1

Slide 1 text

iOS. 
 Advanced Programming Techniques.
 Vasyl Krychun


Slide 2

Slide 2 text

Agenda • Multithreading • Networking • Runtime • General tricks

Slide 3

Slide 3 text

Task #1 Application with responsive UI, silent data synchronization and offline mode support.

Slide 4

Slide 4 text

Multithreading Main thread Background thread Data Model View

Slide 5

Slide 5 text

Solution Main thread Background thread Data Model (Core Data) View Data Model (Core Data)

Slide 6

Slide 6 text

MVC

Slide 7

Slide 7 text

• NSThread+NSRunLoop • GCD (Grand Central Dispatch) • NSOperation+NSOperationQueue ! ! ! Approaches

Slide 8

Slide 8 text

Data Synchronization • NSManagedObjectContextDidSaveNotification

Slide 9

Slide 9 text

UI notification • NSFetchedResultsController

Slide 10

Slide 10 text

Task #2 Hybrid app with web UI. Interface between UI and native functionality.

Slide 11

Slide 11 text

Benefits

Slide 12

Slide 12 text

Native bridge
 (approach #1) Native: - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType; ! JS: window.location = http://something.com?func=¶ms='list of params (json)'&callback=jsFunc;

Slide 13

Slide 13 text

Native bridge
 (approach #2) • Custom NSURLProtocol

Slide 14

Slide 14 text

Native bridge
 (approach #3) • XMLHttpRequest extension with window.location

Slide 15

Slide 15 text

Task #3 Custom synchronous request implementation with delegation.

Slide 16

Slide 16 text

- (NSDictionary*) sendRequest: (NSURLRequest*) request { _connecion = [[NSURLConnection alloc] initWithRequest: request delegate:self startImmediately:NO]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [_connecion scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode]; [_connecion start]; do { [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } while (!_isDone && !_isCanceled); [_connecion cancel]; return _responseData; } Synchronous request

Slide 17

Slide 17 text

Task #4 Observing an object change

Slide 18

Slide 18 text

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"synchronized"]) { //DO something! } } KVO

Slide 19

Slide 19 text

+ (NSSet *)keyPathsForValuesAffectingSynchronized { unsigned int num_props; objc_property_t *prop_list = class_copyPropertyList(self, &num_props); NSMutableSet * propSet = [NSMutableSet set]; for( unsigned int i = 0; i < num_props; i++ ) { NSString * propName = [NSString stringWithFormat:@"%s", property_getName(prop_list[i])]; if(![propName isEqualToString:@"synchronized"] ) { [propSet addObject:propName]; } } free(prop_list); return propSet; } KVO

Slide 20

Slide 20 text

Tricks • Method swizzling • Network operations when app in background • Blocks as arguments • Device unique identifier !

Slide 21

Slide 21 text

Method swizzling Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(own_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(originalMethod, swizzledMethod);

Slide 22

Slide 22 text

App in background UIApplication *app = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier bgTask; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"background task has ended"); [app endBackgroundTask:bgTask]; }];

Slide 23

Slide 23 text

Blocks as arguments - (void) downloadFile: (NSURL*) url options: (NSDictionary*) options completionBlock:(void(^)(BOOL result)) completionBlock progressBlock:(void(^)(long long data_size, long long downloaded_data_size)) progressBlock; ! typedef void (^CompletionEvent) (BOOL result); typedef void (^ProgressEvent) (long long data_size, long long downloaded_data_size); ! - (void) downloadFile: (NSURL*) url options: (NSDictionary*) options completionBlock:(CompletionEvent) completionBlock progressBlock:(ProgressEvent) progressBlock;

Slide 24

Slide 24 text

Blocks as arguments - (void) downloadFile: (NSURL*) url options: (NSDictionary*) options completionBlock:(CompletionEvent) completionBlock progressBlock:(ProgressEvent) progressBlock { //downloading is completed completionBlock (YES); //downloading in progress progressBlock(123334,456); } ! [self downloadFile:url options:options completionBlock:^(BOOL result) { if (result) { //DO smth. } } progressBlock:^(long long data_size, long long downloaded_data_size) { //handle data_size and downloaded_data_size }];

Slide 25

Slide 25 text

Device unique Id + (NSString*) getUniqueDeviceIdentifier { KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"yourApplication" accessGroup:nil]; [wrapper setObject:(__bridge id)kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible]; NSString *uniqueIdentifier = [wrapper objectForKey:(__bridge id)kSecAttrAccount]; if (!uniqueIdentifier || [uniqueIdentifier length] == 0) { CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault); uniqueIdentifier = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, newUniqueId); CFRelease(newUniqueId); [wrapper setObject:uniqueIdentifier forKey:(__bridge id)kSecAttrAccount]; } return uniqueIdentifier; }

Slide 26

Slide 26 text

Questions