Skip to content

Commit a1c2f0d

Browse files
committed
Deprecates UIBindingObserver in favor of Binder.
1 parent 0363de5 commit a1c2f0d

40 files changed

+282
-206
lines changed

.jazzy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ custom_categories:
172172
- PrimitiveSequence
173173
- name: RxCocoa/Common
174174
children:
175+
- Binder
175176
- ControlTarget
176177
- DelegateProxy
177178
- DelegateProxyType
@@ -278,7 +279,6 @@ custom_categories:
278279
- ControlEvent
279280
- ControlProperty
280281
- PublishRelay
281-
- UIBindingObserver
282282
- name: RxCocoa/Traits/Driver
283283
children:
284284
- ControlEvent+Driver

Rx.xcodeproj/project.pbxproj

Lines changed: 18 additions & 18 deletions
Large diffs are not rendered by default.

Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-macOS.xcscheme

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
language = ""
2930
shouldUseLaunchSchemeArgsEnv = "YES"
3031
codeCoverageEnabled = "YES">
3132
<Testables>
@@ -56,6 +57,7 @@
5657
buildConfiguration = "Debug"
5758
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
5859
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
60+
language = ""
5961
launchStyle = "0"
6062
useCustomWorkingDirectory = "NO"
6163
ignoresPersistentStateOnLaunch = "NO"

RxCocoa/Common/Binder.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// Binder.swift
3+
// RxCocoa
4+
//
5+
// Created by Krunoslav Zaher on 9/17/17.
6+
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
7+
//
8+
9+
import RxSwift
10+
11+
/**
12+
Observer that enforces interface binding rules:
13+
* can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged)
14+
* ensures binding is performed on a specific scheduler
15+
16+
`Binder` doesn't retain target and in case target is released, element isn't bound.
17+
18+
By default it binds elements on main scheduler.
19+
*/
20+
public struct Binder<Value>: ObserverType {
21+
public typealias E = Value
22+
23+
private let _binding: (Event<Value>) -> ()
24+
25+
/// Initializes `Binder`
26+
///
27+
/// - parameter target: Target object.
28+
/// - parameter scheduler: Scheduler used to bind the events.
29+
/// - parameter binding: Binding logic.
30+
public init<Target: AnyObject>(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> ()) {
31+
weak var weakTarget = target
32+
33+
_binding = { event in
34+
switch event {
35+
case .next(let element):
36+
_ = scheduler.schedule(element) { element in
37+
if let target = weakTarget {
38+
binding(target, element)
39+
}
40+
return Disposables.create()
41+
}
42+
case .error(let error):
43+
bindingError(error)
44+
case .completed:
45+
#if DEBUG
46+
print("Source observable sequence has completed and no further element will be bound. This may be something unexpected.")
47+
#endif
48+
break
49+
}
50+
}
51+
}
52+
53+
/// Binds next element to owner view as described in `binding`.
54+
public func on(_ event: Event<Value>) {
55+
_binding(event)
56+
}
57+
58+
/// Erases type of observer.
59+
///
60+
/// - returns: type erased observer.
61+
public func asObserver() -> AnyObserver<Value> {
62+
return AnyObserver(eventHandler: on)
63+
}
64+
}

RxCocoa/Common/DelegateProxyType.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ extension DelegateProxyType
257257
let subscription = self.asObservable()
258258
.observeOn(MainScheduler())
259259
.catchError { error in
260-
bindingErrorToInterface(error)
260+
bindingError(error)
261261
return Observable.empty()
262262
}
263263
// source can never end, otherwise it would release the subscriber, and deallocate the data source
@@ -273,7 +273,7 @@ extension DelegateProxyType
273273

