Fusion
Lightweight, property-wrapper based Dependency Injection for Swift
Install / Use
/learn @joshuawright11/FusionREADME
Fusion
Fusion is a lightweight dependency injection library for Swift. It uses macros to make injection a breeze so you can keep your code testable and modular in modern, Swifty style.
Installation
Install with the Swift Package Manager.
.package(url: "https://github.com/joshuawright11/fusion", from: "0.5.0")
Basics
Declare a Service
Declare services by extending Container and adding properties annotated with @Service. Leverage built-in contexts such as isTest to return the right implementation for the right context.
extension Container {
@Service var apiService: APIService = isTest ? MockAPIService() : LiveAPIService()
}
Resolve a Service
Access your shiny new service with @Inject.
@Inject(\.apiService) var api
let user = try await api.getUser(id: id)
Define Service Scope
Use scopes to control the lifetime of each service. Each service is "transient" by default and will be freshly resolved on each injection.
// a new `MyService()` will be initialized and returned each time `transientService` is resolved
@Service var transientService = MyService()
// a new `MyService()` will be created the first time `singletonService` is resolved, and that instance will be returned each subsequent resolution
@Service(.singleton) var singletonService = MyService()
Advanced
Override Service Values
You can globally override the value of a single service (for example, in a test) by just setting the value on Container.
@Test func testFetchUser() {
let mockUser = User()
let mockService = UserRepository(currentUser: mockUser)
Container.userRepository = mockService
}
To override the value of services on a per-task basis, use Container.mock. The override value will be set via @TaskLocal and only accessible inside the then block.
extension Container {
@Service var logger = LiveLogger()
}
Container.mock {
$0.logger = MockLogger()
} then: {
@Inject(\.logger) var logger // MockLogger
}
@Inject(\.testService) var testService // LiveLogger
Custom Scopes
Define custom scopes to control the lifecycle of various service types. Services of a custom scope are treated as .singletons until their scope is reset.
private extension Container.Scope {
static let session = id("session")
}
extension Container {
// the first time this service is resolved, the created instance will be cached until the `.session` scope is reset
@Service(.session) var userService = UserService()
}
func userDidLogout() {
Container.main.reset(.session) // the next time `userService` is resolved, it will return a new value
}
Contexts
Control service resolution behavior via contexts on Container such as isPreview, isTest, isSimulator, isDevice, etc. Add your own contexts by extending Container.
extension Container {
@Service(.singleton) var apiClient: APIClient {
switch env {
case .prod:
return LiveAPIClient(host: "stage.project.com")
case .stage:
return LiveAPIClient(host: "stage.project.com")
case .dev:
return LiveAPIClient(host: "localhost:9001")
}
}
}
enum Env {
case prod
case stage
case dev
}
extension Container {
var env: Env {
let envString = UserDefaults.standard.string(forKey: "env")
switch envString {
case "prod":
return .prod
case "stage":
return .stage
case "dev":
return .dev
default:
return .dev
}
}
}
Resolving By Type
You can skip the keypath syntax and resolve a service via direct type.
let liveDatabase = PostgresDatabase()
Container.main.set(liveDatabase, for: Database.self)
@Inject var database: Database // PostgresDatabase
Note that unlike defining services as properties on Container, the existence of a service can't be compile-time guaranteed so you'll need to be sure to set the service via Container.set before accessing.
Resolving By Type with alias
Use Container.setAlias to define a specific service property as the one that should be resolved when its type is resolved.
extension Container {
@Service(.singleton) var datadog = DatadogLogger()
@Service(.singleton) var splunk = SplunkLogger()
}
Container.setAlias(\.datadog, for: Logger.self)
@Inject var logger: Logger // DatadogLogger
License
Fusion is available under the MIT license. See LICENSE for details.
Related Skills
node-connect
349.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.7kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
