SlideShare a Scribd company logo
PRESCRIBING RX RESPONSIBLY 💊
2 0 1 7
2
AGENDA
01RX INTRO
02WHEN TO USE RX (OR NOT)
03RX BEST PRACTICES
04CONCLUSION AND
TAKEAWAYS
WHAT IS RX?
4
today’s talk
not today’s
talk
RX-BERG
W H A T I S R X ?
5
•RxSwift - Swift implementation of ReactiveX
•Follows the “Observer pattern”
•Declarative way of defining the data flow in your app
•Avoid “callback hell”
•Data flow is handled via manageable streams
W H A T I S R X ?
6
STREAMS
Observable<WaterMolecule>
Observable<Bool>
Observable<MeetUp>
of things.
One thing at a time.
W H A T I S R X ?
7RxMarbles.com
W H A T I S R X ?
8RxMarbles.com
W H A T I S R X ?
9
Observable
RX ECOSYSTEM
Variable
Subject
PublishSubject
Driver
DisposeBag
BehaviorSubject
Observer
W H A T I S R X ?
10
Observable
RX ECOSYSTEM
Variable
Subject
PublishSubject
Driver
DisposeBag
BehaviorSubject
Observer
WHEN TO USE RX
12
01User actions (button taps, text field delegates)
02Async operations (Network calls, processing)
03Bindings (VC!!<-> VM !!<-> Model)
L I S T
WHEN TO
USE RX
04Prevent code 🍝
B U T T O N A C T I O N
13
WITHOUT RX
@IBAction func logoTapped(_ sender: UIButton) {
dismissUntilHome()
}
navBar.logoButton !=> dismissUntilHome !!>>> rx_disposeBag
WITH RX
Drag and drop to create IBAction function. A bit more complicated if it is nested in a custom
view.
We are using Fira Code font: https://github.com/tonsky/FiraCode
D A T E P I C K E R
14
WITH RX
WITHOUT RX
Drag and drop to create IBAction function. A bit more complicated if it is nested in a custom
view, or number of date pickers are not constant.
datePicker.rx.date !=> viewModel.endDate !!>>> rx_disposeBag
@IBAction func datePicked(_ sender: UIDatePicker) {
viewModel.endDate = sender.date
}
T E X T F I E L D
15
WITH RX
titleField.textView.rx.text.orEmpty !!<-> viewModel.title !!>>> rx_disposeBag
Create binding in view controller.
WITHOUT RX
Set up delegate for the text field to listen for edit events to update view model, and manually
trigger UI update when view model’s property has changed.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
replacementString string: String)
var title: String = "" {
didSet {
updateTextFields()
}
}
SCROLLING
UNDER
S C R O L L V I E W
S C R O L L V I E W
17
WITH RX
tableView.rx_scrolledUnderTop !=> viewModel.showTopGradient !!>>> rx_disposeBag
tableView.rx_scrolledUnderBottom !=> viewModel.showBottomGradient !!>>> rx_disposeBag
Create binding in view controller.
WITHOUT RX
Set up delegate extensions and do the calculation within the method, at multiple places for
multiple classes:
func scrollViewDidScroll(_ scrollView: UIScrollView)
P A G I N A T I O N
18
SET UP DATA CONTROLLER
func getPaginatedData<T: RealmSwift.Object>(resource: Resource, loadNextPageTrigger: Observable<Void>, dataParser: @escaping (Data) !-> ([T], Int)) !->
Observable<[T]> {
let existingObjects: [T] = Realm.ip_objects(type: T.self)!?.toArray() !?? []
return recursiveGetPaginatedData(resource: resource, lastModified: lastModifiedDate, dataParser: dataParser, loadedSoFar: [], page: 1, loadNextPageTrigger:
loadNextPageTrigger).startWith(existingObjects)
}
func recursiveGetPaginatedData<T: RealmSwift.Object>(resource: Resource, dataParser: @escaping (Data) !-> ([T], Int), loadedSoFar: [T], page: Int,
loadNextPageTrigger: Observable<Void>) !-> Observable<[T]> {
guard let urlRequest = URLRequest(builder: URLRequestBuilder(resource: resource, paginationPage: page, authenticationToken = authenticationToken) else {
return Observable.just(loadedSoFar)
}
return networkOperationQueue.add(dataRequest: urlRequest).observeOn(MainScheduler.instance)
.flatMap { data !-> Observable<[T]> in
var justLoaded = loadedSoFar
let (models, paginationTotalItems) = dataParser(data)
justLoaded.append(contentsOf: models)
if justLoaded.count !== paginationTotalItems {
Realm.ip_add(justLoaded, update: true, configuration: self.realmConfiguration)
return Observable.just(justLoaded)
}
return Observable.concat([
Observable.just(justLoaded),
Observable.never().takeUntil(loadNextPageTrigger),
Observable.deferred { self.recursiveGetPaginatedData(resource: resource, dataParser: dataParser, loadedSoFar: justLoaded, page: page + 1,
loadNextPageTrigger: loadNextPageTrigger) }
])
}
}
Functions of the network call in data controller:
P A G I N A T I O N
19
SET UP VIEW MODEL
func opportunities(loadNextPageTrigger: Observable<Void>) !-> Observable<[OpportunityModel]> {
return getPaginatedData(resource: Resource.opportunities, loadNextPageTrigger: loadNextPageTrigger) { (data) !-> ([OpportunityRealmModel], Int) in
let opportunitiesModel = try! OpportunitiesModel(node: data)
return (opportunitiesModel.opportunities, opportunitiesModel.total)
}
.map { $0 as [OpportunityModel] }
}
Function of the API call in data controller:
Where we make the API call in view model:
dataController.opportunities(loadNextPageTrigger: nextPageTrigger.asObservable())
.map { $0.map { OpportunityCellViewModel(opportunity: $0) } }
.subscribe(
onNext: {
self.opportunityCellViewModels = $0
self.hasMoreOpportunities = true
},
onError: {
Logger.error($0)
NotificationCenter.postMessage(type: .requestFailure)
self.hasMoreOpportunities = false
},
onCompleted: {
self.opportunityCellViewModels.append(EndOfListViewModel())
self.hasMoreOpportunities = false
}) !!>>> rx_disposeBag
P A G I N A T I O N
20
GET NEXT PAGE IN VIEW MODEL
func nextPage() {
nextPageTrigger.fire()
}
How we get the next page in the view model:
N E T W O R K C A L L S
21
CHAINED NETWORK CALLS
guard let s3Object = requestS3Object(for: .opportunity) else { return nil }
return s3Object.observeOn(MainScheduler.instance).flatMap { s3Object !-> Observable<Bool> in
opportunity.imageURL = URL(string: s3Object.publicURL)
opportunity.imageKey = s3Object.key
guard let presignedURL = URL(string: s3Object.presignedURL) else {
return Observable.error(RxURLSessionError.requestCreationError)
}
return self.uploadImage(data: imageData, to: presignedURL)
}.observeOn(MainScheduler.instance).flatMap { imageUploadSuccess !-> Observable<Data> in
requestBuilder.data = opportunity.toJson()
guard let urlRequest = URLRequest(builder: requestBuilder) else {
return Observable.error(RxURLSessionError.requestCreationError)
}
return self.networkOperationQueue.add(dataRequest: urlRequest)
}
R E A C H A B I L I T Y
22
CREATE REACHABILITY SERVICE
class DefaultReachabilityService: ReachabilityService {
private let _reachabilitySubject: BehaviorSubject<ReachabilityStatus>
var reachability: Observable<ReachabilityStatus> {
return _reachabilitySubject.asObservable()
}
let _reachability: Reachability
init() throws {
guard let reachabilityRef = Reachability() else { throw ReachabilityServiceError.failedToCreate }
let reachabilitySubject = BehaviorSubject<ReachabilityStatus>(value: .unreachable)
let backgroundQueue = DispatchQueue(label: "reachability.wificheck")
reachabilityRef.whenReachable = { reachability in
backgroundQueue.async {
reachabilitySubject.on(.next(.reachable(viaWiFi: reachabilityRef.isReachableViaWiFi)))
}
}
reachabilityRef.whenUnreachable = { reachability in
backgroundQueue.async {
reachabilitySubject.on(.next(.unreachable))
}
}
try reachabilityRef.startNotifier()
_reachability = reachabilityRef
_reachabilitySubject = reachabilitySubject
}
}
How we create observable for reachability of network (by Krunoslav Zaher):
R E A C H A B I L I T Y
23
DISPLAY REACHABILITY MESSAGE
reachabilityService.reachability
.skip(1)
.throttle(10, scheduler: MainScheduler.instance)
.observeOn(MainScheduler.instance)
.subscribe(onNext: {
$0.reachable ? self.hideMessage() : self.showMessage(.lostConnection)
}) !!>>> disposeBag
How we subscribe to reachability observable:
B L U E T O O T H
24
SUBSCRIBING TO A BLUETOOTH STREAM
class AwesomeViewController: UIViewController {
let viewModel = DeviceStatusViewModel()
@IBOutlet weak var batteryImageView: UIImageView!
func viewDidLoad() {
bindToViewModel()
}
override func bindToViewModel() {
super.viewDidLoad()
viewModel.devicesManager.batteryStatus
.subscribeOn(MainScheduler.instance)
.subscribe(next: { batteryStatus in
self.batteryImageView.image = self.batteryImageForStatus(batteryStatus)
}) !!>>> rx_diposeBag
}
}
L O O K S G R E A T B U T …
25
STACKTRACE
HELL
RX BEST PRACTICES
B E S T P R A C T I C E S
27
infix operator !=> : Binding
infix operator !!>>> : Binding
public func !=> <T, P: ObserverType>(left: Variable<T>, right: P) !-> Disposable where P.E !== T {
return left.asObservable().bindTo(right)
}
public func !=> (left: UIButton, right: @escaping () !-> Void) !-> Disposable {
return left.rx.tap.subscribe(onNext: { right() })
}
CREATE OPERATORS FOR COMMON TASKS
Syntax sugar that greatly reduces boilerplate code:
B E S T P R A C T I C E S
28
public func !!<-> <T>(property: ControlProperty<T>, variable: Variable<T>) !->
Disposable {
let bindToUIDisposable = variable
.asObservable()
.bindTo(property)
let bindToVariable = property
.subscribe(
onNext: { n in
variable.value = n
},
onCompleted: {
bindToUIDisposable.dispose()
}
)
return Disposables.create(bindToUIDisposable, bindToVariable)
}
TWO-WAY BINDING
S C R O L L V I E W
29
SCROLL VIEW EXTENSIONS (AS PROMISED)
extension UIScrollView {
public var rx_scrolledUnderTop: Observable<Bool> {
return self.rx.contentOffset
.map { $0.y > 0 }
.distinctUntilChanged()
}
public var rx_scrolledUnderBottom: Observable<Bool> {
return self.rx.contentOffset
.map { $0.y < self.contentSize.height - self.frame.size.height - 1 }
.distinctUntilChanged()
}
}
Create extension for scroll view.
B E S T P R A C T I C E S
30
cell.viewOpportunityOverlayView.rx_tapGesture !=> {
self.showOpportunityDetail(opportunityVM.opportunity)
} !!>>> cell.cellDisposeBag
WATCH OUT FOR CELL REUSE
Be sure to reset bindings on cell reuse! In view controller:
override func prepareForReuse() {
super.prepareForReuse()
cellDisposeBag = DisposeBag()
}
In table view cell:
B E S T P R A C T I C E S
31
func bindToViewModel() {
Observable.combineLatest(vm.passwordValid, vm.passwordIsMinLength) {
$0 !&& $1
} !=> passwordReqsLabel.rx_hidden !!>>> rx_disposeBag
vm.emailAddress !<- emailAddressField.rx_text !!>>> rx_disposeBag
vm.password !<- passwordField.rx_text !!>>> rx_disposeBag
vm.passwordConfirmation !<- confirmPasswordField.rx_text !!>>> rx_disposeBag
}
@IBOutlet weak var settingsButton: UIButton! {
didSet {
settingsButton !=> showSettingsVC !!>>> rx_disposeBag
}
}
DESIGNATED METHOD FOR BINDING
B E S T P R A C T I C E S
32
class DeviceManager {
private var batteryStatus = Variable<BatteryLevel>(.low)
public var batteryStatusObs = batteryStatus.asObservable()
}
PUBLIC VS. PRIVATE
B E S T P R A C T I C E S
33
extension ObservableType {
public func ip_repeatingTimeouts(
interval dueTime: RxTimeInterval,
element: E,
scheduler: SchedulerType = MainScheduler.instance
) !-> Observable<E> {
return
Observable.of(
self.asObservable(),
debounce(dueTime, scheduler: scheduler).map { _ in element }
)
.merge()
}
}
REPEATING TIMEOUTS
CONCLUSIONS
35
• What are you reacting to?
• Are you using a struct or a class?
• Observable vs. Variable?
• Does the subscription need to update things on the screen?
• Will the view update while it’s being displayed?
ASK YOURSELF…
C O N C L U S I O N S
36
CLOSING
THOUGHTS
C O N C L U S I O N S
© Christian Howland
37
• RxMarbles.com
• ReactiveX.io
• https://github.com/IntrepidPursuits/swift-wisdom
• https://github.com/ReactiveX/RxSwift
• rxswift.slack.com
USEFUL LINKS
C O N C L U S I O N S
THANKS!

More Related Content

What's hot

Everything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View ControllersEverything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View Controllers
Brian Gesiak
 
What's new in iOS9
What's new in iOS9What's new in iOS9
What's new in iOS9
CocoaHeads France
 
Introductionandgreetings
IntroductionandgreetingsIntroductionandgreetings
Introductionandgreetings
Pozz ZaRat
 
Testing view controllers with Quick and Nimble
Testing view controllers with Quick and NimbleTesting 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 SetupQuick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental Setup
Brian Gesiak
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
Natasha Murashev
 
The rise and fall of a techno DJ, plus more new reviews and notable screenings
The rise and fall of a techno DJ, plus more new reviews and notable screeningsThe rise and fall of a techno DJ, plus more new reviews and notable screenings
The rise and fall of a techno DJ, plus more new reviews and notable screenings
chicagonewsyesterday
 
Java awt
Java awtJava awt
KODE JS POKENNNNN
KODE JS POKENNNNNKODE JS POKENNNNN
KODE JS POKENNNNN
Pipo Atem
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear Essentials
Nilhcem
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev
 
Angular.js is super cool
Angular.js is super coolAngular.js is super cool
Angular.js is super cool
Maksym Hopei
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
InnovationM
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
AgileThought
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]
Nilhcem
 
