[weak self]
does not mean that the closure can be discarded, it only prevents the closure from retaining the VC (and therefore preventing the VC from being deinited).
Simply begin your closure with:
guard let self = self else { return }
... to exit early if the VC no longer exists.
As for why the closure supplied by the VC is being called but the one in the singleton isn't, it sounds like your bluetooth class doesn't understand the concept of multiple 'users'. Whoever registers their callback last is the one that is called.
An approach to handling your own observer registration with convenient self-unregistering tokens:
class ObserverToken {
let id = UUID()
private let onDeinit: (UUID) -> ()
init(onDeinit: @escaping (UUID) -> ()) {
self.onDeinit = onDeinit
}
deinit {
onDeinit(id)
}
}
class BluetoothThing {
// Associate observers with the .id of the corresponding token
private var observers = [UUID: (Int) -> ()]()
func addObserver(using closure: @escaping (Int) -> ()) -> ObserverToken {
// Create a token which sets the corresponding observer to nil
// when it is deinit'd
let token = ObserverToken { [weak self] in self?.observers[$0] = nil }
observers[token.id] = closure
return token
}
func tellObserversThatSomethingHappened(newValue: Int) {
// However many observers we currently have, tell them all
observers.values.forEach { $0(newValue) }
}
deinit {
print("")
}
}
// I've only made this var optional so that it can later be set to nil
// to prove there's no retain cycle with the tokens
var bluetooth: BluetoothThing? = BluetoothThing()
// For as long as this token exists, updates will cause this closure
// to be called. As soon as this token is set to nil, it's deinit
// will automatically deregister the closure
var observerA: ObserverToken? = bluetooth?.addObserver { newValue in
print("Observer A saw: \(newValue)")
}
// Results in:
// Observer A saw: 42
bluetooth?.tellObserversThatSomethingHappened(newValue: 42)
// A second observer
var observerB: ObserverToken? = bluetooth?.addObserver { newValue in
print("Observer B saw: \(newValue)")
}
// Results in:
// Observer A saw: 123
// Observer B saw: 123
bluetooth?.tellObserversThatSomethingHappened(newValue: 123)
// The first observer goes away.
observerA = nil
// Results in:
// Observer B saw: 99
bluetooth?.tellObserversThatSomethingHappened(newValue: 99)
// There is still one 'live' token. If it is retaining the
// Bluetooth object then this assignment won't allow the
// Bluetooth to deinit (no wavey hand)
bluetooth = nil
So if your VC stores it's token as a property, when the VC goes away, the token goes away and the closure is deregistered.