Mutable observable object in RxSwift or Combine - ios

What I'm trying to achieve is to have a mutable Array in viewModel that can also be observed by Controller and reload tableView when its value changes.
I tried to use BehaviorRelay but looks like it's immutable. I can't delete and insert data inside it's value.
The best thing I managed to write is this custom ObservableObject class:
final class CustomObservableObject<T> {
var value: T {
didSet {
listener?(value)
}
}
init(value: T) {
self.value = value
}
var listener: ((T) -> Void)?
func bind(listener: #escaping ((T) -> Void)) {
self.listener = listener
}
}
which works exactly how I want. Is there anything I can use in the Combine or RxSwift framework like this class?
Thanks in advance

A BehaviorRelay is built for the specific purpose of storing state and allowing you to imperatively update it which is what you are asking for.
The BehaviorRelay type is a wrapper around the BehaviorSubject type and provides a different interface, one that does not allow the internal subject to emit an error or completed event. The Element contained in the BehaviorSubject is mutable. However, in order to mutate it you must first extract it into a var with value and then push the updated value back in with accept(_:). This will help ensure that no mutations are missed and allows you to batch up multiple mutations into a single update. These are things missing in the class you wrote.
Please always remember though:
Subjects [and Relays] provide a convenient way to poke around Rx, however they are not recommended for day to day use.
-- Introduction to Rx
As you learn more about, and grow more comfortable with, the reactive paradigm you will find yourself reaching for this particular crutch less often.

Related

Refactoring large Swift struct with mutating functions

I'm trying to simplify a huge file in Swift and I'm not sure what would be the best approach. It might be a very naive question as I'm still fairly new to Swift :)
For the sake of simplicity, let's say I have one User struct and in it I can call a ton of different setupModeA, setupModeB and so on, called depending on various conditions:
struct User {
var currentMode: String
mutating func setupModeA() {
currentMode = "A"
}
mutating func setupModeB() {
currentMode = "B"
}
// and so on for each mode
}
I'd like to be able to take the setupMode*() functions and their associated helper functions into a separate file to make it easier to read through, since most of the time I'd be focusing on only one mode.
I was thinking:
struct ModeA {
mutating func setup() {
// change the mode from there
}
}
However I can't figure out how to simply pass and mutate the User info to this new struct.
I thought about:
initializing a bunch of ModeX objects with a user variable in the User struct and passing the User on init, but it creates weird behaviours.
using static methods on the ModeX structs, but then I'm not quite sure how to mutate the User
taking a more functional approach & not mutating the User but instead cloning it and re-assigning it each time... this would probably work but since my code is very sequencial it feels like added complexity / memory usage for nothing.
switching from struct to classes and hoping it gives me other options... but I'm afraid everything would break
really complex things that feel too weird and unintuitive to be mentioned here :)
Any pointers would be much appreciated.

What is PassthroughSubject & CurrentValueSubject

I happen to look into Apple's new Combine framework, where I see two things
PassthroughSubject<String, Failure>
CurrentValueSubject<String, Failure>
Can someone explain to me what is meaning & use of them?
I think we can make analogies with real world cases.
PassthroughSubject = A doorbell push button
When someone rings the door, you are notified only if you are at home (you are the subscriber)
PassthroughSubject doesn't have a state, it emits whatever it receives to its subscribers.
CurrentValueSubject = A light switch
Someone turns on the lights in your home when you are outside. You get back home and you know someone has turned them on.
CurrentValueSubject has an initial state, it retains the data you put in as its state.
Both PassthroughSubject and CurrentValueSubject are publishers that conform to the Subject protocol which means you can call send on them to push new values downstream at will.
The main difference is that CurrentValueSubject has a sense of state (current value) and PassthroughSubject simply relays values directly to its subscribers without remembering the "current" value:
var current = CurrentValueSubject<Int, Never>(10)
var passthrough = PassthroughSubject<Int, Never>()
current.send(1)
passthrough.send(1)
current.sink(receiveValue: { print($0) })
passthrough.sink(receiveValue: { print($0) })
You'd see that the current.sink is called immediately with 1. The passthrough.sink is not called because it has no current value. The sink will only be called for values that are emitted after you subscribe.
Note that you can also get and set the current value of a CurrentValueSubject using its value property:
current.value // 1
current.value = 5 // equivalent to current.send(5)
This isn't possible for a passthrough subject.
PassthroughSubject is used for representing events. Use it for events like button tap.
CurrentValueSubject is used representing state. Use it for storing any value, say state of switch as off and on.
Note: #Published is kind of CurrentValueSubject.
PassthroughSubject and CurrentValueSubject are both Publishers — a type introduced by Combine — that you can subscribe to (performing operations on values when values are available).
They both are designed to make it easy to transfer to using the Combine paradigm. They both have a value and an error type, and you can "send" values to them (making the values available to all subscribers)
The main difference between the two that I've seen is that CurrentValueSubject starts with a value, while PassthroughSubject does not. PassthroughSubject seems easier to grasp conceptually, at least for me.
PassthroughSubject can easily be used in place of a delegate pattern, or to convert an existing delegate pattern to Combine.
//Replacing the delegate pattern
class MyType {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func doSomething() {
//do whatever this class does
//instead of this:
//self.delegate?.handleValue(value)
//do this:
publisher.send(value)
}
}
//Converting delegate pattern to Combine
class MyDel: SomeTypeDelegate {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func handle(_ value: String) {
publisher.send(value)
}
}
Both of these examples use String as the type of the value, while it could be anything.
Hope this helps!
PassthroughSubject is suitable for event like tap action
CurrentValueSubject is suitable for state
Already a lot of good answers posted in this thread, just thought of adding this answer for someone who is coming from using RxSwift.
PassThroughSubject is like PublishSubject where it broadcasts an event to its subscribers, probably with some value passed along.
CurrentValueSubject is similar to BehaviorRelay where a single value is persisted with the subject instance and passed along during the event broadcast.

