Hello, I'm new to combine and really trying to replace all of my delegates with it to learn it well.
Is it ok to have static PassthroughSubjects in your objects? This way they can be easily accessed from any other Object instead of passing around a reference to the containing object everywhere or having them global?
class myObject {
static let mySubject: PassthroughSubject<String, Never> = .init()
}
class anotherObject {
myObject.mySubect.sink .... etc.....
}
Sorry for my ignorance, I've been searching a ton today for a clear pattern to use subjects across objects without nesting them and haven't found anything.
A commonly used pattern is to have a static shared property for 'MyObject' if you want to easily connect things without passing references. This is called the singleton pattern and it comes with a lot of downsides that I don't want to get into now.
Your code would look like this:
class MyObject {
static let shared = MyObject()
let mySubject: PassthroughSubject<String, Never> = .init()
}
class AnotherObject {
func doSomething() {
MyObject.shared.mySubect.sink(...) // .... etc.....
}
}
PassthroughSubject doesn't conforms to Sendable, so it is unsafe to use it as nonisolated static property. As well as shared version of non-Sendable class within it. To have it safely as static property, you need to isolate on some global actor (@MainActor easiest one), but in that case your usage of this property might be more limited than you want.
Implicit dependencies not the best solution. By having shared subject/publisher in that way you really can be lost in the flow of events and complicate project's structure and dependencies.
If we talk about Combine (or any other reactive approach), you better think here of a chain of publishers, rather then single instance passed around. Then you have an emitter – an object, that will hold PassthroughSubject and send events to it, and pass it to the subscribers as AnyPublisher to observe and/or manipulate values down the stream.
final class EmittingObject {
var publisher: AnyPublisher<String, Never> {
return subject.eraseToAnyPublisher()
}
private let subject = PassthroughSubject<String, Never>()
func emit() {
subject.send("Hello")
}
}
final class SomeSubscriber {
private var subscription: AnyCancellable?
init(publisher: AnyPublisher<String, Never>) {
subscription = publisher.sink { value in
print(value)
}
}
}
let emitter = EmittingObject()
let subscriber = SomeSubscriber(publisher: emitter.publisher)
emitter.emit()
In general, explicit dependencies are better than implicit.
Due to abandonment, it's not a good idea to use Combine anymore, if you can avoid it. But if you must use Combine, you should avoid AnyPublisher, which was a solution necessary only before we had primary associated types (three years after Combine was released).
final class EmittingObject {
var publisher: some Publisher<String, Never> { subject }
final class SomeSubscriber {
init(publisher: some Publisher<String, Never>) {