SlideShare a Scribd company logo
Introduction to Reactive
Programming with RxSwift
Xinran Wang
Software Engineer
Digital Home, Comcast NBCUniversal
What is Reactive Programming?
Programming with asynchronous data streams
Treat events as sequences (streams) of data
Improve API client retries and error handling
Simplify app auth management
someAsyncFunction {
anotherAsyncFunction1 {
anotherAsyncFunction2 {
anotherAsyncFunction3 {
anotherAsyncFunction4 {
// Thank God this has no error handling 😰
}
}
}
}
}
asyncObservable()
.flatMap { _ in return asyncObservable2() }
.flatMap { _ in return asyncObservable3() }
.flatMap { _ in return asyncObservable4() }
.subscribe(onNext: { data in
// handle last result
}, onError{ error in
// handle errors
})
Main Terminology
(the boring stuff)
Streams of data
(think Swift sequences + async)
Observable
Event
An element/value of the data stream
onNext:
onError:
onComplete:
Observer/Subscriber
Subscribes to the … Observable
Handles Events (the data stream elements)
Stream starts on“subscription”
Disposable
Disconnects the data stream
Ends Event ingestion
Handles cleanup of resources
Main Terminology
- Observable
- Event
- Subscriber
- Disposable
Icons made by https://www.flaticon.com/authors/smashicons from www.flaticon.com
Main Terminology
- Observable
- Event
- Subscriber
- Disposable
Main Terminology
- Observable
- Event
- Subscriber
- Disposable
Main Terminology
- Observable
- Event
- Subscriber
- Disposable
Ok, now the fun stuff:
what do I do with these data streams?
Rx Operators!
(the fun stuff)
// Creating:
create, from, just …
// Transforming
map, flatMap, buffer …
// Filtering
filter, first, skip, debounce …
// Combining
merge, zip, combineLatest …
// Error Handling
catch, retry
// Utility
subscribe, delay, do …
// Conditional/Boolean
contains, all, skipUntil …
// Mathematical and Aggregation
count, max, min, reduce …
Creating!
// Creating:
create, from, just …
// Transforming
map, flatMap, buffer …
// Filtering
filter, first, skip, debounce …
// Combining
merge, zip, combineLatest …
// Error Handling
catch, retry
// Utility
subscribe, delay, do …
// Conditional/Boolean
contains, all, skipUntil …
// Mathematical and Aggregation
count, max, min, reduce …
Observable.create { observer in
asyncFunction(completion: {
// create the event for each piece of data
// observer.onNext()
// observer.onError()
// observer.onComplete()
})
return Disposables.create()
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushi(completion: @escaping (Sushi?, Error?)->Void)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
makeSushiObservable()
// observable stream STARTS on subscription
// so makeSushi actually first gets called here
.subscribe(onNext: { sushi in
// eat the sushi!
}, onError: { error in
// complain to the waiter
})
.disposed(by: disposeBag)
func makeSushiObservable() -> Observable<Sushi?> {
return Observable.create { observer in
self.makeSushi { (sushi, error) in
if let error = error {
observer.onError(error)
}
observer.onNext(sushi)
observer.onCompleted()
}
return Disposables.create()
}
}
makeSushiObservable()
// observable stream STARTS on subscription
// so makeSushi actually first gets called here
.subscribe(onNext: { sushi in
// eat the sushi!
}, onError: { error in
// complain to the waiter
})
.disposed(by: disposeBag)
More Operators!
// Creating:
create, from, just …
// Transforming
map, flatMap, buffer …
// Filtering
filter, first, skip, debounce …
// Combining
merge, zip, combineLatest …
// Error Handling
catch, retry
// Utility
subscribe, delay, do …
// Conditional/Boolean
contains, all, skipUntil …
// Mathematical and Aggregation
count, max, min, reduce …
let sushi = Observable.zip(makeRice(), sliceFish(tuna)) { (rice, tuna) -> Sushi in
return Sushi(rice, tuna)
}
sushi.filter { sushi in
return !sushi.isVegetarian()
}
.map { (sushi: Sushi) -> Sushi in
return addSoySauce(sushi)
}
.map { addWasabi }
let sushi = Observable.zip(makeRice(), sliceFish(tuna)) { (rice, tuna) -> Sushi in
return Sushi(rice, tuna)
}
sushi.filter { sushi in
return !sushi.isVegetarian()
}
.map { (sushi: Sushi) -> Sushi in
return addSoySauce(sushi)
}
.map { addWasabi }
Cool …
Why and how would I actually use
this for building my iOS app?
Networking
Unified Error Handling & Better Retries
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
.retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
return rxResponse(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
APIClient.responseObservable(request: req)
.subscribe(onNext: { data in
// handle data
})
private static func handleResponse(
data: Data?, response: URLResponse?
) throws -> Observable<[String: Any]> {
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.invalidResponse(response)
}
guard 200..<300 ~= httpResponse.statusCode else {
throw APIError.badStatusCode(httpResponse)
}
guard let responseData = data else {
throw APIError.badData(data, httpResponse)
}
if let json = try JSONSerialization
.jsonObject(with: responseData, options: []) as? [String: Any] {
return Observable.just(json)
} else {
throw APIError.jsonParsingError
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
private static func handleResponse(
data: Data?, response: URLResponse?
) throws -> Observable<[String: Any]> {
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.invalidResponse(response)
}
guard 200..<300 ~= httpResponse.statusCode else {
throw APIError.badStatusCode(httpResponse)
}
guard let responseData = data else {
throw APIError.badData(data, httpResponse)
}
if let json = try JSONSerialization
.jsonObject(with: responseData, options: []) as? [String: Any] {
return Observable.just(json)
} else {
throw APIError.jsonParsingError
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
private static func handleErrors(errors: Observable<Error>)
-> Observable<Void> {
return errors.enumerated()
.flatMap { (i, error) -> Observable<Void> in
// handle max number of retries based on `i`
guard i < 3 else {
return Observable.error(error) // propagate error
}
switch error {
case .expiredToken:
self.refreshToken()
return Observable.just(()) // <- forces retry
default:
return Observable.just(()) // <- forces retry
}
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
private static func handleErrors(errors: Observable<Error>)
-> Observable<Void> {
return errors.enumerated()
.flatMap { (i, error) -> Observable<Void> in
// handle max number of retries based on `i`
guard i < 3 else {
return Observable.error(error) // propagate error
}
switch error {
case .expiredToken:
self.refreshToken()
return Observable.just(()) // <- forces retry
default:
return Observable.just(()) // <- forces retry
}
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
private static func handleErrors(errors: Observable<Error>)
-> Observable<Void> {
return errors.enumerated()
.flatMap { (i, error) -> Observable<Void> in
// handle max number of retries based on `i`
guard i < 3 else {
return Observable.error(error) // propagate error
}
switch error {
case .expiredToken:
self.refreshToken()
return Observable.just(()) // <- forces retry
default:
return Observable.just(()) // <- forces retry
}
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
private static func handleErrors(errors: Observable<Error>)
-> Observable<Void> {
return errors.enumerated()
.flatMap { (i, error) -> Observable<Void> in
// handle max number of retries based on `i`
guard i < 3 else {
return Observable.error(error) // propagate error
}
switch error {
case .expiredToken:
self.refreshToken()
return Observable.just(()) // <- forces retry
default:
return Observable.just(()) // <- forces retry
}
}
}
class APIClient {
let apiClientDisposeBag = DisposeBag()
static func responseObservable(
request: URLRequest
) -> Observable<[String: Any]> {
return customRequest(request)
.flatMap { handleResponse }
// .retry(3)
.retryWhen { handleErrors }
}
}
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
...
}
static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) {
responseObservable(request: request)
.subscribe(onNext: { data in
completion(data)
})
.disposed(by: apiClientDisposeBag)
}
}
APIClient.response(request: req) { json in
// do stuff with json
}
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
...
}
static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) {
responseObservable(request: request)
.subscribe(onNext: { data in
completion(data)
})
.disposed(by: apiClientDisposeBag)
}
}
APIClient.response(request: req) { json in
// do stuff with json
}
class APIClient {
static func responseObservable(request: URLRequest)
-> Observable<[String: Any]> {
...
}
static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) {
responseObservable(request: request)
.subscribe(onNext: { data in
completion(data)
})
.disposed(by: apiClientDisposeBag)
}
}
APIClient.response(request: req) { json in
// do stuff with json
}
Final Thoughts
Final Thoughts
1. 👎 Avoid nesting subscriptions
APIClient.fetchMovie(id).subscribe(onNext: { movie in
APIClient.fetchMovieDetails(movie).subscribe(onNext: { details in
...
})
.addDisposableTo(disposeBag)
})
.addDisposableTo(disposeBag)
APIClient.fetchMovie(id)
.flatMap { id in
return APIClient.fetchMovieDetails(movie)
}
.subscribe(onNext: { details in
...
})
.addDisposableTo(disposeBag)
APIClient.fetchMovie(id).subscribe(onNext: { movie in
APIClient.fetchMovieDetails(movie).subscribe(onNext: { details in
...
})
.addDisposableTo(disposeBag)
})
.addDisposableTo(disposeBag)
APIClient.fetchMovie(id)
.flatMap { id in
return APIClient.fetchMovieDetails(movie)
}
.subscribe(onNext: { details in
...
})
.addDisposableTo(disposeBag)
Final Thoughts
2. Makes networking cleaner
Final Thoughts
3. Rx forces your code to be more functional
Final Thoughts
4. Can go from callbacks to observable
The whole project does not need to be observable
Useful Resources
General:
- http://reactivex.io/documentation/observable.html
- http://rxmarbles.com/
RxSwift-specific:
- https://github.com/ReactiveX/RxSwift
- https://egghead.io/courses/introduction-to-reactive-programming
- https://www.raywenderlich.com/138547/getting-started-with-rxswift-and-rxcocoa
- https://www.raywenderlich.com/158026/introducing-rxswift-reactive-programming-swift
Thanks!
www.xinran-wang.com
www.linkedin.com/in/xinranw
www.github.com/xinranw
@xw92
Ad

Recommended

Containers & Dependency in Ember.js
Containers & Dependency in Ember.js
Matthew Beale
 
Swift & ReactiveX – Asynchronous Event-Based Funsies with RxSwift
Swift & ReactiveX – Asynchronous Event-Based Funsies with RxSwift
Aaron Douglas
 
Ember and containers
Ember and containers
Matthew Beale
 
Why Redux-Observable?
Why Redux-Observable?
Anna Su
 
Everything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View Controllers
Brian Gesiak
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testing
Visual Engineering
 
You will learn RxJS in 2017
You will learn RxJS in 2017
名辰 洪
 
Callbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascript
Łukasz Kużyński
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
State management in a GraphQL era
State management in a GraphQL era
kristijanmkd
 
Asynchronous and event-driven Grails applications
Asynchronous and event-driven Grails applications
Alvaro Sanchez-Mariscal
 
Testing view controllers with Quick and Nimble
Testing view controllers with Quick and Nimble
Marcio Klepacz
 
Quick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing Dependency
Matthew Beale
 
Complex Architectures in Ember
Complex Architectures in Ember
Matthew Beale
 
Angular promises and http
Angular promises and http
Alexe Bogdan
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
Leonardo Borges
 
Angular server-side communication
Angular server-side communication
Alexe Bogdan
 
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Codemotion
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
JavaScript Promise
JavaScript Promise
Joseph Chiang
 
ESCMAScript 6: Get Ready For The Future. Now
ESCMAScript 6: Get Ready For The Future. Now
Krzysztof Szafranek
 
Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
Visual Engineering
 
Functional Reactive Programming in Clojurescript
Functional Reactive Programming in Clojurescript
Leonardo Borges
 
The Strange World of Javascript and all its little Asynchronous Beasts
The Strange World of Javascript and all its little Asynchronous Beasts
Federico Galassi
 
Reactive, component 그리고 angular2
Reactive, component 그리고 angular2
Jeado Ko
 
The Open Web and what it means
The Open Web and what it means
Robert Nyman
 
Reactive programming with RxSwift
Reactive programming with RxSwift
Scott Gardner
 
Rx for Android & iOS by Harin Trivedi
Rx for Android & iOS by Harin Trivedi
harintrivedi
 

More Related Content

What's hot (20)

Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
State management in a GraphQL era
State management in a GraphQL era
kristijanmkd
 
Asynchronous and event-driven Grails applications
Asynchronous and event-driven Grails applications
Alvaro Sanchez-Mariscal
 
Testing view controllers with Quick and Nimble
Testing view controllers with Quick and Nimble
Marcio Klepacz
 
Quick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing Dependency
Matthew Beale
 
Complex Architectures in Ember
Complex Architectures in Ember
Matthew Beale
 
Angular promises and http
Angular promises and http
Alexe Bogdan
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
Leonardo Borges
 
Angular server-side communication
Angular server-side communication
Alexe Bogdan
 
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Codemotion
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
JavaScript Promise
JavaScript Promise
Joseph Chiang
 
ESCMAScript 6: Get Ready For The Future. Now
ESCMAScript 6: Get Ready For The Future. Now
Krzysztof Szafranek
 
Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
Visual Engineering
 
Functional Reactive Programming in Clojurescript
Functional Reactive Programming in Clojurescript
Leonardo Borges
 
The Strange World of Javascript and all its little Asynchronous Beasts
The Strange World of Javascript and all its little Asynchronous Beasts
Federico Galassi
 
Reactive, component 그리고 angular2
Reactive, component 그리고 angular2
Jeado Ko
 
The Open Web and what it means
The Open Web and what it means
Robert Nyman
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
State management in a GraphQL era
State management in a GraphQL era
kristijanmkd
 
Asynchronous and event-driven Grails applications
Asynchronous and event-driven Grails applications
Alvaro Sanchez-Mariscal
 
Testing view controllers with Quick and Nimble
Testing view controllers with Quick and Nimble
Marcio Klepacz
 
Quick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing Dependency
Matthew Beale
 
Complex Architectures in Ember
Complex Architectures in Ember
Matthew Beale
 
Angular promises and http
Angular promises and http
Alexe Bogdan
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
Leonardo Borges
 
Angular server-side communication
Angular server-side communication
Alexe Bogdan
 
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Codemotion
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
ESCMAScript 6: Get Ready For The Future. Now
ESCMAScript 6: Get Ready For The Future. Now
Krzysztof Szafranek
 
Functional Reactive Programming in Clojurescript
Functional Reactive Programming in Clojurescript
Leonardo Borges
 
The Strange World of Javascript and all its little Asynchronous Beasts
The Strange World of Javascript and all its little Asynchronous Beasts
Federico Galassi
 
Reactive, component 그리고 angular2
Reactive, component 그리고 angular2
Jeado Ko
 
The Open Web and what it means
The Open Web and what it means
Robert Nyman
 

Similar to Intro to Reactive Programming with Swift (20)

Reactive programming with RxSwift
Reactive programming with RxSwift
Scott Gardner
 
Rx for Android & iOS by Harin Trivedi
Rx for Android & iOS by Harin Trivedi
harintrivedi
 
Reactive Programming Patterns with RxSwift
Reactive Programming Patterns with RxSwift
Florent Pillet
 
Reactive Programming with RxSwift
Reactive Programming with RxSwift
Scott Gardner
 
Reactive programming
Reactive programming
Jianbin LIN
 
Rxjs ngvikings
Rxjs ngvikings
Christoffer Noring
 
RxJS - The Reactive extensions for JavaScript
RxJS - The Reactive extensions for JavaScript
Viliam Elischer
 
Rxjs vienna
Rxjs vienna
Christoffer Noring
 
RxSwift
RxSwift
Sally Ahmed
 
Introduction to Reactive programming
Introduction to Reactive programming
Dwi Randy Herdinanto
 
FRP: What does "declarative" mean
FRP: What does "declarative" mean
Peter Ovchinnikov
 
RxJava@Android
RxJava@Android
Maxim Volgin
 
Tech fest
Tech fest
Eliasz Sawicki
 
Reactive programming with rx java
Reactive programming with rx java
CongTrung Vnit
 
Consuming Web Services with Swift and Rx
Consuming Web Services with Swift and Rx
Guillermo Gonzalez
 
RxJava2 Slides
RxJava2 Slides
YarikS
 
Reactive frontends with RxJS and Angular
Reactive frontends with RxJS and Angular
VMware Tanzu
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
Rodrigo Leite
 
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
名辰 洪
 
Cocoa heads 09112017
Cocoa heads 09112017
Vincent Pradeilles
 
Reactive programming with RxSwift
Reactive programming with RxSwift
Scott Gardner
 
Rx for Android & iOS by Harin Trivedi
Rx for Android & iOS by Harin Trivedi
harintrivedi
 
Reactive Programming Patterns with RxSwift
Reactive Programming Patterns with RxSwift
Florent Pillet
 
Reactive Programming with RxSwift
Reactive Programming with RxSwift
Scott Gardner
 
Reactive programming
Reactive programming
Jianbin LIN
 
RxJS - The Reactive extensions for JavaScript
RxJS - The Reactive extensions for JavaScript
Viliam Elischer
 
Introduction to Reactive programming
Introduction to Reactive programming
Dwi Randy Herdinanto
 
FRP: What does "declarative" mean
FRP: What does "declarative" mean
Peter Ovchinnikov
 
Reactive programming with rx java
Reactive programming with rx java
CongTrung Vnit
 
Consuming Web Services with Swift and Rx
Consuming Web Services with Swift and Rx
Guillermo Gonzalez
 
RxJava2 Slides
RxJava2 Slides
YarikS
 
Reactive frontends with RxJS and Angular
Reactive frontends with RxJS and Angular
VMware Tanzu
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
Rodrigo Leite
 
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
名辰 洪
 
Ad

Recently uploaded (20)

Simplify Task, Team, and Project Management with Orangescrum Work
Simplify Task, Team, and Project Management with Orangescrum Work
Orangescrum
 
declaration of Variables and constants.pptx
declaration of Variables and constants.pptx
meemee7378
 
Modern Platform Engineering with Choreo - The AI-Native Internal Developer Pl...
Modern Platform Engineering with Choreo - The AI-Native Internal Developer Pl...
WSO2
 
Enable Your Cloud Journey With Microsoft Trusted Partner | IFI Tech
Enable Your Cloud Journey With Microsoft Trusted Partner | IFI Tech
IFI Techsolutions
 
Threat Modeling a Batch Job Framework - Teri Radichel - AWS re:Inforce 2025
Threat Modeling a Batch Job Framework - Teri Radichel - AWS re:Inforce 2025
2nd Sight Lab
 
Decipher SEO Solutions for your startup needs.
Decipher SEO Solutions for your startup needs.
mathai2
 
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
Zoneranker’s Digital marketing solutions
Zoneranker’s Digital marketing solutions
reenashriee
 
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
Shane Coughlan
 
Zoho Creator Solution for EI by Elsner Technologies.docx
Zoho Creator Solution for EI by Elsner Technologies.docx
Elsner Technologies Pvt. Ltd.
 
From Data Preparation to Inference: How Alluxio Speeds Up AI
From Data Preparation to Inference: How Alluxio Speeds Up AI
Alluxio, Inc.
 
Test Case Design Techniques – Practical Examples & Best Practices in Software...
Test Case Design Techniques – Practical Examples & Best Practices in Software...
Muhammad Fahad Bashir
 
Canva Pro Crack Free Download 2025-FREE LATEST
Canva Pro Crack Free Download 2025-FREE LATEST
grete1122g
 
ElectraSuite_Prsentation(online voting system).pptx
ElectraSuite_Prsentation(online voting system).pptx
mrsinankhan01
 
Advance Doctor Appointment Booking App With Online Payment
Advance Doctor Appointment Booking App With Online Payment
AxisTechnolabs
 
Why Edge Computing Matters in Mobile Application Tech.pdf
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
Azure AI Foundry: The AI app and agent factory
Azure AI Foundry: The AI app and agent factory
Maxim Salnikov
 
Microsoft-365-Administrator-s-Guide1.pdf
Microsoft-365-Administrator-s-Guide1.pdf
mazharatknl
 
Digital Transformation: Automating the Placement of Medical Interns
Digital Transformation: Automating the Placement of Medical Interns
Safe Software
 
Which Hiring Management Tools Offer the Best ROI?
Which Hiring Management Tools Offer the Best ROI?
HireME
 
Simplify Task, Team, and Project Management with Orangescrum Work
Simplify Task, Team, and Project Management with Orangescrum Work
Orangescrum
 
declaration of Variables and constants.pptx
declaration of Variables and constants.pptx
meemee7378
 
Modern Platform Engineering with Choreo - The AI-Native Internal Developer Pl...
Modern Platform Engineering with Choreo - The AI-Native Internal Developer Pl...
WSO2
 
Enable Your Cloud Journey With Microsoft Trusted Partner | IFI Tech
Enable Your Cloud Journey With Microsoft Trusted Partner | IFI Tech
IFI Techsolutions
 
Threat Modeling a Batch Job Framework - Teri Radichel - AWS re:Inforce 2025
Threat Modeling a Batch Job Framework - Teri Radichel - AWS re:Inforce 2025
2nd Sight Lab
 
Decipher SEO Solutions for your startup needs.
Decipher SEO Solutions for your startup needs.
mathai2
 
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
Zoneranker’s Digital marketing solutions
Zoneranker’s Digital marketing solutions
reenashriee
 
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
OpenChain Webinar - AboutCode - Practical Compliance in One Stack – Licensing...
Shane Coughlan
 
Zoho Creator Solution for EI by Elsner Technologies.docx
Zoho Creator Solution for EI by Elsner Technologies.docx
Elsner Technologies Pvt. Ltd.
 
From Data Preparation to Inference: How Alluxio Speeds Up AI
From Data Preparation to Inference: How Alluxio Speeds Up AI
Alluxio, Inc.
 
Test Case Design Techniques – Practical Examples & Best Practices in Software...
Test Case Design Techniques – Practical Examples & Best Practices in Software...
Muhammad Fahad Bashir
 
Canva Pro Crack Free Download 2025-FREE LATEST
Canva Pro Crack Free Download 2025-FREE LATEST
grete1122g
 
ElectraSuite_Prsentation(online voting system).pptx
ElectraSuite_Prsentation(online voting system).pptx
mrsinankhan01
 
Advance Doctor Appointment Booking App With Online Payment
Advance Doctor Appointment Booking App With Online Payment
AxisTechnolabs
 
Why Edge Computing Matters in Mobile Application Tech.pdf
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
Azure AI Foundry: The AI app and agent factory
Azure AI Foundry: The AI app and agent factory
Maxim Salnikov
 
Microsoft-365-Administrator-s-Guide1.pdf
Microsoft-365-Administrator-s-Guide1.pdf
mazharatknl
 
Digital Transformation: Automating the Placement of Medical Interns
Digital Transformation: Automating the Placement of Medical Interns
Safe Software
 
Which Hiring Management Tools Offer the Best ROI?
Which Hiring Management Tools Offer the Best ROI?
HireME
 
Ad

Intro to Reactive Programming with Swift

  • 1. Introduction to Reactive Programming with RxSwift Xinran Wang Software Engineer Digital Home, Comcast NBCUniversal
  • 2. What is Reactive Programming? Programming with asynchronous data streams Treat events as sequences (streams) of data
  • 3. Improve API client retries and error handling Simplify app auth management
  • 4. someAsyncFunction { anotherAsyncFunction1 { anotherAsyncFunction2 { anotherAsyncFunction3 { anotherAsyncFunction4 { // Thank God this has no error handling 😰 } } } } }
  • 5. asyncObservable() .flatMap { _ in return asyncObservable2() } .flatMap { _ in return asyncObservable3() } .flatMap { _ in return asyncObservable4() } .subscribe(onNext: { data in // handle last result }, onError{ error in // handle errors })
  • 7. Streams of data (think Swift sequences + async) Observable
  • 8. Event An element/value of the data stream onNext: onError: onComplete:
  • 9. Observer/Subscriber Subscribes to the … Observable Handles Events (the data stream elements) Stream starts on“subscription”
  • 10. Disposable Disconnects the data stream Ends Event ingestion Handles cleanup of resources
  • 11. Main Terminology - Observable - Event - Subscriber - Disposable Icons made by https://www.flaticon.com/authors/smashicons from www.flaticon.com
  • 12. Main Terminology - Observable - Event - Subscriber - Disposable
  • 13. Main Terminology - Observable - Event - Subscriber - Disposable
  • 14. Main Terminology - Observable - Event - Subscriber - Disposable
  • 15. Ok, now the fun stuff: what do I do with these data streams?
  • 16. Rx Operators! (the fun stuff) // Creating: create, from, just … // Transforming map, flatMap, buffer … // Filtering filter, first, skip, debounce … // Combining merge, zip, combineLatest … // Error Handling catch, retry // Utility subscribe, delay, do … // Conditional/Boolean contains, all, skipUntil … // Mathematical and Aggregation count, max, min, reduce …
  • 17. Creating! // Creating: create, from, just … // Transforming map, flatMap, buffer … // Filtering filter, first, skip, debounce … // Combining merge, zip, combineLatest … // Error Handling catch, retry // Utility subscribe, delay, do … // Conditional/Boolean contains, all, skipUntil … // Mathematical and Aggregation count, max, min, reduce …
  • 18. Observable.create { observer in asyncFunction(completion: { // create the event for each piece of data // observer.onNext() // observer.onError() // observer.onComplete() }) return Disposables.create() }
  • 19. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 20. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 21. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 22. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 23. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 24. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 25. func makeSushi(completion: @escaping (Sushi?, Error?)->Void) func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } }
  • 26. func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } } makeSushiObservable() // observable stream STARTS on subscription // so makeSushi actually first gets called here .subscribe(onNext: { sushi in // eat the sushi! }, onError: { error in // complain to the waiter }) .disposed(by: disposeBag)
  • 27. func makeSushiObservable() -> Observable<Sushi?> { return Observable.create { observer in self.makeSushi { (sushi, error) in if let error = error { observer.onError(error) } observer.onNext(sushi) observer.onCompleted() } return Disposables.create() } } makeSushiObservable() // observable stream STARTS on subscription // so makeSushi actually first gets called here .subscribe(onNext: { sushi in // eat the sushi! }, onError: { error in // complain to the waiter }) .disposed(by: disposeBag)
  • 28. More Operators! // Creating: create, from, just … // Transforming map, flatMap, buffer … // Filtering filter, first, skip, debounce … // Combining merge, zip, combineLatest … // Error Handling catch, retry // Utility subscribe, delay, do … // Conditional/Boolean contains, all, skipUntil … // Mathematical and Aggregation count, max, min, reduce …
  • 29. let sushi = Observable.zip(makeRice(), sliceFish(tuna)) { (rice, tuna) -> Sushi in return Sushi(rice, tuna) } sushi.filter { sushi in return !sushi.isVegetarian() } .map { (sushi: Sushi) -> Sushi in return addSoySauce(sushi) } .map { addWasabi }
  • 30. let sushi = Observable.zip(makeRice(), sliceFish(tuna)) { (rice, tuna) -> Sushi in return Sushi(rice, tuna) } sushi.filter { sushi in return !sushi.isVegetarian() } .map { (sushi: Sushi) -> Sushi in return addSoySauce(sushi) } .map { addWasabi }
  • 31. Cool … Why and how would I actually use this for building my iOS app?
  • 33. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 34. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 35. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 36. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 37. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 38. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { return rxResponse(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } } APIClient.responseObservable(request: req) .subscribe(onNext: { data in // handle data })
  • 39. private static func handleResponse( data: Data?, response: URLResponse? ) throws -> Observable<[String: Any]> { guard let httpResponse = response as? HTTPURLResponse else { throw APIError.invalidResponse(response) } guard 200..<300 ~= httpResponse.statusCode else { throw APIError.badStatusCode(httpResponse) } guard let responseData = data else { throw APIError.badData(data, httpResponse) } if let json = try JSONSerialization .jsonObject(with: responseData, options: []) as? [String: Any] { return Observable.just(json) } else { throw APIError.jsonParsingError } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 40. private static func handleResponse( data: Data?, response: URLResponse? ) throws -> Observable<[String: Any]> { guard let httpResponse = response as? HTTPURLResponse else { throw APIError.invalidResponse(response) } guard 200..<300 ~= httpResponse.statusCode else { throw APIError.badStatusCode(httpResponse) } guard let responseData = data else { throw APIError.badData(data, httpResponse) } if let json = try JSONSerialization .jsonObject(with: responseData, options: []) as? [String: Any] { return Observable.just(json) } else { throw APIError.jsonParsingError } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 41. private static func handleErrors(errors: Observable<Error>) -> Observable<Void> { return errors.enumerated() .flatMap { (i, error) -> Observable<Void> in // handle max number of retries based on `i` guard i < 3 else { return Observable.error(error) // propagate error } switch error { case .expiredToken: self.refreshToken() return Observable.just(()) // <- forces retry default: return Observable.just(()) // <- forces retry } } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 42. private static func handleErrors(errors: Observable<Error>) -> Observable<Void> { return errors.enumerated() .flatMap { (i, error) -> Observable<Void> in // handle max number of retries based on `i` guard i < 3 else { return Observable.error(error) // propagate error } switch error { case .expiredToken: self.refreshToken() return Observable.just(()) // <- forces retry default: return Observable.just(()) // <- forces retry } } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 43. private static func handleErrors(errors: Observable<Error>) -> Observable<Void> { return errors.enumerated() .flatMap { (i, error) -> Observable<Void> in // handle max number of retries based on `i` guard i < 3 else { return Observable.error(error) // propagate error } switch error { case .expiredToken: self.refreshToken() return Observable.just(()) // <- forces retry default: return Observable.just(()) // <- forces retry } } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 44. private static func handleErrors(errors: Observable<Error>) -> Observable<Void> { return errors.enumerated() .flatMap { (i, error) -> Observable<Void> in // handle max number of retries based on `i` guard i < 3 else { return Observable.error(error) // propagate error } switch error { case .expiredToken: self.refreshToken() return Observable.just(()) // <- forces retry default: return Observable.just(()) // <- forces retry } } } class APIClient { let apiClientDisposeBag = DisposeBag() static func responseObservable( request: URLRequest ) -> Observable<[String: Any]> { return customRequest(request) .flatMap { handleResponse } // .retry(3) .retryWhen { handleErrors } } }
  • 45. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { ... } static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) { responseObservable(request: request) .subscribe(onNext: { data in completion(data) }) .disposed(by: apiClientDisposeBag) } } APIClient.response(request: req) { json in // do stuff with json }
  • 46. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { ... } static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) { responseObservable(request: request) .subscribe(onNext: { data in completion(data) }) .disposed(by: apiClientDisposeBag) } } APIClient.response(request: req) { json in // do stuff with json }
  • 47. class APIClient { static func responseObservable(request: URLRequest) -> Observable<[String: Any]> { ... } static func response(request: URLRequest, completion: @escaping ([String: Any])->Void) { responseObservable(request: request) .subscribe(onNext: { data in completion(data) }) .disposed(by: apiClientDisposeBag) } } APIClient.response(request: req) { json in // do stuff with json }
  • 49. Final Thoughts 1. 👎 Avoid nesting subscriptions
  • 50. APIClient.fetchMovie(id).subscribe(onNext: { movie in APIClient.fetchMovieDetails(movie).subscribe(onNext: { details in ... }) .addDisposableTo(disposeBag) }) .addDisposableTo(disposeBag) APIClient.fetchMovie(id) .flatMap { id in return APIClient.fetchMovieDetails(movie) } .subscribe(onNext: { details in ... }) .addDisposableTo(disposeBag)
  • 51. APIClient.fetchMovie(id).subscribe(onNext: { movie in APIClient.fetchMovieDetails(movie).subscribe(onNext: { details in ... }) .addDisposableTo(disposeBag) }) .addDisposableTo(disposeBag) APIClient.fetchMovie(id) .flatMap { id in return APIClient.fetchMovieDetails(movie) } .subscribe(onNext: { details in ... }) .addDisposableTo(disposeBag)
  • 52. Final Thoughts 2. Makes networking cleaner
  • 53. Final Thoughts 3. Rx forces your code to be more functional
  • 54. Final Thoughts 4. Can go from callbacks to observable The whole project does not need to be observable
  • 55. Useful Resources General: - http://reactivex.io/documentation/observable.html - http://rxmarbles.com/ RxSwift-specific: - https://github.com/ReactiveX/RxSwift - https://egghead.io/courses/introduction-to-reactive-programming - https://www.raywenderlich.com/138547/getting-started-with-rxswift-and-rxcocoa - https://www.raywenderlich.com/158026/introducing-rxswift-reactive-programming-swift