KVO(Key Value Observe) with Foundation in Swift


# KVOとは

* Key Value Observeの略
* 「キー値監視」と訳されている (Swiftのプロパティ監視とは別）
* オブジェクトの属性値の監視をプロパティを通してではなく、キー(文字列、#keyPath式)を使って行うことができる

# SwiftでKVOを行うには
* 通知を受け取る方も通知する方もNSObjectを継承する
	* [NSKeyValueObserving](https://developer.apple.com/documentation/foundation/notifications/nskeyvalueobserving) プロトコルが自動的に適合される

# NSKeyValueObserving

以下の機能が用意されている

* 監視の登録
	* `addObserver(_:forKeyPath:options:context:)`
* 変更の受信
	* `observeValue(forKeyPath:of:change:context:)`メソッドをオーバーライドする
* 変更の通知
	* Objective-Cでは、監視キーの値の変更は自動的に通知されるが、Swiftではされない。
	* 以下のメソッドがワンセットとなっている
		* `willChangeValue(forKey:)` 変更前に呼び出す
		* `didChangeValue(forKey:)` 変更後に呼び出す

# KVOプログラミング例

```kvo_sample.swift
class Account: NSObject {
    @objc var balance: Double {
        // KVOによる値の変更通知
        willSet {
            self.willChangeValue(forKey: #keyPath(Account.balance))
        }
        didSet {
            self.didChangeValue(forKey: #keyPath(Account.balance))
        }
    }

    @objc var interestRate: Double

    init(_ balance: Double, _ interestRate: Double) {
        self.balance = balance
        self.interestRate = interestRate
    }
}

class Person: NSObject {
    // 変更通知の登録
    func registerAccount(_ account: Account) {
        account.addObserver(self, forKeyPath: #keyPath(Account.balance), options: [.new, .old], context: nil)
        account.addObserver(self, forKeyPath: #keyPath(Account.interestRate), options: [.new, .old], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, 
               of object: Any?, 
           change: [NSKeyValueChangeKey : Any]?, 
          context: UnsafeMutableRawPointer?) {

          // 変更通知を受け取る
    }
}

let account = Account(0, 0)
let person = Person()
person.registerAccount(account)

account.balance = 100

```

## KVCによる値の変更通知

KVOで用意されているメソッド(willChange,didChange)ではなく、KVCで値を変更すると、直接変更を通知してくれる。


### 注意

KVOで通知が実装されている状態でKVCによる値の変更を行うと２回変更通知が飛ぶので注意。
上記を回避したい場合は、以下のメソッドをオーバーライドして`false`を返すにようにする。

```notify_with_kvc.swift
// 自動的に通知するかどうか
override class func automaticallyNotifiesObservers(forKey key: String) -> Bool {
        return false
}
```

```notify_with_kvc_sample.swift
class Account: NSObject {
    @objc var balance: Double {
        willSet {
            self.willChangeValue(forKey: #keyPath(Account.balance))
        }
        didSet {
            self.didChangeValue(forKey: #keyPath(Account.balance))
        }
    }

    @objc var interestRate: Double

    init(_ balance: Double, _ interestRate: Double) {
        self.balance = balance
        self.interestRate = interestRate
    }

    override class func automaticallyNotifiesObservers(forKey key: String) -> Bool {
        // 自動的には通知しないようにする
        return false
    }
}

class Person: NSObject {
    func registerAccount(_ account: Account) {
        account.addObserver(self, forKeyPath: #keyPath(Account.balance), options: [.new, .old], context: nil)
        account.addObserver(self, forKeyPath: #keyPath(Account.interestRate), options: [.new, .old], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, 
               of object: Any?, 
           change: [NSKeyValueChangeKey : Any]?, 
          context: UnsafeMutableRawPointer?) {
    }
}

let account = Account(0, 0)
let person = Person()
person.registerAccount(account)

// KVCによる変更通知
account.setValue(100.0, forKey: #keyPath(Account.balance))

```

**NOTE:**

* Objective-Cでは `automaticallyNotifiesObserversForKey:` で `false` を返した場合、手動で実装する必要がある(willChange, didChange)


# 参考

* [KVC(Key Value Codeing) について](https://qiita.com/ysn/items/fdf183ce9c7a87d279cb)