はじめに
よくあるPackage.swiftはこんな感じになると思う。
文字列で指定をする場所が多いため、とにかくタイプミスが多発する。また、この程度の分量であればまだ良いが、規模が大きいアプリの非常に長大なPackage.swiftの中から、該当の.productや.targetを箇所を探すのは非常に手間がかかる。
やりたいこと
文字列で指定せずに、
.product(.rswift), .target(.welcome)
と指定できるようにしたい。
Productを対応する
まずは外部ライブラリから対応していく。Package.swiftにこういったコードを用意しておく。
ExternalPackage と Products が分離しているので冗長な気もするが、firebase-ios-sdk のように1つのPackageに複数のライブラリが含まれていたり、R.swiftのようにライブラリとプラグインが含まれている場合に対応するには、この定義方法だと都合が良かった。
上述したコードを利用することで、最初に示した Package.swiftはこのようになる。
.product(.firebaseCrashlytics) と指定でき、もちろん補完も聞くので簡単にライブラリを指定できる。
Targetを対応する
Productと同じ流れで .target も対応していく。
これによって、最初に示した Package.swift はこのようになる。
Pluginを対応する
こういったコードを書いてあげることで、Targets内で次のように書ける。
case .welcome: .target( name: "Welcome", dependencies: [ .product(.rswift), .product(.firebaseCrashlytics), .product(.firebaseAnalytics) ], plugins: [ .plugin(.rswiftGenerateInternalResources) ] )
TestTargetを対応する
.testTarget も同じ流れで対応できる。
private enum TestTargets: CaseIterable を新たに実装して、そこにTestたちを集めても良いし、上述した private enum Targets: CaseIterable 内部に書いても良い。
TargetとTestTargetが1対1で対応することが多いのであれば、Targetsで隣に書いてあげることで探しやすいかもしれない。
各々のプロダクトで管理しやすい場所に書くのが良いだろう。
完成品
最終的な Package.swift はこのようになる。
今回のコードではenum内部で分岐をして記述しているが、これをstructに分離したりstatic letに分離することも可能だろう。
さいごに
.product(.hoge)と書けて補完も効くのは非常に便利。しかし、この方式で最も便利さを感じたのは、ProductやTargetを削除したときだった。
今までは大元を削除したとしても、その呼び出し方は
.product(name: "FirebaseAnalytics", package: "firebase-ios-sdk"), .target(name: "Search")
と文字列で指定されているため、エラーを眺めたり検索機能で探す必要があった。この方式ではenumのcaseが消えるため、 Type 'Targets' has no member 'setting' といったエラーが出て簡単に該当箇所を探しやすくなった。