Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DI with NIB/XIB #95

Closed
SantoDE opened this issue May 9, 2016 · 22 comments
Closed

DI with NIB/XIB #95

SantoDE opened this issue May 9, 2016 · 22 comments
Labels

Comments

@SantoDE
Copy link

SantoDE commented May 9, 2016

Hey there,

I'm currently trying to get Swinject running. However, I'm struggling heavily to get the injection up and running. In my Setting.class, the property "dataSource" is always nil as it's not getting injected. What am I doing wrong? Looking for advice :)

Regards,
Manuel

extension SwinjectStoryboard {
    class func setup() {

        defaultContainer.registerForStoryboard(SettingsPreferencesController.self) {r, c in
            c.settings = r.resolve(Setting.self)
        }

        defaultContainer.register(DataSource.self) { _ in RealmService() }
        defaultContainer.register(Setting.self) { r in
            let setting = Setting()
            setting.dataSource = r.resolve(DataSource.self)
            return setting
        }
    }
}
protocol SettingType {
    var sourceHost: String? { get set }
    var sourceRoot: String? { get set }
    var dataSource: DataSource? { get set }
}

class Setting : Object , SettingType  {

    var dataSource: DataSource? = nil

    dynamic var sourceHost: String? = nil
    dynamic var sourceRoot: String? = nil

    override static func ignoredProperties() -> [String] {
        return ["dataSource"]
    }

    static func getSetting() -> Setting? {
        return RealmService().realm.objects(Setting).first
    }

    func save() {
        dataSource!.write(self)
    }
}
import Foundation

protocol DataSource {
    func write(itemToWrite : AnyObject)
}
class RealmService : DataSource {

    let realm = try! Realm()

    func write(itemToWrite: AnyObject) {
        let object = itemToWrite as! Object
        try! realm.write() {
            realm.add(object, update: true)
        }
    }
}
@yoichitgy
Copy link
Member

Hi @SantoDE. Thank you for using Swinject😃

This document might be helpful. I'll read your post and reply later.

@SantoDE
Copy link
Author

SantoDE commented May 9, 2016

Thanks @yoichitgy . I looked that up already. However, looks like I'm too dumb ;) As I missed 1 code block, here we go:

import Cocoa
import RealmSwift

class SettingsPreferencesController: NSViewController, CCNPreferencesWindowControllerProtocol {

    var settings: SettingType? = nil

    @IBAction func chooseLocationButtonClicked(sender: AnyObject) {
            var openPanel = NSOpenPanel()
            openPanel.canChooseDirectories = true
            openPanel.canChooseFiles = false
            openPanel.canCreateDirectories = false
            openPanel.allowsMultipleSelection = false

            if openPanel.runModal() == NSOKButton
            {
                // Das ausgewählte Stammverzeichniss
                var rootPath = openPanel.URLs[0] as NSURL
                fileLocation.stringValue = rootPath.absoluteString
            }
    }

    @IBOutlet weak var sourceHost: NSTextField!

    @IBOutlet weak var sourceRoot: NSTextField!

    @IBOutlet weak var fileLocation: NSTextField!

    @IBAction func saveSettings(sender: AnyObject) {

        let existingSettings = Setting.getSetting()

        if existingSettings != nil {
            existingSettings!.sourceHost = sourceHost.stringValue
            existingSettings!.sourceRoot = sourceRoot.stringValue
            existingSettings!.save()
        } else {
            let settings = Setting()
            settings.sourceRoot = sourceRoot.stringValue
            settings.sourceHost = sourceHost.stringValue
            settings.save()
        }
    }


    convenience init() {
        self.init(nibName: "SettingsPreferencesController", bundle: nil)
        self.loadView()

        /*
        let existingSettings = Settings.find()

        if existingSettings != nil {
            sourceHost.stringValue = existingSettings!.sourceHost
            sourceRoot.stringValue = existingSettings!.sourceRoot
        }
        */
    }

