How to make a proper reactive extension on Eureka SelectableSection - swift

This is my first question to the StackOverflow community so excuse me if I'm doing something wrong.
1. What I'm trying to achieve
Basically, I want to make a custom reactive wrapper around Eureka's SelectableSection class in order to observe the value of the selected row when it is changed. I'm thinking to get this data from the onSelectSelectableRow closure which is called every time a row is selected.
2. What I've tried to do for that
Actually, I've got this working but it's not a generic use of the custom wrapper, here is the example that works but only when I specify the row and its value type, for example ListCheckRow<Int>.
extension SelectableSection: ReactiveCompatible {}
extension Reactive where Base : SelectableSection<ListCheckRow<Int>> {
var selectedValue: Observable<Base.SelectableRow.Cell.Value?> {
return Observable.create { observer in
self.base.onSelectSelectableRow = {cell, row in
observer.onNext(row.value)
}
return Disposables.create {
observer.onCompleted()
}
}
}
}
This works fine and as I expected but when it comes to something more generic like the next code example, I get an error saying that: "Cannot assign to property: 'base' is a 'let' constant"
extension SelectableSection: ReactiveCompatible {}
extension Reactive where Base : SelectableSectionType {
var selectedValue: Observable<Base.SelectableRow.Cell.Value?> {
return Observable.create { observer in
self.base.onSelectSelectableRow = {cell, row in // Error: Cannot assign to property: 'base' is a 'let' constant
observer.onNext(row.value)
}
return Disposables.create {
observer.onCompleted()
}
}
}
}
Any help will be much appreciated, thanks. 🙏

The fundamental problem here is that SelectableSectionType is a protocol that isn't restricted to class types and Reactive assumes that Base is a class (or otherwise is not going to be modified by the observable creation.)
I think the most generic you can make this is something like:
extension Reactive {
func selectedValue<Row, T>() -> Observable<T?> where Base: SelectableSection<Row>, Row: SelectableRowType, T == Row.Cell.Value {
Observable.create { [base] observer in
base.onSelectSelectableRow = { cell, row in
observer.onNext(row.value) // this is problematic. See below.
}
return Disposables.create {
observer.onCompleted() // this is wrong. See below.
}
}
}
}
The biggest problem with the above though is that if you subscribe to the resulting Observable more than once or create more than one Observable using this computed property, all but the last subscription will silently fail. The simple way to fix this is to always remember to share any result but that's rather error prone.
The way to fix this would be to associate a Subject with each SelectableSection, but you can't modify the class, so what are we to do?
Here's a solution:
extension Reactive {
func selectedValue<Row, T>() -> Observable<T?> where Base: SelectableSection<Row>, Row: SelectableRowType, T == Row.Cell.Value {
Observable.create { [base] observer in
if let block = selectableSections.first(where: { $0.section === base }) {
let subject = block.subject as! PublishSubject<T?>
return Disposables.create(
block.disposable.retain(),
subject.subscribe(observer)
)
}
else {
let subject = PublishSubject<T?>()
let block = SelectableSectionBlock(
section: base,
subject: subject,
disposable: RefCountDisposable(disposable: Disposables.create {
selectableSections.removeAll(where: { $0.section === base })
})
)
base.onSelectSelectableRow = { cell, row in
subject.onNext(row.value)
}
selectableSections.append(block)
return Disposables.create(
block.disposable,
subject.subscribe(observer)
)
}
}
}
}
private struct SelectableSectionBlock {
let section: Section
let subject: Any
let disposable: RefCountDisposable
}
private var selectableSections = [SelectableSectionBlock]()
The selectableSections array stores a Subject and RefCountDisposable for each SelectableSection.
Whenever an Observable is created, or subscribed to...
if it's the first time working with this section, it will create a Subject and RefCountDisposable assign the onSelectSelectableRow to send a next event to the subject and store the subject in the array.
otherwise it will find the subject and disposable associated with this Section and retain the disposable.
Once it has the subject and disposable from above, it will subscribe the new observer to the subject and return a new Disposable that will remove that subscription and decrement the ref-count when the time comes.
Yes this is quite a bit more complex than the simple assignment case, but it's the right thing to do.
As for calling onCompleted() inside the disposable closure. By the time the closure is called, the observer has already emitted an onCompleted/onError event, or the observer has stopped listening to the observable. So this event will never be seen.

Related

Is there a way to check if downstream handles a event a publisher publishes in Combine?

