When rethinking my everyday programming patterns to be more swifty, there is one, that I really struggle with: observing changes. After a lot of thinking and research I have yet to find a satisfying solution. That is one,
- that is easy to use,
- leverages the full potential of swift's strong type system,
- is compatible with value types and
- allows for static dispatch.
Maybe the latter one is not possible and that's the reason why my search is unsuccessful so far. If so, I would like to know why?
Some of the work-arounds I found so far:
- SO: Observer Pattern in Swift (Using
Anyor objective_c runtime functionality) - Solving the binding problem with Swift (IMHO rather awkward to use)
- The ones based on closures e.g Answer by Airspeed Velocity
The third one is so far the one that ticks the most boxes it's type safe, straight forward to use, and compatible with value types. For reference:
// central part is an observable protocol that can be fulfilled by
// any observable type.
protocol Observable {
associatedtype Value
func register(f: Value->())
}
// this would be implemented like
struct ObeservableInt: Observable {
// observable value
var value: Int {
didSet {
// inform all observers
for f in observers {
f(value)
}
}
}
var observers = Array<Int->()>()
mutating func register(f: Int->()) {
observers.append(f)
}
}
So far so good. But what happens if our observer is not needed any more? To avoid a memory leak we should unregister it. But this is seemingly not possible with this architecture. Despite being reference types, closures don't have a reliable identity. That is unless we force dynamic dispatch by annotation with @objc_block. Another alternative is to return a token from register(f:) and use that in an the unregister(token:) call.
So my question is, are there any better alternatives regarding the listed criteria? If not, which one of the two solutions @objc_block or using token is the preferred one?