Here's a fake representation of my setup (my code is too complicated to paste directly).
extension NSFetchedResultsController: MDFFetchedResultsController { }
class TableViewControllerAssembly: AssemblyType {
func assemble(container: Container) {
container.registerForStoryboard(TableViewController.self) { (r, c) in
let controller = r.resolve(MDFFetchedResultsController.self)!
// This line throws an error when unwrapping the optional.
let dataSource = r.resolve(MDFDataSource.self, arguments: (c.tableview, controller))!
c.dataSource = dataSource
c.tableView.dataSource = dataSource
}
}
}
class MDFConcreteDataSource: MDFDataSource {
let tableView: UITableView
let controller: MDFFetchedResultsController
init(tableView: UITableView, controller: MDFFetchedResultsController) {
self.tableView = tableView
self.controller = controller
}
}
class DataSourceAssembly: AssemblyType {
func assemble(container: Container) {
container.register(MDFDataSource.self) { r, tableView, fetchedResultsController in
MDFConcreteDataSource(tableView: tableView, controller: fetchedResultsController)
}
}
}
I believe the issue is Swinject doesn't find the registry due to how it determines the type of an object when using arguments. The registry is looking for a protocol (MDFFetchedResultsController), and I'm passing in a concrete implementation of the protocol (NSFetchedResultsController) as an argument.
I've also tried explicitly typing controller like so: let controller: MDFFetchedResultsController = ....
Any thoughts on what's going on, or how I could work around this issue?
I'm using Swinject at this commit: c598e7a.
I think the problem is your registration of MDFDataSource.self with registerForStoryboar expects arguments though it does not support arguments. Reading #27 might be helpful for you.
Ah sorry, that was a mistake when I was writing this up. I've updated to fix the mistake.
No worries. I think the naming of registerForStoryboard is confusing. I submitted an issue to rename the method: https://github.com/Swinject/SwinjectStoryboard/issues/21
Just to be clear, this was a mistake when I was writing the fake example. My problem still persists with the above setup. Swinject can't find the correct registry with the given arguments.
I suspect the problem is passing implicitly unwrapped optionals as arguments instead of wrapped values:
(c.tableview, controller) is of type (UITableView!, MDFFetchedResultsController!), while your registration is with arguments of type (UITableView, MDFFetchedResultsController)
Explicit upcasting should fix the issue, i.e:
r.resolve(MDFDataSource.self, arguments: (c.tableview as UITableView, controller as MDFFetchedResultsController))
Ah good call. I made the following change and it was able to resolve the dependency properly:
let dataSource = r.resolve(MDFDataSource.self, arguments: (c.tableview as UITableView, controller))!
Upcasting the controller didn't seem to make a difference.
For some reason I thought that implicitly unwrapping an optional returns value of type ImplicitlyUnwrappedOptional instead of wrapped type. Good to know that is not the case 馃槃
I guess we can close this now.
I think it's good to modify the documentation for arguments to explicitly specify types like
container.register(AnimalType.self) { (_: ResolverType, name: String, running: Bool) in
...
}
and
let name: String = "Lucky"
let running: Bool = true
container.resolve(AnimalType.self, arguments: name, running)!
to recommend typing the arguments explicitly. Swinject users can drop the explicit types by their own understandings.
https://github.com/Swinject/Swinject/blob/master/Documentation/DIContainer.md#registration-with-arguments-in-a-di-container
@yoichitgy I'm not sure if we should be recommending usage involving a lot of boilerplate - it is not very Swift-y, and encourages bad habits (reducing readability of the code). Maybe extending the Remark section with common type mismatches (e.g. Optionals) might help?
However, as user-friendliness usually beats documentation, maybe we could implement some run-time warning messages similar to checkedResolved, which would somehow log that resolve failed, what arguments were given, and maybe some suggestion to what might be the problem
Not sure about implementability yet, but something like
Resolving of MDFDataSource failed.
Name: -
Arguments: UITableView!, MDFFetchedResultsController
Found similar registration for MDFDataSource:
Name: -
Arguments: UITableView, MDFFetchedResultsController
@jakubvano, yes you are right. The Swifty way is important. I'll update the Remark section shortly. An example with optionals makes sense!
Most helpful comment
I suspect the problem is passing implicitly unwrapped optionals as arguments instead of wrapped values:
(c.tableview, controller)is of type(UITableView!, MDFFetchedResultsController!), while your registration is with arguments of type(UITableView, MDFFetchedResultsController)Explicit upcasting should fix the issue, i.e: