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

Taking a peek at ReactiveCocoa 3.0

Taking a peek at ReactiveCocoa 3.0

Cocoaheads Montréal - 14/07/2015

Romain Pouclet

July 14, 2015
Tweet

More Decks by Romain Pouclet

Other Decks in Programming

Transcript

  1. My twitter avatar shows me casually playing guitar. I got

    nice comments when I also used it on Facebook.
  2. I started taking lessons a few months ago, I have

    a great teacher but to be honest, I'm still not very good at it. !
  3. Me, Myself and ReactiveCocoa • I have been using it

    for a couple of years, on and off • I'm no expert, but I love the tech, the idea and the community • Ash Furrow said it was okay
  4. ReactiveCocoa, a history • OpenSourced by the fancy people at

    Github in 2012 • Used in Github for Mac, for example • A new version written in swift released (as a beta) a few month ago • (The history is not that important)
  5. In ReactiveCocoa, you play with Signals - (RACSignal *)callWebservice {

    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSURLSessionDataTask *task = [self.session dataTaskWithURL: url completionHandler:^(...) { // ... }]; [task resume]; return [RACDisposable disposableWithBlock:^{ [task cancel]; }]; }]; }
  6. In ReactiveCocoa, you play with Signals NSArray *payload = [NSJSONSerialization

    JSONObjectWithData: data options: 0 error: nil]; // Some error handling would be nice ! [payload enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) { [subscriber sendNext: obj]; }]; [subscriber sendCompleted];
  7. In ReactiveCocoa, you play with Signals [[client callWebservice] map:^(NSDictionary *payload)

    { return [[FancyObject alloc] initWithDictionary: payload]; }];
  8. But things can rapidly get out of hands [[[[client logInUser]

    flattenMap:^(User *user) { return [client loadCachedMessagesForUser:user]; }] flattenMap:^(NSArray *messages) { return [client fetchMessagesAfterMessage:messages.lastObject]; }] subscribeNext:^(NSArray *newMessages) { NSLog(@"New messages: %@", newMessages); } completed:^{ NSLog(@"Fetched all messages."); }];
  9. ReactiveCocoa and Swift • Rewrote from scratch to benefit from

    Swift awesomeness • Strong typing everywhere • Still in beta, use with caution
  10. Getting started with ReactiveCocoa 3.0 • Drop the framework available

    on Github directly in your project • Use Cocoapods • Use Carthage, which uses ReactiveCocoa internally :mindblown.gif:
  11. Simple example func tick() -> Signal<String, NoError> { var count

    = 0 return Signal { sink in NSTimer.schedule(repeatInterval: 1.0) { timer in sendNext(sink, "tick #\(count++)") } return nil } }
  12. Simple example func fetchCharacterNames() -> SignalProducer<String, NoError> { return SignalProducer

    { observable, disposable in let names = ["Buffy", "Angel", "Spike", "Xander"] for (_, name) in enumerate(names) { sendNext(observable, name) } sendCompleted(observable) } }
  13. WRONG! ReactiveCocoa 3.0 is a lot more "functional" and map

    is actually a function that returns a new signal public func map<T, U, E>(transform: T -> U) -> Signal<T, E> -> Signal<U, E> { // ... }
  14. If this code makes you feel stupid, join the club

    and we'll all get matching tatoos
  15. Transforming values: using map let charactersSignal = createSignalProducer() let mappedSignal

    = map(charactersSignal, { Character(name: $0) }) mappedSignal.start(...)
  16. Invoking the power of the |> operator createSignalProducer() |> map({

    Character(name: $0) }) • Coming from the F# language • Makes your code pretty • Confuses Xcode like nothing before
  17. More complex (and relevant) example: the Photos framework • Working

    on an application that uses the Photos framework • Retrieving the latest photos = callbacksoup
  18. More complex example: the Photos framework 1. Iterating over a

    list of albums 2. For each of these albums, request the assets 3. For each asset, request a UIImage (asynchronously)
  19. Step 1: Fetching albums func fetchCollections() -> SignalProducer<PHAssetCollection, NoError> {

    return SignalProducer { observer, disposable in let fetchOptions = PHFetchOptions() let collections = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype: .Any, options: fetchOptions) for index in 0..<collections.count { let collection = collections.objectAtIndex(index) as! PHAssetCollection sendNext(observer, collection) } sendCompleted(observer) } }
  20. Step 2: Fetching assets in specific collection func fetchAssets(inCollection collection:

    PHAssetCollection) -> SignalProducer<PHAsset, NoError> { return SignalProducer { observer, disposable in // We only want the images let assetsOptions = PHFetchOptions() assetsOptions.predicate = NSPredicate(format: “mediaType = %i”, PHAssetMediaType.Image.rawValue) let assets = PHAsset.fetchAssetsInAssetCollection(collection, options: assetsOptions) for assetIndex in 0..<assets.count { sendNext(observer, assets.objectAtIndex(assetIndex) as! PHAsset) } sendCompleted(observer) } }
  21. Step 3: Fetching thumbnails func fetchImage(forAsset asset: PHAsset) -> SignalProducer<UIImage,

    NoError> { return SignalProducer { observer, disposable in let size = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: size, contentMode: .AspectFit, options: PHImageRequestOptions(), resultHandler: { (image, infos) -> Void in sendNext(observer, image) sendCompleted(observer) }) } }
  22. Step 4: Merging all the things func fetchLatestScreenshot() -> SignalProducer<UIImage,

    NoError> { return fetchCollections() |> flatMap(.Merge, { collection in return fetchAssets(inCollection: collection) |> flatMap(.Latest, { asset in return fetchImage(forAsset: asset) }) }) }
  23. flatMap is a common concept in functional programing • Apply

    a function that returns a sequence for each element in the "pipe" • Here it applies a function to every collection and returns a sequence of assets
  24. Bonus: Filtering assets // ... fetchAssets(inCollection: collection) |> filter({ asset

    in let size = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) return find(resolutions, size) != nil }) // ...
  25. Conclusion • ReactiveCocoa is a great tool, even if it

    sounds confusing your app can benefit from it • Xcode will be confused a lot, it makes the development suspensefull • There is a lot I haven't covered (observing properties...) • Questions?
  26. Thanks! 1. Picture by the awesome @Misszonzonnette 2. Simple tick

    example Shamelessly borrowed from http:// blog.scottlogic.com 3. We're hiring at Mirego!