RxSwift - Who is the Observer? - swift

I've just started studying Rxswift lately. And there's this one question that's been bothering me since. Take this code snippet for example:
class MyClass {
var v = Variable("")
var bag = DisposeBag()
func subscribe() {
let ob = v.asObservable()
ob.subscribe(onNext: { (value) in
print("Value changed: " + value)
}).disposed(by: bag)
}
}
What bothers me is, where/who is the real observer in the scenario of subscribe() method? In term of objects, here we have ob who acts as an observable, but I can't really see the observer object anywhere.
Can anyone pls brighten my mind?

The observer in the example above is really the closure which you provide to the subscribe(onNext:) function.
Of course, how long that closure sticks around is determined by the lifetime of your DisposeBag: when your instance of MyClass dies, bag dies, and therefore the closure dies. For this reason, you may find people calling your instance of MyClass the "observer".

Related

Implicitly unwrapped optional var destroyed by compiler before end of scope?

With swift compiler optimizations implicitly unwrapped optional variables do not survive the whole scope, but are released immediately after usage.
Here is my environment:
swift --version
outputs
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.2.0
Xcode version is Version 12.3 (12C33)
Consider this most rudimentary example that shows the issue:
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
func do() {
var someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
This should ouput
done
deinit
However, in release builds (with Swift code optimizations enabled "-O") it prints the other way round:
deinit
done
This is ONLY the case for var someObject: SomeClass!.
The following alterations of that code ALL output correctly (meaning the Object is released when the scope of the function is left):
Define var as constant:
func doSthSpecial() {
let someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
Define var as optional explicitly:
func doSthSpecial() {
var someObject: SomeClass? = SomeClass()
someObject.doSth()
print("done")
}
Access like an optional:
func doSthSpecial() {
var someObject: SomeClass! = SomeClass()
someObject?.doSth()
print("done")
}
These last three implementations all output
done
deinit
in that order.
Somehow this leaves me speechless 🤷‍♂️.
I understand this optimization, it makes sense. But as a programmer we are used to local variables inside of functions being available until leaving the scope.
The problem I have here is about the lifetime of an object that is stored in such an implicitly unwrapped optional variable. If I have code that depends on the lifetime of this object (which is the case with RxSwift and its DisposeBags for example) then I am getting weird behavior, unexpected behavior!
I could consider this as a bug in Swift, but what do you think? Bug or no bug?
Here is a more real-world scenario with RxSwift where you could be using such a construct:
import UIKit
import RxSwift
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
class ViewController: UIViewController {
let providePassword = PublishSubject<String>()
lazy var askForPassword: Observable<String> = {
return Observable.create { observer in
_ = self.providePassword.subscribe(observer)
return Disposables.create()
}
.debug(">>> ask for password signal")
}()
private func performAsyncSyncTask() {
DispatchQueue.global().async {
var disposeBag: DisposeBag! = DisposeBag()
let sema = DispatchSemaphore(value: 0)
self.askForPassword
.subscribe(onNext: { pw in
print(pw)
sema.signal()
})
.disposed(by: disposeBag)
_ = sema.wait(timeout: DispatchTime.distantFuture)
disposeBag = nil
}
}
#IBAction func startAskPassword(sender: AnyObject) {
self.performAsyncSyncTask()
}
#IBAction func sendPassword(sender: AnyObject) {
self.providePassword.on(.next("hardcoded pw"))
}
}
The problem here is: When executing self.performAsyncSyncTask() it is subscribed to askForPassword but because in optimized builds the implicitly unwrapped optional variable is purged immediately after using it in .disposed(by: disposeBag).
This destroys the signal immediately after subscribing to it.
But as a programmer we are used to local variables inside of functions being available until leaving the scope.
This hasn't been the case since ARC was first released for ObjC. ARC has always had the option to release objects after their last use (and very often makes use of this). This is by design, and is not a bug in Swift (or in ObjC, where it's also true).
In Swift, if you want to extend the lifetime of an object beyond its last use,withExtendedLifetime is explicitly for this purpose.
var someObject: SomeClass! = SomeClass()
withExtendedLifetime(someObject) {
someObject.doSth()
print("done")
}
Keep in mind that it is legal for objects to have balanced retain/autorelease calls on them, which may cause them to outlive their scope as well. This is much less common in Swift, but still legal and happens if you pass a Swift object to ObjC (which can happen in many places you may not expect).
You should be very careful relying on when deinit will be called. It can surprise you, and isn't even promised in all cases (for example, deinit is not called during program quit on Mac, which tends to surprise C++ developers).
IMO performAsyncSyncTask is a dangerous pattern, and should be redesigned with clearer ownership. I don't do enough RxSwift work to immediately redesign it, but blocking the whole thread on a DispatchSemaphore seems the wrong way to integrate with any reactive system. Threads are a finite resource, and this forces the system to create more while this one is blocked doing nothing.

Concurrency issues with observable.observeOn() and common resources

