Swift 4 KVO


(Patrick Gili) #1

I'm experimenting with the observer introduced by Swift 4. Here's a snippet of code I've been playing with:

> import Cocoa
> 
> typealias StockPrice = Int
> 
> class Stock: NSObject {
>     let symbol: String
>     @objc dynamic var price: StockPrice = 0
>     
>     init(symbol: String) {
>         self.symbol = symbol
>     }
>     
>     func update(price: StockPrice) {
>         self[keyPath: \.price] = price
>     }
> }
> 
> class StockWatcher: NSObject {
>     @objc var stock: Stock
>     
>     var observersation: NSKeyValueObservation?
>     
>     init(stock: Stock) {
>         self.stock = stock
>         super.init()
>         
>         observersation = observe(\.stock.price, options: [.old, .new]) {
>             object, change in
>             print("\(stock.symbol) changed from \(change.oldValue!) to \(change.newValue!)")
>         }
>     }
> }
> 
> var aapl = Stock(symbol: "appl")
> aapl.update(price: 171)
> var watcher = StockWatcher(stock: aapl)
> aapl.update(price: 190)

This code works as expected. However, when I change the definition of StockPrice to Decimal, I receive the followed unhandled exception:

> 2019-03-11 15:23:21.816057-0400 KeyPath[8868:4286496] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSMethodSignature signatureWithObjCTypes:]: unsupported type encoding spec '{?}''
> *** First throw call stack:
> (
> 	0   CoreFoundation                      0x00007fff4630fded __exceptionPreprocess + 256
> 	1   libobjc.A.dylib                     0x00007fff723db720 objc_exception_throw + 48
> 	2   CoreFoundation                      0x00007fff46263571 +[NSMethodSignature signatureWithObjCTypes:] + 2413
> 	3   CoreFoundation                      0x00007fff462b1617 ___forwarding___ + 389
> 	4   CoreFoundation                      0x00007fff462b1408 _CF_forwarding_prep_0 + 120
> 	5   KeyPath                             0x0000000100006f15 $s7KeyPath5StockC5priceSo9NSDecimalavpACTk + 229
> 	6   libswiftCore.dylib                  0x0000000100497d63 $ss26NonmutatingWritebackBufferCfD + 67
> 	7   libswiftCore.dylib                  0x000000010063d550 _swift_release_dealloc + 16
> 	8   KeyPath                             0x0000000100006d53 $s7KeyPath5StockC6update5priceySo9NSDecimala_tF + 371
> 	9   KeyPath                             0x00000001000046ce main + 11566
> 	10  libdyld.dylib                       0x00007fff734a9ed9 start + 1
> 	11  ???                                 0x0000000000000001 0x0 + 1
> )
> libc++abi.dylib: terminating with uncaught exception of type NSException

Are there restrictions concerning the type being observed?


(Thomas Krajacic) #2

If you change it to NSDecimalNumber it should work I guess.

Decimal is a struct which cannot be observed. And it doesn't automatically bridge to NSDecimalNumber iirc


(Patrick Gili) #3

Yes, defining StockPrice as NSDecimalNumber worked fine.

You mentioned that Decimal is a structure, which cannot be observed. However, defining StockPrice as Int works fine, and Int is a structure.

Are there rules that dictate what can be observed?