Swift

Swift4のKVOに感動した。

KVOについて

まずKVO自体の説明を少し。
KVO(Key Value Observing)とはオブジェクトの値の変更の監視をする技術です。
後付けできるsetter,getterをイメージしてもいいかもしれませんし、
delegateパターンもKVOと似た働きをすることがあります。

ただsetter,getterだとオブジェクトそのものに組み込むぶん依存性が上がりやすいですし。
delegateパターンではdelegateに設定できるオブジェクトは一つのみで、protocolでデリゲートを作って行かなくてはなりません。

ここで活躍するのがKVOです。

簡単な例だとこんな感じ

KVO
class KVOSample: NSObject{
    @objc dynamic var value:Int = 0
}

こうすると後からvalueの値の変更を監視することができます。

Swift3.0のKVO

SwiftにはObjC時代から続くKVOの機能があります。
Swift3.0までを例に、WKWebViewestimatedProgressの値を監視しようとすると
こんな感じ

swift3.0まで
class ViewController: UIViewController{

    var webView: WKWebView
    //中略
    override func viewDidLoad(){
        //webViewにObserverを追加
        webView.addObserver(self, forKeyPath:"estimatedProgress", options:.new, context:nil)
    }
    //NSObjectのもつメソッドを継承
    override func observeValueForKeyPath(keyPath:String, ofObject object:Any, change:[NSObject:Any], context:UnsafeMutablePointer<Void>) {
        switch keyPath {
            case "estimatedProgress":
                if let progress = change[.newKey] as? Float {
                    print("Progress: \(progress)")
                }
            default: break
        }
    }
}


ただなんかやっている事に比べてコードが複雑過ぎますよね。
まず、KeyPathがString指定なので、
ミスタイプを起こしやすいですし、型も明示的にAnyから変換する必要があります。
特にViewControllerでobserveValueForKeyPathなんて言う長ったらしいコードを継承するのもKVOが好きになれなかった理由です。

Swift 4.0 のKVO!

Swift4.0になってKVOの実装は非常にシンプルかつ
Swiftらしいものになりました!

KeyPath生成用の独自演算子まで用意してかなりAppleも本気で取り組んでいる感じです。

これを見てください。

class ViewController: UIViewController{
    var webView: WKWebView
    private var _observers = [NSKeyValueObservation]()
    //中略
    override func viewDidLoad(){
        _observers.append(webview.observe(\.estimatedProgress, options: .new){_,change in
            print("Progress: \(change.newValue)")
        })
    }
}

なかなかスッキリしているでしょう!
それぞれ説明していきます。

NSObject::observe(keyPath:,options:,changeHandler:)

Swift4.0でNSObjectに以下のメソッドが追加されました。

public func observe<Value>(
        _ keyPath: KeyPath<NSObject, Value>, options: NSKeyValueObservingOptions,
        changeHandler: @escaping (NSObject, NSKeyValueObservedChange<Value>) -> Void
    ) -> NSKeyValueObservation

このメソッドの何がすごいかと言うと値の変更をclosureで受け取れるようになるだけでなく
KeyPathに監視する対象の型を渡せるのです!
なので上記のように明示的型変換をすることなくvalueを受け取ることができます。

新演算子 "\"

コレについてまとめている情報が少なかったので演算子と定義していいか微妙なところではあるのですが

webview.observe(\.estimatedProgress, options: .new)

このestimatedProgressの前についているバックスラッシュがKeyPathを生成する演算子です。
省略をしないで書くとこんな感じ

webview.observe(\WKWebView.estimatedProgress, options: .new)

selectorに近い見た目ですがずっとシンプルですよね。

追記:
\は演算子ではなく式expressionであると
教えていただきました。
こちら
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID383
に詳しい情報が乗っているそうです。

まとめ

Swift4.0で大きく変わったKVOですが
あまりまとめた記事がない印象だったのでまとめてみました。

今までKVOは記述の冗長さからあまり積極的に使ってこなかったのですが
コレからはガンガン使っていこうと思っています(`・ω・´)

ではでは