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

More Related Content

PDF
Containers & Dependency in Ember.js
PDF
Swift & ReactiveX – Asynchronous Event-Based Funsies with RxSwift
PDF
Ember and containers
PDF
Why Redux-Observable?
PDF
Everything You (N)ever Wanted to Know about Testing View Controllers
PDF
Workshop 5: JavaScript testing
PDF
You will learn RxJS in 2017
PDF
Callbacks, promises, generators - asynchronous javascript
Containers & Dependency in Ember.js
Swift & ReactiveX – Asynchronous Event-Based Funsies with RxSwift
Ember and containers
Why Redux-Observable?
Everything You (N)ever Wanted to Know about Testing View Controllers
Workshop 5: JavaScript testing
You will learn RxJS in 2017
Callbacks, promises, generators - asynchronous javascript

What's hot (20)

PPTX
Avoiding callback hell in Node js using promises
PDF
State management in a GraphQL era
PDF
Asynchronous and event-driven Grails applications
PDF
Testing view controllers with Quick and Nimble
PDF
Quick: Better Tests via Incremental Setup
PDF
Practical Protocol-Oriented-Programming
PDF
Testing Ember Apps: Managing Dependency
PDF
Complex Architectures in Ember
PDF
Angular promises and http
PDF
High Performance web apps in Om, React and ClojureScript
PDF
Angular server-side communication
PDF
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
PDF
Asynchronous Programming FTW! 2 (with AnyEvent)
PDF
JavaScript Promise
PDF
ESCMAScript 6: Get Ready For The Future. Now
PDF
Workshop 10: ECMAScript 6
PDF
Functional Reactive Programming in Clojurescript
PDF
The Strange World of Javascript and all its little Asynchronous Beasts
PDF
Reactive, component 그리고 angular2
PDF
The Open Web and what it means
Avoiding callback hell in Node js using promises
State management in a GraphQL era
Asynchronous and event-driven Grails applications
Testing view controllers with Quick and Nimble
Quick: Better Tests via Incremental Setup
Practical Protocol-Oriented-Programming
Testing Ember Apps: Managing Dependency
Complex Architectures in Ember
Angular promises and http
High Performance web apps in Om, React and ClojureScript
Angular server-side communication
Reactive Thinking in iOS Development - Pedro Piñera Buendía - Codemotion Amst...
Asynchronous Programming FTW! 2 (with AnyEvent)
JavaScript Promise
ESCMAScript 6: Get Ready For The Future. Now
Workshop 10: ECMAScript 6
Functional Reactive Programming in Clojurescript
The Strange World of Javascript and all its little Asynchronous Beasts
Reactive, component 그리고 angular2
The Open Web and what it means
Ad

Similar to Intro to Reactive Programming with Swift (20)

PPTX
Rxjs ngvikings
PDF
Advanced redux
PDF
RxJS - 封裝程式的藝術
PDF
The evolution of asynchronous JavaScript
PDF
Think Async: Asynchronous Patterns in NodeJS
PPTX
Reactive Java (33rd Degree)
PDF
Nevyn — Promise, It's Async! Swift Language User Group Lightning Talk 2015-09-24
PDF
Cycle.js - A functional reactive UI framework
PDF
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
PPTX
Reactive Java (GeeCON 2014)
PDF
Chaining and function composition with lodash / underscore
PPTX
Rxjs swetugg
PDF
Rxjs kyivjs 2015
PDF
Reactive Programming Patterns with RxSwift
PDF
WebCamp:Front-end Developers Day. Александр Мостовенко "Rx.js - делаем асинхр...
PDF
rx.js make async programming simpler
PDF
Rxjs vienna
PDF
Reduxing like a pro
PDF
Understanding Asynchronous JavaScript
PPTX
Avoiding Callback Hell with Async.js
Rxjs ngvikings
Advanced redux
RxJS - 封裝程式的藝術
The evolution of asynchronous JavaScript
Think Async: Asynchronous Patterns in NodeJS
Reactive Java (33rd Degree)
Nevyn — Promise, It's Async! Swift Language User Group Lightning Talk 2015-09-24
Cycle.js - A functional reactive UI framework
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Reactive Java (GeeCON 2014)
Chaining and function composition with lodash / underscore
Rxjs swetugg
Rxjs kyivjs 2015
Reactive Programming Patterns with RxSwift
WebCamp:Front-end Developers Day. Александр Мостовенко "Rx.js - делаем асинхр...
rx.js make async programming simpler
Rxjs vienna
Reduxing like a pro
Understanding Asynchronous JavaScript
Avoiding Callback Hell with Async.js
Ad

Recently uploaded (20)

PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PPTX
L1 - Introduction to python Backend.pptx
PPT
Introduction Database Management System for Course Database
PDF
Nekopoi APK 2025 free lastest update
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Digital Strategies for Manufacturing Companies
PDF
top salesforce developer skills in 2025.pdf
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
System and Network Administraation Chapter 3
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
System and Network Administration Chapter 2
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
L1 - Introduction to python Backend.pptx
Introduction Database Management System for Course Database
Nekopoi APK 2025 free lastest update
Design an Analysis of Algorithms I-SECS-1021-03
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Digital Strategies for Manufacturing Companies
top salesforce developer skills in 2025.pdf
Softaken Excel to vCard Converter Software.pdf
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
System and Network Administraation Chapter 3
Design an Analysis of Algorithms II-SECS-1021-03
2025 Textile ERP Trends: SAP, Odoo & Oracle
Navsoft: AI-Powered Business Solutions & Custom Software Development
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Operating system designcfffgfgggggggvggggggggg
System and Network Administration Chapter 2

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