Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Objective-C on Lambdas

Objective-C on Lambdas

Intro to functional & reactive programming with some ReactiveCocoa for Objective-C (revision 3, Feb 27 2014 specially for CocoaHeads Krakow)

Video (in Russian): http://vimeo.com/79623400

Volodymyr Kyrylov

October 24, 2013
Tweet

More Decks by Volodymyr Kyrylov

Other Decks in Programming

Transcript

  1. -[NSBitmapImageRep initWithBitmapDataPlanes:(unsigned char **)planes pixelsWide:(NSInteger)width pixelsHigh:(NSInteger)height bitsPerSample:(NSInteger)bps samplesPerPixel:(NSInteger)spp hasAlpha:(BOOL)alpha isPlanar:(BOOL)isPlanar

    colorSpaceName:(NSString *)colorSpaceName bitmapFormat:(NSBitmapFormat)bitmapFormat bytesPerRow:(NSInteger)rowBytes bitsPerPixel:(NSInteger)pixelBits]; AppKit
  2. NSMutableArray *result = [NSMutableArray arrayWithCapacity:mutableOperations.count]; [mutableOperations enumerateObjectsUsingBlock:^(AFHTTPRequestOperation *op, NSUInteger idx,

    BOOL *stop) { [result addObject:change_op(op)]; }]; return result; state change state return state! also, way too much code. Check AFIncrementalStore
  3. 68: if (isDataRetrieved == NO) { 86: if(line.Id == tripId){

    110: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] > 0) 120: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] > 0) 133: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] == 0) 163: if (aTrip.Id == pushedTrip.Id) { 189: if (line.Id == aTrip.Id) { 197: if (isFound == NO) { 200: if (line.Id == aTrip.Id) { 214: if (isFound == YES) { +
  4. 68: if (isDataRetrieved == NO) { 86: if(line.Id == tripId){

    110: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] > 0) 120: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] > 0) 133: if([[Config ArrPsgWaitingTrips] count]+[[Config ArrPsgPastTrips] count] == 0) 163: if (aTrip.Id == pushedTrip.Id) { 189: if (line.Id == aTrip.Id) { 197: if (isFound == NO) { 200: if (line.Id == aTrip.Id) { 214: if (isFound == YES) { + state? state? state? state? state? state?
  5. > let numbers = [1,2,3,4,5] > map (+1) numbers [2,3,4,5,6]

    > filter even numbers [2,4] > foldl1 (+) numbers 15 > sum numbers 15 > zip numbers numbers [(1,1),(2,2),(3,3),(4,4),(5,5)]
  6. NSArray *newMaps = [maps map:^id(CXMap *map) { return [map changeSomehow];

    }]; http://underscorem.org/ https://github.com/mogeneration/functionalkit and more!
  7. NSArray *newMaps = [maps map:^id(CXMap *map) { return [map changeSomehow];

    }]; entirely new array! mapping function (HOF) returns a new object
  8. > let numbers = [1,2,3,4,5] > map (+1) numbers [2,3,4,5,6]

    > filter even numbers [2,4] > foldl1 (+) numbers 15 > sum numbers 15
  9. > let numbers = [1,2,3,4,5] > map (+1) numbers [2,3,4,5,6]

    > filter even numbers [2,4] > foldl1 (+) numbers 15 > sum numbers 15 ! ! > foldl1 (+) . filter even . map (+1) $ numbers 12
  10. > let numbers = [1,2,3,4,5] > map (+1) numbers [2,3,4,5,6]

    > filter even numbers [2,4] > foldl1 (+) numbers 15 > sum numbers 15 ! ! > foldl1 (+) . filter even . map (+1) $ numbers 12 composition ^ composition ^
  11. > let f = foldl1 (+) . filter even .

    map (+1) > :type f f :: [Integer] -> Integer > f numbers 12 int f(int array[])
  12. > let f = foldl1 (+) . filter even .

    map (+1) > :type f f :: [Integer] -> Integer > f numbers 12 int f(int array[]) NSInvocation a block
  13. [client logInWithSuccess:^{ [client loadCachedMessagesWithSuccess:^(NSArray *messages) { [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages)

    { NSLog(@"Fetched all messages."); } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }];
  14. -> * login * load data from cache * load

    data from network * send stats
  15. Qs! actors (queues associated with an execution context) ! -performSelector:

    https://code.google.com/p/plankton-platform/ https://code.google.com/p/plactorkit/
  16. [client logInWithSuccess:^{ [client loadCachedMessagesWithSuccess:^(NSArray *messages) { [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages)

    { NSLog(@"Fetched all messages."); } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }];
  17. __block BOOL stop = NO; __block NSError *topError = nil;

    CXParse *client = [CXParse clientWithApplicationId:@"my application” RESTKey:@"yeah right"]; RACSignal *login = [client login:@"johndoe" password:@"password"]; RACSignal *maps = [client GETClass:@"Map" parameters:nil]; [[[login concat:maps] takeLast:1].logAll subscribeNext:^(NSArray *maps) { NSLog(@"maps: %@", maps); } error:^(NSError *error) { topError = error; stop = YES; } completed:^{ stop = YES; }]; while (!stop && [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]) ;
  18. __block BOOL stop = NO; __block NSError *topError = nil;

    CXParse *client = [CXParse clientWithApplicationId:@"my application" RESTKey:@"yeah right"]; RACSignal *login = [client login:@“johndoe" password:@"password"]; RACSignal *maps = [client GETClass:@“Map" parameters:nil]; [[[login concat:maps] takeLast:1].logAll subscribeNext:^(NSArray *maps) { NSLog(@"maps: %@", maps); } error:^(NSError *error) { topError = error; stop = YES; } completed:^{ stop = YES; }]; while (!stop && [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]) ;
  19. __block BOOL stop = NO; __block NSError *topError = nil;

    CXParse *client = [CXParse clientWithApplicationId:@"my application" RESTKey:@"yeah right"]; RACSignal *login = [client login:@"johndoe" password:@"password"]; RACSignal *maps = [client GETClass:@"Map" parameters:nil]; [[[login concat:maps] takeLast:1].logAll subscribeNext:^(NSArray *maps) { NSLog(@"maps: %@", maps); } error:^(NSError *error) { topError = error; stop = YES; } completed:^{ stop = YES; }]; while (!stop && [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]) ;
  20. __block BOOL stop = NO; __block NSError *topError = nil;

    CXParse *client = [CXParse clientWithApplicationId:@"my application" RESTKey:@"yeah right"]; RACSignal *login = [client login:@"johndoe" password:@"password"]; RACSignal *maps = [client GETClass:@"Map" parameters:nil]; [[[login concat:maps] takeLast:1].logAll subscribeNext:^(NSArray *maps) { NSLog(@"maps: %@", maps); } error:^(NSError *error) { topError = error; stop = YES; } completed:^{ stop = YES; }]; while (!stop && [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]) ;
  21. NSArray *otherUsers; return [[RACSignal concat:[otherUsers.rac_sequence map: ^RACSignal *(id user) {

    return [[client GETUser:user] catchTo:RACSignal.empty]; }]] collect];
  22. [[[login flattenMap:^RACStream *(NSDictionary *login) { return [client GETRelatedTo:login className:@“_User" byKey:@"maps"

    parameters:nil]; }] flattenMap:^RACStream *(NSDictionary *mapsResult1) { return [client GETObjectsRelatedToCurrentUserByKey:@“maps" parameters:nil]; }] subscribeNext:^(NSDictionary *mapsResult2) { NSLog(@"maps for user: %@", @(mapsResult2.count)); } error:^(NSError *error) { topError = error; stop = YES; } completed:^{ stop = YES; }];
  23. RACTupleUnpack(NSArray *implicit, NSArray *explicit) = [results.rac_sequence foldLeftWithStart:RACTuplePack(NSMutableArray.array, NSMutableArray.array) reduce: ^id(RACTuple

    *tuple, NSDictionary *map) { NSMutableArray *receiver = [map[@"ACL"] objectForKey:acl] == nil ? tuple.first : tuple.second; [receiver addObject:map]; return tuple; }];
  24. RACSignal *fetch = /* ... */; ! RACSignal *save =

    [NSNotificationCenter.defaultCenter rac_addObserverForName: NSManagedObjectContextDidSaveNotification object:CXAppDelegate.shared.managedObjectContext]; RACSignal *storeFetch = [NSNotificationCenter.defaultCenter rac_addObserverForName: AFIncrementalStoreContextDidFetchRemoteValues object:nil]; RACSignal *storeSave = [NSNotificationCenter.defaultCenter rac_addObserverForName: AFIncrementalStoreContextDidSaveRemoteValues object:nil]; RACSignal *anyUpdate = [[RACSignal merge:@[save, storeFetch, storeSave]] take:1]; ! RACSignal *rest = [anyUpdate then:^RACSignal *{ return fetch; }]; return [rest repeat];
  25. self.btLogin.enabled = NO; RAC(self.btLogin.enabled) = [RACSignal combineLatest:@[ RACObserve(self, tfLogin.text), RACObserve(self,

    tfPassword.text) ] reduce:^(NSString *login, NSString *password) { return @(login.length > 0 && password.length > 0); }];
  26. - (RACSignal *)thumbnail { NSURL *url = self.thumbnailURL; if ([NSFileManager.defaultManager

    fileExistsAtPath:url.path] == NO) { return [[SomeViewController drawMap:self] flattenMap:^RACStream *(UIImage *m) { [UIImagePNGRepresentation(m) writeToURL:url options:NSDataWritingAtomic error:nil]; return [RACSignal return:m]; }]; } else { return [RACSignal return:[UIImage imageWithContentsOfFile:url.path]]; } }
  27. [self.thumbnailCancelation sendError:[NSError errorWithDomain:NSPOSIXErrorDomain code:EINTR userInfo:nil]]; self.thumbnailCancelation = RACSubject.subject; ! [[RACSignal

    merge:@[[self.map thumbnail], self.thumbnailCancelation]] subscribeNext:^(UIImage *image) { self.thumbnailView.image = image; self.thumbnailView.contentMode = UIViewContentModeScaleAspectFit; self.thumbnailCancelation = nil; } error:^(NSError *error) { if (error.domain != NSPOSIXErrorDomain && error.code != EINTR) { NSLog(@"thumb error: %@ (map: %@)", error, self.map); } }];
  28. RACSignal *wait = [[[RACObserve(self, p12) doNext:^(id x) { NSLog(@"new p12!

    %@", self.p12); }] filter:^BOOL(id value) { return value != NSNull.null && value != nil; }] take:1]; ! RACSignal *access = [rq accessRequest]; access = [access doNext:^(id response) { NSLog(@"access: %@", response); }]; access = [access doError:^(NSError *error) { NSLog(@"error: %@", error); }]; ! access = [access catchTo:[RACSignal empty]]; access = [access delay:2].repeat; ! RACSignal *op = [wait then:^RACSignal *{ return access; }]; ! [op subscribeNext:^(id x) { NSLog(@"next %@", x); } error:^(NSError *error) { NSLog(@"err: %@", error); } completed:^{ NSLog(@“completed"); }];
  29. return [[[self requestAuth] flattenMap:^RACStream *(NSDictionary *args) { return [[self startTunnel:args]

    flattenMap:^RACStream *(NMSSHSession *session) { return [[self startPortForwarding:args session:session] flattenMap:^RACStream *(NSValue *channelPointer) { LIBSSH2_CHANNEL *channel = [channelPointer pointerValue]; ! dispatch_async(self.ssh_queue, ^{ libssh2_session_set_blocking(session.session, 0); rdps_proxyloop(session.sock, channel); }); return [self rac_sessionControllerWithArgs:args]; }]; }]; }] subscribeOn:self.ssh_scheduler];
  30. Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSData+RACSupport.h 20:+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler; ! Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSFileHandle+RACSupport.h

    17:- (RACSignal *)rac_readInBackground; ! Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSNotificationCenter+RACSupport.h 16:- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object; ! Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSupport.h 20:+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler; ! Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSURLConnection+RACSupport.h 23:+ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request; ! Pods/ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h 55:- (RACSignal *)rac_signalForSelector:(SEL)selector; 77:- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol; & many more
  31. __block dispatch_group_t group = dispatch_group_create(); ! NSArray *operationsArray = /*

    ... */; [operationsArray enumerateObjectsUsingBlock:^(AFHTTPRequestOperation *op, NSUInteger idx, BOOL *stop) { dispatch_group_enter(group); }]; ! dispatch_group_notify(group, dispatch_get_main_queue(), ^{ [self notifyManagedObjectContext:context aboutRequestOperations:[NSArray arrayWithArray:mutableOperations] forSaveChangesRequest:saveChangesRequestCopy]; }); https://github.com/proger/AFIncrementalStore/blob/a38c0566f96525586e64639144a315f5ea0848a2/ AFIncrementalStore/AFIncrementalStore.m#L500
  32. # mv -v /Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/system/ libdispatch.dylib{,.backup} ! # cp -v

    /Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/system/ {introspection/,}libdispatch.dylib ! # dtrace -p $(pgrep CXParseApp) -s ~/dev/darwinkit/sys/dispatch_trace.d https://github.com/proger/darwinkit/blob/master/sys/dispatch_trace.d