Re: Translating KVO-ed property to Swift
Re: Translating KVO-ed property to Swift
- Subject: Re: Translating KVO-ed property to Swift
- From: Charles Srstka <email@hidden>
- Date: Wed, 19 Apr 2017 18:21:40 -0500
> On Apr 19, 2017, at 4:50 PM, Quincey Morris <email@hidden> wrote:
>
>> 3. Computed properties do not need to be dynamic, […].
>
> This is also not exactly true. Computed properties that have only a getter do not need to be dynamic, because they don’t generate any KVO notifications via a setter, and so “dynamic” is irrelevant.
>
> However, a computed property that has a setter will *not* produce the expected notifications unless it is marked “dynamic”, even if marked “@objc”. (I tested this to be sure.) There’s nothing special about computed vs. stored properties in this regard.
I should add that if a computed property needs ‘dynamic’ in order for its notifications to fire, the property is not properly KVO-compliant. There are three ways this could happen that I can think of off the top of my head:
Case #1: Derived from another KVO property
> @objc dynamic var foo: String
>
> @objc var bar: String {
> get { return self.foo }
> set { self.foo = newValue }
> }
The above will not fire notifications for bar. Making bar ‘dynamic’ appears to fix the problem, but it is in fact still not compliant; if foo changes, bar’s value is changed as well, but its notifications will not fire. The proper way to fix this is to add keyPathsForValuesAffectingBar.
Case #2: Backed by something not exposed to Objective-C
> enum Option: String {
> case foo = “Foo"
> case bar = “Bar"
> case baz = “Baz"
> }
>
> var option: Option
>
> @objc var objcOption: String {
> get { return self.option.rawValue }
> set {
> if let option = Option(rawValue: newValue) {
> self.option = option
> }
> }
> }
The above will not fire notifications for objcOption. Making objcOption ‘dynamic’ appears to fix the problem, but if option is changed, objcOptions’s value will change as well, and its notifications will not fire. The proper way to fix this is to add willChangeValue(forKey:) and didChangeValue(forKey:) calls in option’s willSet and didSet accessors.
Case #3: It’s backed by something other than a property
> @objc var isFoo: Bool {
> get { return UserDefaults.bool(forKey: “Foo”) }
> set { UserDefaults.set(newValue, forKey: “Foo”) }
> }
The above will not fire notifications for isFoo. Making isFoo ‘dynamic’ appears to fix the problem, but if the “Foo” defaults key changes by some mechanism other than isFoo’s setter, isFoo’s value will have changed, but the notifications will not fire. The better solution is to register for changes on UserDefaults or NSUserDefaultsController via one of the several available mechanisms that Cocoa provides to do that and ensure that isFoo’s clients are notified when the underlying value changes.
Charles
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden