Setting Observer for Swift Objects/Properties
You can observe the NSApplication.didChangeScreenParametersNotification
notification. This example will only print once each time a display is either connected or disconnected, and what the change was in the number of screens.
Code:
class EventObserverDemo {
var lastCount = NSScreen.screens.count
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(trigger),
name: NSApplication.didChangeScreenParametersNotification,
object: nil
)
}
@objc private func trigger(notification: NSNotification) {
let newCount = NSScreen.screens.count
if newCount != lastCount {
print("Switched from \(lastCount) to \(newCount) displays")
lastCount = newCount
}
}
}
You don't need to remove/invalidate the observer either, easier to let the system handle it:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you do not need to unregister an observer that you created with this function. If you forget or are unable to remove an observer, the system cleans up the next time it would have posted to it.
Swift property observers, initial value
As Hamish's comment points out, in the case of willSet
there's not a valid value that state
could have here (and in the case of didSet
, there's not a valid value the newValue
argument could have).
Is it possible to add an observer on struct variable in Swift?
The standard Swift “property observers” (didSet
and willSet
) are designed to let a type observe changes to its own properties, but not for letting external objects add their own observers. And KVO, which does support external observers, is only for dynamic
and @objc
properties NSObject
subclasses (as outlined in Using Key-Value Observing in Swift).
So, if you want to have an external object observe changes within a struct
, as others have pointed out, you have to create your own observer mechanism using Swift didSet
and the like. But rather than implementing that yourself, property by property, you can write a generic type to do this for you. E.g.,
struct Observable<T> {
typealias Observer = String
private var handlers: [Observer: (T) -> Void] = [:]
var value: T {
didSet {
handlers.forEach { $0.value(value) }
}
}
init(_ value: T) {
self.value = value
}
@discardableResult
mutating func observeNext(_ handler: @escaping (T) -> Void) -> Observer {
let key = UUID().uuidString as Observer
handlers[key] = handler
return key
}
mutating func remove(_ key: Observer) {
handlers.removeValue(forKey: key)
}
}
Then you can do things like:
struct Foo {
var i: Observable<Int>
var text: Observable<String>
init(i: Int, text: String) {
self.i = Observable(i)
self.text = Observable(text)
}
}
class MyClass {
var foo: Foo
init() {
foo = Foo(i: 0, text: "foo")
}
}
let object = MyClass()
object.foo.i.observeNext { [weak self] value in // the weak reference is really only needed if you reference self, but if you do, make sure to make it weak to avoid strong reference cycle
print("new value", value)
}
And then, when you update the property, for example like below, your observer handler closure will be called:
object.foo.i.value = 42
It’s worth noting that frameworks like Bond or RxSwift offer this sort of functionality, plus a lot more.
Related Topics
After Getting Image from Uiimagepickercontroller, Uiimageview Rotates Image for iPhone 5
Why Does Function Has Multiple Return Types in Swift
How to Retrieve Audio File from Parse Swift
Filtering Dictionary in Swift 4 Fails in Xcode, But Succeeds in Playground
Learning Swift: Expressions Are Not Allowed at the Top Level
Swift: Download Image from Internet and Cache Them Doesn't Work Properly. Need Suggestions
Uibutton Causing Unrecognized Selector Sent to Instance
Differencebetween Convenience Init VS Init in Swift, Explicit Examples Better
What Does Cloning a Github Repository Mean
"Unexpectedly Found Nil While Unwrapping an Optional Value" When Retriveing Pffile from Parse.Com
Swift: Casting a Floatingpoint Conforming Value to Double
Autoscrolling Infinite Effect in .Linear Type of Icarousel in Swift
Xcode Gm: No Swift Language for Os X Command Line Tool Project
How to Use a Specific Gmt for a Function Which Will Be Recognised by Other Time Zones
Interrupted Purchase Not Calling Delegate After Accepting T&C