I have an observable inside a function.
The function happens in a certain queue, queueA, and the observable is subscribed to with observeOn(schedulerB). In onNext, I'm changing a class variable.
In another function, I'm changing the same class variable, from a different queue.
Here is some code to demonstrate my situation:
class SomeClass {
var commonResource: [String: String] = [:]
var queueA = DispatchQueue(label: "A")
var queueB = DispatchQueue(label: "B")
var schedulerB = ConcurrentDispatchQueueScheduler(queue: QueueB)
func writeToResourceInOnNext() {
let obs: PublishSubject<String> = OtherClass.GetObservable()
obs.observeOn(schedulerB)
.subscribe(onNext: { [weak self] res in
// this happens on queue B
self.commonResource["key"] = res
}
}
func writeToResource() {
// this happens on queue A
commonResource["key"] = "otherValue"
}
}
My question is, is it likely to have concurrency issues, if commonResource is modified in both places at the same time?
What is the common practice for writing/reading from class/global variables inside onNext in an observable with observeOn?
Thanks all!
Since your SomeClass has no control over when these functions will be called or on what threads the answer is yes, you are setup to have concurrency issues in this code due to its passive nature.
The obvious solution here is to dispatch to queue B inside writeToResource() in order to avoid the race condition.
Another option would be to use an NSLock (or NSRecursiveLock) and lock it before you write to the resource and unlock it after.
The best practice is: when you have a side effect happening inside a subscribe function's closure (in this case writing to commonResource that the closure is the only place where the side effect occurs. This would mean doing away with the passive writeToResource() function and instead passing in an Observable that was generated by whatever code currently is calling the function.

RxSwift: compactMap never executed

I'm trying to implement compactMap on RxSwift but it seems like is never executed.
Here is my code:
class MyClass{
var disposeBag = DisposeBag()
let subject = BehaviorRelay(value: 1)
func doSomething() {
Observable.from(optional: subject).compactMap{ $0
}.subscribe( onNext:{
print($0)
}).disposed(by: disposeBag)
subject.accept(2)
subject.accept(4)
subject.accept(5)
subject.accept(8)
}
}
When I change the value on subject the compactMap never gets called. Why not?
You are creating an Observable<BehaviorRelay<Int>> by using the from operator which only emits one value (the behavior relay itself) and then completes. The accept calls are being ignored because nothing is subscribing to the behavior relay itself.
I think you need to step back and figure out what you are trying to accomplish, and then read the documentation on the operators to find one that does what you need.

Swift issue when storing Object inside a struct

I am confused of how memory is managed here, lets say there is a scenario:
import Foundation
class SomeObject {
deinit {
print("deinitCalled")
}
}
struct SomeStruct {
let object = SomeObject()
var closure: (() -> Void)?
}
func someFunction() {
var someStruct = SomeStruct()
someStruct.closure = {
print(someStruct)
}
someStruct.closure?()
}
someFunction()
print("the end")
What I would expect here is:
Optional(test.SomeStruct(object: test.SomeObject, closure: Optional((Function))))
deinitCalled
the end
However what I get is this:
SomeStruct(object: test.SomeObject, closure: Optional((Function)))
the end
And if I look at memory map:
retain cycle
How do I manage memory in this case
First, you should be very, very careful about putting reference types inside of value types, and especially a mutable reference type that is visible to the outside world. Structs are always value types, but you also want them to have value semantics, and it's challenging to do that while containing reference types. (It's very possible, lots of stdlib types do it in order to implement copy-on-write; it's just challenging.)
So the short version is "you almost certainly don't want to do what you're doing here."
But if you have maintained value semantics in SomeStruct, then the answer is to just make a copy. It's always fine to make a copy of a value type.
someStruct.closure = { [someStruct] in
print(someStruct)
}
This gives the closure it's own immutable value that is a copy of someStruct. Future changes to someStruct won't impact this closure.
If you mean for future changes to someStruct to impact this closure, then you may be violating value semantics, and you should redesign (likely by making SomeStruct a class, if you mean it to have reference semantics).

How do I emit events manually (i.e., make an EventEmitter) using RxSwift?

How do I implement an event emitter using RxSwift? (An object that can emit data that is consumed by other objects that are subscribed to it.)
After going through the Rx docs and examples, I feel like a complete idiot and am still extremely confused on how to manually emit events from Observers to Observables. My understanding that we have some Observable that can emit events with data to all Observers that are subscribed to that Observable. However, I have zero idea on how this is actually implemented in Swift.
Here's an example of something I'm trying to implement:
class VendingMachine {
let dispenser = Observable<Drink>
// Notify all subscribed Observers that this machine dispensed a drink.
func dispenseDrink(item: Drink) {
dispenser.onNext(item)
}
}
And a second file:
class MachineReporter: Observer {
let dispenser = VendingMachine().dispenser
init() {
dispenser.subscribe(self)
}
onNext { drink in
print("Vending machine dispensed a drink: \(drink)"
}
}
My brain is fried. I'm just going to switch to a specialized library like EmitterKit for now because I'm clearly misunderstanding how this works.
But I need to figure out how Rx works or I will go crazy. Help!
I'm pretty confused about what you're planning on doing with MachineReporter, so my code just addresses making it work, although it may not be very useful.
I would highly suggest you go through the RxSwift Playground examples and make sure you feel comfortable with those. Your question in particular deals with Subjects. That should get you over the initial "hump" of uncomfortability and frustration. I definitely hear you about that though, it's a different way of thinking, but it's completely worth pushing past that first hurdle. Stick with it.
class VendingMachine {
let dispenser = PublishSubject<Drink>()
func dispenseDrink(item: Drink) {
dispenser.onNext(item)
}
}
class MachineReporter {
let disposeBag = DisposeBag()
let dispenser = VendingMachine().dispenser
init() {
dispenser.asObservable()
.subscribeNext { drink in
print("Vending machine dispensed a drink: \(drink)")
}
.addDisposableTo(disposeBag)
}
}
Should use a Subject for this.
A Subject is a special form of an Observable Sequence, you can
subscribe to and add elements to it. There are 4
kinds of Subjects in RxSwift
http://reactivex.io/documentation/subject.html
I would recommend starting off using a PublishSubject here.
let observable = PublishSubject<SomeTypeOrEvent>()
observable.subscribe(onNext: { (SomeTypeOrEvent) in
self.doSomethingWithEvent(something: SomeTypeOrEvent)
}).addDisposableTo(disposeBag)
...
// emits some type/event
observerable.onNext(SomeTypeOrEvent)