diff --git a/.travis.yml b/.travis.yml index 31c435c..5192d0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,3 @@ -env: - global: - - MODULE_NAME=Dip - matrix: allow_failures: - os: linux @@ -28,11 +24,11 @@ matrix: before_install: - wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - - cd .. - - export SWIFT_VERSION=swift-DEVELOPMENT-SNAPSHOT-2016-09-07-a - - wget https://swift.org/builds/development/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz + - export SWIFT_VERSION=swift-3.0-RELEASE + - wget https://swift.org/builds/swift-3.0-release/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz - tar xzf $SWIFT_VERSION-ubuntu14.04.tar.gz - export PATH="${PWD}/${SWIFT_VERSION}-ubuntu14.04/usr/bin:${PATH}" - - cd $MODULE_NAME + - cd Dip notifications: email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index e572bfa..cfd34ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG +## 5.0.2 + +#### Fixed + +* Fixed Swift 3 issues related to reflection and IUO + [#125](https://github.com/AliSoftware/Dip/issues/125), [@ilyapuchka](https://github.com/ilyapuchka) + +## 5.0.1 + +This release is the same as 5.0.0 and only fixes CocoaPods speck pushed to trunk without macOS, tvOS and watchOS deployment targets. Please use this release instead of 5.0.0 if you integrate Dip via Cocoapods. + ## 5.0.0 * Migrated to Swift 3.0 diff --git a/Dip.podspec b/Dip.podspec index 34cfca4..a2fe4fa 100644 --- a/Dip.podspec +++ b/Dip.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Dip" - s.version = "5.0.1" + s.version = "5.0.2" s.summary = "Dependency Injection for Swift made easy." s.description = <<-DESC diff --git a/Dip/Dip.xcodeproj/project.pbxproj b/Dip/Dip.xcodeproj/project.pbxproj index 82fe7f6..a8eb6fe 100644 --- a/Dip/Dip.xcodeproj/project.pbxproj +++ b/Dip/Dip.xcodeproj/project.pbxproj @@ -379,7 +379,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 5.0.1; + CURRENT_PROJECT_VERSION = 5.0.2; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -434,7 +434,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 5.0.1; + CURRENT_PROJECT_VERSION = 5.0.2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; diff --git a/DipPlayground.playground/Pages/Auto-injection.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Auto-injection.xcplaygroundpage/Contents.swift index 828a389..cda8041 100644 --- a/DipPlayground.playground/Pages/Auto-injection.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Auto-injection.xcplaygroundpage/Contents.swift @@ -113,10 +113,7 @@ class ServerImp: Server { weak var client: ServerClient? } -//There is currently a bug in Swift that causes runtime crash -//when trying to auto-inject not-NSObject weak property. -//https://bugs.swift.org/browse/SR-2144 -class ServerClientImp: NSObject, ServerClient { +class ServerClientImp: ServerClient { var server: Server? init(server: Server) { @@ -149,10 +146,7 @@ class InjectedServerImp: Server { var client: ServerClient? { return injectedClient.value } } -//There is currently a bug in Swift that causes runtime crash -//when trying to auto-inject not-NSObject weak property. -//https://bugs.swift.org/browse/SR-2144 -class InjectedClientImp: NSObject, ServerClient { +class InjectedClientImp: ServerClient { private var injectedServer = Injected() var server: Server? { get { return injectedServer.value } } } diff --git a/README.md b/README.md index 86ff73b..95e165b 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![License](https://img.shields.io/cocoapods/l/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip) [![Platform](https://img.shields.io/cocoapods/p/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip) -[![Swift Version](https://img.shields.io/badge/Linux-compatible-4BC51D.svg?style=flat)](https://developer.apple.com/swift) -[![Swift Version](https://img.shields.io/badge/Swift-2.2-3.0-F16D39.svg?style=flat)](https://developer.apple.com/swift) +[![Swift Version](https://img.shields.io/badge/Swift-2.2--3.0-F16D39.svg?style=flat)](https://developer.apple.com/swift) +[![Swift Version](https://img.shields.io/badge/Linux-3.0--RELEASE-4BC51D.svg?style=flat)](https://developer.apple.com/swift) ![Animated Dipping GIF](cinnamon-pretzels-caramel-dipping.gif) _Photo courtesy of [www.kevinandamanda.com](http://www.kevinandamanda.com/recipes/appetizer/homemade-soft-cinnamon-sugar-pretzel-bites-with-salted-caramel-dipping-sauce.html)_ @@ -17,46 +17,12 @@ _Photo courtesy of [www.kevinandamanda.com](http://www.kevinandamanda.com/recipe It's aimed to be as simple as possible yet provide rich functionality usual for DI containers on other platforms. It's inspired by `.NET`'s [Unity Container](https://msdn.microsoft.com/library/ff647202.aspx) and other DI containers. -* You start by creating `let container = DependencyContainer()` and **registering your dependencies, by associating a _protocol_ or _type_ to a `factory`**. -* Then you can call `container.resolve()` to **resolve an instance of _protocol_ or _type_** using that `DependencyContainer`. +* You start by creating `let container = DependencyContainer()` and **registering your dependencies, by associating a _protocol_ or _type_ to a `factory`** using `container.register { MyService() as Service }`. +* Then you can call `container.resolve() as Service` to **resolve an instance of _protocol_ or _type_** using that `DependencyContainer`. +* You can easily use Dip along with **Storyboards and Nibs** - checkout **[Dip-UI](https://github.com/AliSoftware/Dip-UI)** extensions. There is also a **[code generator](https://github.com/ilyapuchka/dipgen)** that can help to simplify registering new components. -> You can easily use Dip along with Storyboards and Nibs - checkout [Dip-UI](https://github.com/AliSoftware/Dip-UI) extensions. - -## Documentation & Usage Examples - -Dip is completely [documented](http://cocoadocs.org/docsets/Dip/5.0.0/) and comes with a Playground that lets you try all its features and become familiar with API. You can find it in `Dip.xcworkspace`. - -> Note: it may happen that you will need to build Dip framework before playground will be able to use it. For that select `Dip-iOS` scheme and build. - -You can find bunch of usage examples in a [wiki](../../wiki). - -If your are using [VIPER](https://www.objc.io/issues/13-architecture/viper/) architecture - [here](https://github.com/ilyapuchka/VIPER-SWIFT) is VIPER demo app that uses Dip instead of manual dependency injection. - -There are also several blog posts that describe how to use Dip and some of its implementation details: - -- [IoC container in Swift](http://ilya.puchka.me/ioc-container-in-swift/) -- [IoC container in Swift. Circular dependencies and auto-injection](http://ilya.puchka.me/ioc-container-in-swift-circular-dependencies-and-auto-injection/) -- [Dependency injection with Dip](http://ilya.puchka.me/dependency-injecinjection-with-dip/) - -File an issue if you have any question. - - -## Features - -- **[Scopes](../../wiki/scopes)**. Dip supports 5 different scopes (or life cycle strategies): _Unique_, _Shared_, _Singleton_, _EagerSingleton_, _WeakSingleton_; -- **[Named definitions](../../wiki/named-definitions)**. You can register different factories for the same protocol or type by registering them with [tags](); -- **[Runtime arguments](../../wiki/runtime-arguments)**. You can register factories that accept up to 6 runtime arguments; -- **[Circular dependencies](../../wiki/circular-dependencies)**. Dip can resolve circular dependencies; -- **[Auto-wiring](../../wiki/auto-wiring)** & **[Auto-injection](../../wiki/auto-injection)**. Dip can infer your components' dependencies injected in constructor and automatically resolve them as well as dependencies injected with properties. -- **[Resolving optionals](../../wiki/resolving-optionals)**. Dip is able to resolve constructor or property dependencies defined as optionals. -- **[Type forwarding](../../wiki/type-forwarding)**. You can register the same factory to resolve different types. -- **[Storyboards integration](../../wiki/storyboards-integration)**. You can easily use Dip along with storyboards and Xibs without ever referencing container in your view controller's code; -- **Weakly typed components**. Dip can resolve weak types when they are unknown at compile time. -- **[Easy configuration](../../wiki/containers-collaboration)**. No complex container hierarchy, no unneeded functionality; -- **Thread safety**. Registering and resolving components is thread safe; -- **Helpful error messages and configuration validation**. You can validate your container configuration. If something can not be resolved at runtime Dip throws an error that completely describes the issue; - -## Basic usage +
+Basic usage ```swift import Dip @@ -82,7 +48,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ``` -## More sophisticated example +
+ +
+More sophisticated example ```swift import Dip @@ -100,6 +69,9 @@ extension DependencyContainer { static func configure() -> DependencyContainer { return DependencyContainer { container in + unowned let container = container + DependencyContainer.uiContainers = [container] + container.register(tag: "ViewController") { ViewController() } .resolvingProperties { container, controller in controller.animationsFactory = try container.resolve() as AnimatonsFactory @@ -137,49 +109,84 @@ class ViewController { ``` +
+ +## Documentation & Usage Examples + +Dip is completely [documented](http://cocoadocs.org/docsets/Dip/5.0.0/) and comes with a Playground that lets you try all its features and become familiar with API. You can find it in `Dip.xcworkspace`. + +> Note: it may happen that you will need to build Dip framework before playground will be able to use it. For that select `Dip` scheme and build for iPhone Simulator. + +You can find bunch of usage examples and usfull tips in a [wiki](../../wiki). + +If your are using [VIPER](https://www.objc.io/issues/13-architecture/viper/) architecture - [here](https://github.com/ilyapuchka/VIPER-SWIFT) is VIPER demo app that uses Dip instead of manual dependency injection. + +There are also several blog posts that describe how to use Dip and some of its implementation details: + +- [IoC container in Swift](http://ilya.puchka.me/ioc-container-in-swift/) +- [IoC container in Swift. Circular dependencies and auto-injection](http://ilya.puchka.me/ioc-container-in-swift-circular-dependencies-and-auto-injection/) +- [Dependency injection with Dip](http://ilya.puchka.me/dependency-injecinjection-with-dip/) + +File an issue if you have any question. Pull requests are warmly welcome too. + + +## Features + +- **[Scopes](../../wiki/scopes)**. Dip supports 5 different scopes (or life cycle strategies): _Unique_, _Shared_, _Singleton_, _EagerSingleton_, _WeakSingleton_; +- **[Auto-wiring](../../wiki/auto-wiring)** & **[Auto-injection](../../wiki/auto-injection)**. Dip can infer your components' dependencies injected in constructor and automatically resolve them as well as dependencies injected with properties. +- **[Resolving optionals](../../wiki/resolving-optionals)**. Dip is able to resolve constructor or property dependencies defined as optionals. +- **[Type forwarding](../../wiki/type-forwarding)**. You can register the same factory to resolve different types implemeted by a single class. +- **[Circular dependencies](../../wiki/circular-dependencies)**. Dip will be able to resolve circular dependencies if you will follow some simple rules; +- **[Storyboards integration](../../wiki/storyboards-integration)**. You can easily use Dip along with storyboards and Xibs without ever referencing container in your view controller's code; +- **[Named definitions](../../wiki/named-definitions)**. You can register different factories for the same protocol or type by registering them with [tags](); +- **[Runtime arguments](../../wiki/runtime-arguments)**. You can register factories that accept up to 6 runtime arguments (and extend it if you need); +- **[Easy configuration](../../wiki/containers-collaboration)** & **Code generation**. No complex containers hierarchy, no unneeded functionality. Tired of writing all registrations by hand? There is a [cool code generator](https://github.com/ilyapuchka/dipgen) that will create them for you. The only thing you need is to annotate your code with some comments. +- **Weakly typed components**. Dip can resolve "weak" types when they are unknown at compile time. +- **Thread safety**. Registering and resolving components is thread safe; +- **Helpful error messages and configuration validation**. You can validate your container configuration. If something can not be resolved at runtime Dip throws an error that completely describes the issue; + + ## Installation Since version 5.0.0 Dip is built with Swift 3.0. For Swift 2.2-2.3 compatible version use "swift2.3" branch. -Dip is available through [CocoaPods](http://cocoapods.org). To install -it, simply add the following line to your Podfile: +You can install Dip using your favorite dependency manager: + +
+CocoaPods ```ruby pod "Dip" ``` +> You need at least 1.1.0.rc.2 version of CocoaPods. + +
-If you use [Carthage](https://github.com/Carthage/Carthage) add this line to your Cartfile: +
+Carthage ``` github "AliSoftware/Dip" ``` -If you use [Swift Package Manager](https://swift.org/package-manager/) add Dip as dependency to you `Package.swift`: +
+ +
+Swift Package Manager ```swift -let package = Package( - name: "MyPackage", - dependencies: [ - .Package(url: "https://github.com/AliSoftware/Dip.git", "5.0.0") - ] -) +.Package(url: "https://github.com/AliSoftware/Dip", majorVersion: 5, minor: 0) ``` -## Running tests - -On OSX you can run tests from Xcode. On Linux you need to have Swift Package Manager installed and use it to build and run tests: +
-``` -cd Dip -swift build && swift test -``` +## Running tests -> Note: Swift Package Manager is destributed with Swift development snapshots only, so it builds packages using Swift 3. To build Dip you will need to build it with Swift 2.2, for that you need to set [`$SWIFT_EXEC`](https://github.com/apple/swift-package-manager#choosing-swift-version) environment variable. +On OSX you can run tests from Xcode. On Linux you need to have Swift Package Manager installed and use it to build and run tests using this command: `swift build --clean && swift build && swift test` ## Credits -This library has been created by [**Olivier Halligon**](olivier@halligon.net). -I'd also like to thank [**Ilya Puchka**](https://twitter.com/ilyapuchka) for his big contribution to it, as he added a lot of great features to it. +This library has been created by [**Olivier Halligon**](olivier@halligon.net) and is maintained by [**Ilya Puchka**](https://twitter.com/ilyapuchka). **Dip** is available under the **MIT license**. See the `LICENSE` file for more info. diff --git a/Sources/AutoInjection.swift b/Sources/AutoInjection.swift index 00acb08..5a09f87 100644 --- a/Sources/AutoInjection.swift +++ b/Sources/AutoInjection.swift @@ -43,6 +43,8 @@ extension DependencyContainer { } private func resolveChild(child: Mirror.Child) throws { + //HOTFIX for https://bugs.swift.org/browse/SR-2282 + guard !String(describing: type(of: child.value)).hasPrefix("ImplicitlyUnwrappedOptional") else { return } guard let injectedPropertyBox = child.value as? AutoInjectedPropertyBox else { return } let contextKey = DefinitionKey(type: type(of: injectedPropertyBox).wrappedType, typeOfArguments: Void.self, tag: context.tag) diff --git a/Sources/Utils.swift b/Sources/Utils.swift index 4b7d95f..c2a7184 100644 --- a/Sources/Utils.swift +++ b/Sources/Utils.swift @@ -22,15 +22,16 @@ // THE SOFTWARE. // -public enum LogLevel { - case Verbose - case Errors +public enum LogLevel: Int { case None + case Errors + case Verbose } + public var logLevel: LogLevel = .Errors func log(_ logLevel: LogLevel, _ message: Any) { - guard case logLevel = Dip.logLevel else { return } + guard logLevel.rawValue <= Dip.logLevel.rawValue else { return } print(message) } @@ -117,6 +118,14 @@ extension Collection where Index: Comparable, Self.Indices.Index == Index { } #if os(Linux) + + extension String { + public func hasPrefix(_ prefix: String) -> Bool { + return prefix == + String(self.characters.prefix(prefix.characters.count)) + } + } + import Glibc class RecursiveLock { private var _lock = _initializeRecursiveMutex() diff --git a/Tests/DipTests/AutoInjectionTests.swift b/Tests/DipTests/AutoInjectionTests.swift index eb89a8a..7a0a40a 100644 --- a/Tests/DipTests/AutoInjectionTests.swift +++ b/Tests/DipTests/AutoInjectionTests.swift @@ -26,13 +26,13 @@ import XCTest @testable import Dip private protocol Server: class { - weak var client: Client? {get} - var anotherClient: Client? {get set} + weak var client: Client! {get} + var anotherClient: Client! {get set} } private protocol Client: class { - var server: Server? {get} - var anotherServer: Server? {get set} + var server: Server! {get} + var anotherServer: Server! {get set} } private class ServerImp: Server { @@ -41,11 +41,11 @@ private class ServerImp: Server { AutoInjectionTests.clientDidInjectCalled = true } - var client: Client? { + var client: Client! { return _client.value } - weak var anotherClient: Client? + weak var anotherClient: Client! weak var _optionalProperty = InjectedWeak(required: false) } @@ -56,11 +56,11 @@ private class ClientImp: Client { AutoInjectionTests.serverDidInjectCalled = true } - var server: Server? { + var server: Server! { return _server.value } - var anotherServer: Server? + var anotherServer: Server! var _optionalProperty = Injected(required: false) diff --git a/Tests/DipTests/AutoWiringTests.swift b/Tests/DipTests/AutoWiringTests.swift index 1a0dcf5..ecee84c 100644 --- a/Tests/DipTests/AutoWiringTests.swift +++ b/Tests/DipTests/AutoWiringTests.swift @@ -31,13 +31,13 @@ private class ServiceImp2: Service { } private class ServiceImp3 {} private protocol AutoWiredClient: class { - var service1: Service? { get set } - var service2: Service? { get set } + var service1: Service! { get set } + var service2: Service! { get set } } private class AutoWiredClientImp: AutoWiredClient { - var service1: Service? - var service2: Service? + var service1: Service! + var service2: Service! init(service1: Service?, service2: ServiceImp2) { self.service1 = service1 diff --git a/Tests/DipTests/DipTests.swift b/Tests/DipTests/DipTests.swift index cb88b23..c646e80 100644 --- a/Tests/DipTests/DipTests.swift +++ b/Tests/DipTests/DipTests.swift @@ -30,10 +30,10 @@ private class ServiceImp1: Service { } private class ServiceImp2: Service { } private protocol Server: class { - weak var client: Client? { get } + weak var client: Client! { get } } private protocol Client: class { - var server: Server? { get } + var server: Server! { get } } class ResolvableService: Service, Resolvable { @@ -483,8 +483,8 @@ class DipTests: XCTestCase { func testThatItResolvesCircularDependencies() { class ResolvableServer: Server, Resolvable { - weak var client: Client? - weak var secondClient: Client? + weak var client: Client! + weak var secondClient: Client! init(client: Client) { self.client = client @@ -505,17 +505,9 @@ class DipTests: XCTestCase { } - //Due to a bug in Swift 3 Mirror fails if weak property is not NSObject - //https://bugs.swift.org/browse/SR-2144 - class ResolvableClient: NSObject, Client, Resolvable { - var server: Server? - var secondServer: Server? - - #if os(Linux) - init() {} - #else - override init() { super.init() } - #endif + class ResolvableClient: Client, Resolvable { + var server: Server! + var secondServer: Server! var didResolveDependenciesCalled = false @@ -698,21 +690,13 @@ extension DipTests { func testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer() { //given class ServerImp: Server { - weak var client: Client? + weak var client: Client! init(client: Client) { self.client = client } } - //Due to a bug in Swift 3 Mirror fails if weak property is not NSObject - //https://bugs.swift.org/browse/SR-2144 - class ClientImp: NSObject, Client { - var server: Server? - var anotherServer: Server? - - #if os(Linux) - init() {} - #else - override init() { super.init() } - #endif + class ClientImp: Client { + var server: Server! + var anotherServer: Server! } let serverContainer = DependencyContainer() diff --git a/Tests/DipTests/RuntimeArgumentsTests.swift b/Tests/DipTests/RuntimeArgumentsTests.swift index d7e161b..8e61537 100644 --- a/Tests/DipTests/RuntimeArgumentsTests.swift +++ b/Tests/DipTests/RuntimeArgumentsTests.swift @@ -290,7 +290,7 @@ class RuntimeArgumentsTests: XCTestCase { // let name3 = "3" // container.register { (port: Int, url: String!) in ServiceImp(name: name3, baseURL: url, port: port) as Service } -// let service3 = try! container.resolve(withArguments: 80, "http://example.com" as String!) as Service +// let service3 = try! container.resolve(arguments: 80, "http://example.com" as String!) as Service // XCTAssertEqual(service3.name, name3) } diff --git a/Tests/DipTests/ThreadSafetyTests.swift b/Tests/DipTests/ThreadSafetyTests.swift index 4447ff1..3482d44 100644 --- a/Tests/DipTests/ThreadSafetyTests.swift +++ b/Tests/DipTests/ThreadSafetyTests.swift @@ -26,7 +26,7 @@ import XCTest @testable import Dip private protocol Server: class { - var client: Client? { get set } + var client: Client! { get set } } private protocol Client: class { @@ -45,7 +45,7 @@ private func ==(lhs: T, rhs: T) -> Bool { } private class ServerImp: Server, Hashable { - weak var client: Client? + weak var client: Client! init() {} var hashValue: Int {