I have an event bus that publishes events happening over a custom text field(a chat message input box), like attachMediaEvent, sendMessageEvent, selectTextEvent. I want to provide provide default actions for the events, but only in case downstream doesn't handle it(kind of like how one can provide protocol default implementation). Is there a way to check if the downstream handles the event and handle it if doesn't?
I know that I'm being loose with the phrase handling the event but it was intentional and I would like any sort of solution for it. And I'm not adding any code, but I can do so, if needed. It is a really simple publisher that pushes events, anyhow.
EDIT:
Ok, I dug deeper in SO and I started to get the idea that I had to implement custom operator. But I have hit the exact same roadblock. I have no idea how a downstream can speak to the upstream about how it has handled its values. Although I tried to create a protocol(EventConsumer) that one could check if implemented by a object that is passed to the subscriber, but which felt very wrong. Any help would be a godsend.
Here's the code I tried in playground.
#objc protocol EventConsumer {
#objc optional func sendEventAction()
#objc optional func attachEventAction()
}
class DefaultEventConsumer: EventConsumer {
}
struct CustomPublisher<Upstream: Publisher>: Publisher where Upstream.Output == String, Upstream.Failure == Never {
typealias Output = String
typealias Failure = Never
var upstreamEventPublisher: Upstream
// var eventConsumer: EventConsumer
func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, String == S.Input {
let subscription = CustomSubscription<S>(/*eventHandler: eventConsumer*/)
subscription.downstream = subscriber
subscriber.receive(subscription: subscription)
upstreamEventPublisher.sink { eventString in
subscription.sendEvent(event: eventString)
}
}
}
class CustomSubscription<Downstream: Subscriber>: Subscription where Downstream.Input == String {
var downstream: Downstream?
// var eventHandler: EventConsumer
// init(eventHandler: EventConsumer) {
// self.eventHandler = eventHandler
// }
func request(_ demand: Subscribers.Demand) {
}
func cancel() {
downstream = nil
}
func sendEvent(event: String) {
//How do I check if downstream processes the event somehow and send event if it doesnt
downstream?.receive(event)
}
}
//
//
//extension AnyPublisher where Output == String, Failure == Never {
//
// func checkAndPublishEvents(_ eventConsumer: EventConsumer) -> Self {
//
// CustomPublisher(upstreamEventPublisher: self/*, eventConsumer: eventConsumer*/)
// .eraseToAnyPublisher()
// }
//}

Not getting all expected events when subscribing to sequence with TestScheduler

I'm trying to write an integration test for a Reactor in an app built with ReactorKit and Realm/RxRealm.
I'm having trouble using TestScheduler to simulate user actions and test the expected emitted states.
In a nutshell, my problem is this: I'm binding an action that will make my Reactor save an item to Realm, my Reactor also observes changes to this object in Realm, and I expect my Reactor to emit the new state of this item observed from Realm.
What I'm seeing is that my test does not get the emission of the newly saved object in time to assert its value, it's emitted after my test assertion runs.
There is a fair amount of code involved, but attempting to whittle it down into a self-contained example of what it all roughly looks like below:
struct MyObject {
var counter: Int = 0
}
class MyReactor: Reactor {
enum Action {
case load
case mutateState
}
enum Mutation {
case setObject(MyObject)
}
struct State {
var object: MyObject?
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .load:
return service.monitorObject().map(Mutation.setObject)
case .mutateState:
guard var myObject = currentState.object else { return .empty() }
myObject.counter += 1
return service.save(myObject).andThen(.empty())
}
}
func reduce(state: State, mutation: Mutation) -> Observable<State> {
var newState = state
switch mutation {
case let .setObject(object):
// Should be called twice in tests, once on load, once after mutateState action
newState.object = object
}
}
}
struct Service {
// There is always at least one default instance of `MyObject` in Realm.
func monitorObject() -> Observable<MyObject> {
return Observable
.collection(from: realm.objects(MyObject.self))
.map { $0.first! }
}
func save(_ object: MyObject) -> Completable {
return Completable.create { emitter in
try! realm.write {
realm.add(object, update: .modified)
}
emitter(.completed)
return Disposables.create()
}
}
}
class MyTest: QuickSpec {
var scheduler: TestScheduler!
var sut: MyReactor!
var disposeBag: DisposeBag!
var service: Service!
var config: Realm.Configuration!
override func spec() {
beforeEach {
config = Realm.Configuration(inMemoryIdentifier: UUID().uuidString)
scheduler = TestScheduler(initialClock: 0)
disposeBag = DisposeBag()
sut = MyReactor()
service = Service(realmConfig: config)
}
describe("when my reactor gets a mutateState action") {
it("should mutate state") {
scheduler.createHotObservable([
.next(1, Action.load),
.next(2, Action.mutateState),
])
.bind(to: sut.action)
.disposed(by: disposeBag)
let response = scheduler.start(created: 0, subscribed: 0, disposed: 1000) {
sut.state.map(\.object)
}
// Counter always equals 0
XCTAssertTrue(response.events.last!.value.element!!.counter == 1)
}
}
}
}
What I'm expecting to happen is my Reactor's state is set for a 2nd time, before the XCTAssertTrue is hit. What is actually happening is the assert is hit with the initially loaded state, and then, my reactor's state is set again.
I thought my problem might be related to schedulers. Something I tried was injecting the test scheduler into my Service and doing observeOn(testScheduler) on my monitorObject function. But I'm still observing the assert get hit before the reactor's state is set for the 2nd time. I'm also not sure if a nuance of RxRealm/Realm change set notifications is the cause - not sure how to verify whether that might be the case.
Hopefully the problem and question is clear. Thanks in advance for any help.
I decided attempting to write an integration test was more trouble than it was worth and probably not going to result in very useful tests anyway.
So you are trying to test to see if Realm works. I don't use Realm, but based on your description, it probably updates the object on an internal thread and then you get the emission on a subsequent cycle.
You can test it by using an XCTestExpectation. Here is documentation from Apple: https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations/testing_asynchronous_operations_with_expectations
Note however, that if something goes wrong in Realm and this test fails, there isn't anything you can do about it.

