diff --git a/.travis.yml b/.travis.yml index 7f34abb..3312a22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode10.2 +osx_image: xcode11.6 script: - swift build diff --git a/README.md b/README.md index 6f4aa2b..01bb896 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # SwiftDI +![Github Actions](https://github.com/achernoprudov/SwiftDI/workflows/Swift/badge.svg) [![Travis CI](https://travis-ci.org/achernoprudov/SwiftDI.svg?branch=master)](https://travis-ci.org/achernoprudov/SwiftDI) [![CocoaPods Version](https://img.shields.io/cocoapods/v/SwiftDI.svg?style=flat)](http://cocoapods.org/pods/SwiftDI) [![License](https://img.shields.io/cocoapods/l/SwiftDI.svg?style=flat)](http://cocoapods.org/pods/SwiftDI) diff --git a/Sources/SwiftDI/Inject.swift b/Sources/SwiftDI/Inject.swift new file mode 100644 index 0000000..e4e6fe7 --- /dev/null +++ b/Sources/SwiftDI/Inject.swift @@ -0,0 +1,46 @@ +// +// Inject.swift +// SwiftDI +// +// Created by Andrey Chernoprudov on 25.04.2020. +// + +#if swift(>=5.1) + + @propertyWrapper + public struct Inject { + // MARK: - Instance variables + + let dependency: Value + + // MARK: - Public + + public init(injector: Injector = .default, tag: String? = nil) { + let dependencyTag = tag ?? injector.config.tag + dependency = injector.resolve(Value.self, tag: dependencyTag) + } + + public var wrappedValue: Value { + return dependency + } + } + + @propertyWrapper + public struct OptionalInject { + // MARK: - Instance variables + + let dependency: Value? + + // MARK: - Public + + public init(injector: Injector = .default, tag: String? = nil) { + let dependencyTag = tag ?? injector.config.tag + dependency = injector.resolveSafe(Value.self, tag: dependencyTag) + } + + public var wrappedValue: Value? { + return dependency + } + } + +#endif diff --git a/Sources/SwiftDI/Injector.swift b/Sources/SwiftDI/Injector.swift index bb2b009..7b9396a 100644 --- a/Sources/SwiftDI/Injector.swift +++ b/Sources/SwiftDI/Injector.swift @@ -7,6 +7,10 @@ // public class Injector { + // MARK: - Static + + public static var `default` = Injector() + // MARK: - Instance variables let config: Config diff --git a/SwiftDI.xcodeproj/project.pbxproj b/SwiftDI.xcodeproj/project.pbxproj index 394178f..dcfbe10 100644 --- a/SwiftDI.xcodeproj/project.pbxproj +++ b/SwiftDI.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 552197D2245480170003C013 /* Inject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552197D1245480170003C013 /* Inject.swift */; }; + 552197D4245482130003C013 /* InjectViaWrapperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552197D3245482130003C013 /* InjectViaWrapperTest.swift */; }; 556EED3922BCF40E004195B8 /* InjectorConcurrencyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556EED3722BCF407004195B8 /* InjectorConcurrencyTest.swift */; }; 556EED3B22BD05EA004195B8 /* SimpleDependencyStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556EED3A22BD05EA004195B8 /* SimpleDependencyStorage.swift */; }; 55FA370C2431C7DF0091CF06 /* ThreadLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55FA370A2431C6F20091CF06 /* ThreadLock.swift */; }; @@ -185,6 +187,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 552197D1245480170003C013 /* Inject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inject.swift; sourceTree = ""; }; + 552197D3245482130003C013 /* InjectViaWrapperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectViaWrapperTest.swift; sourceTree = ""; }; 556EED3722BCF407004195B8 /* InjectorConcurrencyTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectorConcurrencyTest.swift; sourceTree = ""; }; 556EED3A22BD05EA004195B8 /* SimpleDependencyStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleDependencyStorage.swift; sourceTree = ""; }; 55FA370A2431C6F20091CF06 /* ThreadLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadLock.swift; sourceTree = ""; }; @@ -201,7 +205,7 @@ 55FA37252431EDD50091CF06 /* SpinThreadLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinThreadLock.swift; sourceTree = ""; }; 55FA37272431EDF00091CF06 /* ReadWriteLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; 55FEE0C52431B692008818A0 /* InjectorKeysTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectorKeysTest.swift; sourceTree = ""; }; - "Nimble::Nimble::Product" /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + "Nimble::Nimble::Product" /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; OBJ_100 /* Contain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contain.swift; sourceTree = ""; }; OBJ_101 /* ContainElementSatisfying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainElementSatisfying.swift; sourceTree = ""; }; @@ -445,6 +449,7 @@ children = ( OBJ_23 /* Info.plist */, 55FA371C2431D6080091CF06 /* Storage */, + OBJ_29 /* Utils */, OBJ_24 /* ConstructorInjectionTest.swift */, OBJ_25 /* InjectorInheritanceTest.swift */, OBJ_26 /* InjectorLeakingTest.swift */, @@ -452,7 +457,7 @@ OBJ_27 /* InjectorPerformanceTest.swift */, 55FEE0C52431B692008818A0 /* InjectorKeysTest.swift */, OBJ_28 /* InjectorTest.swift */, - OBJ_29 /* Utils */, + 552197D3245482130003C013 /* InjectViaWrapperTest.swift */, ); name = SwiftDITests; path = Tests/SwiftDITests; @@ -645,6 +650,7 @@ OBJ_15 /* DependencyScope.swift */, OBJ_16 /* Injector+Config.swift */, OBJ_17 /* Injector.swift */, + 552197D1245480170003C013 /* Inject.swift */, ); name = SwiftDI; path = Sources/SwiftDI; @@ -991,6 +997,7 @@ 55FA370C2431C7DF0091CF06 /* ThreadLock.swift in Sources */, 55FA37202431EC910091CF06 /* ReadWriteLockDependencyStorage.swift in Sources */, OBJ_253 /* DependencyKey.swift in Sources */, + 552197D2245480170003C013 /* Inject.swift in Sources */, 55FA37192431CB3B0091CF06 /* DependencyStorageFactory.swift in Sources */, 55FA37242431EDC40091CF06 /* NSThreadLock.swift in Sources */, 55FA37112431C9B80091CF06 /* DependencyStorageType.swift in Sources */, @@ -1030,6 +1037,7 @@ OBJ_280 /* InjectorTest.swift in Sources */, OBJ_281 /* LeakTest+Nimble.swift in Sources */, 55FA371E2431D6290091CF06 /* DependencyStorageTest.swift in Sources */, + 552197D4245482130003C013 /* InjectViaWrapperTest.swift in Sources */, OBJ_282 /* LeakTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1091,6 +1099,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; + SWIFT_SUPPRESS_WARNINGS = YES; SWIFT_VERSION = 5.0; TARGET_NAME = Nimble; TVOS_DEPLOYMENT_TARGET = 9.0; @@ -1119,6 +1128,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; + SWIFT_SUPPRESS_WARNINGS = YES; SWIFT_VERSION = 5.0; TARGET_NAME = Nimble; TVOS_DEPLOYMENT_TARGET = 9.0; diff --git a/Tests/SwiftDITests/InjectViaWrapperTest.swift b/Tests/SwiftDITests/InjectViaWrapperTest.swift new file mode 100644 index 0000000..b34b581 --- /dev/null +++ b/Tests/SwiftDITests/InjectViaWrapperTest.swift @@ -0,0 +1,142 @@ +// +// InjectViaWrapperTest.swift +// SwiftDITests +// +// Created by Andrey Chernoprudov on 25.04.2020. +// + +import Nimble +import Quick + +@testable import SwiftDI + +#if swift(>=5.1) && compiler(>=5.1) + + class InjectViaWrapperTest: QuickSpec { + private static var customInjector: Injector! + + override func spec() { + beforeEach { + Injector.default = Injector() + } + + afterEach { + Injector.default = Injector() + Self.customInjector = nil + } + + describe("inject") { + it("with default injector and tag") { + class TestObject { + @Inject + var value: String + } + + // prepare + let expectedValue = "foo" + Injector.default.bind(String.self).with(expectedValue) + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value) == expectedValue + } + + it("with default injector and custom tag tag") { + // prepare + let expectedValue = "foo" + + class TestObject { + @Inject(tag: "bar") + var value: String + } + + Injector.default.bind(String.self) + .tag("bar") + .with(expectedValue) + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value) == expectedValue + } + + it("with custom injector") { + // prepare + Self.customInjector = Injector() + let expectedValue = "foo" + + Self.customInjector.bind(String.self) + .with(expectedValue) + + class TestObject { + @Inject(injector: InjectViaWrapperTest.customInjector) + var value: String + } + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value) == expectedValue + } + + it("with custom injector") { + // prepare + Self.customInjector = Injector() + let expectedValue = "foo" + + Self.customInjector.bind(String.self) + .with(expectedValue) + + class TestObject { + @Inject(injector: InjectViaWrapperTest.customInjector) + var value: String + } + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value) == expectedValue + } + } + + describe("optional inject") { + it("sets dependency") { + class TestObject { + @OptionalInject + var value: String? + } + + // prepare + let expectedValue = "foo" + Injector.default.bind(String.self).with(expectedValue) + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value) == expectedValue + } + + it("sets nil") { + // prepare + class TestObject { + @OptionalInject + var value: String? + } + + // actions + let testObject = TestObject() + + // assertions + expect(testObject.value).to(beNil()) + } + } + } + } + +#endif