swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.38k stars 10.34k forks source link

[SR-5116] KeyPath-based KVO: All values are nil on NSKeyValueObservedChange #47692

Open zwaldowski opened 7 years ago

zwaldowski commented 7 years ago
Previous ID SR-5116
Radar rdar://problem/32593789
Original Reporter @zwaldowski
Type Bug
Environment Xcode Version 9.0 beta (9M136h) / swiftlang 900.0.43
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 5 | |Component/s | | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 25ddd18650c37c688c03b65c1a59f909

Issue Description:

Consider the following:

class MyObject: NSObject {
    @objc dynamic var foo: Int = 0
    @objc dynamic var bar: String = ""
    @objc dynamic var baz: NSAttributedStringKey?
}

let x = MyObject()
x.observe(\.foo) { (x, y) in print(x, y) }
x.observe(\.bar) { (x, y) in print(x, y) }
x.observe(\.baz) { (x, y) in print(x, y) }
x.foo = 5 // => <__lldb_expr_75.MyObject: 0x61800007e900> NSKeyValueObservedChange<Int>(kind: __C.NSKeyValueChange, newValue: nil, oldValue: nil, indexes: nil, isPrior: false)
x.bar = "blah" // => <__lldb_expr_79.MyObject: 0x6180002654c0> NSKeyValueObservedChange<String>(kind: __C.NSKeyValueChange, newValue: nil, oldValue: nil, indexes: nil, isPrior: false)
x.baz = .link // => <__lldb_expr_79.MyObject: 0x6180002654c0> NSKeyValueObservedChange<Optional<NSAttributedStringKey>>(kind: __C.NSKeyValueChange, newValue: nil, oldValue: nil, indexes: nil, isPrior: false)
x.foo = 6 // => <__lldb_expr_81.MyObject: 0x61000006f4c0> NSKeyValueObservedChange<Int>(kind: __C.NSKeyValueChange, newValue: nil, oldValue: nil, indexes: nil, isPrior: false)

NSKeyValueObservedChange doesn't appear to be initialized correctly. This is not just the print; value getters from the change object also don't work.

jckarter commented 7 years ago

@swift-ci create

zwaldowski commented 7 years ago

Ah, this is the same as the older KVO API: you need to pass `options: .new` to have the change dictionary fully initialized. This should be documented alongside the final API. Shall I close this or leave it open as a documentation request?

swift-ci commented 6 years ago

Comment by Andrew McLean (JIRA)

I don't see documentation for passing options with the new new NSObject.observer(keyPath: AnyKeyPath) -> NSKeyValueObservation api? Can you pass the options parameter as well?

swift-ci commented 6 years ago

Comment by Martin Massera (JIRA)

When I pass options: [.new, .old] , this works well with Int or String, but does not work with enum. Did not try with other types.

This does not work:

import Foundation

@objc enum Enum: Int {
    case A = 0
    case B
}
class Foo: NSObject {
    @objc dynamic var bar = Enum.A
}

let foo = Foo()
let observer = foo.observe(\.bar, options: [.new, .old]) { o, change in
    print("old: \(change.oldValue) - new: \(change.newValue)")
}
foo.bar = Enum.B

However, this does work:

import Foundation

class Foo: NSObject {
    @objc dynamic var bar = 0
}

let foo = Foo()
let observer = foo.observe(\.bar, options: [.new, .old]) { o, change in
    print("old: \(change.oldValue) - new: \(change.newValue)")
}
foo.bar = 1
swift-ci commented 6 years ago

Comment by Holger Wiedemann (JIRA)

@newearthmartin (JIRA User) the current bug of the block based KVO method not supporting properties with enum types has also been described here.