[RxCocoa] bind๋?
์๋ ํ์ธ์ Foma ๐ ์ ๋๋ค!
์ค๋๋ง์ Rx๊ด๋ จ ํฌ์คํ ์ ํ๋ค์.
์ค๋์ UI ํนํ๋ Operator ์ค bind์ ๋ํด์ ์์๋ณด๋ ค๊ณ ํฉ๋๋ค.
๋ฐ๋ก ์์ํ ๊ฒ์~
Binder
๋จผ์ bind๋ฅผ ์ดํดํ๊ธฐ ์ํด์ Binder๋ฅผ ์์์ผ ํ๋๋ฐ์.
Binder๋ ์ต์ ๋ฒ ํ์ ์ผ๋ก 3๊ฐ์ง ํน์ง์ด ์๋๋ฐ์.
์ฒซ ๋ฒ์งธ๋ก๋ ๋ฐ๋์ ๋ฉ์ธ์ค์ผ์ฅด๋ฌ์์ ์คํ๋๋ค๋ ๊ฒ์ ๋๋ค.
๋ ๋ฒ์งธ๋ก๋ ์๋ฌ ์ด๋ฒคํธ๋ฅผ ๋ฐ๋ก ๋ฐฉ์ถํ์ง ์๊ณ ๋ก๊ทธ๋ก๋ง ์ถ๋ ฅ๋ฉ๋๋ค.
์ธ ๋ฒ์งธ๋ก๋ ์ต์ ๋ฒํ์ ์ด๊ธฐ ๋๋ฌธ์ ์๋ก์ด ๊ฐ์ ์ ๋ฌํ ์ ์์ง๋ง ๊ตฌ๋ ์๋ฅผ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
public struct Binder<Value>: ObserverType {
public typealias Element = Value
private let binding: (Event<Value>) -> Void
/// Initializes `Binder`
///
/// - parameter target: Target object.
/// - parameter scheduler: Scheduler used to bind the events.
/// - parameter binding: Binding logic.
public init<Target: AnyObject>(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> Void) {
weak var weakTarget = target
self.binding = { event in
switch event {
case .next(let element):
_ = scheduler.schedule(element) { element in
if let target = weakTarget {
binding(target, element)
}
return Disposables.create()
}
case .error(let error):
rxFatalErrorInDebug("Binding error: \(error)")
case .completed:
break
}
}
}
bind๋?
Binder ์ต์ ๋ฒ๋ฅผ ์ฌ์ฉํด์ UI์ ์ต์ ๋ฒ๋ธ์ ํ๋๋ก ๋ฌถ๋ ํ์์ ๋๋ค.
์ฝ๊ฒ ์ค๋ช ํ๋ฉด Observable์ด ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๊ณ subscribe๋ฅผ ํตํด ๊ด์ฐฐ์ ํ์ฃ ?
bind๋ ์ต์ ๋ฒ๋ธ๊ณผ ๊ด์ฐฐํ๋ ์ต์ ๋ฒ๋ฅผ ๊ทธ๋ฅ ํ๋๋ก ๋ฌถ์ด๋ฒ๋ฆฌ๋ ๊ฒ์ด์ฃ .
๋ง์ฝ ๋ ์ด๋ธ์ ํ ์คํธ๋ฅผ ์ต์ ๋ฒ๋ธ์ด ๋ฐฉ์ถํ ์ด๋ฒคํธ๋๋ก ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
observable.subscribe{
self.label.text = $0
}.disposed(by: disposeBag)
ํ์ง๋ง bind๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์๋์ ๊ฐ์ด ๋ฐฉ์ถํ ์ด๋ฒคํธ ๊ทธ๋๋ก๋ฅผ label์ ํ ์คํธ์ bind ํด์ค๋ฒ๋ฆฌ๋ ๊ฒ์ด์ฃ !
(bind๋ ๊ฐ์ ์ ๋ฌํ๊ธฐ๋ง ํ๊ธฐ ๋๋ฌธ์ ๊ด์ฐฐ์ ํ ์๋ ์์ต๋๋ค.)
observable
.bind(to: label.rx.text)
.disposed(by: disposeBag)
bind ๋ด๋ถ ์ฝ๋๋ ์๋์ ๊ฐ์ด subscribe๋ฅผ ํตํด์ ์ด๋ฒคํธ๋ฅผ ๊ด์ฐฐํ๊ณ ์์ต๋๋ค.
public func bind<Observer: ObserverType>(to observers: Observer...) -> Disposable where Observer.Element == Element {
self.subscribe { event in
observers.forEach { $0.on(event) }
}
}
๋ง์ฝ ์๋ฌ๊ฐ ์๊ฒผ๋ค๋ฉด bind๋ ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง ์๊ณ ๋ก๊ทธ๋ก ์ถ๋ ฅํด์ค๋๋ค.
public func bind(onNext: @escaping (Element) -> Void) -> Disposable {
self.subscribe(onNext: onNext,
onError: { error in
rxFatalErrorInDebug("Binding error: \(error)")
})
}
UIButton๊ณผ title์ rx๋ก ๋ฐ์ธ๋ฉํ ๋ ์ค์ ๋ก Binder๋ฅผ returnํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
extension Reactive where Base: UIButton {
/// Reactive wrapper for `setTitle(_:for:)`
public func title(for controlState: UIControl.State = []) -> Binder<String?> {
Binder(self.base) { button, title in
button.setTitle(title, for: controlState)
}
}
์ ๋ฆฌ
bind๋ UI์ ์ต์ ๋ฒ๋ธ์ ๋ฌถ๋ ํ์๋ก ๊ฐ์ ์ ๋ฌํ๊ธฐ๋ง ํ๋ฉฐ ๋ฉ์ธ ์ฐ๋ ๋์์ ์๋ํ๊ณ ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง๋ ์๋๋ค.