├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Package.swift ├── README.md └── Sources └── Dependency └── Dependency.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Dependency", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "Dependency", 12 | targets: ["Dependency"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 21 | .target( 22 | name: "Dependency", 23 | dependencies: []), 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dependency 2 | 3 | A dependency Injection solution for SwiftUI. 4 | Thank you [Antoine van der Lee](https://www.avanderlee.com/swift/dependency-injection/) and [Vincent Pradeilles](https://youtu.be/TLmBeqA53bo) for initially showcasing this idea. 5 | 6 | ## Usage 7 | 8 | ### Register a Service as a Dependency 9 | 10 | ```swift 11 | private struct UserServiceKey: DependencyKey { 12 | static var currentValue: UserService = MyUserService() 13 | } 14 | 15 | extension DependencyValues { 16 | var userService: UserService { 17 | get { Self[UserServiceKey.self] } 18 | set { Self[UserServiceKey.self] = newValue } 19 | } 20 | } 21 | ``` 22 | 23 | ### Access a Service, e.g. inside a ViewModel 24 | 25 | ```swift 26 | @Dependency(\.userService) private var userService 27 | ``` 28 | 29 | ### Set a Service, e.g. inside a Unit Test 30 | 31 | ```swift 32 | DependencyValues[\.userService] = MockUserService() 33 | ``` 34 | -------------------------------------------------------------------------------- /Sources/Dependency/Dependency.swift: -------------------------------------------------------------------------------- 1 | public protocol DependencyKey { 2 | associatedtype Value 3 | static var currentValue: Self.Value { get set } 4 | } 5 | 6 | public struct DependencyValues { 7 | private static var current = DependencyValues() 8 | 9 | public static subscript(key: K.Type) -> K.Value where K: DependencyKey { 10 | get { key.currentValue } 11 | set { key.currentValue = newValue } 12 | } 13 | 14 | public static subscript(_ keyPath: WritableKeyPath) -> T { 15 | get { current[keyPath: keyPath] } 16 | set { current[keyPath: keyPath] = newValue } 17 | } 18 | } 19 | 20 | @propertyWrapper 21 | public struct Dependency { 22 | private let keyPath: WritableKeyPath 23 | public var wrappedValue: T { 24 | get { DependencyValues[keyPath] } 25 | set { DependencyValues[keyPath] = newValue } 26 | } 27 | 28 | public init(_ keyPath: WritableKeyPath) { 29 | self.keyPath = keyPath 30 | } 31 | } 32 | --------------------------------------------------------------------------------