Consider this simple class:
import Foundation
class ExampleClass {
init() {
let notificationCenter = NotificationCenter.default
var observer: NSObjectProtocol? = nil
// A warning is emitted for the next line
observer = notificationCenter.addObserver(
forName: .NSExtensionHostDidEnterBackground,
object: nil,
queue: nil
) { [weak self] _ in
self?.doSomething()
notificationCenter.removeObserver(observer!)
}
}
func doSomething() {
print("We got the notification")
}
}
This code uses the exact pattern that Apple suggests in their documentation for NotificationCenter.addObserver(forName:object:queue:using:), where the NotificationCenter gives us some opaque token that conforms to NSObjectProtocol, and we later use that token to remove the observer.
Recently, though, this code has started to produce a warning. On the line where observer is assigned, the compiler complains that
'observer' mutated after capture by sendable closure
I understand where the compiler is coming from here: if observer is a value type, then the closure will indeed get an “old” version of it. This code does work, though, which suggests to me that either addObserver() returns a reference type, or else that it returns a value-type handle into data that NotificationCenter is storing itself. (It’s unfortunate that Apple doesn’t give us a more specific return type for the method.)
Does this warning indicate an actual problem in this case? If so, what’s the best alternative pattern to use?
You can store the observer on the object itself:
This workaround silences the warning, and it shouldn’t change the semantics of the program in a meaningful way. It’s a bit less elegant, in that a variable that could have stayed local to
init(and its closures) is now exposed to the entire class, but it should have the same effect as the previous version of the code.