Dependency injection framework for Swift with iOS/macOS/Linux
Swift HTML Other
Clone or download
jakubvano Merge pull request #353 from jakubvano/recursive_lock_tests
Add tests for nested thread safe resolver
Latest commit 5df1454 Jun 5, 2018
Permalink
Failed to load latest commit information.
Assets Add Swinject icon source file. Nov 22, 2016
Configurations Update jspahrsummers/xcconfigs to the following version for Xcode 9. Sep 20, 2017
Documentation Update docs [skip ci] Apr 17, 2018
Sample-iOS.playground Prevent warning in Xcode 9.1 Nov 26, 2017
Sources Add tests for nested thread safe resolver Jun 4, 2018
Swinject.xcodeproj Update the project for Xcode 9.3 Apr 2, 2018
Tests Add tests for nested thread safe resolver Jun 4, 2018
script Implement convenient way of declaring type forwarding Feb 16, 2018
.Package.test.swift Fix .Package.test.swift in the correct way to match Cartfile. Aug 6, 2017
.gitignore Remove Quick and Nimble submodules, and use pre-built frameworks instead Jun 2, 2017
.gitmodules Remove Quick and Nimble submodules, and use pre-built frameworks instead Jun 2, 2017
.hound.yml Use Hound CI to run SwiftLint. Nov 28, 2015
.swift-version Update the version of Swift for Linux on Travis CI to 4.0.3. Feb 27, 2018
.swiftlint.yml Implement convenient way of declaring type forwarding Feb 16, 2018
.travis.yml Disable temporarily the iOS 8.x env job Apr 4, 2018
CHANGELOG.md No op, to re-kick off CI Dec 17, 2017
CONTRIBUTING.md Update CONTRIBUTING.md Jun 12, 2017
Cartfile.private Update Nimble to support both Xcode 8 and 9. Aug 6, 2017
Cartfile.resolved Update Quick to 7.0.3 and Nimble to 1.2.0 for full support of Xcode 9. Feb 27, 2018
LICENSE.txt Add readme and license files. Jul 31, 2015
Package.swift Fix Package.swift to support SPM. Jul 2, 2016
README.md Fix README for swift 4 Mar 11, 2018
Swinject.podspec Tag 2.4.1 May 26, 2018

README.md

Swinject

Travis CI Carthage compatible CocoaPods Version License Platforms Swift Version

Swinject is a lightweight dependency injection framework for Swift.

Dependency injection (DI) is a software design pattern that implements Inversion of Control (IoC) for resolving dependencies. In the pattern, Swinject helps your app split into loosely-coupled components, which can be developed, tested and maintained more easily. Swinject is powered by the Swift generic type system and first class functions to define dependencies of your app simply and fluently.

Features

Extensions

Requirements

  • iOS 8.0+ / Mac OS X 10.10+ / watchOS 2.0+ / tvOS 9.0+
  • Swift 2.2 or 2.3
    • Xcode 7.0+
  • Swift 3
    • Xcode 8.0+
  • Swift 3.2, 4.x
    • Xcode 9.0+
  • Carthage 0.18+ (if you use)
  • CocoaPods 1.1.1+ (if you use)

Installation

Swinject is available through Carthage or CocoaPods.

Carthage

To install Swinject with Carthage, add the following line to your Cartfile.

Swift 2.2 or 2.3

github "Swinject/Swinject" ~> 1.1.4

Swift 3.x or 4.x

github "Swinject/Swinject"

# Uncomment if you use SwinjectStoryboard
# github "Swinject/SwinjectStoryboard"

Then run carthage update --no-use-binaries command or just carthage update. For details of the installation and usage of Carthage, visit its project page.

CocoaPods

To install Swinject with CocoaPods, add the following lines to your Podfile.

Swift 2.2 or 2.3

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' # or platform :osx, '10.10' if your target is OS X.
use_frameworks!

pod 'Swinject', '~> 1.1.4'

Swift 3.x

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' # or platform :osx, '10.10' if your target is OS X.
use_frameworks!

pod 'Swinject'

# Uncomment if you use SwinjectStoryboard
# pod 'SwinjectStoryboard'