274274
switch event {
275275
case .error(let error):
276-
bindingErrorToInterface(error)
276+
bindingError(error)
277277
unregisterDelegate.dispose()
278278
case .completed:
279279
unregisterDelegate.dispose()

RxCocoa/Common/NSLayoutConstraint+Rx.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ import RxSwift
2121
#if os(iOS) || os(macOS) || os(tvOS)
2222
extension Reactive where Base: NSLayoutConstraint {
2323
/// Bindable sink for `constant` property.
24-
public var constant: UIBindingObserver<Base, CGFloat> {
25-
return UIBindingObserver(UIElement: self.base) { constraint, constant in
24+
public var constant: Binder<CGFloat> {
25+
return Binder(self.base) { constraint, constant in
2626
constraint.constant = constant
2727
}
2828
}
2929

3030
/// Bindable sink for `active` property.
3131
@available(iOS 8, OSX 10.10, *)
32-
public var active: UIBindingObserver<Base, Bool> {
33-
return UIBindingObserver(UIElement: self.base) { constraint, value in
32+
public var active: Binder<Bool> {
33+
return Binder(self.base) { constraint, value in
3434
constraint.isActive = value
3535
}
3636
}

RxCocoa/Deprecated.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,69 @@ extension DelegateProxy {
273273
fatalError()
274274
}
275275
}
276+
277+
/**
278+
Observer that enforces interface binding rules:
279+
* can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged)
280+
* ensures binding is performed on main thread
281+
282+
`UIBindingObserver` doesn't retain target interface and in case owned interface element is released, element isn't bound.
283+
284+
In case event binding is attempted from non main dispatch queue, event binding will be dispatched async to main dispatch
285+
queue.
286+
*/
287+
@available(*, deprecated, renamed: "Binder")
288+
public final class UIBindingObserver<UIElementType, Value> : ObserverType where UIElementType: AnyObject {
289+
public typealias E = Value
290+
291+
weak var UIElement: UIElementType?
292+
293+
let binding: (UIElementType, Value) -> Void
294+
295+
/// Initializes `ViewBindingObserver` using
296+
@available(*, deprecated, renamed: "UIBinder.init(_:scheduler:binding:)")
297+
public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Void) {
298+
self.UIElement = UIElement
299+
self.binding = binding
300+
}
301+
302+
/// Binds next element to owner view as described in `binding`.
303+
public func on(_ event: Event<Value>) {
304+
if !DispatchQueue.isMain {
305+
DispatchQueue.main.async {
306+
self.on(event)
307+
}
308+
return
309+
}
310+
311+
switch event {
312+
case .next(let element):
313+
if let view = self.UIElement {
314+
binding(view, element)
315+
}
316+
case .error(let error):
317+
bindingError(error)
318+
case .completed:
319+
break
320+
}
321+
}
322+
323+
/// Erases type of observer.
324+
///
325+
/// - returns: type erased observer.
326+
public func asObserver() -> AnyObserver<Value> {
327+
return AnyObserver(eventHandler: on)
328+
}
329+
}
330+
331+
332+
#if os(iOS)
333+
extension Reactive where Base: UIRefreshControl {
334+
335+
/// Bindable sink for `beginRefreshing()`, `endRefreshing()` methods.
336+
@available(*, deprecated, renamed: "isRefreshing")
337+
public var refreshing: Binder<Bool> {
338+
return self.isRefreshing
339+
}
340+
}
341+
#endif

RxCocoa/RxCocoa.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ extension RxCocoaError {
6464

6565
// MARK: Error binding policies
6666

67-
func bindingErrorToInterface(_ error: Swift.Error) {
68-
let error = "Binding error to UI: \(error)"
67+
func bindingError(_ error: Swift.Error) {
68+
let error = "Binding error: \(error)"
6969
#if DEBUG
7070
rxFatalError(error)
7171
#else

RxCocoa/Traits/ControlProperty.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public struct ControlProperty<PropertyType> : ControlPropertyType {
102102
public func on(_ event: Event<E>) {
103103
switch event {
104104
case .error(let error):
105-
bindingErrorToInterface(error)
105+
bindingError(error)
106106
case .next:
107107
_valueSink.on(event)
108108
case .completed:

RxCocoa/Traits/UIBindingObserver.swift

Lines changed: 0 additions & 64 deletions
This file was deleted.

RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S: Sequence>
5050
}
5151

5252
func collectionView(_ collectionView: UICollectionView, observedEvent: Event<S>) {
53-
UIBindingObserver(UIElement: self) { collectionViewDataSource, sectionModels in
53+
Binder(self) { collectionViewDataSource, sectionModels in
5454
let sections = Array(sectionModels)
5555
collectionViewDataSource.collectionView(collectionView, observedElements: sections)
5656
}.on(observedEvent)

RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class RxPickerViewSequenceDataSource<S: Sequence>
3737
typealias Element = S
3838

3939
func pickerView(_ pickerView: UIPickerView, observedEvent: Event<S>) {
40-
UIBindingObserver(UIElement: self) { dataSource, items in
40+
Binder(self) { dataSource, items in
4141
dataSource.items = items
4242
pickerView.reloadAllComponents()
4343
}

RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class RxTableViewReactiveArrayDataSourceSequenceWrapper<S: Sequence>
5050
}
5151

5252
func tableView(_ tableView: UITableView, observedEvent: Event<S>) {
53-
UIBindingObserver(UIElement: self) { tableViewDataSource, sectionModels in
53+
Binder(self) { tableViewDataSource, sectionModels in
5454
let sections = Array(sectionModels)
5555
tableViewDataSource.tableView(tableView, observedElements: sections)
5656
}.on(observedEvent)

RxCocoa/iOS/UIActivityIndicatorView+Rx.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import RxSwift
1616
extension Reactive where Base: UIActivityIndicatorView {
1717

1818
/// Bindable sink for `startAnimating()`, `stopAnimating()` methods.
19-
public var isAnimating: UIBindingObserver<Base, Bool> {
20-
return UIBindingObserver(UIElement: self.base) { activityIndicator, active in
19+
public var isAnimating: Binder<Bool> {
20+
return Binder(self.base) { activityIndicator, active in
2121
if active {
2222
activityIndicator.startAnimating()
2323
} else {

RxCocoa/iOS/UIAlertAction+Rx.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import RxSwift
1717
extension Reactive where Base: UIAlertAction {
1818

1919
/// Bindable sink for `enabled` property.
20-
public var isEnabled: UIBindingObserver<Base, Bool> {
21-
return UIBindingObserver(UIElement: self.base) { alertAction, value in
20+
public var isEnabled: Binder<Bool> {
21+
return Binder(self.base) { alertAction, value in
2222
alertAction.isEnabled = value
2323
}
2424
}

RxCocoa/iOS/UIApplication+Rx.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
extension Reactive where Base: UIApplication {
1717

1818
/// Bindable sink for `networkActivityIndicatorVisible`.
19-
public var isNetworkActivityIndicatorVisible: UIBindingObserver<Base, Bool> {
20-
return UIBindingObserver(UIElement: self.base) { application, active in
19+
public var isNetworkActivityIndicatorVisible: Binder<Bool> {
20+
return Binder(self.base) { application, active in
2121
application.isNetworkActivityIndicatorVisible = active
2222
}
2323
}

RxCocoa/iOS/UIBarButtonItem+Rx.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@ fileprivate var rx_tap_key: UInt8 = 0
1818
extension Reactive where Base: UIBarButtonItem {
1919

2020
/// Bindable sink for `enabled` property.
21-
public var isEnabled: UIBindingObserver<Base, Bool> {
22-
return UIBindingObserver(UIElement: self.base) { UIElement, value in
23-
UIElement.isEnabled = value
21+
public var isEnabled: Binder<Bool> {
22+
return Binder(self.base) { element, value in
23+
element.isEnabled = value
2424
}
2525
}
2626

2727
/// Bindable sink for `title` property.
28-
public var title: UIBindingObserver<Base, String> {
29-
return UIBindingObserver(UIElement: self.base) { UIElement, value in
30-
UIElement.title = value
28+
public var title: Binder<String> {
29+
return Binder(self.base) { element, value in
30+
element.title = value
3131
}
3232
}
3333

3434
/// Reactive wrapper for target action pattern on `self`.
35-
public var tap: ControlEvent<Void> {
35+
public var tap: ControlEvent<()> {
3636
let source = lazyInstanceObservable(&rx_tap_key) { () -> Observable<Void> in
3737
Observable.create { [weak control = self.base] observer in
3838
guard let control = control else {

0 commit comments

Comments
 (0)