依賴性管理庫,靈感來自Swiftui的“環境”。
該圖書館的動機和設計在許多無點上的情節中,這是一個視頻系列,探索了功能編程和由布蘭登·威廉姆斯(Brandon Williams)和斯蒂芬·塞利斯(Stephen Celis)主持的Swift語言。
依賴項是您應用程序中需要與您無法控制的外部系統進行交互的類型和功能。此類的經典示例是向服務器提出網絡請求的API客戶端,但也可以將諸如UUID
和Date
初始化器,文件訪問,用戶默認值甚至時鐘和計時器之類的內容視為依賴關係。
您可以在應用程序開發的情況下真正走得很遠,而無需考慮依賴性管理(或者,某些人喜歡稱其為“依賴注入”),但是最終不受控制的依賴關係可能會在您的代碼基礎和開發週期中引起許多問題:
不受控制的依賴性使得很難快速編寫,確定性的測試,因為您容易受到外界的變化的影響,例如文件系統,網絡連接,互聯網速度,服務器正常運行時間等。
許多依賴項在SwiftUI預覽中無法很好地工作,例如位置經理和語音認可者,即使在模擬器(例如運動經理)等中,有些依賴項也無法正常工作。如果您使用這些框架,這使您無法輕鬆迭代功能的設計。
與第三方,非應用程序庫(例如Firebase,Web插座庫,網絡庫等)相互作用的依賴關係往往是重量級的,並且需要很長時間才能編譯。這可以減慢您的開發週期。
由於這些原因,還有很多事情強烈鼓勵您控制自己的依賴關係,而不是讓他們控制您。
但是,控制依賴性只是開始。一旦控制了依賴項,就面臨著一系列新問題:
在整個應用程序中,您如何以更符合人體工程學的方式來傳播依賴關係,而不是明確地將它們傳播到任何地方,而是比擁有全球依賴性更安全?
您如何僅在應用程序的一部分中超越依賴項?這對於測試和SwiftUI預覽的重大依賴性以及特定的用戶流(例如入職經驗)可能會很方便。
您如何確定您將功能在測試中使用的所有依賴性佔據了所有依賴性?測試嘲笑一些依賴性,但將其他人與外界互動是不正確的。
該庫解決了上面的所有要點,還有更多。
該庫允許您註冊自己的依賴項,但它也帶有許多可控的依賴關係(請參閱完整列表的DependencyValues
),並且很有可能您可以立即使用一個。如果您使用Date()
, UUID()
, Task.sleep
或直接在功能邏輯中組合調度程序,則可以開始使用此庫。
@ 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-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
依賴性,每次調用它時都會返回自動提出的UUID,甚至我們甚至使用ImmediateClock
電信來控制clock
依賴性立即的。如果我們不控制這些依賴關係,那麼本測試將很難編寫,因為沒有辦法準確預測Date()
和UUID()
將返回的內容,我們必須等待現實世界的過去時間,使測試緩慢。
但是,可控的依賴性不僅對測試有用。它們也可以在Xcode預覽中使用。假設上面的功能會使用時鐘睡一段時間才能在視圖中發生。如果您不想等待時間流逝以查看視圖的變化,則可以使用.dependencies
PREVIEW特徵來覆蓋時鐘依賴性為“立即”時鐘:
#Preview (
traits : . dependencies {
$0 . continuousClock = . immediate
}
) {
// All access of '@Dependency(.continuousClock)' in this preview will
// use an immediate clock.
FeatureView ( model : FeatureModel ( ) )
}
這將使預覽在運行時使用即時時鐘,但是在模擬器或設備上運行時,它仍將使用實時連續ContinuousClock
。這使得僅用於預覽而不影響您的應用程序在生產中的運行方式而成為可能。
這是開始使用庫的基礎知識,但是您還有很多事情可以做。您可以通過探索文檔和文章來深入了解圖書館:
快速啟動(與上面的信息相同) :在深入研究其所有功能之前,請學習從圖書館開始的基礎知識。
什麼是依賴性? :了解什麼是依賴性,它們如何使您的代碼複雜化以及為什麼要控制它們。
使用依賴項:了解如何使用在庫中註冊的依賴項。
註冊依賴項:了解如何在庫中註冊自己的依賴項,以便它們立即從代碼庫中的任何部分可用。
實時,預覽和測試依賴性:了解如何提供在實時應用程序中以及XCode預覽甚至測試中使用的依賴項的不同實現。
測試:控制依賴性的主要原因之一是允許更輕鬆的測試。學習一些技巧和技巧,以編寫圖書館更好的測試。
設計依賴項:學習設計依賴性的技術,以便它們最靈活地註入功能並覆蓋測試。
重大依賴性:了解如何在運行時更改依賴項,以便應用程序的某些部分可以使用不同的依賴關係。
依賴性壽命:了解依賴性的壽命,如何延長依賴的壽命以及如何遺傳依賴性。
單個入口點系統:了解“單個入口點”系統,以及為什麼它們最適合此依賴項庫,儘管可以將庫與非單個入口點系統一起使用。
我們使用SwiftUI開發的現代最佳實踐重建了Apple的Scrumdinger演示應用程序,包括使用此庫來控製文件系統訪問,計時器和語音識別API的依賴關係。該演示可以在這裡找到。
有關依賴項API的最新文檔可在此處找到。
您可以通過將其作為包裝添加到項目中,將依賴項添加到Xcode項目中。
https://github.com/pointfreeco/swift-dependencies
如果您想在SwiftPM項目中使用依賴項,那麼將其添加到Package.swift
中一樣簡單。
dependencies: [
. package ( url : " https://github.com/pointfreeco/swift-dependencies " , from : " 1.0.0 " )
]
然後將產品添加到需要訪問庫的任何目標:
. product ( name : " Dependencies " , package : " swift-dependencies " ) ,
如果您想討論此圖書館或對如何使用它解決特定問題有疑問,那麼您可以在許多地方與其他無點的愛好者討論:
該庫控制了一些依賴性,但也可以開放擴展。以下項目都建立在依賴項之上:
Swift社區中還有許多其他依賴注入庫。每個都有自己的一套優先級和權衡,與依賴關係有所不同。這裡有一些眾所周知的例子:
該庫是根據麻省理工學院許可證發布的。有關詳細信息,請參見許可證。