Then run pod install command. For details of the installation and usage of CocoaPods, visit its official website.

Documentation

Basic Usage

First, register a service and component pair to a Container, where the component is created by the registered closure as a factory. In this example, Cat and PetOwner are component classes implementing Animal and Person service protocols, respectively.

let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") }
container.register(Person.self) { r in
    PetOwner(pet: r.resolve(Animal.self)!)
}

Then get an instance of a service from the container. The person is resolved to a pet owner, and playing with the cat named Mimi!

let person = container.resolve(Person.self)!
person.play() // prints "I'm playing with Mimi."

Where definitions of the protocols and classes are

protocol Animal {
    var name: String? { get }
}

class Cat: Animal {
    let name: String?

    init(name: String?) {
        self.name = name
    }
}

and

protocol Person {
    func play()
}

class PetOwner: Person {
    let pet: Animal

    init(pet: Animal) {
        self.pet = pet
    }

    func play() {
        let name = pet.name ?? "someone"
        print("I'm playing with \(name).")
    }
}

Notice that the pet of PetOwner is automatically set as the instance of Cat when Person is resolved to the instance of PetOwner. If a container already set up is given, you do not have to care what are the actual types of the services and how they are created with their dependency.

Where to Register Services

Services must be registered to a container before they are used. The typical registration approach will differ depending upon whether you are using SwinjectStoryboard or not.

The following view controller class is used in addition to the protocols and classes above in the examples below.

class PersonViewController: UIViewController {
    var person: Person?
}

With SwinjectStoryboard

Import SwinjectStoryboard at the top of your swift source file if you use Swinject v2 in Swift 3.

// Only Swinject v2 in Swift 3.
import SwinjectStoryboard

Services should be registered in an extension of SwinjectStoryboard if you use SwinjectStoryboard. Refer to the project page of SwinjectStoryboard for further details.

extension SwinjectStoryboard {
    @objc class func setup() {
        defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") }
        defaultContainer.register(Person.self) { r in
            PetOwner(pet: r.resolve(Animal.self)!)
        }
        defaultContainer.register(PersonViewController.self) { r in
            let controller = PersonViewController()
            controller.person = r.resolve(Person.self)
            return controller
        }
    }
}

Without SwinjectStoryboard

If you do not use SwinjectStoryboard to instantiate view controllers, services should be registered to a container in your application's AppDelegate. Registering before exiting application:didFinishLaunchingWithOptions: will ensure that the services are setup appropriately before they are used.

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    let container: Container = {
        let container = Container()
        container.register(Animal.self) { _ in Cat(name: "Mimi") }
        container.register(Person.self) { r in
            PetOwner(pet: r.resolve(Animal.self)!)
        }
        container.register(PersonViewController.self) { r in
            let controller = PersonViewController()
            controller.person = r.resolve(Person.self)
            return controller
        }
        return container
    }()

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

        // Instantiate a window.
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        self.window = window

        // Instantiate the root view controller with dependencies injected by the container.
        window.rootViewController = container.resolve(PersonViewController.self)

        return true
    }
}

Notice that the example uses a convenience initializer taking a closure to register services to the new instance of Container.

Play in Playground!

The project contains Sample-iOS.playground to demonstrate the features of Swinject. Download or clone the project, run the playground, modify it, and play with it to learn Swinject.

To run the playground in the project, first build the project, then select Editor > Execute Playground menu in Xcode.

Example Apps

  • SwinjectSimpleExample demonstrates dependency injection and Swinject in a simple weather app that lists current weather information at some locations.
  • SwinjectMVVMExample demonstrates dependency injection with Swift and reactive programming with ReactiveCocoa in MVVM architecture.

Blog Posts

The following blog posts introduce Swinject and the concept of dependency injection.

Contribution Guide

A guide to submit issues, to ask general questions, or to open pull requests is here.

Question?

If you have a general question and are feeling hesitant about submitting a Github issue, feel free to ask the question at Stack Overflow. The author of Swinject monitors the swinject tag so as to answer as quickly as possible.

Credits

The DI container features of Swinject are inspired by:

and highly inspired by:

License

MIT license. See the LICENSE file for details.