RxSwift state changes trigger "Warning: Recursive call or synchronization error!"

I've inherited some Swift 3 code which uses RxSwift to manage a store. The basic layout of the class is:
class UserActivityStore {
fileprivate lazy var internalDataCache: Variable<Set<NSUserActivity>?> = Variable(nil)
func addAction(_ actionToAdd: NSUserActivity) {
var content = internalDataCache.value ?? Set<NSUserActivity>()
content.insert(actionToAdd)
internalDataCache.value = content
}
func resolveAction(_ action: NSUserActivity) {
var content = internalDataCache.value
_ = content?.remove(action)
internalDataCache.value = content
}
func expectActions(_ filter: #escaping ((NSUserActivity) -> Bool)) -> Observable<NSUserActivity> {
let observable = Observable<NSUserActivity>.create { (observer) -> Disposable in
return self.internalDataCache.asObservable().subscribeNext { (newActions) in
guard let newActions = newActions else { return }
for eachAction in newActions where filter(eachAction) {
observer.onNext(eachAction)
self.resolveAction(eachAction)
}
}
}
return observable
}
}
When an action is added to this, it adds the item to the set correctly. However, the observer (in expectActions) catches that change and resolves it. Since this is all in a single thread, the error "Warning: Recursive call or synchronization error!" is triggered.
I think this is a perfectly legitimate error and that RxSwift is probably correct in its handling. However, I can't help thinking that this is a bad model. The code is essentially handling a queue of NSUserActivity objects itself.
Is this genuinely a modelling error / abuse of RxSwift or is my limited understanding of RxSwift misunderstanding this? As a hacky solution, I've tried replacing the resolveAction function with a single line internalDataCache.value?.remove(action) but that still triggers the observable and hence the bug.
Changing the observable to use a different queue (Serial or Concurrent dispatch) fixes the problem but I'm not convinced its the correct fix.

RxSwift: Nested Queries and ReplaySubject

