UINibでDI

# 要約
`UINib.OptionsKey.externalObjects` を使ってDIします。

# やり方
- External Object用のクラスを用意する
- External Object付きのXibファイルを作成する
- instantiate(withOwner:options:)時にexternalObjectsキーでExternal Objectにオブジェクトを割り当てる

## External Object用のクラスを用意する
今回は計算をさせたいので計算用オブジェクトを用意します。
これらのクラスは後ほど使います。

```swift
class CalcObject: NSObject {
    func calc(num1: Int, num2: Int) -> Int {
        fatalError()
    }
    
    var operaterText: String {
        fatalError()
    }
}

class AdditionObject: CalcObject {
    override func calc(num1: Int, num2: Int) -> Int {
        num1 + num2
    }
    
    override var operaterText: String {
        "+"
    }
}

class MultiplicationObject: CalcObject {
    override func calc(num1: Int, num2: Int) -> Int {
        num1 * num2
    }
    
    override var operaterText: String {
        "×"
    }
}

```


## External Object付きのXibファイルを作成する
まず、xibファイルを作成し、Interface Builderを開き、Object Libraryより `External Object` を追加します。
![スクリーンショット 2020-11-08 14.38.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/0e4a4b66-b63a-e8ef-94db-1f3875d40322.png)

追加したらExternal Objectのインスペクタより設定を行います。
- Identifier → 今回は`CalcObject`にします
- Class → 先ほど作成したクラス`CalcObject`にします

![スクリーンショット 2020-11-08 15.12.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/1a4a710d-af02-63c7-9ae9-5bb8e73e27b2.png)

ついでにFile's Ownerも実装しておきます。
File's Ownerは、先ほどのExternal Objectと計算結果を表示するラベル、ボタンのアクションが認識できるよう、IBOutletやIBActionを設定しておきます。

```swift
class NibOwner: NSObject {
    @IBOutlet var calcObject: CalcObject!
    @IBOutlet weak var label: UILabel!
    @IBInspectable var num1: Int = 0
    @IBInspectable var num2: Int = 0

    @IBAction func calc() {
        label.text = "\(num1) \(calcObject.operaterText) \(num2) = \(calcObject.calc(num1: num1, num2: num2))"
    }
}
```

## instantiate(withOwner:options:)時にexternalObjectsキーでExternal Objectにオブジェクトを割り当てる

先ほど作成したxibファイルをコードから使います。
xibを使うときに、`instantiate(withOwner:options:)`でインスタンス化しますが、その時のoptionsに`. externalObjects`を付与します。
External ObjectのIdentifierをキーにして、実際にExternal Objectとして割り当てたいオブジェクトをvalueとします。
`CalcObject()` の部分を`AdditionObject`や`MultiplicationObject`に差し替えると動作を変えることができます。
以下は画面にxibの内容を反映させるコードの例です。

```swift
class ViewController: UIViewController {
    @IBOutlet weak var stackView: UIStackView!
    let nibOwner = NibOwner()

    override func viewDidLoad() {
        super.viewDidLoad()
        let nib = UINib(nibName: "View", bundle: nil)
        let externalObjects = ["CalcObject" : CalcObject()] // ここのオブジェクトを差し替える
        let instance = nib.instantiate(withOwner: nibOwner,
                                       options: [.externalObjects : externalObjects])
        instance.compactMap { $0 as? UIView }.forEach {
            self.stackView.addArrangedSubview($0)
        }
    }
}

```

# 実際の動作

## AdditionObjectの場合
![Simulator Screen Shot - iPod touch (7th generation) - 2020-11-08 at 15.28.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/c8fafc2f-b2cd-c321-6e3d-552daa18c3bf.png)

## MultiplicationObjectの場合
![Simulator Screen Shot - iPod touch (7th generation) - 2020-11-08 at 15.28.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/733022ff-b5b8-23c6-9086-cf6a17ab10ef.png)


# まとめ
つかおう！DI！