Wild Flies and a Camel Java EE Integration Stories
Wild Flies and a Camel Java EE Integration StoriesWild Flies and a Camel Java EE Integration Stories
Wild Flies and a Camel Java EE Integration Stories
Markus Eisele
 
Joan miro
Joan miroJoan miro
Joan miro
ahcb
 
The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]
Nilhcem
 

What's hot (18)

Everything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View ControllersEverything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View Controllers
 
What's new in iOS9
What's new in iOS9What's new in iOS9
What's new in iOS9
 
Introductionandgreetings
IntroductionandgreetingsIntroductionandgreetings
Introductionandgreetings
 
Testing view controllers with Quick and Nimble
Testing view controllers with Quick and NimbleTesting view controllers with Quick and Nimble
Testing view controllers with Quick and Nimble
 
Quick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental SetupQuick: Better Tests via Incremental Setup
Quick: Better Tests via Incremental Setup
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
 
The rise and fall of a techno DJ, plus more new reviews and notable screenings
The rise and fall of a techno DJ, plus more new reviews and notable screeningsThe rise and fall of a techno DJ, plus more new reviews and notable screenings
The rise and fall of a techno DJ, plus more new reviews and notable screenings
 
Java awt
Java awtJava awt
Java awt
 
KODE JS POKENNNNN
KODE JS POKENNNNNKODE JS POKENNNNN
KODE JS POKENNNNN
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear Essentials
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
Angular.js is super cool
Angular.js is super coolAngular.js is super cool
Angular.js is super cool
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]
 
