Binder는 UI 바인딩에 쓰는 특별한 Observer이다. 아래와 같은 특징을 가진다.
• 에러를 방출하지 않는다.
Error가 방출되면 Observable의 시퀀스가 끊기기 때문에 UI 업데이트가 종료되기 때문이다.
• 특정 스케줄러를 지정하지 않는 이상 메인 스케줄러에서 동작한다.
UI 업데이트는 메인 스케줄러에서 동작하기 때문이다.
타입
public struct Binder<Value>: ObserverType
ObserverType의 제네릭 구조체이다.
생성자
target
: Binder가 확장하는 UI 객체 클래스이다. (ex UILabel...)scheduler
: 동작할 스케줄러 기본 값이 메인 스케줄러이다.binding
: 보통 Value로 전달된 값을 Target에 있는 속성에 저장하는 클로저.
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
}
}
}
Next 이벤트
weak var weakTarget = target
case .next(let element):
_ = scheduler.schedule(element) { element in
if let target = weakTarget {
binding(target, element)
}
return Disposables.create()
}
매개 변수로 전달한 스케줄러에서 binding 클로저를 실행한다.
Error 이벤트
case .error(let error):
rxFatalErrorInDebug("Binding error: \(error)")
func rxFatalErrorInDebug(_ lastMessage: @autoclosure () -> String,
file: StaticString = #file,
line: UInt = #line) {
#if DEBUG
fatalError(lastMessage(), file: file, line: line)
#else
print("\(file):\(line): \(lastMessage())")
#endif
}
에러가 방출되지 않은 이유는 그냥 출력하기 때문이다.
Binder 예시
extension Reactive where Base: UILabel {
var countText: Binder<String> {
return Binder(self.base) { label, text in
label.text = "\(text.count)개"
}
}
}
Reactive 객체를 extension 하여 Base로 확장할 UI 객체를 지정하면 된다. Binder 객체를 리턴하면 되는데 trailing closure로 bind 함수를 만들면 된다.