Swinject 将 self 的属性注入新的 UIViewController
Posted
技术标签:
【中文标题】Swinject 将 self 的属性注入新的 UIViewController【英文标题】:Swinject inject self's property into new UIViewController 【发布时间】:2017-06-04 17:16:05 【问题描述】:假设我们有一个UITableViewController
,它在didSelectRowAtSection
上加载了一个名为ClassToInject
的类的实例,并且它想通过属性注入来注入它,因为我们的ViewControllerToBePushed
具有ClassToInject
的属性,随后(因为它是一个UITabBarViewController
)在didSet
回调中它搜索其所有符合ClassToInjectPresentable
的viewControllers
属性,简单如下:
protocol ClassToInjectPresentable
var property: ClassToInject get set
到目前为止,我只会做这样的事情:
func didSelectRowAtIndexPath
let classToInject = self.loadClassToInjectFor(indexPath)
let tabBarViewController = SomeTabBarViewController()
tabBarViewController.property = classToInject
self.navigationController.push(tabBarViewController, animated: true)
在SomeTabBarViewController
...
class SomeTabBarViewController: ClassToInjectPresentable
var property: ClassToInject?
didSet(newValue)
self.viewControllers.filter $0 is ClassToInjectPresentable .map $0 as! ClassToInjectPresentable .forEach $0.property = newValue
并且所有内容都应该可以轻松轻松地加载(但事实并非如此)。我读过Swinject
,这可能会解决。我已经看到很多注册的例子,比如:
container.register(Animal.self) _ in Cat(name: "Mimi")
但是不知道能不能注册一些在self
中加载的属性:
container.register(ClassToInjectInjector.self) _ in
self.loadClassToInjectFor(indexPath)
// And then
container.register(ClassToInjectPresentable.self) _ in
SomeTabBarViewController()
.initCompleted r, p in
let tabBar = p as! SomeTabBarViewController
tabBar.property = r.resolve(ClassToInjectInjector.self)
// And lastly?
self.navigationController.pushViewController(tabBar, animated: true)
【问题讨论】:
【参考方案1】:在不了解应用程序细节的情况下很难推荐合适的解决方案,但这里有一些建议:
container.register(ClassToInjectInjector.self) _ in
self.loadClassToInjectFor(indexPath)
一般来说,所有register
-ations 都应该在您的对象之外完成。常见设置是拥有一个全局 Container
,其中包含所有注册 - 您应该将它们视为构建应用程序对象的指令,而无需任何隐式上下文。如果您的依赖项需要在UITableViewController
中创建,您可以将其作为参数传递给resolve
方法:
container.register(ClassToInjectPresentable.self) resolver, property in
let tabBar = SomeTabBarViewController()
tabBar.property = property
return tabBar
// in UItableVIewController
container.resolve(ClassToInjectPresentable.self,
argument: self.loadClassToInjectFor(indexPath))
这通常也是个坏主意:
.initCompleted r, p in
...
self.navigationController.pushViewController(tabBar, animated: true)
您不应该将应用程序逻辑与 DI 混合使用 - 纯粹使用 Swinject 来构建您的依赖项。
所以你的UITableViewController
可能看起来像这样:
func didSelectRowAtIndexPath
let classToInject = self.loadClassToInjectFor(indexPath)
let tabBar = container.resolve(
SomeTabBarViewController.self, argument: loadClassToInjectFor(indexPath)
)
navigationController.push(tabBar, animated: true)
至于您的TabBar
及其视图控制器:UIViewControllers
如何进入TabBar
?有可能做这样的事情吗?
class SomeTabBarViewController
init(viewControllers: [UIViewController])
...
container.register(SomeTabBarViewController.self) r, property
SomeTabBarViewController(viewControllers:[
r.resolve(MyViewController.self, argument: property),
r.resolve(MyViewController2.self, argument: property)
])
【讨论】:
tabBar.property = injector
来自哪里?
抱歉,本来是tabBar.property = property
。我已经更新了答案【参考方案2】:
最后我按照提出的建议得到了最终的答案。
public class Containers
fileprivate init()
extension Containers
static let activityPresentableContainer: Container =
let container = Container()
container.register(ActivityTabBarController.self) (r: Resolver, arg1: Activity) in
return ActivityTabBarController(activity: arg1)
container.register(ActivityPresentable.self)
(r: Resolver, arg1: ActivityPresentableTabs, arg2: Activity) in
switch arg1
case .summary:
return ActivitySummaryViewController(activity: arg2)
case .detail:
return ActivityDetailPageViewController(activity: arg2)
case .map:
return ActivityMapViewController(activity: arg2)
case .charts:
return ActivityChartsViewController(activity: arg2)
case .strava:
return ActivityStravaViewController(activity: arg2)
.inObjectScope(.transient)
return container
()
使用这种方法,ActivityTabBarController
总是由 activityPresentableContainer
使用以下语句实例化:
let controller = Containers.activityPresentableContainer.resolve(
ActivityTabBarController.self, argument: activity
)!
然后,TabBarController 中的每个选项卡都使用所需的参数Activity
和选项卡本身的类型使用.transient
上下文进行实例化。它是这样解决的:
let activitySummary = Containers.activityPresentableContainer.resolve(
ActivityPresentable.self, arguments: ActivityPresentableTabs.summary, activity!
) as! UIViewController
通过这种方式,我可以根据标签栏所使用的信息来概括标签栏的标签。如果其中一个选项卡随时发生变化,我可以按照ActivityPresentable
协议更改注册。
【讨论】:
以上是关于Swinject 将 self 的属性注入新的 UIViewController的主要内容,如果未能解决你的问题,请参考以下文章