Coder Social home page Coder Social logo

swinject / swinject Goto Github PK

View Code? Open in Web Editor NEW
6.2K 108.0 506.0 1.71 MB

Dependency injection framework for Swift with iOS/macOS/Linux

License: MIT License

Swift 94.86% Ruby 0.36% HTML 3.75% Shell 0.11% Makefile 0.93%
dependency-injection di-container swift ios ioc-container inversion-of-control

swinject's Introduction

Swinject

Github Actions Carthage compatible CocoaPods Version License Platforms Swift Version Reviewed by Hound

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.

Swinject is maintained by the Faire Wholesale Inc. mobile platform team.

Features

Extensions

Requirements

  • iOS 11.0+ / Mac OS X 10.13+ / watchOS 4.0+ / tvOS 11.0+
  • Xcode 14.3+
  • Swift 4.2+
  • Carthage 0.18+ (if you use)
  • CocoaPods 1.1.1+ (if you use)

Installation

Swinject is available through Carthage, CocoaPods, or Swift Package Manager.

Carthage

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

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.

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '11.0' # or platform :osx, '10.13' 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.

Swift Package Manager

in Package.swift add the following:

dependencies: [
    // Dependencies declare other packages that this package depends on.
    // .package(url: /* package url */, from: "1.0.0"),
    .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")
],
targets: [
    .target(
        name: "MyProject",
        dependencies: [..., "Swinject"]
    )
    ...
]

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.

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

Some example apps using Swinject can be found on GitHub.

Blog Posts

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

Thanks the authors!

Contribution Guide

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

Question?

Credits

The DI container features of Swinject are inspired by:

and highly inspired by:

License

MIT license. See the LICENSE file for details.

swinject's People

Contributors

1ucas avatar acevif avatar bradfol avatar charlesmuchene avatar delebedev avatar devioustree avatar gilroykilroy avatar jakubvano avatar janhalousek avatar jlyonsmith avatar karinberg avatar libec avatar lutzifer avatar manwithbear avatar marcorei avatar maxim-chipeev avatar mpdifran avatar nikita2k avatar s0ta91 avatar salbertson avatar sanju-naik avatar tarunon avatar tcldr avatar thbonk avatar thomasburguiere avatar tkohout avatar vkt0r avatar welshm avatar ybodson avatar yoichitgy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swinject's Issues

Use "inObjectScope" cause "Segmentation fault: 11"

XCode 6.4, Swift 1.2

import UIKit
import Swinject

class Cat {
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        let container = Container()
        container.register(Cat.self) { _ in Cat() }
            .inObjectScope(.Container)
    }
}

Calling .inObjectScope(.Container) will succeed only when you do typecasting like

(container.register(Cat.self) { _ in Cat() } as ServiceEntry<Cat>).inObjectScope(.Container)

I guess that's the bug of XCode?

Proposal for modular assembly of services in container

Proposal for modular assembly of services in container

Provide a simple mechanism to populate service definitions in a container that allows implementations to group related components together across frameworks/application

The current implementation has no opinion to how services are registered to the container which allows users to assembly their container(s) however you want. This is all well and good, but it would be nice to provide consumers of the framework an opinionated way to register their services. My proposal is to provide an AssemblyType and Assembler to provide such a feature. This approach would allow sane bootstrapping of the container (this concept is taking from is very vaguely taken from Typhoon

Proposed Implementation

There are 2 components to the implementation: AssemblyType and Assembler. The AssemblyType provides an interface that can be implemented by classes/structs (depending on your use case) that is provided a container from elsewhere.

protocol AssemblyType {
    func assemble(container: Container)
}

The Assembler will take a list of AssemblyType instances and build the container. The Assembler will either:
a) Own the container
b) Create the container to be owned else where

The implementation would be really straight forward:

class Assembler {
   let container = Container()
   init(assemblies: [AssemblyType]) {
       for assembly in assemblies {
           assembly.assemble(container)
       }
   }
}

