Would you add more context or background behind your question to help me understand it?
I would like to inject some properties into AppDelegate like in the following way.
class AppDelegate: UIResponder, UIApplicationDelegate {
private let appearanceConfigurator: AppearanceConfigurator!
private let persistenceConfigurator: PersistenceConfigurator!
private let container = Container() { c in
c.registerForAppDelegate(AppDelegate.self) { _, appDelegate in
appDelegate.appearanceConfigurator = AppearanceConfiguratorImpl()
appDelegate. persistenceConfigurator = PersistenceConfiguratorImpl()
}
}
override init() {
container.resolve(self)
}
}
Generally I would like to build number of Assemblies and inject them automatically or in an easy way, like Typhoon allows.
I like the structure and architecture the "RamblerConferences" project, please see the source code https://github.com/rambler-ios/RamblerConferences/tree/develop/conferences/Classes/ApplicationLayer/Assemblies.
But they use objc and Typhoon (which doesn't support native Swift).
The architecture of RamblerConferences is interesting!
Swinject does not have registerForAppDelegate function, but you can do the similar thing by adding your own assembler class like this:
class AppDelegateAssembler {
func resolveDependencies(appDelegate: AppDelegate) {
appDelegate.appearanceConfigurator = AppearanceConfiguratorImpl()
appDelegate.persistenceConfigurator = PersistenceConfiguratorImpl()
}
}
Then AppDelegate can be:
class AppDelegate: UIResponder, UIApplicationDelegate {
private let appearanceConfigurator: AppearanceConfigurator!
private let persistenceConfigurator: PersistenceConfigurator!
override init() { // or application(_:didFinishLaunchingWithOptions:)
super.init()
AppDelegateAssembler().resolveDependencies(self)
}
}
, though I'm not sure this is a good way to do... The difficult thing is that AppDelegate is the entry point of an iOS program in Swift.
Thank you for the suggestion.
I think that there should be some native library mechanism to do such things.
What do you think about updating your library with the [UIApplication setAppDelegate] swizzling, like Typhoon does, and create solution like you did for _SwinjectStoryboard_.
I agree that the framework should be as much as possible native for Swift, but AppDelegate is obj-c object. And we're allowed to do the same things with AppDelegate that we do for objc objects.
I even think that resolving the most of dependencies should be done automatically if it's possible.
Looking at RamblerConferences example doesn't have any direct injections in the code, everything is done by Typhoon with Assemblers.
I understand Typhoon has the advantage of the automatic DI (though I prefer explicit DI like I did in my sample projects because of my background of tools or languages).
I would like to avoid swizzling UIApplication class or its methods because of its impact if Apple changes its implementation when they update Xcode or iOS. I want Swinject to be stable for the updates.
(In the same way, SwinjectStoryboard is not stable for the future updates of the environment. We might not be able to keep the SwinjectStoryboard feature if Apple changes something in the future. So, swizzling of SwinjectStoryboard is compromise.)
I keep this issue open to gather more information about DI to AppDelegate. Someone might have a good idea to solve it. Thank you @larryonoff for posting the issue.
Thank you for the feedback.
Hope that Swift / UIKit will allow us to solve this issue very soon.
I also would like to have some kind of auto-resolving injections both on AppDelegate and UIViewController descendants, I described my thoughts of the way we could use in #59
Hello guys, @larryonoff @yoichitgy
I was facing a similar problem and this is how I solved it
My Swinject extension looks like this
extension SwinjectStoryboard {
class func setup() {
// Dependencies injections
defaultContainer.register(CustomClass.self) { _ in CustomClass(...) }
}
class func getCustomClass() -> CustomClass {
return defaultContainer.resolve(CustomClass.self)!
}
}
And my AppDelegate like this
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var customClass: CustomClass!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Gets the dependency from SwinjectStoryboard extension
customClass = SwinjectStoryboard.getCustomClass()
}
...
Hope it helps you!
@robertofrontado thank you for sharing the great idea!
I'm going to close this issue for the idea by @robertofrontado. Also auto-injection, discussed in #59, can be alternative way for this issue.
Isn't it better to do like that:
customClass = SwinjectStoryboard.defaultContainer.resolve(CustomClass.self)
?? .. instead of creating each function in SwinjectStoryboard ...
Unless there's something wrong with @igordeoliveirasa's approach, I used it and it seems to work well. No extension to SwinjectStoryboard was required. Just this single line of code. 馃挴
I'd like to offer my solution that I've found to be a bit cleaner. I create an AppDelegateProxy class and inject there via method injection. The proxy's job is to create and dependency inject the AppDelegate class and that's it
The end result looks like:
@UIApplicationMain
final class AppDelegateProxy: UIResponder, UIApplicationDelegate {
private let container = Container()
private weak var applicationDelegate: UIApplicationDelegate?
// MARK: - Initialization
override init() {
super.init()
container.register(UIWindow.self, factory: { _ in UIWindow() })
container.register(ApplicationProtocol.self, factory: { _ in UIApplication.shared })
container.register(AppDelegate.self, factory: { resolver in AppDelegate(application: resolver.resolve(ApplicationProtocol.self)!, window: resolver.resolve(UIWindow.self)!) })
container.register(UIApplicationDelegate.self, factory: { resolver in resolver.resolve(AppDelegate.self)! })
container.register(AppDelegateProxy.self, factory: { [weak self] resolver in
guard let vSelf = self else { fatalError("self should exist") }
vSelf.injectProperties(resolver: resolver)
return vSelf
})
}
// MARK: - Property Injection
func injectProperties(resolver: Resolver) {
self.applicationDelegate = resolver.resolve(UIApplicationDelegate.self)
}
}
with an added initializer in AppDelegate:
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let application: ApplicationProtocol
init(application: ApplicationProtocol, UIWindow?) {
self.window = window
self.application = application
}
}
Now you can completely control how and when AppDelegate gets instantiated.
There is one caveat however. Because AppDelegateProxy is now @UIApplicationMain, if you want to use an appDelegate method you have to forward it from the proxy to the real delegate.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return applicationDelegate?.application?(application, didFinishLaunchingWithOptions: launchOptions) ?? true
}
You'll also have to forward the UIResponder methods like touchesBegan.
Most helpful comment
Hello guys, @larryonoff @yoichitgy
I was facing a similar problem and this is how I solved it
My Swinject extension looks like this
And my AppDelegate like this
Hope it helps you!