I have to fetch three types of data (AType, BType, CType) using three separate API requests. The objects returned by the APIs are related by one-to-many:
1 AType object is parent of N BType objects
1 BType object is parent of P CType objects)
I'm using the following three functions to fetch each type:
func get_A_objects() -> Observable<AType> { /* code here */ }
func get_B_objects(a_parentid:Int) -> Observable<BType> { /* code here */}
func get_C_objects(b_parentid:Int) -> Observable<CType> { /* code here */}
and to avoid nested subscriptions, these three functions are chained using flatMap:
func getAll() -> Observable<CType> {
return self.get_A_objects()
.flatMap { (aa:AType) in return get_B_objects(aa.id) }
.flatMap { (bb:BType) in return get_C_objects(bb.id) }
}
func setup() {
self.getAll().subscribeNext { _ in
print ("One more item fetched")
}
}
The above code works fine, when there are M objects of AType, I could see the text "One more item fetched" printed MxNxP times.
I'd like to setup the getAll() function to deliver status updates throughout the chain using ReplaySubject<String>. My initial thought is to write something like:
func getAll() -> ReplaySubject<String> {
let msg = ReplaySubject<String>.createUnbounded()
self.get_A_objects().doOnNext { aobj in msg.onNext ("Fetching A \(aobj)") }
.flatMap { (aa:AType) in
return get_B_objects(aa.id).doOnNext { bobj in msg.onNext ("Fetching B \(bobj)") }
}
.flatMap { (bb:BType) in
return get_C_objects(bb.id).doOnNext { cobj in msg.onNext ("Fetching C \(cobj)") }
}
return msg
}
but this attempt failed, i.e., the following print() does not print anything.
getAll().subscribeNext {
print ($0)
}
How should I rewrite my logic?
Problem
It's because you're not retaining your Disposables, so they're being deallocated immediately, and thus do nothing.
In getAll, you create an Observable<AType> via get_A_objects(), yet it is not added to a DisposeBag. When it goes out of scope (at the end of the func), it will be deallocated. So { aobj in msg.onNext ("Fetching A \(aobj)") } will never happen (or at least isn't likely to, if it's async).
Also, you aren't retaining the ReplaySubject<String> returned from getAll().subscribeNext either. So for the same reason, this would also be a deal-breaker.
Solution
Since you want two Observables: one for the actual final results (Observable<CType>), and one for the progress status (ReplaySubject<String>), you should return both from your getAll() function, so that both can be "owned", and their lifetime managed.
func getAll() -> (Observable<CType>, ReplaySubject<String>) {
let progress = ReplaySubject<String>.createUnbounded()
let results = self.get_A_objects()......
return (results, progress)
}
let (results, progress) = getAll()
progress
.subscribeNext {
print ($0)
}
.addDisposableTo(disposeBag)
results
.subscribeNext {
print ($0)
}
.addDisposableTo(disposeBag)
Some notes:
You shouldn't need to use createUnbounded, which could be dangerous if you aren't careful.
You probably don't really want to use ReplaySubject at all, since it would be a lie to say that you're "fetching" something later if someone subscribes after, and gets an old progress status message. Consider using PublishSubject.
If you follow the above recommendation, then you just need to make sure that you subscribe to progress before results to be sure that you don't miss any progress status messages, since the output won't be buffered anymore.
Also, just my opinion, but I would re-word "Fetching X Y" to something else, since you aren't "fetching", but you have already "fetched" it.

What's the best Swift pattern for returning a pending PromiseKit promise if one is already in running?

I have some expensive promises that get called in different spots. Ideally, I'd like to just chain off an existing in-flight promise (with an optional force), so I find myself doing something like this:
class Expensive {
var fooPromise : Promise<Foo>?
var barPromise : Promise<Bar>?
func doExpensiveFoo(force: bool = false) -> Promise<Foo> {
if let existing = fooPromise where existing.pending || (existing.fufilled && !force) {
// Return the existing promise
return existing
}
// Start a new Foo
return firstly {
// ...
}
}
func doExpensiveBar(force: bool = false) -> Promise<Bar> {
if let existing = barPromise where existing.pending || (existing.fufilled && !force) {
// Return the existing promise
return existing
}
// Start a new Bar
return firstly {
// ...
}
}
}
But that feels like a fair amount of boiler-plate (a local variable for each promise, and the existing chunk at the start of each function), so I'm wondering if anyone has seen a good pattern for abstracting away the variables and wrapper?
To borrow a term from Python, I'm looking for a decorator that would hide all that. Something like:
class Expensive {
private func startFoo() -> Promise<Foo> {
return firstly {
//..
}
}
public doExpensiveFoo = wrapExpensive(startFoo)
}
Any suggestions, or should I look at rolling my own?
I'm no expert, but this pattern worked for me:
private var fooPromise : Promise<Foo>?
func doExpensiveFoo() -> Promise<Foo> {
if let fooPromise = self.fooPromise, fooPromise.isPending {
// return the pending promise
return fooPromise
}
// reassign a newly created promise
fooPromise = firstly {
// do your thing
...
}
return fooPromise!
}
What I like about this pattern is that the method handles pending state internally, and that the promise automatically re-executes if called after it is finished. This allows callers to be ignorant of the internal mechanism or the state of the promise. Obviously, if you need to caller to be part of the decision, then keep the "force" flag approach.
I do not see any common base of Foo and Bar in your example. But even if they would have one Swift still does not support covariance on generic type parameters. At first you would need to create a common protocol for both types. Maybe this helps you to get on track:
Storing generic objects in Swift Array