Wild Flies and a Camel Java EE Integration Stories
Wild Flies and a Camel Java EE Integration StoriesWild Flies and a Camel Java EE Integration Stories
Wild Flies and a Camel Java EE Integration Stories
 
Joan miro
Joan miroJoan miro
Joan miro
 
The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]
 

Similar to Prescribing RX Responsibly

Redux. From twitter hype to production
Redux. From twitter hype to productionRedux. From twitter hype to production
Redux. From twitter hype to production
FDConf
 
Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016
Nir Kaufman
 
Redux. From twitter hype to production
Redux. From twitter hype to productionRedux. From twitter hype to production
Redux. From twitter hype to production
Jenya Terpil
 
From mvc to redux: 停看聽
From mvc to redux: 停看聽From mvc to redux: 停看聽
From mvc to redux: 停看聽
Jeff Lin
 
From zero to hero with the reactive extensions for java script
From zero to hero with the reactive extensions for java scriptFrom zero to hero with the reactive extensions for java script
From zero to hero with the reactive extensions for java script
Maurice De Beijer [MVP]
 
From zero to hero with the reactive extensions for JavaScript
From zero to hero with the reactive extensions for JavaScriptFrom zero to hero with the reactive extensions for JavaScript
From zero to hero with the reactive extensions for JavaScript
Maurice De Beijer [MVP]
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
Pavel Lahoda
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
Droidcon Berlin
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with Redux
Vedran Blaženka
 
