I have a viewmode class like this :
class ViewMode {
let validateCountResult: Driver<Bool>
init(username: Driver<String>) {
validateCountResult = username
.flatMapLatest { username in
return // validate username
}
}
And I have a subclass of UIViewController as follow :
class ViewController : UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
let viewmode =
ViewMode(textfiled.rx.texttext.orEmpty.asDriver())
viewmode.validateCountResult.drive(onNext:{
// TODO Something
FuncA()
})
}
}
When viewDidload finishes, I believe the viewmode should deinits as well. But I see the binding still exists and FuncA still get called!
Why is it so?
The easiest way to understand binding is to think of the relationship as a connection between two entities:
A producer, which produces the value。
A receiver, which processes the values from the producer.
A receiver cannot return a value. This is a general rule when using bindings of RxSwift.
The fundamental function of binding is bind(to:), and to bind an observable to another entity it's required that the receiver conforms to ObserverType.
You should dispose your Disposable types by creating a DisposeBag.
If you don’t, there is no guarrantee that your viewController even deinits or your disposable types get disposed as well. They create a mutal like together via your ViewMode and all three objects stay in memory.
Take a look at this article to see how to find memory leaks.
Related
I am using RxSwift and Swinject in my project. The way I bind the inputs / outputs is not exactly the same as in the example given by RxSwift. In RxExample/GitHubSignup, the binding is done in the init(), right? But I found it difficult to implement because I use Swinject+SwinjectStoryboard to do Dependency Injection to the View Controller. Thus, the init() is unavailable because the one instantiating the View Models is the Swinject container. So, is there a way to bind the view controller and view model together besides using init()?
I was thinking that I can maybe use var instead of let for the output observables and make a func bind(observables: [Observable]) or something that will do the bindings and transformations from input to output instead. But because they will be vars and not lets, that means it seems like we are permitted to change the bindings throughout the code. Unlike when we just use lets and bind them in the init(). And also, by using a function instead of the initializer, I have to store the dependencies into a member variable. While if I use the initializer, I can just transform the dependencies inside of a map or flatMap.
And also I have another question. Say, if I have this:
class MyViewController: UIViewController {
#IBOutlet weak var refreshButton: UIButton!
#IBOutlet weak var tableView: UITableView!
var viewModel: MyViewModel!
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel = MyViewModel(refreshTap: refreshButton.rx.tap, dataProvider: ApiAdapter().getData)
}
private func setupEvents() {
viewModel.tableDTOs.bind(to: tableView.rx.items(
cellIdentifier: reuseId, cellType: TableViewCell.self)) { _, dto, cell in
cell.fill(with: dto)
}.disposed(by: disposeBag)
}
}
final class MyViewModel {
let tableDTOs: Observable<[TableDTO]>
init(refreshTap: Observable<Void>, dataProvider: () -> Observable<[TableDTO]>) {
tableDTOs = Observable.merge(.just(), refreshTap) //Merge with .just to emit at once for initial values
.flatMapLatest { dataProvider().asDriver() }
}
}
So in this case, if the dataProvider returned complete or error, the disposable will be disposed, right? So the scene will be unresponsive because the UI is already unbound. Any idea how to fix that?
Thanks.
So, is there a way to bind the view controller and view model together besides using init()?
Yes there is. Give the view model a function that takes the inputs and returns the outputs.
But because they will be vars and not lets, that means it seems like we are permitted to change the bindings throughout the code.
Don't ever make an Observable (or Subject, or Observer) a var always use let Functional Reactive Programming is a functional paradigm so no vars.
So in this case, if the dataProvider returned complete or error, the disposable will be disposed, right? So the scene will be unresponsive because the UI is already unbound. Any idea how to fix that?
Yes and no. If the dataProvider emits a completed event, that will not dispose because the flatMapLatest only disposes if all of its inputs complete. Since the refreshTap has not yet completed, the flatMapLatest will continue to accept events from it and call it's closure for each one.
If the dataProvider emits an error event, that will dispose because and error event short circuits the chain. However, since you use .asDriver() on your dataProvider, the Driver returned from the closure can't possibly emit an error event. You're safe.
Other ways of stopping the error from breaking the chain are to use .materialize() or any of the .catchError operators. For example:
.flatMapLatest {
dataProvider
.map { Result<[TableDTO], Error>.success($0) }
.catchError { Observable.just(Result<[TableDTO], Error>.failure($0) }
}
I have a view structured like a form that creates a model object. I am trying to bind the form elements (UIControl) to the model properties, so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding). The model can change without the view knowing because multiple views can be linked to one same model property.
Approach 1: Plain Swift
My problem is the following: to observe changes to the model properties, I tried to use KVO in Swift, and specifically the observe(_:changeHandler:) method.
class Binding<View: NSObject, Object: NSObject, ValueType> {
weak var object: Object?
weak var view: View?
var objectToViewObservation: NSKeyValueObservation?
var viewToObjectObservation: NSKeyValueObservation?
private var objectKeyPath: WritableKeyPath<Object, ValueType>
private var viewKeyPath: WritableKeyPath<View, ValueType>
init(betweenObject objectKeyPath: WritableKeyPath<Object, ValueType>,
andView viewKeyPath: WritableKeyPath<View, ValueType>) {
self.objectKeyPath = objectKeyPath
self.viewKeyPath = viewKeyPath
}
override func bind(_ object: Object, with view: View) {
super.bind(object, with: view)
self.object = object
self.view = view
// initial value from object to view
self.view![keyPath: viewKeyPath] = self.object![keyPath: objectKeyPath]
// object --> view
objectToViewObservation = object.observe(objectKeyPath) { _, change in
guard var view = self.view else {
// view doesn't exist anymore
self.objectToViewObservation = nil
return
}
guard let value = change.newValue else { return }
view[keyPath: self.viewKeyPath] = value
}
// view --> object
viewToObjectObservation = view.observe(viewKeyPath) { _, change in
guard var object = self.object else {
// object doesn't exist anymore
self.viewToObjectObservation = nil
return
}
guard let value = change.newValue else { return }
object[keyPath: self.objectKeyPath] = value
}
}
}
However some of the properties of my model have types CustomEnum, CustomClass, Bool?, and ClosedRange<Int>, and to use observe I had to mark them as #objc dynamic, which yielded the error:
Property cannot be marked #objc because its type cannot be represented in Objective-C
Approach 2: Using RxSwift rx.observe
I turned to RxSwift and the rx.observe method thinking I could work around this problem, but the same thing happened (at runtime this time).
// In some generic bridge class between the view and the model
func bind(to object: SomeObjectType) {
object.rx
.observe(SomeType.self, "someProperty")
.flatMap { Observable.from(optional: $0) }
.bind(to: self.controlProperty)
.disposed(by: disposeBag)
}
Approach 3: Using RxSwift BehaviorRelays?
This is my first experience with RxSwift, and I know I should be using BehaviorRelay for my model, however I don't want to change all my model properties as my model object is working with other framework. I could try to implement a bridge then, to transform model properties into BehaviorRelay, but I would come across the same problem: how to listen for model changes.
In this question, there were no answer as to how to listen for property changes without refactoring all model properties to RxSwift's Variable (currently deprecated).
Approach 4: Using didSet Swift property observer?
The didSet and willSet property observers in plain Swift would allow listening for changes, however this would require to mark all the properties in the model with these observers, which I find quite inconvenient, since my model object has a lot of properties. If there is a way to add these observers at runtime, this would solve my problem.
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Basically, I'm looking for an answer to one of the following questions:
Is there something I overlooked, is there a better way to achieve what I want?
or How to overcome the "Property cannot be marked #objc" problem?
or How to bridge my model object to BehaviorRelay without changing my model?
or How to add didSet observers at runtime?
You said:
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Actually it's not at all common. One idea you don't mention is to wrap your entire model into a Behavior Relay. Then the set of views can modify your model object.
Each of your views, in turn, can observe the model in the behavior relay and update accordingly. This is the basis of, for example, the Redux pattern.
You could also use your approach #3 and use property wrappers to make the code a bit cleaner:
#propertyWrapper
struct RxPublished<Value> {
private let relay: BehaviorRelay<Value>
public init(wrappedValue: Value) {
self.relay = BehaviorRelay(value: wrappedValue)
}
var wrappedValue: Value {
get { relay.value }
set { relay.accept(newValue) }
}
var projectedValue: Observable<Value> {
relay.asObservable()
}
}
But understand that the whole reason you are having this problem is not due to Rx itself, but rather due to the fact that you are trying to mix paradigms. You are increasing the complexity of your code. Hopefully, it's just a temporary increase during a refactoring.
Old Answer
You said you want to make it "so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding)."
IMO, that way of thinking about the problem is incorrect. Better would be to examine each output independently of all other outputs and deal with it directly. In order to explain what I mean, I will use the example of converting °F to °C and back...
This sounds like a great reason to use 2-way binding but let's see?
// the chain of observables represents a view model
celsiusTextField.rx.text // • this is the input view
.orEmpty // • these next two convert
.compactMap { Double($0) } // the view into an input model
.map { $0 * 9 / 5 + 32 } // • this is the model
.map { "\($0)" } // • this converts the model into a view
.bind(to: fahrenheitTextField) // • this is the output view
.disposed(by: disposeBag)
fahrenheitTextField.rx.text
.orEmpty
.compactMap { Double($0) }
.map { ($0 - 32) * 5 / 9 }
.map { "\($0)" }
.bind(to: celsiusTextField.rx.text)
.disposed(by: disposeBag)
The above code handles the two-way communication between the text fields without two-way binding. It does this by using two separate view models (The view model is the code between the text Observable and the text Observer as described in the comments.)
We can see a lot of duplication. We can DRY it up a bit:
extension ControlProperty where PropertyType == String? {
func viewModel(model: #escaping (Double) -> Double) -> Observable<String> {
orEmpty
.compactMap { Double($0) }
.map(model)
.map { "\($0)" }
}
}
You may prefer a different error handling strategy than what I used above. I was striving for simplicity since this is an example.
The key though is that each observable chain should be centered on a particular effect. It should combine all the causes that contribute to that effect, implement some sort of logic on the inputs, and then emit the needed output for that effect. If you do this to each output individually you will find that you don't need two-way binding at all.
I am new to Reactive programming, and I'm trying to observe a boolean value from my ViewModel in order to let my ViewController know when to start/stop the app's loader screen.
It's fairly simple and I want to use this method to avoid unnecessary delegates, since my ViewModel holds the business logic and my ViewController handles the UI.
My problem is this compiler error: Ambiguous reference to member 'subscribe'.
It also adds the two possible candidates, as you can see in the image below:
In my ViewModel, I've declared the observable as PublishSubject:
let done = PublishSubject<Bool>()
And I use it while observing another stream:
func subscribe() {
done.onNext(false)
anotherObservable.subscribe(
// other events observed here but not relevant to this matter
onCompleted: {
self.done.onNext(true)
}).addDisposableTo(rx_disposeBag)
}
And, finally, this is how I'm trying to handle it in the ViewController:
self.model.done.subscribe(
.onNext { isDone in
if isDone {
self.removeLoader()
}
}).addDisposableTo(rx_disposeBag)
I believe there is something simple I'm probably missing, so any help is appreciated.
In your second subscribe should be:
self.model.done.subscribe(onNext: { isDone in
if isDone {
self.removeLoader()
}
}).addDisposableTo(rx_disposeBag)
Can we communicate between classes like below code instead of using delegate/protocol pattern and notification. I haven't seen any code example like this. But just curious to know why this should work or not work.
class Class1 {
var class2Obj: Class2 = Class2()
init() {
class2Obj.class1Obj = self
}
func class1Method() {
print("Parent")
}
}
class Class2 {
weak var class1Obj: Class1?
func class2Method() {
class1Obj.class1Method()
}
}
What you have here is a delegate pattern. Your delegate property is merely called class1Obj rather than the more customary delegate name. The key issue here is that you're not using a protocol, and as a result these two classes are "tightly coupled," i.e. Class2 is highly dependent upon details of the Class1 implementation. Furthermore, with these two tightly coupled classes, it's not immediately clear which methods of Class1 that Class2 might need. It makes maintenance of Class1 harder because so it's easy to accidentally make a change that breaks behavior in Class2. It also makes it hard to use Class2 in conjunction with some other class other than Class1.
Instead, you'd generally declare a protocol to precisely articulate the nature of the contract between Class2 and other object that might need to use it. The result is that the classes are less tightly coupled, i.e. Class2 needs to know nothing about the other class other than its conformance to the protocol in question. Furthermore, when editing Class1, if you declare it to conform the the protocol, the compiler will warn you when you fail to implement some required method or property.
So, with a negligible amount of work up-front, the protocol makes the code much easier to maintain. It also provides some additional flexibility whereby you may use Class2 in conjunction with something other than Class1 in the future.
Bottom line, protocols can result in code that is easier to maintain and is more flexible, with no hidden assumptions.
If you don't want to use the delegate-protocol pattern, another alternative is to use a closure, where Class1 supplies a block of code that Class2 can call. So you can do something like:
class Class1 {
var class2Obj = Class2()
init() {
class2Obj.handler = { [weak self] in // note `weak` reference which avoids strong reference cycle
self?.class1Method()
}
}
func class1Method() {
print("Parent")
}
}
class Class2 {
var handler: (() -> Void)?
func class2Method() {
handler?()
}
}
While the delegate-protocol pattern is useful when you have a rich interface between the two classes, this closure pattern when you have a very simple interface between the two classes.
Frankly, a more common permutation of the above is where the closure is more directly associated with some particular request that Class1 initiates in Class2. So, you might just make the parameter a closure to the appropriate method in Class2. Furthermore, you're often passing data back, so imagine that we're passing back an optional String:
class Class1 {
var class2Obj = Class2()
func performClass2Method() {
class2Obj.class2Method { string in
guard let string = string else { return }
self.class1Method()
}
}
func class1Method() {
print("Parent")
}
}
class Class2 {
func class2Method(completionHandler: #escaping (String?) -> Void) {
// do something which creates `string`
// when done, call the closure, passing that `string` value back
completionHandler(string)
}
}
These closure patterns a great way to do simple interfaces between objects, but also keeps the two classes very loosely coupled (i.e. Class2 has no dependencies upon Class1), much like using a protocol in the delegate pattern did. But hopefully the above closure examples illustrate a simple alternatives to the rich delegate-protocol pattern.
Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.