Slide 1

Slide 1 text

Taking a peak at ReactiveCocoa 3.0 Cocoaheads Montreal 14/07/2015

Slide 2

Slide 2 text

My twitter avatar shows me casually playing guitar. I got nice comments when I also used it on Facebook.

Slide 3

Slide 3 text

I was actually warming up with a super-simple exercice. (Don't tell my mom, she was super-proud)

Slide 4

Slide 4 text

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. !

Slide 5

Slide 5 text

This is pretty much the same with ReactiveCocoa.

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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)

Slide 8

Slide 8 text

What is ReactiveCocoa ?

Slide 9

Slide 9 text

ReactiveCocoa is a framework used for composing and transforming sequences of values

Slide 10

Slide 10 text

No seriously, what is it?

Slide 11

Slide 11 text

I like to see ReactiveCocoa as a tool to send stuff through pipes

Slide 12

Slide 12 text

Value → Different Value

Slide 13

Slide 13 text

Sequence of Values → Different Values

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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];

Slide 16

Slide 16 text

In ReactiveCocoa, you play with Signals [[client callWebservice] map:^(NSDictionary *payload) { return [[FancyObject alloc] initWithDictionary: payload]; }];

Slide 17

Slide 17 text

ReactiveCocoa 2.0 fitted really well with Objective-C

Slide 18

Slide 18 text

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."); }];

Slide 19

Slide 19 text

[[[[[[[[[[[ !!!!!!!!

Slide 20

Slide 20 text

ReactiveCocoa and Swift • Rewrote from scratch to benefit from Swift awesomeness • Strong typing everywhere • Still in beta, use with caution

Slide 21

Slide 21 text

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:

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Simple example: observing the signal let signal = tick() signal.observe(next: { println($0) })

Slide 24

Slide 24 text

Simple example func fetchCharacterNames() -> SignalProducer { return SignalProducer { observable, disposable in let names = ["Buffy", "Angel", "Spike", "Xander"] for (_, name) in enumerate(names) { sendNext(observable, name) } sendCompleted(observable) } }

Slide 25

Slide 25 text

Simple example: observing the signal let signal = fetchCharacterNames() signal.observe(next: { println($0) })

Slide 26

Slide 26 text

Transforming values: what you'd expect createSignalProducer().map({ name in return Character(name: name) })

Slide 27

Slide 27 text

WRONG! ReactiveCocoa 3.0 is a lot more "functional" and map is actually a function that returns a new signal public func map(transform: T -> U) -> Signal -> Signal { // ... }

Slide 28

Slide 28 text

If this code makes you feel stupid, join the club and we'll all get matching tatoos

Slide 29

Slide 29 text

Transforming values: using map let charactersSignal = createSignalProducer() let mappedSignal = map(charactersSignal, { Character(name: $0) }) mappedSignal.start(...)

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

More complex (and relevant) example: the Photos framework • Working on an application that uses the Photos framework • Retrieving the latest photos = callbacksoup

Slide 32

Slide 32 text

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)

Slide 33

Slide 33 text

Step 1: Fetching albums func fetchCollections() -> SignalProducer { return SignalProducer { observer, disposable in let fetchOptions = PHFetchOptions() let collections = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype: .Any, options: fetchOptions) for index in 0..

Slide 34

Slide 34 text

Step 1: Fetching collections fetchCollections().start(next: { collection in println(“Collection = \(collection.localizedTitle)”) })

Slide 35

Slide 35 text

Step 2: Fetching assets in specific collection func fetchAssets(inCollection collection: PHAssetCollection) -> SignalProducer { 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..

Slide 36

Slide 36 text

Step 3: Fetching thumbnails func fetchImage(forAsset asset: PHAsset) -> SignalProducer { 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) }) } }

Slide 37

Slide 37 text

Step 4: Merging all the things func fetchLatestScreenshot() -> SignalProducer { return fetchCollections() |> flatMap(.Merge, { collection in return fetchAssets(inCollection: collection) |> flatMap(.Latest, { asset in return fetchImage(forAsset: asset) }) }) }

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Bonus: Filtering assets // ... fetchAssets(inCollection: collection) |> filter({ asset in let size = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) return find(resolutions, size) != nil }) // ...

Slide 40

Slide 40 text

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?

Slide 41

Slide 41 text

Thanks! 1. Picture by the awesome @Misszonzonnette 2. Simple tick example Shamelessly borrowed from http:// blog.scottlogic.com 3. We're hiring at Mirego!