Git Product home page Git Product logo

swinjectstoryboard'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.

swinjectstoryboard's People

Contributors

abdullah-s avatar allenhumphreys avatar b-raines avatar barredewe avatar evanjd avatar jakubvano avatar js4vw avatar lutzifer avatar mlilback avatar mpdifran avatar nanashiki avatar relax94 avatar skyylex avatar tengyifei avatar tzxdtc 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

swinjectstoryboard's Issues

Cannot get reference to UIViewController registered for storyboard injection

I'm using Swinject 1.1.1. I really want to be able to inject a reference to my UIViewController which has been registered for storyboard injection. I currently have a SpriteKit app where class GameViewController: UIViewController is injected into Main.storyboard via registerForStoryboard(). Creating this view controller causes creation of a bunch of SKScene's one of which needs a reference to the original GameViewController in order to be able to call presentViewController to show settings. I tried to resolve the GameViewController in the initComplete (to avoid a circular reference) but it comes back as nil. Am I missing something? Can this be made to work and if not, what's the underlying issue?

Controller identifier is NSStoryboard.SceneIdentifier instead of String

Version 1.1.2 doesn't compile for XCode 9.2 / Swift 4 as the type of the identifier of NSStoryboard changed from String to NSStoryboard.SceneIdentifier.

#elseif os(OSX)
/// Instantiates the view/Window controller with the specified identifier.
/// The view/window controller and its child controllers have their dependencies injected
/// as specified in the Container passed to the initializer of the SwinjectStoryboard.
///
/// - Parameter identifier: The identifier set in the storyboard file.
///
/// - Returns: The instantiated view/window controller with its dependencies injected.
public override func instantiateController(withIdentifier identifier: NSStoryboard.SceneIdentifier) -> Any {
SwinjectStoryboard.pushInstantiatingStoryboard(self)
let controller = super.instantiateController(withIdentifier: identifier)
SwinjectStoryboard.popInstantiatingStoryboard()

Error when trying to use swinjectRegistrationName in a storyboard

Following along with the tutorial, I created a storyboard with 2 view controllers of the same type, I gave one of them a swinjectRegistrationName as described in the README, and then I created an assembly with storyboardInitCompleted implemented for both the named and the unnamed registration.

When I run the app, SwinjectStoryboard isn't able to set the swinjectRegistrationName in the object and instead this error is printed in the console

2017-12-05 14:52:41.010112-0800 SwinjectStoryboardBug[29435:2586579] Failed to set (swinjectRegistrationName) user defined inspected property on (SwinjectStoryboardBug.ViewController): [<SwinjectStoryboardBug.ViewController 0x7f862ed13c30> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key swinjectRegistrationName.

and upon placing breakpoints in:

    private func injectDependency(to viewController: UIViewController) { ... }

the swinjectRegistrationName is indeed nil.

After doing some tracking down on the internets, it looks like the protocol RegistrationNameAssociatable needs to be @objc-ified.

When I replace with this:

@objc internal protocol RegistrationNameAssociatable: AnyObject {
    var swinjectRegistrationName: String? { get set }
}

all is well and the swinject registration name is non-nil again.

The workaround is to compile SwinjectStoryboard as Swift Version 3.2.

Here is my assembly:

import Foundation
import Swinject
import SwinjectStoryboard

class MyAssembly: Assembly {
    public func assemble(container: Container) {
        container.storyboardInitCompleted(ViewController.self, name: "InitialViewController") { r, c in
            print(c)
            print(r)
        }

        container.storyboardInitCompleted(ViewController.self) { r, c in
            print(c)
            print(r)
        }
    }
}

and I create the view controller like:

    let container = SwinjectStoryboard.defaultContainer
    private func createViewController<T: AnyObject>(_ type: T.Type, _ storyboardName: String, _ storyboardIdentifer: String, container: Container) -> T {
        let bundle = Bundle.main
        let storyboard = SwinjectStoryboard.create(name: storyboardName, bundle: bundle, container: container)
        return storyboard.instantiateViewController(withIdentifier: "InitialViewController") as! T
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let viewController = createViewController(ViewController.self, "Main", "InitialViewController", container: container)
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = viewController
        self.window?.makeKeyAndVisible()

        return true
    }

Enable `UIViewControllers` in circular dependencies

Currently it is not possible to resolve in a standard way circular dependencies containing UIViewControllers from storyboard. A hack described in Swinject #245 gave me an idea: What if we "temporarily" registered an UIViewController instance during instantiation from storyboard?

Something like:

public func storyboardInitCompleted<C: Controller>(_ controllerType: C.Type, name: String? = nil, initCompleted: @escaping (Resolver, C) -> ()) {
    let factory = { (_: Resolver, controller: Controller) in controller }
    let wrappingClosure: (Resolver, Controller) -> () = { r, c in 
        (r as! Container).register(Controller.self) { _ in c } // Register an instantiated view controller to the container
        initCompleted(r, c as! C) 
    }
    let option = SwinjectStoryboardOption(controllerType: controllerType)
    _register(Controller.self, factory: factory, name: name, option: option)
        .initCompleted(wrappingClosure)
}

This might enable a standard way of resolving circular dependencies:

container.storyboardInitCompleted(ViewController.self) { r, vc in
    vc.presenter = r.resolve(Presenter.self)   
}

container.register(Presenter.self) { _ in Presenter() }
    .initCompleted { r, p in p.view = r.resolve(ViewController.self) }

Obviously, this would only work if ViewController is the entry point to object graph, and might have other restrictions. My fear is that providing a feature which resembles "normal" Swinject usage but has some restrictions might cause confusion for the users (but then again, there already are such restrictions in SwinjectStoryboard usage).
@yoichitgy What do you think? Is it worth pursuing something like this?

ViewController Registration with Name not working?

SwinjectStoryBoard Version: 1.1.1

Hi!

I am using Swinject for several projects and I really enjoy that project! :)
Now I just want to try out a ViewController registration with name, because I need the same ViewController type but with different dependency (view model) instances. Unfortunately this dependency is never set and the initCompleted closure from storyboardInitCompleted is never called.

Maybe I am using it in a wrong way? Here is some example code extract of my current project.

ViewController Storyboard Ids
2 Storyboard ViewControllers of class MyViewController

  1. Storyboard ID: "active"
  2. Storyboard ID: "inactive"

ViewController registration

container.storyboardInitCompleted(MyViewController.self, name: "active") { resolver, vc in
            vc.viewModel = resolver.resolve(MyViewModelType.self, name: "active")
}

container.storyboardInitCompleted(MyViewController.self, name: "inactive") { resolver, vc in
            vc.viewModel = resolver.resolve(MyViewModelType.self, name: "inactive")
}

ViewModel registration

container.register(MyViewModelType.self, name: "active") { resolver in
            return MyViewModel(filter: .active)                  
}

container.register(MyViewModelType.self, name: "inactive") { resolver in
            return MyViewModel(filter: .inactive)                  
}

ViewController Instantiation

let activeViewController = storyboard.instantiateViewController(withIdentifier: "active")
let inactiveViewController = storyboard.instantiateViewController(withIdentifier: "inactive")

activeViewController.viewModel  // <- nil
inactiveViewController.viewModel // <- nil

In SinjectStoryBoard.injectDependency there is nil in registrationName (Line 94 in File ViewController+Swinject.swift). Maybe that causes the problem?

Thank you and kind regards
Phillipp

Storyboard / SwinjectStoryboard How to set the module name of a storyboard programmatically?

Hi,

How can I set module for a storyboard programmatically and also for all of its UI content?

The reason is because the storyboard itself is in an app that references a framework which contains the viewcontroller.

So looking at this screenshot I can tell the storyboard to point to the Presentation Framework. I'd like to know if there is a better and easier way to set the module programmatically.

screen shot 2018-07-13 at 9 14 26 am

Cocoapods Podspec

Thank you for Swinject!

Would it be possible to install SwinjectStoryboard via Cocoapods like Swinject?

Test target resolve issue

Hi, I have a question about dependent injection in Unit Test.
I use SwinjectStoryboard as follows to inject dependency to top viewController.

extension SwinjectStoryboard {
    class func setup() {
        
        defaultContainer.storyboardInitCompleted(TopViewController.self){ _, c in
            c.repository = Repository()
        }
    }
}

In normal target, injection is success. But, injection is failure in tests target.
I think that there is a issue about controllerType of SwinjectStoryboardOption.
In registration phase, controllerType is 'AppNameTests.TopViewController. But, in resolving phase, controllerType is 'AppName.TopViewController'.

Is there any solution about this?

Env

Swinject branch master (using CocoaPods)
SwinjectStoryboard branch master (using CocoaPods)
Xcode 8.1 Swift 3.0
iOS 10.1 Device

EXC_BAD_ACCESS

After upgrading to Xcode 9.4.1, we're getting EXC_BAD_ACCESS when deploying to device. Any clue what's going on?

github "Swinject/Swinject" "2.4.1"
github "Swinject/SwinjectAutoregistration" "2.1.1"
github "Swinject/SwinjectStoryboard" "2.0.2"

image

Thanks!

ViewController with custom initialiser

I have a controller with a custom initialiser:

class MyController: UIViewController {
    private let obj: MyObj! // This MUST exist, it cannot and should not be optional
    init(withObject obj: MyObject) {
        self.obj = obj
    }

    // Looks like I have to have this here...
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

How can I use SwinjectStoryboard to create this controller with the initialiser called? I can't find the documentation on this exact use case. The required init is the one that's called.

Swinject crashes swiftc in Xcode 9

Segmentation fault happens when using SwinjectStoryboard and Swinject (swift4 branch). Have anybody investigated that?

That happens if we pass the controller as protocol (ProjectsListViewInputProtocol).

container.storyboardInitCompleted(ProjectsListViewController.self) { (r, controller: ProjectsListViewInputProtocol) in
            let presenter = r.resolve(ProjectsListPresenterProtocol.self, argument: controller)!
            controller.presenter = presenter
}

Objective-C class method 'initialize', which is not guaranteed to be invoked by Swift and will be disallowed in future versions

Posted by @mrkd: Swinject/Swinject#238

Objective-C class method 'initialize', which is not guaranteed to be invoked by Swift and will be disallowed in future versions

Xcode - Version 8.3 (8E162), Swift 3.1

  • Swinject (2.0.0)
  • SwinjectStoryboard (1.0.0):
    • Swinject (= 2.0.0)

/Pods/SwinjectStoryboard/Sources/Storyboard+Swizzling.swift:25:30: Method 'initialize()' defines Objective-C class method 'initialize', which is not guaranteed to be invoked by Swift and will be disallowed in future versions

Storyboard injection with `Assembly`

I'm working on an app that uses Swinject via the Assembly approach to managing injection. I'm looking at adding support for storyboard injection.

Since we're currently instantiating the window and main view controller from the storyboard, I think I need to use SwinjectStoryboard.setup() to manage storyboard injection. But the defaultContainer used there isn't the same as the container argument passed to my Assembly subclass. Also, setup() is called early enough that I don't think I can create my assembly objects before setup() is called.

Is there some way I can use SwinjectStoryboard to manage a dependency on an object that's created in an Assembly? It's possible I haven't grasped Swinject well enough to see the obvious approach here.

Registering storyboard view controller on a child container of "defaultContainer"

Hello, so I have the following view controller class, which is actually my initial view controller in storyboard:

class RootViewController: UIViewController {
    var presenter: RootPresenter?
}

On the "SwinjectStoryboard" extension, I do the following:

class func setup() {
    let childContainer = Container(parent: defaultContainer)
        childContainer.registerForStoryboard(RootViewController.self) { (resolver, controller) in
            let presenter = resolver.resolve(RootPresenter.self)
            controller.presenter = presenter
        }
        childContainer.register(RootPresenter.self) { _ in
            RootPresenter()
        }
}

Resolver closures are never called, could you please help me understand why?

Note: When I register directly on "defaultContainer" (not on a child of it), it works ok.

Thank you in advance,
Alin

Feature brainstorm: Inject arguments on `instantiateViewController`

Hi,

I really love the feature of Swinject where I can add additional arguments when I call resolve:
Declaration:

func resolve<Service, Arg1>(
        _ serviceType: Service.Type,
        argument: Arg1) -> Service?

Usage:
r.resolve(OfferSettingsViewModeling.self, argument: "Some Argument")

I wonder if we could implement a similar thing when instantiating view controllers from SwinjectStoryBoard:
Something like:
func instantiateViewController<Arg1>(withIdentifier identifier: String, arg1: Arg1)
And use it like this:

let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container)
storyboard.instantiateViewController(withIdentifier "MainViewController", arg1: 123)

and then I can catch it on storyboardInitCompleted like this:

container.storyboardInitCompleted(MainViewController.self) { r, c, arg1 in
            c.viewModel = r.resolve(MainViewModeling.self, arg1: arg1)!
        }

I think this would be really helpful for use cases where you want to instantiate a view controller with a given context, mostly relevant in master/detail stories, where the detail view controller's view model need to be instantiated with an identifier regarding the chosen detail on the master view controller.

What do you think?

Making SwinjectStoryboard inheritance friendly

This is an enhancement request
In case of inheriting from SwinjectStoryboard There should be overridable method which accepts Container.Controller and ResolverType as parameters, so subclass could check for controller protocol conformance and alter resolve process.

use case:

protocol ViewModeleledView: NSObjectProtocol {
    associatedtype ViewModel
    var viewModel: ViewModel! {get set}
}
private protocol ViewModelResolvableView {
    func resolve(resolver: ResolverType)
}
private extension ViewModeleledView: ViewModelResolvableView  {
    func resolve(resolver: ResolverType) {
        viewModel = resolver. resolve(ViewModel.self)
   }
}
class AltStoryboard: SwinjectStoryboard {
    override func resolve(controller: Container.Controller, with resolver: ResolverType) {
        if let controller = controller as? ViewModelResolvableView {
            controller. resolve(resolver)
        } else {
            super. resolve(controller, with: resolver)
        }
    }
}

With this I can omit registration of ViewModeleledViews in container

OSX | HighSierra | Doesn't invoke storyboardInitCompleted

Hi guys,

I have been using SwinjectStoryboard for a long time and didn't have any issues. Now I have some problem with High Sierra and want to know maybe you have some solution for this issue.
After latest changes (tag 1.1.2) setup() class method is invoke, but doesn't invoke closure in storyboardInitCompleted method.
I've checked problem and found the method swinject_storyboardWithName:bundle: that not invoke.

SwinjectStoryboard 1.0.0 doesn't work with Swinject 2.1.0

Hello,
So I'm having a project written in Swift 3.1 and tried to add the Swinject and SwinjectStoryboard with Cocoapods like this:

pod 'Swinject', '~> 2.1.0'
pod 'SwinjectStoryboard', '~> 1.0.0'

I'm getting the following error when running "pod install". I'm guessing it's a compatibility issue.

[!] Unable to satisfy the following requirements:

- `Swinject (~> 2.1.0)` required by `Podfile`
- `Swinject (= 2.0.0)` required by `SwinjectStoryboard (1.0.0)`

Using Swinject 2.0.0 works fine, but I'm getting the 2 warnings regarding the 'initialize()' functions 🙂 (because of Swift 3.1).

Thanks,
Alin

Embedded View Controllers

Hey guys, for the past week I’ve been having a real issue trying to get an embedded view controller to have a property injected into it by the container.

All the VCs exist in the storyboard. They are subclasses of UIVC.

  • I have the initial VC initialised programmatically in AppDelegate. This is great as it is resolved from the container.
  • The container view with an embed segue to VC2 does not automatically have its properties resolved.

Can the embedded VC2 have its properties resolved as well, even if it is also designed in the storyboard? I must be d

Question about storyboardInitCompleted

Just for clarity: registerForStoryboard has been replaced with storyboardInitCompleted. And it's just a straight swap, no additional code or other changes needed?

No object named "swift4" exists

When I try to update the Carthage I have this error:
*** Fetching SwinjectStoryboard Failed to check out repository into /Users/user/Library/Caches/org.carthage.CarthageKit/dependencies/SwinjectStoryboard: No object named "swift4" exists (Error Domain=CarthageKit.CarthageError Code=22 "(null)")

Avoiding duplicate "completed" calls

I'm using a SwinjectStoryboard extension to set up injection for my main VC. That's working fine. I added the following to set up injection for another VC in the same storyboard. This view controller is reached by a segue from the initial view controller:

defaultContainer.storyboardInitCompleted(DetailViewController.self) { (r:Resolver, c:DetailViewController) in
    c.bar = "Injected bar"
}

[I'm trying out SwinjectStoryboard with Xcode's default master/detail view app template, in case that's not obvious.]

Here's the thing: With the above code, injection works, but the closure is called twice with different instances of DetailViewController. The first is when setup() is called, and the second is when the DetailViewController appears on the screen. If I set a breakpoint in the closure, c is a different instance in the second call, and it's the second one that appears. I don't know why the first is created or what happens to it.

Why do I get two calls to this closure with different instances, and how would I avoid that?

OSX initialization

Hi guys,

im doing sample OSX project and i have huge problems with Swinject Storyborad initialization - i was using it for iOS and never had any issues.

I've tried both: Implicit and Explicit Instantation and none of them works

  • Implicit crashes entire app -i assume because of all dependencies are resolved after app startup and after Initial View Controller is loaded (which expect all dependencies to be resolved before launch)
  • Explicit (removing initial storyboard in plist and trying to set up in NSApplicationDelegate's applicationDidFinishLaunching) causes app never to launch. applicationDidFinishLaunching is never called when entry from plist is removed. Where to put initialization code?

I also found that when i use Implict initialization storyboardInitCompleted is never called:

extension SwinjectStoryboard{
    public static func setup() {
        defaultContainer.register(PlayerVCViewModelling.self){
            c in
            PlayerViewModel()
        }

        defaultContainer.storyboardInitCompleted(PlayerViewController.self){
            r,c in
            c.playerViewModel = r.resolve(PlayerVCViewModelling.self)
        }
    }
}

Can anyone please explain or give any tips how to properly instantiate Swinject Storyboard for OSX project?

Thanks

Explicit instantiation for macOS apps

The README mentions you can do something like this in iOS apps to explicitly load your root view controller from SwinjectStoryboard:

let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.makeKeyAndVisible()
self.window = window

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

Is there an equivalent practice for macOS?

Instances inside Container are different in app and test enviroment

Hello,

I'm using the Library right now and during the last days I was doing some UI-tests retrieving some data market as Singleton from the SwinjectStoryboard instance.

The thing is that when I try to retrieve an unique instance from the library:

override func setUp() {
		accountStore = SwinjectStoryboard.defaultContainer.resolve(AccountStore.self)!
		super.setUp()
	}

The instance is not the same that I have on my ViewController, even when the Scope is ObjectScope.container :

class AccountStoreAssembly: Assembly {
    func assemble(container: Container) {
        container.register(AccountStoreController.self) { _ in
            AccountStoreController(dispatcher: container.resolve(Dispatcher.self)!,
                    repository: container.resolve(LumiereService.self)!)
        }.inObjectScope(ObjectScope.container)

        container.register(AccountStore.self) { _ in
            AccountStore(controller: container.resolve(AccountStoreController.self)!,
                    dispatcher: container.resolve(Dispatcher.self)!,
                    repository: container.resolve(LumiereService.self)!)
        }.inObjectScope(ObjectScope.container)
    }
}

This is normal? There is no way to keep the same instance for both places? Tests and normal application controllers? Is this an issue or it works as designed?

Thank you so much in advance!

Dynamically resolve differently for same protocl

From @gilgol on April 9, 2018 11:40

I'm trying to dynamically resolve a protocol ViewModeling that 2 different view models are conforming to..
I am able to do it on the service level (one protocol that 2 services are conforming to, and 2 different viewModel that each consume a different service can resolve the required one using a specific name)

I'm struggling with doing this on the viewController level, trying to inject the same view controller a specific viewModel (both conforms as mentioned to the same protocol).

I believe the issue is with SwinjectStoryboard that doesn't let me instantiate a view controller using its storyboard id (like I would normally do), and in addition define few different names that will be resolved on runtime.

Am I missing something?

Just realised it might be more appropriate as a stackoverflaw post, so here is a link to the post:
https://stackoverflow.com/questions/49732300/how-to-inject-the-right-viewmodel-to-a-single-viewcontroller

Copied from original issue: Swinject/Swinject#345

SwinjectStoryboard resolve nil object

SwinjectStoryboard cannot hold reference to object or it resolves nil object when block of storyboardInitCompleted finished. That's why all objects in my ViewController are nil, it makes my app crash in Swift 4. But in Swift 3.3 is working fine. please help me to solve this problem.

Circular Dependencies with UIViewController instantiating

I'm following VIPER principles. And there are circular dependencies, for example between View(UIViewController)<->Presenter, Presenter<->Interactor.

How to resolve circular dependencies between UIViewController, for example

class LoginViewController: UIViewController, LoginViewInput {    
    var output: LoginViewOutput?

    override func viewDidLoad() {
        super.viewDidLoad()
        output?.didTriggerViewDidLoad();
    }

    func say(message: String?) {
        print(message)
    }
}

class LoginPresenter: LoginViewOutput {
    weak var view: LoginViewInput?

    func didTriggerViewDidLoad() {
        print("Did trigger View Did Load")
        view?.say(message: "Hello")
    }
}

I'm trying this way but it doesn't work

import Swinject
import SwinjectStoryboard

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    var container: Container = {
        let container = Container()

        container.registerForStoryboard(LoginViewController.self) { r, c in
            c.output = r.resolve(LoginViewOutput.self)
        }
        container.register(LoginViewOutput.self) { _ in LoginPresenter() }
            .initCompleted { r, c in
                let presenter = c as! LoginPresenter
                presenter.view = r.resolve(LoginViewController.self)
        }

        return container
    }()


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        self.window = window

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

There is some discussion here
Thanks

registerForStoryboard closure is never called on child container using AssemblyType

Hi, I saw the similar question here but we can unite them later.
I'm trying to cook Swinject with VIPER and struggling the issue with registerForStoryboard on child container.
Here is the structure of how I'm trying to implement this. I want to achieve having one AssemblyType and Assembler per module.
Two modules:

class TabBarAssembler {
    
    let assembler: Assembler
    
    init(parentAssembler: Assembler) {
        assembler = try! Assembler(assemblies: [TabBarAssembly()], parentAssembler: parentAssembler)
    }
}

class TabBarAssembly: AssemblyType {
    
    func assemble(container: Container) {
        container.register(SketchesListAssembler.self) { r in
            let parentAssembler = r.resolve(TabBarAssembler.self)!
            return SketchesListAssembler(parentAssembler: parentAssembler.assembler)
        }
        container.registerForStoryboard(TabBarViewController.self) { (r, controller: TabBarViewInputProtocol) in
            let presenter = r.resolve(TabBarPresenterProtocol.self, argument: controller)!
            controller.presenter = presenter
        }
        container.register(TabBarInteractorInputProtocol.self) { _ in
            TabBarInteractor()
        }
        container.register(TabBarPresenterProtocol.self) { (r, view: TabBarViewInputProtocol) in
            let interactor = r.resolve(TabBarInteractorInputProtocol.self)!
            let router = r.resolve(TabBarRouterInputProtocol.self)!
            let presenter = TabBarPresenter(view: view, interactor: interactor, router: router)
            interactor.presenter = presenter
            return presenter
        }
        container.register(TabBarRouterInputProtocol.self) { r in
            let router = TabBarRouter()
            router.sketchesListAssembler = r.resolve(SketchesListAssembler.self)
            return router
        }
    }
}

class SketchesListAssembler {
    
    private let assembler: Assembler
    
    init(parentAssembler: Assembler) {
        assembler = try! Assembler(assemblies: [SketchesListAssembly()], parentAssembler: parentAssembler)
    }
    
}

class SketchesListAssembly: AssemblyType {
    
    func assemble(container: Container) {
        container.registerForStoryboard(SketchesListViewController.self) { (r, controller: SketchesListViewInputProtocol) in
            let presenter = r.resolve(SketchesListPresenterProtocol.self, argument: controller)!
            controller.presenter = presenter
        }
        container.register(SketchesListInteractorInputProtocol.self) { _ in
            SketchesListInteractor()
        }
        container.register(SketchesListPresenterProtocol.self) { (r, view: SketchesListViewInputProtocol) in
            let interactor = r.resolve(SketchesListInteractorInputProtocol.self)!
            let router = r.resolve(SketchesListRouterInputProtocol.self)!
            let presenter = SketchesListPresenter(view: view, interactor: interactor, router: router)
            interactor.presenter = presenter
            return presenter
        }
        container.register(SketchesListRouterInputProtocol.self) { _ in
            SketchesListRouter()
        }
    }
}

registerForStoryboard is used in both of them. And as you can see I'm passing Assembler to SketchesListAssembler as part of parent/child relationship. Resolver clusures are called in TabBarAssembly but never called in SketchesListAssembly.

The question is how to implement this in a proper way and to get SketchesListAssembly closures called?

Versions:
'Swinject', '2.0.0-beta.2'
'SwinjectStoryboard', '1.0.0-beta.2'

SwinjectStoryboard + Assemblies

I am having trouble understanding how to combine different assemblies/assemblers with SwinjectStoryboard. I am using the implicit storyboard extension, but I want to separate assemblies for clean code.

Here is an example:

extension SwinjectStoryboard {
    public static func setup() {
        defaultContainer.register(ColorRandomizer.self) { _ in
            DefaultColorRandomizer()
        }
        defaultContainer.register(NameGenerator.self) { _ in
            DeveloperNameGenerator()
        }
        defaultContainer.storyboardInitCompleted(ViewController.self) { r, c in
            c.colorRandomizer = r.resolve(ColorRandomizer.self)
            c.nameGenerator = r.resolve(NameGenerator.self)
        }
    }
}

This works for the initial view controller, but let's say I have another assembly that I want to assemble. Am I supposed to do this?

let assembler = Assembler([SomeAssembly()], container: SwinjectStoryboard.defaultContainer)

Also I understand that I need to maintain a strong reference to the assembler, so should I have only 1 assembler, and apply assemblies when I need them? Should the assembler be in the AppDelegate?

Swinject, injecting an object in a subclass of UICollectionView Or UIView

I have created a subclass of UICollectionView and assigned this custom class into storyboard's collection view. I have to inject a shared object in that custom class.

// ViewController
class BZConnectSocialAcccountViewC: BZSignupWizardBase {

    @IBOutlet weak var collectionView: BZConnectSocialListView!
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}

// Sub Class
class BZConnectSocialListView: UICollectionView {
    
    var connectSocialAccountViewModeling: BZConnectSocialAccountViewModeling?
}

// View Model
protocol BZConnectSocialAccountViewModeling {
}

class BZConnectSocialAccountVM: BZConnectSocialAccountViewModeling {

 private var objApiManager: APIManagerModeling?
    init(_ apiManager: APIManagerModeling) {
        
        self.objApiManager = apiManager
    }
}

// API Manager

protocol APIManagerModeling {
}
class APIManager: SessionManager, APIManagerModeling {
}

// Injection

container.register(APIManagerModeling.self) { _ in
            APIManager()
        }.inObjectScope(.container)


container.register(BZConnectSocialAccountViewModeling.self) { r in
            BZConnectSocialAccountVM(r.resolve(APIManagerModeling.self)!)
        }

container.register(BZConnectSocialAcccountViewC.self) { r in
            
            return self.viewControllerFor(storyboard: self.storyboardSettings(className: BZConnectSocialAcccountViewC.self), identifier: BZConnectSocialAcccountViewC.className) as! BZConnectSocialAcccountViewC
            
            }.initCompleted { r, c in
                
                c.collectionView.connectSocialAccountViewModeling = r.resolve(BZConnectSocialAccountViewModeling.self)
                
        }

c.collectionView in getting nil

SwinjectStoryboard setup function no longer called automatically

Hello,

I'am starting a new project on Xcode 9 beta 5 (and tested today on beta 6) and the static function setup() is no longer called automatically. I need to call it manually in AppDelegate. But the issue with this workaround, the initial view controller is not injected only the next are injected.

Anybody have the same issue ?

Thank for helping.

Stable version

Hi,
I'm wondering if i should use SwinjectStoryboard in my production code, this project has only 1 release 2 months ago, when will be production-ready version?

Missing configs when cloning SwinjectStoryboard through Carthage

I'm getting the following errors when I try and build SwinjectStoryboard.framework after cloning with Carthage:

The file “Debug.xcconfig” couldn’t be opened because there is no such file. (/Users/mpdifran/Sandbox/Speedometer/Carthage/Checkouts/Swinject/Carthage/Checkouts/xcconfigs/Base/Configurations/Debug.xcconfig)
The file “iOS-Framework.xcconfig” couldn’t be opened because there is no such file. (/Users/mpdifran/Sandbox/Speedometer/Carthage/Checkouts/Swinject/Carthage/Checkouts/xcconfigs/iOS/iOS-Framework.xcconfig)
Build setting PRODUCT_NAME undefined

The SwinjectStoryboard.xcodeproj is included in my project as a sub-project. I'm also noticing that the Nimble and Quick subprojects are missing within SwinjectStoryboard.xcodeproj.

screen shot 2017-05-25 at 9 21 14 pm

I am running the following Carthage command:

carthage update --use-ssh --platform iOS --no-use-binaries --no-build

Injection not working in Xcode 9 and Swift 4

I have recently upgraded a project to Xcode 9 and Swift 4.

The SwinjectStoryboard.setup() method is no longer firing.

also, UIViewControllers are not invoking their associated storyboardInitCompleted calls

Override of method 'initialize()' not permitted by Swift 4

This seems to be finally removed in Swift 4. Any suggested workarounds? This is a compile error in Storyboard+Swizzling.swift

... /Pods/SwinjectStoryboard/Sources/Storyboard+Swizzling.swift:25:30: error: method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift
    open override class func initialize() {

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.