With this approach, one can separate their service definitions like so:

class ServiceAssembly: AssemblyType {
    func assemble(container: Container) {
        container.register(FooServiceType.self) { r in 
             FooService() 
        }
        container.register(BarServiceType.self) { r in 
            BarService() 
        }
    }
}

class ManagerAssembly: AssemblyType {
    func assemble(container: Container) {
        container.register(FooManagerType.self) { r in 
            FooManager(service: r.resolve(FooServiceType.self)!)
        }
        container.register(BarManagerType.self) { r in 
             BarManager(service: r.resolve(BarServiceType.self)!) 
        }
    }
}

Now like service definitions are grouped and we can build our container like so:

let assembler = Assembler(assemblies: [
    ServiceAssembly(),
    ManagerAssembly()
])
let container = assembler.container // will contain all registered services

Additionally, this provides an easy means to override service definitions with mocks using inheritance. Let's say we want to replace FooService with MockFooService:

class MockServiceAssembly: ServiceAssembly {
    override func assemble(container: Container) {
        super.assemble(container)

        container.register(FooServiceType.self) { r in 
             MockFooService() 
        }
    }
}

Then we would load our assembler with the MockServiceAssembly instead:

let assembler = Assembler(assemblies: [
    MockServiceAssembly(),
    ManagerAssembly()
])

Exception on registering simple service / component

Hi,
I'm making tests to consider using Swinject in a new project (it looks great :) !) ; however, anytime I try to use it, I get a runtime exception.
I tried with a simple code to create a container and register service / component ; and when running the application, I had a runtime exception:
Exception: EXC_BREAKPOINT (code=1, subcode=0x120031088))
capture d ecran 2015-09-24 a 13 30 44

In order to be sure the problem didn't come from my code, I simply copied your sample code, what resulted in the same exception on this part of code :
container.register(AnimalType.self) {
_ in Cat(name: "Mimi")
}

I'm using Xcode 7.0 (7A220), OSX 10.11 (15A278b), and added Swinject via Carthage.
Do you have any idea of what might cause this, am I doing something wrong ?
Thanks a lot for your help.

registerForStoryboard should be renamed to more informative one.