Visualforce: Using JavaScript Remoting for Apex Controllers
Visualforce: Using JavaScript Remoting for Apex ControllersVisualforce: Using JavaScript Remoting for Apex Controllers
Visualforce: Using JavaScript Remoting for Apex Controllers
prabhat gangwar
 
Reactive Programming - ReactFoo 2020 - Aziz Khambati
Reactive Programming - ReactFoo 2020 - Aziz KhambatiReactive Programming - ReactFoo 2020 - Aziz Khambati
Reactive Programming - ReactFoo 2020 - Aziz Khambati
Aziz Khambati
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - Components
Visual Engineering
 
Jsf intro
Jsf introJsf intro
Jsf intro
vantinhkhuc
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonf
Nataliya Patsovska
 
Unidirectional Data Flow in Swift
Unidirectional Data Flow in SwiftUnidirectional Data Flow in Swift
Unidirectional Data Flow in Swift
Jason Larsen
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
Rodrigo Leite
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
Tracy Lee
 
Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)
Natasha Murashev
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
NAVER Engineering
 
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
GreeceJS
 

Similar to Prescribing RX Responsibly (20)

Redux. From twitter hype to production
Redux. From twitter hype to productionRedux. From twitter hype to production
Redux. From twitter hype to production
 
Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016
 
Redux. From twitter hype to production
Redux. From twitter hype to productionRedux. From twitter hype to production
Redux. From twitter hype to production
 
