RxSwift 예제로 감잡기
RxSwift 시작을 위한 간단한 예제들
유용하
@inkyfox
github.com/inkyfox
ReactiveX
• 왜 쓰는가?
• 장점?
• 설치는?
• 비동기.. 함수형.. LINQ.. MVVM..?
• Observable? Observer?
ReactiveX
• 왜 쓰는가?
• 장점?
• 설치는?
• 비동기.. 함수형.. LINQ.. MVVM..?
• Observable? Observer?
일단 생략
ReactiveX
먼저 코드 예제를 보며 감을 잡아 보는 걸로
UI Event
func reload() { }
func setup() {
reloadButton.rx.tap // Observable<Void>
.subscribe(onNext: { [weak self] in self?.reload() })
.addDisposableTo(disposeBag)
}
UI Event
func reload() { }
func setup() {
reloadButton.rx.tap // Observable<Void>
.subscribe(onNext: { [weak self] in self?.reload() })
.addDisposableTo(disposeBag)
}
UI Event
func reload() { }
func setup() {
reloadButton.rx.tap // Observable<Void>
.bindNext(reload)
.addDisposableTo(disposeBag)
}
UI Event
func reload() { }
func setup() {
reloadButton.rx.tap // Observable<Void>
.do(onNext: {
print(“Reload Button Clicked.”)
Analytics.buttonReload.send()
})
.bindNext(reload)
.addDisposableTo(disposeBag)
}
UI Event
func reload() { }
func setup() {
reloadButton.rx.tap // Observable<Void>
.debounce(0.3, scheduler: MainScheduler.instance)
.do(onNext: {
print(“Reload Button Clicked.”)
Analytics.buttonReload.send()
})
.bindNext(reload)
.addDisposableTo(disposeBag)
}
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.map { json in Page(json: json) } // Observable<Page>
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] page in
self?.display(page: page)
})
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.map { json in Page(json: json) } // Observable<Page>
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] page in
self?.display(page: page)
})
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.map { json in Page(json: json) } // Observable<Page>
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] page in
self?.display(page: page)
})
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.map { json in Page(json: json) } // Observable<Page>
.observeOn(MainScheduler.instance)
.bindNext(display)
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.map { Page(json: $0) } // Observable<Page>
.observeOn(MainScheduler.instance)
.bindNext(display)
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.retry(2)
.map { Page(json: $0) } // Observable<Page>
.observeOn(MainScheduler.instance)
.bindNext(display)
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.subscribeOn(SerialDispatchQueueScheduler(qos: .background))
.retry(2)
.observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.map(Page.init) // Observable<Page>
.observeOn(MainScheduler.instance)
.bindNext(display)
.addDisposableTo(reloadDisposeBag)
}
func display(page: Page) { }
REST API
func reload() {
API.default.request(.getPage) // Observable<JSON>
.subscribeOn(SerialDispatchQueueScheduler(qos: .background))
.retry(2)
.observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.map(Page.init) // Observable<Page>
.observeOn(MainScheduler.instance)
.do(
onNext: { print("Reload success: ($0)") },
onError: { error in print("Reload failed! (error)") },
onCompleted: { print("Reload completed") }
)
.bindNext(display)
.addDisposableTo(reloadDisposeBag)
}
Property Binding
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] reloading in
self?.reloadButton.isEnabled = !reloading
})
.addDisposableTo(disposeBag)
}
Property Binding
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] reloading in
self?.reloadButton.isEnabled = !reloading
})
.addDisposableTo(disposeBag)
}
Property Binding
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] enabled in
self?.reloadButton.isEnabled = enabled
})
.addDisposableTo(disposeBag)
}
Property Binding
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] enabled in
self?.reloadButton.isEnabled = enabled
})
.addDisposableTo(disposeBag)
}
Property Binding
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.observeOn(MainScheduler.instance)
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
}
Merge Operator
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
reloadButton.rx.tap // Observable<Void>
.map { false } // Observable<Bool>
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
}
Merge Operator
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
reloadButton.rx.tap // Observable<Void>
.map { false } // Observable<Bool>
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
}
Merge Operator
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // Observable<Bool>
.map { !$0 }
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
reloadButton.rx.tap // Observable<Void>
.map { false } // Observable<Bool>
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
}
Merge Operator
let isReloading: Variable<Bool> = Variable(false)
func setup() {
Observable.from(
[isReloading.asObservable().map { !$0 },
reloadButton.rx.tap.map { false } ]
).merge() // Observable<Bool>
.bindTo(reloadButton.rx.isEnabled)
.addDisposableTo(disposeBag)
}
Operators
let isReloading: Variable<Bool> = Variable(false)
func setup() {
isReloading.asObservable() // |-F-T-F-T-F-
.filter { $0 == false } // |-F---F---F-
.skip(1) // |-----F---F-
.take(1) // |-----F|
.observeOn(MainScheduler.instance)
.bindTo(messageView.rx.isHidden)
.addDisposableTo(disposeBag)
}
그 밖에
• NotificationCenter
• Animation
• 등의 모든 이벤트
• 예제: https://github.com/inkyfox/RxCurrency_iOS
ReactiveX
• Pulling이 아닌 Pushing Data (수동적)
• LINQ style 연산자 & 함수형 프로그래밍
• 비동기 (Async) 프로그래밍
ReactiveX
앞의 예제들을
Callback (Delegate pattern)으로 했으면
어땠을까 상상해봅시다
공부
• http://www.introtorx.com
• http://reactivex.io/intro.html
• http://rxmarbles.com
• https://github.com/ReactiveX/RxSwift/tree/master/
Documentation