PromiseKit to act as RxSwift on variable observing

I use RxSwift at work (ont a big fan, too complex, but really powerful indeed), and I want to try to use PromiseKit as a "smaller" tool.
I try to create sort of an Observable with PromiseKit, but I can't figure it out. I tried to use the NSObject's extension observe(key path: String) -> Promise, but it does not seems to work.
I noted that I have a struct (hence does not inherit from NSObject) and I tried to observe an NSObject, but it does not seems to work.
Here's what I've done :
in my controller that has the value I want to observe, I declared a variable as follows :
lazy var homeworkPromise: Promise<Homework> = self.observe(keyPath: "homework")
Somewhere in my code, I update the value of the homework variable.
And in my presentingVC, here's what I do :
create.homeworkPromise
.then(execute: { h -> Void in
print(h)
})
It seems pretty straightforward to me, but I guess I'm missing something ;-)
Thanks for your help.

Creating an observable with RxSwift

I'm using RxSwift and I'm trying to extend another library I'm using to make something observable.
The library basically calls a delegate method every time a value changes, and I want to hook into this and whenever it calls the delegate, also add the new value to an observable sequence I want to create in my subclass.
I've seen how you can create observable sequences, but in each example next events are sent to the observer within the block given to the Observable.create method. I have no idea how I can add things to the observable sequence from "outside" this block passed to create.
I just want to create something that I can observe or can drive things with RxSwift, and manually add to the sequence at certain points.
I'd be very grateful if someone could point me in the right direction, as I'm very new to this.
Observable is readonly interface. Sequences created by Observable.create can only produce the value(s) given at construction time and nothing more. You can't "add things" to it, to use your words. Speaking in RxSwift terms, you can't do away with just Observable interface, you need also ObserverType - it must also observe your mutating value. There is more than one way to do it in RxSwift, but i think that you need PublishSubject:
let value = PublishSubject<YourType>()
let disposer = DisposeBag()
init() {
value.subscribe(onNext: { (newValue) in
// use newValue ...
}).addDisposableTo(disposer)
}
func yourDelegateHandler(newValue: YourType)
{
value.onNext(newValue)
}

RxSwift - Custom class as observable

I'm creating a custom generic TableView model to work with MVVM and RxSwift. I'm aware of the RxTableViewSectionedReloadDataSource class but not willing to use it now in my project.
The model is created and working
typealias TableViewModel = TableModel<CellDescriptor>
class TableModel<T> {
var sections = [SectionModel<T>]()
func add(item: SectionModel<T>) {
sections.append(item)
}
// More funcs there...
}
I created an instance inside my ViewModel as such :
var tableViewModel = Variable<TableViewModel>(TableViewModel())
And then listen to event in my viewController
viewModel.tableViewModel.asObservable().subscribe({ [weak self] value in
self?.tableView.reloadData()
}).addDisposableTo(dispose)
Several questions here (I'm fairly new to FRP)
:
How can I "emit" an event to trigger my subscription in the viewController from my custom class?
I know that a solution would be to create my sections array as a RxSwift Variable() and then listen to it directly, but wonder if there's a way to make a class (or struct) itself Observable.
Is there a better approach to this problem ? I went through the RxSwift playground and example project but it sounds like there's a thousand ways to do the same thing. Maybe an Observable is not what i would be looking for. :)
Don't make the view model itself an Observable. It's unnecessary complexity and it's just not intuitive. If you really wanted to, then take a look at how Variable is implemented, which should give you an idea of how to do it.
Instead, just use a subject (such as Variable) to hold onto your data within your view model.

Resources