Swiftuiの「環境」に触発された依存管理ライブラリ。
このライブラリは、ブランドンウィリアムズとスティーブンセリスが主催する機能プログラミングと迅速な言語を探索するビデオシリーズであるPoint-Freeに関する多くのエピソードの過程で動機付けられ、設計されました。
依存関係は、アプリケーションのタイプと関数であり、制御しない外部システムと対話する必要があります。これの古典的な例は、サーバーにネットワークリクエストを行うAPIクライアントですが、 UUID
やDate
初期化、ファイルアクセス、ユーザーのデフォルト、さらにはクロックやタイマーなどの無害なものも、すべて依存関係と考えることができます。
依存関係の管理(または、「依存関係のインジェクション」と呼ぶのが好きな人)について考えずにアプリケーション開発で非常に遠くに行くことができますが、最終的に制御されていない依存関係は、コードベースと開発サイクルに多くの問題を引き起こす可能性があります。
制御されていない依存関係により、ファイルシステム、ネットワーク接続、インターネット速度、サーバーアップタイムなど、外の世界の気まぐれに敏感であるため、高速で決定論的なテストを書くことが困難になります。
多くの依存関係は、ロケーションマネージャーや音声認識者などのSwiftUIプレビューではうまく機能しません。また、モーションマネージャーなどのシミュレーターなどでも機能しません。これにより、これらのフレームワークを使用すると、機能の設計を簡単に反復することができなくなります。
サードパーティと相互作用する依存関係、非アプルライブラリ(Firebase、Web Socketライブラリ、ネットワークライブラリなど)と相互作用する依存関係は、ヘビー級であり、コンパイルに時間がかかる傾向があります。これにより、開発サイクルが遅くなる可能性があります。
これらの理由により、さらに多くの理由により、依存関係をコントロールさせるのではなく、依存関係を制御することが非常に奨励されています。
しかし、依存関係を制御することは始まりに過ぎません。依存関係を制御すると、新しい問題のセット全体に直面します。
アプリケーション全体の依存関係をどのようにして、どこでも明示的にそれらを渡すよりも人間工学に基づいている方法でどのように伝播できますか?
アプリケーションの一部のみの依存関係をどのようにオーバーライドできますか?これは、テストやSwiftUIプレビューの依存関係をオーバーライドするだけでなく、オンボーディングエクスペリエンスなどの特定のユーザーフローに便利です。
機能がテストで使用するすべての依存関係を無効にするにはどうすればよいですか?テストがいくつかの依存関係を模倣するのは間違っていますが、他の人は外の世界と相互作用しているようにします。
このライブラリは、上記のすべてのポイントを扱っています。
ライブラリを使用すると、独自の依存関係を登録できますが、ボックスから多くの制御可能な依存関係が付属しています(完全なリストについてはDependencyValues
参照)。すぐに使用できる可能性が高くなります。 Date()
、 UUID()
、 Task.sleep
、またはspeedを使用している場合、または機能のロジックでスケジューラを直接組み合わせている場合、このライブラリの使用を開始できます。
@ Observable
final class FeatureModel {
var items : [ Item ] = [ ]
@ ObservationIgnored
@ Dependency ( . continuousClock ) var clock // Controllable way to sleep a task
@ ObservationIgnored
@ Dependency ( . date . now ) var now // Controllable way to ask for current date
@ ObservationIgnored
@ Dependency ( . mainQueue ) var mainQueue // Controllable scheduling on main queue
@ ObservationIgnored
@ Dependency ( . uuid ) var uuid // Controllable UUID creation
// ...
}
依存関係が宣言されたら、 Date()
、 UUID()
などに直接手を差し伸べるのではなく、機能のモデルに定義されている依存関係を使用できます。
@ Observable
final class FeatureModel {
// ...
func addButtonTapped ( ) async throws {
try await clock . sleep ( for : . seconds ( 1 ) ) // ? Don't use 'Task.sleep'
items . append (
Item (
id : uuid ( ) , // ? Don't use 'UUID()'
name : " " ,
createdAt : now // ? Don't use 'Date()'
)
)
}
}
これは、機能に制御可能な依存関係の使用を開始するために必要なすべてです。その少し前の作業が完了すると、図書館の力を利用し始めることができます。
たとえば、これらの依存関係をテストで簡単に制御できます。 addButtonTapped
メソッド内のロジックをテストする場合は、 withDependencies
関数を使用して、1つのテストの範囲の依存関係をオーバーライドできます。 1-2-3と同じくらい簡単です:
@ Test
func add ( ) async throws {
let model = withDependencies {
// 1️⃣ Override any dependencies that your feature uses.
$0 . clock = . immediate
$0 . date . now = Date ( timeIntervalSinceReferenceDate : 1234567890 )
$0 . uuid = . incrementing
} operation : {
// 2️⃣ Construct the feature's model
FeatureModel ( )
}
// 3️⃣ The model now executes in a controlled environment of dependencies,
// and so we can make assertions against its behavior.
try await model . addButtonTapped ( )
#expect (
model . items == [
Item (
id : UUID ( uuidString : " 00000000-0000-0000-0000-000000000000 " ) ! ,
name : " " ,
createdAt : Date ( timeIntervalSinceReferenceDate : 1234567890 )
)
]
)
}
ここでは、 date
依存関係を制御して常に同じ日付を返すようにし、 uuid
依存関係をImmediateClock
して、呼び出されるたびにclock
インクリメントUUIDを返すようにしました。インスタント。これらの依存関係を制御しなかった場合、このテストは、 Date()
とUUID()
で返されるものを正確に予測する方法がないため、記述するのが非常に困難です。テストを遅くします。
ただし、制御可能な依存関係は、テストのみに役立ちません。 Xcodeプレビューでも使用できます。上記の機能が、ビューで何かが起こる前に、時計を使用して時間をかけて寝るとします。ビューがどのように変化するかを確認するために文字通り時間を待ちたくない場合は、 .dependencies
のプレビュー特性を使用して、クロック依存関係を「即時」クロックにオーバーライドすることができます。
#Preview (
traits : . dependencies {
$0 . continuousClock = . immediate
}
) {
// All access of '@Dependency(.continuousClock)' in this preview will
// use an immediate clock.
FeatureView ( model : FeatureModel ( ) )
}
これにより、プレビューが実行時に即時クロックを使用するようになりますが、シミュレータまたはデバイスで実行すると、ライブContinuousClock
が使用されます。これにより、アプリが本番環境でどのように実行されるかに影響を与えることなく、プレビューの依存関係をオーバーライドすることができます。
それが図書館の使用を始めるための基本ですが、まだできることはまだまだあります。ドキュメントと記事を探索することで、ライブラリについて詳しく知ることができます。
クイックスタート(上記の情報と同じ) :すべての機能に深く潜る前に、ライブラリを始めることの基本を学びます。
依存関係とは何ですか? :依存関係が何であるか、コードをどのように複雑にし、なぜそれらを制御したいのかを学びます。
依存関係の使用:ライブラリに登録されている依存関係を使用する方法を学びます。
依存関係の登録:ライブラリに独自の依存関係を登録する方法を学び、コードベースの任意の部分からすぐに利用できるようにします。
ライブ、プレビュー、テスト依存関係:ライブアプリケーション、Xcodeプレビュー、さらにはテストで使用するために、依存関係のさまざまな実装を提供する方法を学びます。
テスト:依存関係を制御する主な理由の1つは、テストを容易にすることです。ライブラリでより良いテストを書くためのいくつかのヒントとコツを学びましょう。
依存関係の設計:依存関係を設計する技術を学び、機能に注入してテストをオーバーライドするために最も柔軟になります。
オーバーライド依存関係:アプリケーションの特定の部分が異なる依存関係を使用できるように、実行時に依存関係を変更する方法を学びます。
依存関係の寿命:依存関係の生涯、依存関係の生涯を延長する方法、依存関係がどのように継承されるかについて学びます。
シングルエントリポイントシステム:「シングルエントリポイント」システム、およびこれらがこの依存関係ライブラリに最適な理由について学びますが、シングル非シングルエントリポイントシステムでライブラリを使用することは可能です。
このライブラリを使用してファイルシステムアクセス、タイマー、音声認識APIの依存関係を制御するなど、SwiftUI開発のための最新のベストプラクティスを使用して、AppleのScrumdingerデモアプリケーションを再構築しました。そのデモはここにあります。
依存関係APIの最新のドキュメントはこちらから入手できます。
パッケージとしてプロジェクトに追加することにより、Xcodeプロジェクトに依存関係を追加できます。
https://github.com/pointfreeco/swift-dependencies
SwiftPMプロジェクトで依存関係を使用したい場合は、 Package.swift
に追加するのと同じくらい簡単です。Swift:
dependencies: [
. package ( url : " https://github.com/pointfreeco/swift-dependencies " , from : " 1.0.0 " )
]
次に、ライブラリへのアクセスが必要な任意のターゲットに製品を追加します。
. product ( name : " Dependencies " , package : " swift-dependencies " ) ,
このライブラリについて話し合いたい場合、または特定の問題を解決するためにそれを使用する方法について質問したい場合は、仲間のポイントフリーの愛好家と話し合うことができる場所がたくさんあります。
このライブラリは、箱から出して多くの依存関係を制御しますが、拡張機能も開かれています。以下のプロジェクトはすべて、依存関係の上に構築されています。
Swiftコミュニティには、他にも多くの依存関係噴射ライブラリがあります。それぞれには、依存関係とは異なる独自の優先順位とトレードオフがあります。ここにいくつかのよく知られた例があります:
このライブラリはMITライセンスの下でリリースされます。詳細については、ライセンスを参照してください。