    override init!(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {

        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

    }

    required init?(coder: NSCoder) {

        fatalError("Method not implemented")

    }

    func preferencesIcon() -> NSImage {

        return NSImage(named: NSImageNamePreferencesGeneral)!

    }

    func preferencesIdentifier() -> String {

        return "Settings"

    }

    func preferencesTitle() -> String {

        return "Settings"

    }
}

@yoichitgy
Copy link
Member

Hi @SantoDE. I'm sorry but would you help me to investigate the issue? If you provide me the minimum code to reproduce the issue, it's much easier for me to investigate the possible bug of Swinject. Thanks😆

@SantoDE
Copy link
Author

SantoDE commented May 10, 2016

What would you need @yoichitgy ? :) I can provide what ever you need, because I'm stuck without ;)

@yoichitgy
Copy link
Member

yoichitgy commented May 10, 2016

Reducing the number of properties and lines of code in your example is helpful. Thanks👍

@SantoDE
Copy link
Author

SantoDE commented May 10, 2016

Does this help @yoichitgy ?

import Cocoa
import RealmSwift

class SettingsPreferencesController: NSViewController, CCNPreferencesWindowControllerProtocol {

    var settings: SettingType? = nil

    @IBAction func saveSettings(sender: AnyObject) {

        let existingSettings = Setting.getSetting()

        if existingSettings != nil {
            existingSettings!.sourceHost = sourceHost.stringValue
            existingSettings!.sourceRoot = sourceRoot.stringValue
            existingSettings!.save()
        } else {
            let settings = Setting()
            settings.sourceRoot = sourceRoot.stringValue
            settings.sourceHost = sourceHost.stringValue
            settings.save()
        }
    }


    convenience init() {
        self.init(nibName: "SettingsPreferencesController", bundle: nil)
        self.loadView()

        /*
        let existingSettings = Settings.find()

        if existingSettings != nil {
            sourceHost.stringValue = existingSettings!.sourceHost
            sourceRoot.stringValue = existingSettings!.sourceRoot
        }
        */
    }
}
import Foundation

protocol DataSource {
    func write(itemToWrite : AnyObject)
}
import RealmSwift

protocol SettingType {
    var sourceHost: String? { get set }
    var sourceRoot: String? { get set }
    var dataSource: DataSource? { get set }
}

class Setting : Object , SettingType  {

    var dataSource: DataSource? = nil

    dynamic var sourceHost: String? = nil
    dynamic var sourceRoot: String? = nil

    func save() {
        dataSource!.write(self)
    }
}
import RealmSwift

class RealmService : DataSource {

    let realm = try! Realm()

    func write(itemToWrite: AnyObject) {
        let object = itemToWrite as! Object
        try! realm.write() {
            realm.add(object, update: true)
        }
    }
}
import Swinject


extension SwinjectStoryboard {
    class func setup() {

        defaultContainer.registerForStoryboard(SettingsPreferencesController.self) {r, c in
            c.settings = r.resolve(Setting.self)
        }

        defaultContainer.register(DataSource.self) { _ in RealmService() }
        defaultContainer.register(Setting.self) { r in
            let setting = Setting()
            setting.dataSource = r.resolve(DataSource.self)
            return setting
        }
    }
}

@yoichitgy
Copy link
Member

Why do you expect DataSource should be non-nil?

@SantoDE
Copy link
Author

SantoDE commented May 10, 2016

Because I inject it in the SwinjectStoryBoard as a property? At least, that was my understanding. Maybe this is the error already ^.^

@yoichitgy
Copy link
Member

yoichitgy commented May 10, 2016

It's good practice to modify a working example of SwinjectStoryboard to get closer to the final thing you want to implement. Can you try a working example of SwinjectStoryboard first? Then modify it slightly closer to the RealmExample? You can continue the iteration to get the final result you want🚀

(Thanks for giving me to write this practice. I think some other people want to know this kind of things. Also I think I have to update the document of SwinjectStoryboard to provide easier way to understand😃)

@yoichitgy
Copy link
Member

yoichitgy commented May 10, 2016

By the way, just the background of the minimum code to reproduce the issue was the same thing. No worries, I'm not upset😉 I'm just thinking how I can provide good documentation and example about SwinjectStoryboard.

@yoichitgy
Copy link
Member

