Pointfree の提供している swift-dependenciesですが、非常に利用方法も簡単で、テスト時の利用など使い勝手の良いものになっています。
しかし、 riverpod の Provider にできるように、引数を渡してその引数に応じた値を取得できるようにしたい場合はないでしょうか?
例:
ref.watch(activityProvider('recreational'))
このようなあると便利な機能を swift-dependencies でどう実現するのか、その方法を考えて利用してみたので、その方法を共有します。
(自分の)結論
こちらの discussion で提案されているようなやり方で指定できるようにしました。
@Dependency(\.hoge[.paramA]) var hoge
実現の仕方
具体的には、 DependencyValueContainer
という class を作ってあげて、これを @Dependency
で指定して取得する値の型にします。
final class DependencyValueContainer<Value, Argument> where Argument: Hashable {
fileprivate var values: [Argument: Value] = [:]
var create: (Argument) -> Value
public init(create: @escaping (Argument) -> Value) {
self.create = create
}
public subscript(argument: Argument) -> Value {
if let value = values[argument] {
return value
}
let newValue = create(argument)
values[argument] = newValue
return newValue
}
}
swift-dependencies は DependencyValues
に登録した値が持っているプロパティなどにアクセスし、その値を取り出してあげることもできるので、その仕組みを利用するイメージです。
依存関係の登録例:
private enum HogeContainerKey: DependencyKey {
static var liveValue: DependencyValueContainer<Hoge, ParamA> {
DependencyValueContainer { argument in
return Hoge(argument: argument)
}
}
}
extension DependencyValues {
public var hoge: DependencyValueContainer<Hoge, ParamA> {
get { self[HogeContainerKey.self] }
set { self[HogeContainerKey.self] = newValue }
}
}
こうすると、冒頭にあったように以下のように値を取り出すことができるようになります。
@Dependency(\.hoge[.paramA]) var hoge
他の候補
クロージャ
欲しい値を引数を与えると作ってくれるクロージャを DependencyValues
に登録する手法です
イメージ:
private enum HogeContainerKey: DependencyKey {
typealias HogeCreate = (ParamA) -> Hoge
static var liveValue: HogeCreate = { paramA in
Hoge(paramA)
}
}
使い方次第ですが、使う箇所ごとに毎回別のインスタンスが生成されてしまうことから、この手法はやめました。
initialize
のような関数を用意する
初期化用関数を生やす方法ですが、あまり綺麗ではないのと、呼び忘れてランタイムにクラッシュすることに気づきにくいため、やめました。
lazy var paramA: ParamA
func initialize(paramA: ParamA) {
self.paramA = paramA
}
対象のクラスの関数すべてに共通のパラメータを渡せるようにする
名前の通りです。
可能ならこの方法でやるのが一番なのですが、今回元となった対応箇所では20個ほど共通の関数があるのと、 var
で定義されていた computed property 等も中でそのパラメータを利用していたため、対応自体の大変さから見送りました。
終わりに
今回は swift-dependencies の key に引数を渡す(風)の方法を紹介してみました。
DI の方法を何かしらから変えていくような場合には欲しくなる場面もあるかもしれないので、もしそんな場面に遭遇したら試してみてもらえるとありがたいです。