├── .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 |
--------------------------------------------------------------------------------