From mvc to redux: 停看聽
From mvc to redux: 停看聽From mvc to redux: 停看聽
From mvc to redux: 停看聽
 
From zero to hero with the reactive extensions for java script
From zero to hero with the reactive extensions for java scriptFrom zero to hero with the reactive extensions for java script
From zero to hero with the reactive extensions for java script
 
From zero to hero with the reactive extensions for JavaScript
From zero to hero with the reactive extensions for JavaScriptFrom zero to hero with the reactive extensions for JavaScript
From zero to hero with the reactive extensions for JavaScript
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with Redux
 
Visualforce: Using JavaScript Remoting for Apex Controllers
Visualforce: Using JavaScript Remoting for Apex ControllersVisualforce: Using JavaScript Remoting for Apex Controllers
Visualforce: Using JavaScript Remoting for Apex Controllers
 
Reactive Programming - ReactFoo 2020 - Aziz Khambati
Reactive Programming - ReactFoo 2020 - Aziz KhambatiReactive Programming - ReactFoo 2020 - Aziz Khambati
Reactive Programming - ReactFoo 2020 - Aziz Khambati
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - Components
 
Jsf intro
Jsf introJsf intro
Jsf intro
 
Declarative presentations UIKonf
Declarative presentations UIKonfDeclarative presentations UIKonf
Declarative presentations UIKonf
 
Unidirectional Data Flow in Swift
Unidirectional Data Flow in SwiftUnidirectional Data Flow in Swift
Unidirectional Data Flow in Swift
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
 
Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
 

Recently uploaded

Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
Octavian Nadolu
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
Green Software Development
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
Peter Muessig
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
ICS
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
sjcobrien
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
Grant Fritchey
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
Remote DBA Services
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
Remote DBA Services
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
Yara Milbes
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
Green Software Development
 
What next after learning python programming basics
What next after learning python programming basicsWhat next after learning python programming basics
What next after learning python programming basics
Rakesh Kumar R
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
Peter Muessig
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
dakas1
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
Requirement Traceability in Xen Functional Safety
Requirement Traceability in Xen Functional SafetyRequirement Traceability in Xen Functional Safety
Requirement Traceability in Xen Functional Safety
Ayan Halder
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
kalichargn70th171
 

Recently uploaded (20)

Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
 
What next after learning python programming basics
What next after learning python programming basicsWhat next after learning python programming basics
What next after learning python programming basics
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
Requirement Traceability in Xen Functional Safety
Requirement Traceability in Xen Functional SafetyRequirement Traceability in Xen Functional Safety
Requirement Traceability in Xen Functional Safety
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
 