registerForStoryboard should be renamed to more informative one to avoid misunderstanding that may register a new service type (Issue #7). Actually the method only inject dependency as initCompleted does. It should be renamed to inform the function to inject dependencies.

Swinject Podfile for ios/tvos and osx

When using Swinject with cocoapods targeting iOS/tvOS as well OSX, the compiler seems not to be able to find the correct header file in "Swinject.h"

changing the line

import <Swinject/_SwinjectStoryboardBase.h>

to

import "_SwinjectStoryboardBase.h"

resolves the issue. Sorry for me not having seen that issue before.

Singleton service ?

Can we have a singleton from the container? I have a use case that I need always the same service reference from the container. Can I do this ?

Thanks

Add Quick and Nimble project files to Swinject project file to replace their frameworks built by Carthage

Problems

Currently using frameworks of Quick and Nimble built by Carthage causes 2 problems.

  1. Carthage cannot build tvOS target for a bitcode issue: Quick/Nimble#213
  2. Xcode often crashes when debugging: Carthage/Carthage#924 Carthage/Carthage#832

(The 2nd problem is a known issue addressed by Apple - 22492040)

Solution (or Workaround)

Remove the references to built frameworks of Quick and Nimble from the project file of Swinject, and add their project files to Swinject project.

By this change, new contributors can setup the project environment more easily without a workaround of the problems.

Swiftlint fails build with Carthage

When Swiftlint is installed locally, I get the following build failures when directing Carthage to build from source:

config error: 'variable_name_min_length' is not a valid rule identifier
~/Project/Carthage/Checkouts/Swinject/Swinject/Assembler.swift:35:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container+SwinjectStoryboard.swift:27:67: error: Variable Name Violation: Variable name should be between 3 and 40 characters long: 'r' (variable_name)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container+SwinjectStoryboard.swift:27:70: error: Variable Name Violation: Variable name should be between 3 and 40 characters long: 'c' (variable_name)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container+SwinjectStoryboard.swift:23:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:39:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:71:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:103:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:118:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:133:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/Container.swift:220:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/iOS-tvOS/SwinjectStoryboard.swift:61:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/iOS-tvOS/SwinjectStoryboard.swift:79:21: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/JsonPropertyLoader.swift:48:20: warning: Legacy Constructor Violation: Swift constructors are preferred over legacy convenience functions. (legacy_constructor)
~/Project/Carthage/Checkouts/Swinject/Swinject/JsonPropertyLoader.swift:43:13: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/OSX/SwinjectStoryboard.swift:61:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/OSX/SwinjectStoryboard.swift:79:21: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/PropertyLoaderType.swift:32:1: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/PropertyLoaderType.swift:48:1: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/ServiceEntry.swift:41:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/ServiceEntry.swift:53:12: warning: Valid Docs Violation: Documented declarations should be valid. (valid_docs)
~/Project/Carthage/Checkouts/Swinject/Swinject/ServiceKey.swift:29:1: warning: Operator Function Whitespace Violation: Operators should be surrounded by a single whitespace when defining them. (operator_whitespace)
A shell task failed with exit code 65:
** BUILD FAILED **

Reduce the number of arguments on overloads of register and resolve methods

Currently 12 arguments are supported by overloads of register and resolve methods. They look overwhelming when displayed in an autocompletion popup of Xcode.

screenshot

The number of supported arguments should be reduced before releasing version 1.0.0. Currently I am thinking to reduce them to 9 arguments because "9" looks enough and not too short, and is 1-digit number. In total, 12 lines (3 arguments * 2 register/resolve methods * 2 with/without name parameter) can be removed from the popup.

This breaking change, though it has less impact, should be made before releasing version 1.0.0.

View Controller injection MVP

Hello there, I am using the pattern MVP in my app, but I have some trouble when I want to set the view to my presenter.

We can't resolve explicitly the view controllers registered by registerForStoryboard method, so I wonder how to get the view controller when I am registering the presenter, I was thinking about set the view to the presenter in registerForStoryboard method, but I don't know if it is correct. Any help please?

let mainContainer: Container = Container() { container in

        container.register(Presenter.self){ r in
            return Presenter()
        }.initCompleted { r,c in
            let view = /* HOW GET THE VIEW */
            c.view = view
        }

        container.registerForStoryboard(Test1ViewController.self) { r, c in
            let presenter = r.resolve(Presenter.self)
            c.presenter = presenter
        }
}

In AppDelegate:

let sb = SwinjectStoryboard.create(name: "Main", bundle: nil, container: self.mainContainer)
let controller = sb.instantiateViewControllerWithIdentifier("Test1View") as! Test1ViewController

container.registerForStoryboard subclass of AVPlayerViewController fails

On tvOS, I have several view controllers that I inject into. This is from latest xcode7.1 branch.

        container.registerForStoryboard(MainViewController.self) { r, c in
            c.app = r.resolve(App.self)
        }

        container.registerForStoryboard(VideoViewController.self) { r, c in
            c.app = r.resolve(App.self)
            c.video = r.resolve(VideoService.self)
        }

        container.registerForStoryboard(MetadataViewController.self) { r, c in
            c.app = r.resolve(App.self)
        }

        container.registerForStoryboard(ChannelsGuideViewController.self) { r, c in
            c.app = r.resolve(App.self)
        } 

and then

        let storyboard = SwinjectStoryboard.create(name: "Main", bundle: bundle, container: container)
        window.rootViewController = storyboard.instantiateInitialViewController()

VideoViewController, MetadataViewcontroller, and ChannelsGuideViewController are all embedded in MainViewController. Injection fails on VideoViewController if it subclasses from AVPlayerViewController. Inside of internal func runInitCompleted<C: Controller>(controllerType: C.Type, controller: C, name: String? = nil) I can see that normally, controllerType is set, however it is nil for the subclass of VideoViewController.

This seems related to #10

API for resolving dependencies with arguments is typo-prone

After it took us a while of staring at the code to figure out the source of bug, we've identified a place for possible improvement in Swinject API:

Consider following lines:

container.resolve(SomeType.self, argument:(a, b, c))
container.resolve(SomeType.self, arguments:(a, b, c))

The difference in syntax is IMO much more difficult to spot than it should be, and main problem is that both lines are perfectly valid, except former most often does not capture the intent.

Some alternative syntaxes that come to mind:

// change the param name
container.resolve(SomeType.self, multipleArguments: (a, b, c)) 

// use variadic params
container.resolve(SomeType.self, arguments: a, b, c)`

Thoughts?

Registering multiple (protocol) types for one concrete implementation

Hi,

Let's say you have two protocols, Purchasing and Reserving. Reserving will reserv a seat and Purchasing will actually purchase a reserved seat:

protocol Reserving {
    func reserv() -> Reservation
}

protocol Purchasing {
    func purchase(reservation: Reservation) -> Ticket
}

For some reason (that's another discussion :) it makes sense to conform to both protocols in one concrete class, maybe share some state and make things less complex, legacy code base or what ever. For example:

class TicketService: Reserving, Purchasing {
....
}

We also want/need to register TicketService in the global scope (can only have one instance or things will break :).

So to my question... Would it be possible to register multiple protocols for one instance of TicketService, for example:

container.register(Purchasing.self, Reserving.self) { r in TicketService() }.inScopeScope(.Global)

Are there any language limitations in swift of doing so?

view controller injection

Hello guys,

I'm registering one view controller using the method registerForStoryboard but when I get this controller using resolve, the return is nil.

container.registerForStoryboard(PostDetailVC.self) { (r, c) in
    c.presenter = r.resolve(PostDetailPresenter.self)
}

container.register(PostDetailWireframe.self) { (r) in
    let wireframe = PostDetailWireframe()

    wireframe.postDetailViewController = r.resolve(PostDetailVC.self)

    return wireframe
}

There's something wrong with this code?

Thanks

Better explanation of ObjectScope.Graph in docs

I find the description of the .Graph scope slightly lacking. It doesn't really communicate well how this factory method reuse would work. For me the best way to drive the point across would be with an example I guess.

I'm currently guessing that the .Graph scope works as follows. Lets say there is the following dependency graph:

  • MyCoolService depends on OtherService and Logger
  • OtherService depends on Logger
  • Logger has no dependencies

Now, if I were to resolve MyCoolService, it's factory would resolve OtherService first, which in turn initialises the Logger. Now back in MyCoolService factory, since the Logger has already been initialised the same instance is returned. Effectively coolService.otherService.logger === coolService.logger.

Is this correct?

Can't open playground with Xcode 7.2

When you open the project none of the SDK's are set in any of the targets. When you set them correctly and try to build the project it says PRODUCT_NAME undefined. I set that to Swinject then it spits out:

Umbrella header 'Swinject.h' not found,
Could not build Objective-C module 'Swinject'

What now?

Playground Failure: no such module 'Swinject'

When trying to open the playground on d333928 I am getting the following error:

Playground execution failed: /var/folders/cv/6rpdnxyn7qv8vql_5gbvpjz00000gn/T/./lldb/61022/playground1.swift:2:8: error: no such module 'Swinject'
import Swinject

Using Xcode Version 7.1 (7B91b).

Injection between storyboard view controllers

Any tips on how can I do injection between storyboard view controllers.
ie. I have 3 steps (3 UIViewControllers) for creating a user and create the new user object in the first UIViewController.

UINavigationController
-> UIViewController 1 - create new user object here
-> UIViewController 2 - inject new user object here
-> UIViewController 3 - inject new user object here

Thanks,

Pass assembler while assembling containers

This is not an issue, but a suggestion/idea.

Assume I have a RootController and RootRouter. The last one is responsible for presenting (modal/push, animation, etc) other controllers. I want to present AnimalController (which has it's own dependencies).

I have the following assemblies diagram:

  • RootAssembly
    • RootController AssemblyType
    • Services AssemblyType (api, db, etc)
  • AnimalAssembly.

After #50 I can make animal assembly with parent assembly (I really need to access db from AnimalController).

So here is the problem:

class RootControllerContainer: AssemblyType {

    func assemble(container: Container) {

        container.register(RootController.self) { r in
            RootController(router: r.resolve(RootRouter.self)!)
        }

        container.register(RootRouter.self) { _ in
            RootRouter()
        }
    }
}

Now I'd like to create a AnimalController inside RootRouter.swift:

func showDogDetails(dog: Dog) {
    let animalController = ???
    rootController.navigationController.pushViewController(animalController, animated: true)
}

So the question is how do I construct AnimalAssembly with RootAssembly? When we were working with Containers I was able to create new container with given parent.

So I suggest:
Pass assembler reference in assemble func, so I can register components, that is dependant on this assembler.

registerForStoryboard doesn't work with multiple storyboard

// ViewController1 in Mail.storyboard
container.registerForStoryboard(ViewController1.self) { r, c in
     c.viewModel = r.resolve(ViewModeling1.self)!              // 1
}
// ViewController2 in Other.storyboard
container.registerForStoryboard(ViewController2.self) { r, c in
    c.viewModel = r.resolve(ViewModeling2.self)!              // 2
}
// in didFinishLaunchingWithOptions
let storyboard = SwinjectStoryboard.create(name: "Main", bundle: bundle, container: container)

closure 1 is called when push to viewcontroller1, but not with closure 2
How to i resolve this issue ?

Issue when resolving with arguments in the test bundle

In Xcode 7.2 iOS project v1.0.0, I am having trouble resolving an object with three arguments. The first argument is a struct, and the other two are [Int]s. let game = appContainer.resolve(Game.self, arguments: (size, first, second))! works in the AppDelegate, but does not work in an XCTest run in the test bundle.

This is how I'm registering:

    container.register(Game.self) { _, size, first, second in
        return Game(boardSize: size,
            first: first,
            second: second
        )
    }

This is the error that I get: fatal error: unexpectedly found nil while unwrapping an Optional value
Please advise.

Add clearer documentation about runtime arguments

Add documentation as mentioned by @brunopinheiro in Issue #45.

class PetOwner {
   var pet: AnimalType
}

container.register(PetOwner.self) { (_: ResolverType, pet: AnimalType) in
   var petOwner = PetOwner()
   petOwner.pet = pet
   return petOwner
}

var cat = Cat(name: "Bilbo")

// Won't work, since we don't have any Registration Key matching [PetOwner, (Cat) -> PetOwner]
var petOwner = container.resolve(PetOwner.self, argument: cat)!

// This is the correct Registration Key [PetOwner, (AnimalType) -> PertOwner]
var petOwner = container.resolve(PetOwner.self, argument: cat as AnimalType)!

Contribution Guide

Are there any guides/guide lines on contributing? Specifically I was hoping to extend some features/syntax and I wanted to be able to add a test however it is non-obvious to me how I should execute the test suite in the first place. Are there any other pieces of information I should know before undertaking a pull-request?

Can't pass arguments on "register"

For some reason I can't pass arguments to "register" method, getting error "Cannot invoke 'register' with an argument list of type '(BaseFetchDelegate.Type, name: String, (_, _) -> BaseFetchDelegate)'"

It works correctly if I remove the second argument (table).

container.register(BaseFetchDelegate.self, name: "tasksFetchDelegate") { (r,table) in
  return BaseFetchDelegate()
}

Arguments forwarding to resolver's initCompleted

Here demonstration of what I want to do:

container.register(MenuPageModeller.self) {  r, menuId in MenuPageModel(menuId: menuId) }

container.register(MenuPage.self) { _ in MenuPage() }
      .initCompleted { r, page in
        page.viewModel = r.resolve(MenuPageModeller.self, argument: /* ??? */)
      }

Is there a way to do that? Or I need another approach?

ObjectScope.Container object created multiple times

I have the following code:

var container: Container {
  let container = Container()

  container.register(Networking.self) { _ in Network() }.inObjectScope(.Container)
  container.register(RestServicing.self) { r in
    RestService(networking: r.resolve(Networking.self)!, apiBase: "http://192.168.1.12:8080")
  }.inObjectScope(.Container)

  return container
}

then later on:

let restService = container.resolve(RestServicing.self) as! RestService
let restService2 = container.resolve(RestServicing.self) as! RestService

When I run this code and put a breakpoint on the line RestService(networking: r.resolve(Networking.self)!, apiBase: "http://192.168.1.12:8080").inObjectScope(.Container) it hits the breakpoint twice and two different RestService objects are created. I thought only one instance should be created?

Override registered class for testing?

Hello, first of all, great work with swinject! This library makes DI a breeze.

I've posted a question on SO here with swinject tag but haven't got any answer from you yet. So I'm reaching out to you to see if you could answer it!

Thanks 😄

Enhancement of API for container parent-child ownership

I'm kind of perplexed on how to manage a single Container/Assembler with several children Containers/Assemblers. This is how it currently works:

1. The parent Container/Assembler is created.
2. The children Containers/Assemblers are created, passing the parent container in their constructors.
3. The children retain a strong reference to their parents.
4. Something needs to hold on to all Containers/Assemblers (parents and children).

Rather than have the children keep a strong reference to its parent, why not have the parent keep a strong reference to its child, and the child keeps a weak reference to its parent? What this would allow for:

1. The parent Container/Assembler is created.
2. The children Containers/Assemblers are created.
3. The children are added to the parent by calling parent.addChild(child).
4. A single reference to the parent container is needed in order to keep everything alive.

The Container/Assembler constructor that takes a parent parameter can still exist (preserving backwards compatibility) by calling parent.addChild(child) instead of setting the property directly.

Support thread and weak container scopes

Support the following Object Scopes.

  • Thread: an instance is shared within a thread.
  • Weak container: an instance is shared within a container. The container references it weakly.

swift 2.2 warnings

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/Storyboard+Swizzling.swift:35:66: use '#selector' instead of explicitly constructing a 'Selector'

        let original = class_getClassMethod(Storyboard.self, Selector("storyboardWithName:bundle:"))
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/Storyboard+Swizzling.swift:36:66: use '#selector' instead of explicitly constructing a 'Selector'

        let swizzled = class_getClassMethod(Storyboard.self, Selector("swinject_storyboardWithName:bundle:"))
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/Storyboard+Swizzling.swift:35:66: use '#selector' instead of explicitly constructing a 'Selector'

        let original = class_getClassMethod(Storyboard.self, Selector("storyboardWithName:bundle:"))
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/Storyboard+Swizzling.swift:36:66: use '#selector' instead of explicitly constructing a 'Selector'

        let swizzled = class_getClassMethod(Storyboard.self, Selector("swinject_storyboardWithName:bundle:"))
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

▸ Compiling SwinjectStoryboard.swift

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/SwinjectStoryboard.swift:42:54: use '#selector' instead of explicitly constructing a 'Selector'

        if SwinjectStoryboard.respondsToSelector(Selector("setup")) {
                                                 ^~~~~~~~~~~~~~~~~

Thank you for the awesome framework!

We have migrated to swift 2.2 and xcode7.3. This caused the following warnings:

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/SwinjectStoryboard.swift:43:52: use '#selector' instead of explicitly constructing a 'Selector'

            SwinjectStoryboard.performSelector(Selector("setup"))
                                               ^~~~~~~~~~~~~~~~~

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/SwinjectStoryboard.swift:42:54: use '#selector' instead of explicitly constructing a 'Selector'

        if SwinjectStoryboard.respondsToSelector(Selector("setup")) {
                                                 ^~~~~~~~~~~~~~~~~

⚠️ Pods/Swinject/Sources/SwinjectStoryboard/SwinjectStoryboard.swift:43:52: use '#selector' instead of explicitly constructing a 'Selector'

            SwinjectStoryboard.performSelector(Selector("setup"))
                                               ^~~~~~~~~~~~~~~~~

Would it be possible to make a release for swift 2.2 without the warnings?

Thank you!

Use reflection for auto-injections?

In Typhoon we can mark a property as auto-injectable, but it works for Objective-C only.
I think Swinject could use the power of Mirror type [1], [2] to find properties we can automatically resolve.
I think of UIViewControllers and SwinjectStoryboard enhancement, so we could get rid of setup extension method in favour of the code like this (I tried to patсh SwinjectStoryboard class):

private func injectDependency(viewController: UIViewController) {
        //get the reflection info
        let mirror = Mirror(reflecting: viewController)
        //enumerate properties
        for case let (label?, anyValue) in mirror.children {
            let propMirror = Mirror(reflecting: anyValue)
            //I couldn't find a better way to detect auto-injectable properties =) 
            if label.substringToIndex(label.startIndex.advancedBy(6, limit: label.endIndex)) == "inject" {
                //in here we could use propMirror.subjectType as dynamicType to find the type to be instantiated
               //I tried to register this var with registerForStoryboard with no success, maybe you could help here?:
               //Cannot invoke 'resolve' with an argument list of type '(Any.Type)'
                SwinjectStoryboard.defaultContainer.registerForStoryboard(viewController.dynamicType, initCompleted: { r, c in
                    viewController.setValue(r.resolve(propMirror.subjectType), forKey: label)
                })
            }
        }

        let registrationName = viewController.swinjectRegistrationName
        container.runInitCompleted(viewController.dynamicType, controller: viewController, name: registrationName)

        for child in viewController.childViewControllers {
            injectDependency(child)
        }
    }

If we could solve resolving from mirror.subjectType, then we can later have dynamic property in ViewController like this:

dynamic var injectWeatherFetcher: WeatherFetcher?

dynamic var is important part - setValue works with @objc or dynamic vars.

What do you think?

registerForStoryboard with arguments?

I'm trying to assign registered class that accepts arguments to one of my ViewController inside registerForStoryboard.

// Inside setup()
c.register(ViewModel.self) { r, foo in ViewModel(foo: foo) }
c.registerForStoryboard(ViewController.self) { r, vc in vc.vm = r.resolve(ViewModel.self, arg1: ???) }

// Instantiate ViewController ???
sb.instantiateViewControllerWithIdentifier("ViewController")

How should I go about doing this? Perhaps, I'm missing something here?

Carthage: failed to build Swinject

I have a Cartfile:

github "Swinject/Swinject" ~> 1.0.0

and running build: carthage update --no-use-binaries --platform iOS

Here is my build log:

carthage update --no-use-binaries --platform iOS
*** Fetching Swinject
*** Checking out Swinject at "1.0.0"
*** xcodebuild output can be found in /var/folders/xx/wdhyt54n6qg6tvxm21313qsxqqjp3v/T/carthage-xcodebuild.nDq8Y4.log
*** Building scheme "Swinject-iOS" in Swinject.xcodeproj
xcodebuild: error: Failed to build project Swinject with scheme Swinject-iOS.
    Reason: The run destination iPad 2 is not valid for Running the scheme 'Swinject-iOS'.
A shell task failed with exit code 70:
xcodebuild: error: Failed to build project Swinject with scheme Swinject-iOS.
    Reason: The run destination iPad 2 is not valid for Running the scheme 'Swinject-iOS'.

As you can see, for some reason it cannot be built on iPad2. I think issue is very similar to kif-framework/KIF#650

Injection on ViewController looks broken with Xcode 7.1 beta

Hi,
I had an application working with Swinject, but since I tried to compile it with Xcode 7.1, it stopped working, because all of the ViewControllers are not injected with values.
The ViewControllers is correctly created, but all values that should be injected are left to nil ; did something change, or did I do something wrong?
Let me know if you need any details, thanks a lot, here are some code samples (extracts only) :

container.register(AssetModel.self) {
            r in return AssetModel()
        }.inObjectScope(.Hierarchy)

container.register(DemoModel.self) {
            r in DemoModel()
        }.inObjectScope(.Hierarchy)

container.registerForStoryboard(PartnersViewController.self) {
            r, c in
            c.assetModel = r.resolve(AssetModel.self)
            c.demoModel = r.resolve(DemoModel.self)
            c.playViewController = self.slotMachineStoryboard.instantiateViewControllerWithIdentifier(ViewControllerIdentifiers.Play.rawValue) as? PlayViewController
        }

container.registerForStoryboard(PlayViewController.self) {
            r, c in
            c.assetModel = r.resolve(AssetModel.self)
            c.demoModel = r.resolve(DemoModel.self)
        }

let slotMachineStoryboard = SwinjectStoryboard.create(name: StoryboardIdentifiers.SlotMachine.rawValue, bundle: nil, container: mainContainer)
let initialViewController = slotMachineStoryboard.instantiateViewControllerWithIdentifier(ViewControllerIdentifiers.Partners.rawValue) as! PartnersViewController

Problem with circular dependencies

Hi,

I've copied the code from the circular dependency example and added a few things in the classes, didn't modify anything in the dependency registration code.

Classes and protocols look like this:

protocol ParentType: AnyObject {
    func getMyName() -> String
    func getMyChildName() -> String
}

protocol ChildType: AnyObject {
    func getMyName() -> String
    func getMyParentName() -> String
}

class Parent: ParentType {
    let child: ChildType?
    let name = "John"

    init(child: ChildType?) {
        self.child = child
    }

    func getMyName() -> String {
        return name
    }

    func getMyChildName() -> String {
        return child!.getMyName()
    }
}

class Child: ChildType {
    weak var parent: ParentType?
    let name = "John Jr."

    func getMyName() -> String {
        return name
    }

    func getMyParentName() -> String {
        return parent!.getMyName()
    }
}

Dependency configuration code looks like this (unchanged from example):

let container = Container()
container.register(ParentType.self) { r in
    Parent(child: r.resolve(ChildType.self)!)
}
container.register(ChildType.self) { _ in Child() }
    .initCompleted { r, c in
        let child = c as! Child
        child.parent = r.resolve(ParentType.self)
}

The above code is in my AppDelegate's "application:didFinishLaunchingWithOptions" function.
Right after the registration code I added this little testing code:

let parent = container.resolve(ParentType.self)!
let child = container.resolve(ChildType.self)!
print(parent.getMyName())
print(child.getMyName())
print(parent.getMyChildName())
print(child.getMyParentName())

The output is this:

John
John Jr.
John Jr.
fatal error: unexpectedly found nil while unwrapping an Optional value

The error occurs on this line:
return parent!.getMyName()

The weird thing is that I placed a breakpoint on that line and this is what happens:

  • code stops at the breakpoint
  • I look at self, it looks correctly initialised, the parent property looks correct (a reference to the parent instance)
  • I "step over", the error occurs and now the parent property is nil

Am I doing anything wrong with this circular dependency? (This code is in an "empty" single view iOS app, only with Swinject added as a dependency via Carthage).
XCode version 7.2.1
Swinject version 1.1 installed via Carthage

Swift 2.2

I added Swinject via Carthage. After updating my XCode to 7.3 (Swift 2.2) I get this error:

Module file was created by an older version of the compiler; rebuild 'Swinject' and try again: /Users/[*]/Carthage/Build/iOS/Swinject.framework/Modules/Swinject.swiftmodule/x86_64.swiftmodule

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.