RxSwift 예제로 감잡기

  • 1.
    RxSwift 예제로 감잡기 RxSwift시작을 위한 간단한 예제들 유용하 @inkyfox github.com/inkyfox
  • 2.
    ReactiveX • 왜 쓰는가? •장점? • 설치는? • 비동기.. 함수형.. LINQ.. MVVM..? • Observable? Observer?
  • 3.
    ReactiveX • 왜 쓰는가? •장점? • 설치는? • 비동기.. 함수형.. LINQ.. MVVM..? • Observable? Observer? 일단 생략
  • 4.
    ReactiveX 먼저 코드 예제를보며 감을 잡아 보는 걸로
  • 5.
    UI Event func reload(){ } func setup() { reloadButton.rx.tap // Observable<Void> .subscribe(onNext: { [weak self] in self?.reload() }) .addDisposableTo(disposeBag) }
  • 6.
    UI Event func reload(){ } func setup() { reloadButton.rx.tap // Observable<Void> .subscribe(onNext: { [weak self] in self?.reload() }) .addDisposableTo(disposeBag) }
  • 7.
    UI Event func reload(){ } func setup() { reloadButton.rx.tap // Observable<Void> .bindNext(reload) .addDisposableTo(disposeBag) }
  • 8.
    UI Event func reload(){ } func setup() { reloadButton.rx.tap // Observable<Void> .do(onNext: { print(“Reload Button Clicked.”) Analytics.buttonReload.send() }) .bindNext(reload) .addDisposableTo(disposeBag) }
  • 9.
    UI Event func reload(){ } func setup() { reloadButton.rx.tap // Observable<Void> .debounce(0.3, scheduler: MainScheduler.instance) .do(onNext: { print(“Reload Button Clicked.”) Analytics.buttonReload.send() }) .bindNext(reload) .addDisposableTo(disposeBag) }
  • 10.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .map { json in Page(json: json) } // Observable<Page> .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] page in self?.display(page: page) }) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 11.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .map { json in Page(json: json) } // Observable<Page> .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] page in self?.display(page: page) }) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 12.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .map { json in Page(json: json) } // Observable<Page> .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] page in self?.display(page: page) }) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 13.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .map { json in Page(json: json) } // Observable<Page> .observeOn(MainScheduler.instance) .bindNext(display) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 14.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .map { Page(json: $0) } // Observable<Page> .observeOn(MainScheduler.instance) .bindNext(display) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 15.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .retry(2) .map { Page(json: $0) } // Observable<Page> .observeOn(MainScheduler.instance) .bindNext(display) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 16.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .subscribeOn(SerialDispatchQueueScheduler(qos: .background)) .retry(2) .observeOn(ConcurrentDispatchQueueScheduler(qos: .background)) .map(Page.init) // Observable<Page> .observeOn(MainScheduler.instance) .bindNext(display) .addDisposableTo(reloadDisposeBag) } func display(page: Page) { }
  • 17.
    REST API func reload(){ API.default.request(.getPage) // Observable<JSON> .subscribeOn(SerialDispatchQueueScheduler(qos: .background)) .retry(2) .observeOn(ConcurrentDispatchQueueScheduler(qos: .background)) .map(Page.init) // Observable<Page> .observeOn(MainScheduler.instance) .do( onNext: { print("Reload success: ($0)") }, onError: { error in print("Reload failed! (error)") }, onCompleted: { print("Reload completed") } ) .bindNext(display) .addDisposableTo(reloadDisposeBag) }
  • 18.
    Property Binding let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] reloading in self?.reloadButton.isEnabled = !reloading }) .addDisposableTo(disposeBag) }
  • 19.
    Property Binding let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] reloading in self?.reloadButton.isEnabled = !reloading }) .addDisposableTo(disposeBag) }
  • 20.
    Property Binding let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] enabled in self?.reloadButton.isEnabled = enabled }) .addDisposableTo(disposeBag) }
  • 21.
    Property Binding let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] enabled in self?.reloadButton.isEnabled = enabled }) .addDisposableTo(disposeBag) }
  • 22.
    Property Binding let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .observeOn(MainScheduler.instance) .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) }
  • 23.
    Merge Operator let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) reloadButton.rx.tap // Observable<Void> .map { false } // Observable<Bool> .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) }
  • 24.
    Merge Operator let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) reloadButton.rx.tap // Observable<Void> .map { false } // Observable<Bool> .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) }
  • 25.
    Merge Operator let isReloading:Variable<Bool> = Variable(false) func setup() { isReloading.asObservable() // Observable<Bool> .map { !$0 } .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) reloadButton.rx.tap // Observable<Void> .map { false } // Observable<Bool> .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) }
  • 26.
    Merge Operator let isReloading:Variable<Bool> = Variable(false) func setup() { Observable.from( [isReloading.asObservable().map { !$0 }, reloadButton.rx.tap.map { false } ] ).merge() // Observable<Bool> .bindTo(reloadButton.rx.isEnabled) .addDisposableTo(disposeBag) }
  • 27.
    Operators let isReloading: Variable<Bool>= Variable(false) func setup() { isReloading.asObservable() // |-F-T-F-T-F- .filter { $0 == false } // |-F---F---F- .skip(1) // |-----F---F- .take(1) // |-----F| .observeOn(MainScheduler.instance) .bindTo(messageView.rx.isHidden) .addDisposableTo(disposeBag) }
  • 28.
    그 밖에 • NotificationCenter •Animation • 등의 모든 이벤트 • 예제: https://github.com/inkyfox/RxCurrency_iOS
  • 29.
    ReactiveX • Pulling이 아닌Pushing Data (수동적) • LINQ style 연산자 & 함수형 프로그래밍 • 비동기 (Async) 프로그래밍
  • 30.
    ReactiveX 앞의 예제들을 Callback (Delegatepattern)으로 했으면 어땠을까 상상해봅시다
  • 31.
    공부 • http://www.introtorx.com • http://reactivex.io/intro.html •http://rxmarbles.com • https://github.com/ReactiveX/RxSwift/tree/master/ Documentation