Prescribing RX Responsibly

  • 2. 2 AGENDA 01RX INTRO 02WHEN TO USE RX (OR NOT) 03RX BEST PRACTICES 04CONCLUSION AND TAKEAWAYS
  • 5. W H A T I S R X ? 5 •RxSwift - Swift implementation of ReactiveX •Follows the “Observer pattern” •Declarative way of defining the data flow in your app •Avoid “callback hell” •Data flow is handled via manageable streams
  • 6. W H A T I S R X ? 6 STREAMS Observable<WaterMolecule> Observable<Bool> Observable<MeetUp> of things. One thing at a time.
  • 7. W H A T I S R X ? 7RxMarbles.com
  • 8. W H A T I S R X ? 8RxMarbles.com
  • 9. W H A T I S R X ? 9 Observable RX ECOSYSTEM Variable Subject PublishSubject Driver DisposeBag BehaviorSubject Observer
  • 10. W H A T I S R X ? 10 Observable RX ECOSYSTEM Variable Subject PublishSubject Driver DisposeBag BehaviorSubject Observer
  • 12. 12 01User actions (button taps, text field delegates) 02Async operations (Network calls, processing) 03Bindings (VC!!<-> VM !!<-> Model) L I S T WHEN TO USE RX 04Prevent code 🍝
  • 13. B U T T O N A C T I O N 13 WITHOUT RX @IBAction func logoTapped(_ sender: UIButton) { dismissUntilHome() } navBar.logoButton !=> dismissUntilHome !!>>> rx_disposeBag WITH RX Drag and drop to create IBAction function. A bit more complicated if it is nested in a custom view. We are using Fira Code font: https://github.com/tonsky/FiraCode
  • 14. D A T E P I C K E R 14 WITH RX WITHOUT RX Drag and drop to create IBAction function. A bit more complicated if it is nested in a custom view, or number of date pickers are not constant. datePicker.rx.date !=> viewModel.endDate !!>>> rx_disposeBag @IBAction func datePicked(_ sender: UIDatePicker) { viewModel.endDate = sender.date }
  • 15. T E X T F I E L D 15 WITH RX titleField.textView.rx.text.orEmpty !!<-> viewModel.title !!>>> rx_disposeBag Create binding in view controller. WITHOUT RX Set up delegate for the text field to listen for edit events to update view model, and manually trigger UI update when view model’s property has changed. func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) var title: String = "" { didSet { updateTextFields() } }
  • 16. SCROLLING UNDER S C R O L L V I E W
  • 17. S C R O L L V I E W 17 WITH RX tableView.rx_scrolledUnderTop !=> viewModel.showTopGradient !!>>> rx_disposeBag tableView.rx_scrolledUnderBottom !=> viewModel.showBottomGradient !!>>> rx_disposeBag Create binding in view controller. WITHOUT RX Set up delegate extensions and do the calculation within the method, at multiple places for multiple classes: func scrollViewDidScroll(_ scrollView: UIScrollView)
  • 18. P A G I N A T I O N 18 SET UP DATA CONTROLLER func getPaginatedData<T: RealmSwift.Object>(resource: Resource, loadNextPageTrigger: Observable<Void>, dataParser: @escaping (Data) !-> ([T], Int)) !-> Observable<[T]> { let existingObjects: [T] = Realm.ip_objects(type: T.self)!?.toArray() !?? [] return recursiveGetPaginatedData(resource: resource, lastModified: lastModifiedDate, dataParser: dataParser, loadedSoFar: [], page: 1, loadNextPageTrigger: loadNextPageTrigger).startWith(existingObjects) } func recursiveGetPaginatedData<T: RealmSwift.Object>(resource: Resource, dataParser: @escaping (Data) !-> ([T], Int), loadedSoFar: [T], page: Int, loadNextPageTrigger: Observable<Void>) !-> Observable<[T]> { guard let urlRequest = URLRequest(builder: URLRequestBuilder(resource: resource, paginationPage: page, authenticationToken = authenticationToken) else { return Observable.just(loadedSoFar) } return networkOperationQueue.add(dataRequest: urlRequest).observeOn(MainScheduler.instance) .flatMap { data !-> Observable<[T]> in var justLoaded = loadedSoFar let (models, paginationTotalItems) = dataParser(data) justLoaded.append(contentsOf: models) if justLoaded.count !== paginationTotalItems { Realm.ip_add(justLoaded, update: true, configuration: self.realmConfiguration) return Observable.just(justLoaded) } return Observable.concat([ Observable.just(justLoaded), Observable.never().takeUntil(loadNextPageTrigger), Observable.deferred { self.recursiveGetPaginatedData(resource: resource, dataParser: dataParser, loadedSoFar: justLoaded, page: page + 1, loadNextPageTrigger: loadNextPageTrigger) } ]) } } Functions of the network call in data controller:
  • 19. P A G I N A T I O N 19 SET UP VIEW MODEL func opportunities(loadNextPageTrigger: Observable<Void>) !-> Observable<[OpportunityModel]> { return getPaginatedData(resource: Resource.opportunities, loadNextPageTrigger: loadNextPageTrigger) { (data) !-> ([OpportunityRealmModel], Int) in let opportunitiesModel = try! OpportunitiesModel(node: data) return (opportunitiesModel.opportunities, opportunitiesModel.total) } .map { $0 as [OpportunityModel] } } Function of the API call in data controller: Where we make the API call in view model: dataController.opportunities(loadNextPageTrigger: nextPageTrigger.asObservable()) .map { $0.map { OpportunityCellViewModel(opportunity: $0) } } .subscribe( onNext: { self.opportunityCellViewModels = $0 self.hasMoreOpportunities = true }, onError: { Logger.error($0) NotificationCenter.postMessage(type: .requestFailure) self.hasMoreOpportunities = false }, onCompleted: { self.opportunityCellViewModels.append(EndOfListViewModel()) self.hasMoreOpportunities = false }) !!>>> rx_disposeBag
  • 20. P A G I N A T I O N 20 GET NEXT PAGE IN VIEW MODEL func nextPage() { nextPageTrigger.fire() } How we get the next page in the view model:
  • 21. N E T W O R K C A L L S 21 CHAINED NETWORK CALLS guard let s3Object = requestS3Object(for: .opportunity) else { return nil } return s3Object.observeOn(MainScheduler.instance).flatMap { s3Object !-> Observable<Bool> in opportunity.imageURL = URL(string: s3Object.publicURL) opportunity.imageKey = s3Object.key guard let presignedURL = URL(string: s3Object.presignedURL) else { return Observable.error(RxURLSessionError.requestCreationError) } return self.uploadImage(data: imageData, to: presignedURL) }.observeOn(MainScheduler.instance).flatMap { imageUploadSuccess !-> Observable<Data> in requestBuilder.data = opportunity.toJson() guard let urlRequest = URLRequest(builder: requestBuilder) else { return Observable.error(RxURLSessionError.requestCreationError) } return self.networkOperationQueue.add(dataRequest: urlRequest) }
  • 22. R E A C H A B I L I T Y 22 CREATE REACHABILITY SERVICE class DefaultReachabilityService: ReachabilityService { private let _reachabilitySubject: BehaviorSubject<ReachabilityStatus> var reachability: Observable<ReachabilityStatus> { return _reachabilitySubject.asObservable() } let _reachability: Reachability init() throws { guard let reachabilityRef = Reachability() else { throw ReachabilityServiceError.failedToCreate } let reachabilitySubject = BehaviorSubject<ReachabilityStatus>(value: .unreachable) let backgroundQueue = DispatchQueue(label: "reachability.wificheck") reachabilityRef.whenReachable = { reachability in backgroundQueue.async { reachabilitySubject.on(.next(.reachable(viaWiFi: reachabilityRef.isReachableViaWiFi))) } } reachabilityRef.whenUnreachable = { reachability in backgroundQueue.async { reachabilitySubject.on(.next(.unreachable)) } } try reachabilityRef.startNotifier() _reachability = reachabilityRef _reachabilitySubject = reachabilitySubject } } How we create observable for reachability of network (by Krunoslav Zaher):
  • 23. R E A C H A B I L I T Y 23 DISPLAY REACHABILITY MESSAGE reachabilityService.reachability .skip(1) .throttle(10, scheduler: MainScheduler.instance) .observeOn(MainScheduler.instance) .subscribe(onNext: { $0.reachable ? self.hideMessage() : self.showMessage(.lostConnection) }) !!>>> disposeBag How we subscribe to reachability observable:
  • 24. B L U E T O O T H 24 SUBSCRIBING TO A BLUETOOTH STREAM class AwesomeViewController: UIViewController { let viewModel = DeviceStatusViewModel() @IBOutlet weak var batteryImageView: UIImageView! func viewDidLoad() { bindToViewModel() } override func bindToViewModel() { super.viewDidLoad() viewModel.devicesManager.batteryStatus .subscribeOn(MainScheduler.instance) .subscribe(next: { batteryStatus in self.batteryImageView.image = self.batteryImageForStatus(batteryStatus) }) !!>>> rx_diposeBag } }
  • 25. L O O K S G R E A T B U T … 25 STACKTRACE HELL
  • 27. B E S T P R A C T I C E S 27 infix operator !=> : Binding infix operator !!>>> : Binding public func !=> <T, P: ObserverType>(left: Variable<T>, right: P) !-> Disposable where P.E !== T { return left.asObservable().bindTo(right) } public func !=> (left: UIButton, right: @escaping () !-> Void) !-> Disposable { return left.rx.tap.subscribe(onNext: { right() }) } CREATE OPERATORS FOR COMMON TASKS Syntax sugar that greatly reduces boilerplate code:
  • 28. B E S T P R A C T I C E S 28 public func !!<-> <T>(property: ControlProperty<T>, variable: Variable<T>) !-> Disposable { let bindToUIDisposable = variable .asObservable() .bindTo(property) let bindToVariable = property .subscribe( onNext: { n in variable.value = n }, onCompleted: { bindToUIDisposable.dispose() } ) return Disposables.create(bindToUIDisposable, bindToVariable) } TWO-WAY BINDING
  • 29. S C R O L L V I E W 29 SCROLL VIEW EXTENSIONS (AS PROMISED) extension UIScrollView { public var rx_scrolledUnderTop: Observable<Bool> { return self.rx.contentOffset .map { $0.y > 0 } .distinctUntilChanged() } public var rx_scrolledUnderBottom: Observable<Bool> { return self.rx.contentOffset .map { $0.y < self.contentSize.height - self.frame.size.height - 1 } .distinctUntilChanged() } } Create extension for scroll view.
  • 30. B E S T P R A C T I C E S 30 cell.viewOpportunityOverlayView.rx_tapGesture !=> { self.showOpportunityDetail(opportunityVM.opportunity) } !!>>> cell.cellDisposeBag WATCH OUT FOR CELL REUSE Be sure to reset bindings on cell reuse! In view controller: override func prepareForReuse() { super.prepareForReuse() cellDisposeBag = DisposeBag() } In table view cell:
  • 31. B E S T P R A C T I C E S 31 func bindToViewModel() { Observable.combineLatest(vm.passwordValid, vm.passwordIsMinLength) { $0 !&& $1 } !=> passwordReqsLabel.rx_hidden !!>>> rx_disposeBag vm.emailAddress !<- emailAddressField.rx_text !!>>> rx_disposeBag vm.password !<- passwordField.rx_text !!>>> rx_disposeBag vm.passwordConfirmation !<- confirmPasswordField.rx_text !!>>> rx_disposeBag } @IBOutlet weak var settingsButton: UIButton! { didSet { settingsButton !=> showSettingsVC !!>>> rx_disposeBag } } DESIGNATED METHOD FOR BINDING
  • 32. B E S T P R A C T I C E S 32 class DeviceManager { private var batteryStatus = Variable<BatteryLevel>(.low) public var batteryStatusObs = batteryStatus.asObservable() } PUBLIC VS. PRIVATE
  • 33. B E S T P R A C T I C E S 33 extension ObservableType { public func ip_repeatingTimeouts( interval dueTime: RxTimeInterval, element: E, scheduler: SchedulerType = MainScheduler.instance ) !-> Observable<E> { return Observable.of( self.asObservable(), debounce(dueTime, scheduler: scheduler).map { _ in element } ) .merge() } } REPEATING TIMEOUTS
  • 35. 35 • What are you reacting to? • Are you using a struct or a class? • Observable vs. Variable? • Does the subscription need to update things on the screen? • Will the view update while it’s being displayed? ASK YOURSELF… C O N C L U S I O N S
  • 36. 36 CLOSING THOUGHTS C O N C L U S I O N S © Christian Howland
  • 37. 37 • RxMarbles.com • ReactiveX.io • https://github.com/IntrepidPursuits/swift-wisdom • https://github.com/ReactiveX/RxSwift • rxswift.slack.com USEFUL LINKS C O N C L U S I O N S