yoichitgy commented May 10, 2016

Maybe the problem is here?

self.init(nibName: "SettingsPreferencesController", bundle: nil)

It uses a NIB (XIB), not a storyboard.

@SantoDE
Copy link
Author

SantoDE commented May 10, 2016

All fair :) I'll try to move from there then. For me, it looked like I'm missing something very obvious so you guys could see it rather fast. Maybe it just isn't ^.^ The realm part itself is working just fine, I'm just trying to combine both worlds by taking reference to the Weather App. Anyway, thanks and I'll try it again :)

@SantoDE
Copy link
Author

SantoDE commented May 10, 2016

@yoichitgy then this will be the exact problem. Any documentation for that case? Please don't get me wrong, I'm eager to learn ^.^

@yoichitgy
Copy link
Member

Good to hear you got closer to the solution. We have no documentation for the case. I have to update the document to clearly state SwinjectStoryboard doesn't support NIB (XIB) but only Storyboard.

@SantoDE
Copy link
Author

SantoDE commented May 11, 2016

Would it be possibble to Support nib? And what would be the go to way?

Anyway, Thanks for your help :)
Am 11.05.2016 01:04 schrieb "Yoichi Tagaya" notifications@github.com:

Closed #95 #95.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#95 (comment)

@yoichitgy
Copy link
Member

You can just use a simple Container to do DI with nib.

Check below to learn DI with Swinject.
https://twitter.com/matthewswallace/status/729082708219944960
https://twitter.com/jeffbnimble/status/728018616071782400

@yoichitgy
Copy link
Member

This post looks good to learn DI with NIBs/XIBs.
http://bartjacobs.com/dependency-injection-in-swift/

@yoichitgy yoichitgy changed the title Closure not getting hit? DI with NIB/XIB May 13, 2016
@SantoDE
Copy link
Author

SantoDE commented May 13, 2016

Thanks. I'm really struggling there. Will put some time to it during the next days. Currently, my closures are still not getting hit tough I try to register services inside my AppDelegate. It crashes because my DI Containers parent is nil. Not sure where I'm failing tough.

@SantoDE
Copy link
Author

SantoDE commented May 17, 2016

Hey @yoichitgy . Sadly, I still can't resolve the issue of parent nil.

Here's my appdelegate

class AppDelegate: NSObject, NSApplicationDelegate {

    let container = Container() { container in

        container.register(ViewController.self) { _ in ViewController() }
        container.register(DataSource.self) { _ in RealmService() }
        container.register(SettingType.self) { r in
            let setting = Setting()
            setting.dataSource = r.resolve(DataSource.self)
            return setting
        }
    }

It doesnt resolve SettingTypes Closure to add the service, as it gets an nil during working trough the closure. Any thing obvious wrong you can see with my delegate?

EDIT

It crashes here:

    public convenience init(parent: Container? = nil, @noescape registeringClosure: Container -> Void) {
        self.init(parent: parent)
        registeringClosure(self)
    }

with the error

fatal error: unexpectedly found nil while unwrapping an Optional value

EDIT2

Looks like, my initial idea was wrong. Looks like, it crashes in register function of Container.swift as the serviceType appears to be nil. How is this possible, when I register the services to the container as shown above? Maybe you @yoichitgy have an idea :)

@Sajjon
Copy link

Sajjon commented Sep 23, 2016

Is there still no support for Swinject and Xibs?

@jakubvano
Copy link
Member

@Sajjon In what cases would you need special support for Xibs? Support for storyboard is required because you don't have view controller's initialisation under control, but when using Xibs they are constructed via initialiser which is perfect place to pass dependencies.

However, I don't think this issue is the right place for future discussion, as it is bloated by a lot of stuff unrelated to the topic. Would you please create separate issue with description of the problem you are facing? Thanks 😉

@hariszaman
Copy link

@jakubvano how about the case when a view is loaded from the nib in the view controller by defining IBOutlet ?

class MySubView: UIView {
func awakeFromNib() {
super.awakeFromNib()
}
}
class MyViewController: UIViewController {
@IBOutlet weak var mysubView: MySubView!

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants