We're excited to announce Dependencies, which is a general-purpose dependency management library with an API inspired by SwiftUI's "environment" and powered by Swift's task local machinery.
From our Quick start guide:
The library allows you to register your own dependencies, but it also comes with many controllable dependencies out of the box (see
DependencyValues
for a full list), and there is a good chance you can immediately make use of one. If you are usingDate()
,UUID()
,Task.sleep
, or Combine schedulers directly in your featureās logic, you can already start to use this library.Any place you are using one of those dependencies directly in feature logic without passing it explicitly to the feature can be updated to first declare your dependency in the feature using the
@Dependency
property wrapper:final class FeatureModel: ObservableObject { @Dependency(\.continuousClock) var clock // Controllable async sleep @Dependency(\.date.now) var now // Controllable current date @Dependency(\.mainQueue) var mainQueue // Controllable main queue scheduling @Dependency(\.uuid) var uuid // Controllable UUID creation // ... }
Once your dependencies are declared, rather than reaching out to the
Date()
,UUID()
,Task
, > etc., directly, you can use the dependency that is defined on your featureās model:final class FeatureModel: ObservableObject { // ... func addButtonTapped() async throws { try await self.clock.sleep(for: .seconds(1)) // š Don't use 'Task.sleep' self.items.append( Item( id: self.uuid(), // š Don't use 'UUID()' name: "", createdAt: self.now // š Don't use 'Date()' ) ) } }
That is all it takes to start using controllable dependencies in your features. With that little bit of upfront work done you can start to take advantage of the libraryās powers.
For example, you can easily control these dependencies in tests. If you want to test the logic inside the
addButtonTapped
method, you can use thewithDependencies
function to override any dependencies for the scope of one single test. Itās as easy as 1-2-3:func testAdd() async throws { let model = withDependencies { // 1ļøā£ Override any dependencies that your feature uses. $0.clock = ImmediateClock() $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() XCTAssertEqual( model.items, [ Item( id: UUID(uuidString: "00000000-0000-0000-0000-000000000000")!, name: "", createdAt: Date(timeIntervalSinceReferenceDate: 1234567890) ) ] ) }
The library can be used in SwiftUI, UIKit, server-side Swift, and anywhere, really.
To learn more, please check out the documentation and GitHub repo.