LoginSignup
26
13

More than 3 years have passed since last update.

【Swift5.1】DuctTapeで初期化時の値の代入をもっと簡潔にする

Posted at

はじめに

Swiftでオブジェクトのインスタンス化をしつつpropertyに値を代入する場合は、以下のような実装になるかと思います。

let label: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.textColor = .red
    label.text = "Hello, World!!"
    return label
}()

上記と同じ状態のインスタンスを、メソッドチェーンで実現する方法を紹介しようと思います。

DuctTapeを利用する

DuctTapeというOSSを利用することで、先程の実装を以下のような実装にできます。

let label = UILabel().ductTape
    .numberOfLines(0)
    .textColor(.red)
    .text("Hello, World!!")
    .build()

NSObjectを継承したオブジェクトのインスタンスの.ductTapeにアクセスすると、Builderが返ってきます。
値を代入したいpropertyがある場合、そのproperty名と同一名のclosureにアクセスできるので、closureの引数として代入したい値を渡します。
一通りの値の設定が終わったら.build()を呼び出すことで、任意の値が代入された状態のインスタンスが返されます。

メソッドにアクセスする場合

インスタンスのメソッドにアクセスしたい場合は、Builderの.reinforceを介してインスタンスにアクセス
できるようになるため、渡されたインスタンスからメソッドにアクセスします。

let collectionView = UICollectionView().ductTape
    .backgroundColor(.red)
    .reinforce { $0.register(UITableViewCell.self, forCellWithReuseIdentifier: "Cell") }
    .build()

NSObjectを継承していないオブジェクトでも利用する

以下のように、Builderのinitializerの引数にインスタンスを渡すことで利用が可能になります。

class Dog {
    var name: String = ""
}

let dog = Builder(Dog())
    .name("Copernicus")
    .build()

DuctTapeの仕組み

Swift5.1から利用できるKeyPath dynamicMemberLookupを利用して実装してします。
そのためpropertyごとに代入用の処理を実装する必要はなく、subscript<Value>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Value>) -> (Value) -> Builder<Base>を実装してしまえば、返り値のclosureの引数で受け取った値をKeyPathを介してインスタンスに代入する処理を実現できます。
また、KeyPath dynamicMemberLookupによる実装のため、該当のproperty名が自動補完されます。

@dynamicMemberLookup
public struct Builder<Base: AnyObject> {

    private let _build: () -> Base

    public init(_ build: @escaping () -> Base) {
        self._build = build
    }

    public init(_ base: Base) {
        self._build = { base }
    }

    public subscript<Value>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Value>) -> (Value) -> Builder<Base> {
        { [build = _build] value in
            Builder {
                let object = build()
                object[keyPath: keyPath] = value
                return object
            }
        }
    }

    public func build() -> Base {
        _build()
    }
}
26
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
13