4

In a Swift 2.0, Single Page Application project, in XCode 7.0.1, with the Swift dependency injection framework Swinject, I am doing the following:

DuplicateProtocol.swift:

protocol DuplicateProtocol { var id: String { get } }

SingletonProtocol.swift:

protocol SingletonProtocol { var id: String { get } }

DuplicateProxy.swift

class DuplicateProxy: DuplicateProtocol {
    let id: String

    init ( id: String ) {
        self.id = "DuplicateProxy." + id
    }
}

SingletonProxy.swift:

class SingletonProxy: SingletonProtocol {
    let id: String

    init ( id: String ) {
        self.id = "SingletonProxy." + id
    }
}

ViewController.swift:

import UIKit

class ViewController: UIViewController
{
    var duplicate: DuplicateProtocol?
    var singleton: SingletonProtocol?

    required init? ( coder aDecoder: NSCoder ) {
        print( "ViewController.init?", duplicate, singleton )
        super.init( coder: aDecoder )
    }

    override func viewDidLoad () {
        print( "ViewController.viewDidLoad", duplicate, singleton )
        super.viewDidLoad()
    }
}

AppDelegate.swift:

import Swinject
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
    var window: UIWindow?

    func application ( application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]? ) -> Bool {

        let container = Container()
        let singleton = SingletonProxy( id: "first" )

        container.register( SingletonProtocol.self ) { _ in singleton }
        container.register( DuplicateProtocol.self ) { _ in DuplicateProxy( id: "second" ) }

        container.registerForStoryboard( ViewController.self ) {
            r, c in c.duplicate = r.resolve( DuplicateProtocol.self )
        }

        print( "AppDelegate.application" )

        return true
    }
}

And this is the console log I am unfortunately getting:

ViewController.init? nil nil
AppDelegate.application
ViewController.viewDidLoad nil nil

Put as simply as I can: what changes do I need to make? Thank you, JBM.

Claudio Redi
  • 67,454
  • 15
  • 130
  • 155
Joseph Beuys' Mum
  • 2,395
  • 2
  • 24
  • 50

1 Answers1

3

Initializer injection is not available if you want to instantiate the view controller from a storyboard because UIKit framework calls init?(coder:).

Instead, property injection should be used to instantiate it from a storyboard:

class SomeViewController: UIViewController {
    var duplicate: DuplicateProtocol?
    var singleton: SingletonProtocol?

    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
    }
}

UPDATE

Still the implicit instantiation of UIWindow and the initial view controller is not supported by Swinject. You need to instantiate them explicitly as the following example does.

import Swinject
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var container: Container {
        let container = Container()
        let singleton = SingletonProxy( id: "first" )

        container.register( SingletonProtocol.self ) { _ in singleton }
        container.register( DuplicateProtocol.self ) { _ in DuplicateProxy( id: "second" ) }

        container.registerForStoryboard( ViewController.self ) {
            r, c in c.duplicate = r.resolve( DuplicateProtocol.self )
        }

        return container
    }

    func application ( application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]? ) -> Bool {
        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()

        return true
    }
}

By the way, you don't have to handle the singleton instance by yourself. Swinject has Singleton (aka Container) Object Scope, which can be set with .inObjectScope(.Container) as documented here.

UPDATE 2

Swinject version 0.3 supports the implicit instantiation of UIWindow and its root view controller from "Main" storyboard. Here is its documentation.

Yoichi Tagaya
  • 4,547
  • 2
  • 27
  • 38
  • Hi Yoichi. I edited my original question to both include the suggestion you made, and post the entire code from a simple test project, as I tried to implement your suggestion without success. I am sure I am doing something wrong that would be obvious to you, but not to me, so that is why I have posted my entire, stripped-down code. – Joseph Beuys' Mum Oct 08 '15 at 13:24
  • You've lost me a little Yoichi because you said "Initializer injection is not available if you want to instantiate the view controller from a storyboard" but then immediatly said "property injection should be used to instantiate it from a storyboard." So should it be instantiated from a storyboard or not? Do you mean it should be: var newViewController = app.storyboard.instantiateViewControllerWithIdentifier( "ViewController" ) --> app.window.rootViewController = newViewController --> app.window.makeKeyAndVisible() ? – Joseph Beuys' Mum Oct 08 '15 at 14:08
  • Instantiation from a storyboard (**SwinjectStoryboard**) is available. The difference of **initializer injection** and **property injection** is [documented here](https://github.com/Swinject/Swinject/blob/master/Documentation/InjectionPatterns.md). Hope it helps. – Yoichi Tagaya Oct 09 '15 at 01:00
  • Nice Yoichi, THANK YOU. And thank you for being kind about my stupid questions. – Joseph Beuys' Mum Oct 09 '15 at 10:19