Kvo Broken in iOS 9.3

iOS 9: KVO no longer working

If you do a search for UIKit and KVO Compliance you will see that everyone says you can't rely on it. See this question and this question.

I don't know what changed, but I think you should just subclass UITextView and overload setContentSize: if you want to know when it changes.

is KVO still broken?

When Mike Ash says KVO is broken, he isn't talking about if it works or not. Ash is saying that the API is designed badly and this bad design limits what developers can do with it.

A better use of "KVO-esque" programming can be found in libraries like ReactiveCocoa.

If you want to learn more check out these links.

  • https://leanpub.com/iosfrp/
  • https://github.com/ReactiveCocoa/ReactiveCocoa
  • https://github.com/ReactiveX/RxSwift

KVO not working (observing another class's property)

You written the observer in dealloc method. There will be an issue, your object is being released when you are adding an observer, so there will be a crash when the KVO value is changed.

There is no crash and nothing is working, I think the reason is; the dealloc method is never called. It means your view controller is never released (potentially a leak, a strong retain cycle is there).

Instead of that add that in your viewDidLoad:

- (void)viewDidLoad
{
[super viewDidLoad];
ResultDataClass *resultData = [ResultDataClass getInstance];
[resultData addObserver:self forKeyPath:@"xxx" options:NSKeyValueObservingOptionNew context:NULL];
}

KVO not working for custom property of NSManagedObject

Adding as an answer since it became a learning exercise. Hopefully someone else too would be benefitted.

The app uses a multiple managedObjectContext(moc) architecture. 1 private moc to make changes and 1 main thread moc that synchronises itself using mergeChanges.

In above code, navigationItem is using the folder instance kept with main-moc. The DynamicProperty is listening to KVO changes on this main-moc's folder instance. Let's call this main-folder. When I make changes, I modify the folder instance we have on private-moc. Let's call it private-folder.

On modifying private-folder and calling save on private-moc, a notification of name NSManagedObjectContextDidSave is broadcasted. main-moc synchronizes itself using mergeChanges.

mergeChanges changes main-folder, but notice that it would never call the computed-property-setter availability. It directly changes internalAvailability.

And thus, no KVO notifications are posted of our computed property.

TL;DR When doing KVO on a NSManagedObject subclass, use a stored property instead of computed one. In case you have a multi-moc (managed object context) scenario and use mergeChanges to synchronise, setter for your computed property is not called when synchronising.

Edit (Solution): add method of the pattern keyPathsForValuesAffecting<KeyName> KVO relevant documentation

@objc class func keyPathsForValuesAffectingAvailability() -> Set<NSObject> {
return [#keyPath(Folder.internalAvailability) as NSObject]
}

KVO keyPathsForValuesAffecting doesn't work

You need to use @objc on your keyPathsForValuesAffecting method so that the KVO machinery can find it using the Objective-C runtime:

@objc class func keyPathsForValuesAffectingFullName() -> Set<NSObject> {
return ["name, "surname"]
}

By the way, you can use a property instead, and you can use the #keyPath special form to make the compiler help you catch errors:

@objc class var keyPathsForValuesAffectingFullName: Set<String> {
return [#keyPath(name), #keyPath(surname)]
}

You should also use dynamic on the upstream properties (name and surname), as Ken Thomases advised.

Here's a full test program (as a macOS command-line program):

import Foundation

class DependencyTest: NSObject {
@objc dynamic var fullName: String {
return name + " " + surname
}
@objc dynamic var name = ""
@objc dynamic var surname = ""

@objc class var keyPathsForValuesAffectingFullName: Set<String> {
return [#keyPath(name), #keyPath(surname)]
}
}

class Observer: NSObject {
@objc let dep: DependencyTest

init(dep: DependencyTest) {
self.dep = dep
super.init()
addObserver(self, forKeyPath: #keyPath(Observer.dep.fullName), options: .prior, context: nil)
}

deinit {
removeObserver(self, forKeyPath: #keyPath(Observer.dep.fullName), context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("kvo: \(keyPath!) \(change?[.notificationIsPriorKey] as? Bool ?? false ? "prior" : "post")")
}
}

let d = DependencyTest()
let o = Observer(dep: d)
d.name = "Robert"

Output:

kvo: dep.fullName prior
kvo: dep.fullName post
Program ended with exit code: 0

KVO not working for `UIView.bounds` keypath when view resizing happens due to `autoresizingmask`

My issue was solved by observing \.layer.bounds instead of \UIView.bounds because I learned that the layer is the “source of truth” for a view’s layout, so I suspected the autoresizing mask was directly modifying the layer bounds without going through UIView.bounds



Related Topics



Leave a reply



Submit