Skip to content

Commit

Permalink
Dynamic member lookup added
Browse files Browse the repository at this point in the history
  • Loading branch information
avdyushin committed May 27, 2020
1 parent 8889230 commit b58d7b8
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ Define injected properties:
@Injected var storage: StorageService
```

Iterate over all dependencies:

```swift
for service in dependencies {
service.start()
}
```

Access using dynamic member lookup:

```swift
let location: LocationProtocol? = dependencies.locationService // Erase type to protocol
let storage: StorageService = dependencies.storageService! // Force unwarp if needed
```

More details on implementation and usage is [here](https://grigory.nl/posts/swift-property-wrappers/)

### How to add it to Xcode project?
Expand Down
11 changes: 11 additions & 0 deletions Sources/Injected/Injected.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Copyright © 2020 Grigory Avdyushin. All rights reserved.
//

import Foundation

public struct Dependency {

public typealias ResolveBlock<T> = () -> T
Expand All @@ -24,6 +26,7 @@ public struct Dependency {
}
}

@dynamicMemberLookup
open class Dependencies: Sequence {

static private(set) var shared = Dependencies()
Expand All @@ -43,6 +46,7 @@ open class Dependencies: Sequence {
register(dependency())
}

/// Builds (resolves) all dependencies graph
open func build() {
// We assuming that at this point all needed dependencies are registered
for index in dependencies.startIndex..<dependencies.endIndex {
Expand All @@ -51,11 +55,18 @@ open class Dependencies: Sequence {
Self.shared = self
}

/// Returns iterator for all registered dependencies
public func makeIterator() -> AnyIterator<Any> {
var iter = dependencies.makeIterator()
return AnyIterator { iter.next()?.value }
}

/// Return dependency by given camelCase name of the object type
/// For example: if dependency registered as `MyService` name should be `myService`
public subscript<T>(dynamicMember name: String) -> T? {
dependencies.first { $0.name == name.prefix(1).capitalized + name.dropFirst() }?.value as? T
}

// MARK: - Private

fileprivate init() { }
Expand Down
13 changes: 13 additions & 0 deletions Tests/InjectedTests/InjectedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ final class InjectedTests: XCTestCase {
XCTAssertEqual(serviceB.fetch().first, "Foo")
}

func testIterator() {
for service in deps {
XCTAssertTrue(service is ServiceA || service is ServiceB)
}
}

func testMemberLookup() {
let serviceA: ServiceA = deps.serviceAImpl!
XCTAssertTrue(serviceA.run())
let serviceB: ServiceB = deps.serviceBImpl!
XCTAssertEqual(serviceB.fetch().first, "Foo")
}

static var allTests = [
("testExample", testExample),
]
Expand Down

0 comments on commit b58d7b8

Please sign in to comment.