Swinject 将自己的 属性 注入新的 UIViewController
Swinject inject self's property into new UIViewController
假设我们有一个 UITableViewController
,它在 didSelectRowAtSection
上加载了一个名为 class 的实例,即 ClassToInject
并且它想通过 属性 注入,因为我们的 ViewControllerToBePushed
有一个 ClassToInject
的 属性,随后(因为它是一个 UITabBarViewController
)它在 didSet
回调中搜索它的所有viewControllers
属性 符合 ClassToInjectPresentable
简单如:
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{ [=13=] is ClassToInjectPresentable }.map{ [=13=] as! ClassToInjectPresentable }.forEach{ [=13=].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)
}
}
在不了解您的应用细节的情况下很难推荐合适的解决方案,但这里有一些建议:
container.register(ClassToInjectInjector.self) { _ in
self.loadClassToInjectFor(indexPath)
}
一般来说,所有 register
操作都应该在您的对象之外完成。常见设置有一个全局 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)
])
}
最后我按照提出的建议得到了最终答案。
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
协议更改注册。
假设我们有一个 UITableViewController
,它在 didSelectRowAtSection
上加载了一个名为 class 的实例,即 ClassToInject
并且它想通过 属性 注入,因为我们的 ViewControllerToBePushed
有一个 ClassToInject
的 属性,随后(因为它是一个 UITabBarViewController
)它在 didSet
回调中搜索它的所有viewControllers
属性 符合 ClassToInjectPresentable
简单如:
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{ [=13=] is ClassToInjectPresentable }.map{ [=13=] as! ClassToInjectPresentable }.forEach{ [=13=].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)
}
}
在不了解您的应用细节的情况下很难推荐合适的解决方案,但这里有一些建议:
container.register(ClassToInjectInjector.self) { _ in
self.loadClassToInjectFor(indexPath)
}
一般来说,所有 register
操作都应该在您的对象之外完成。常见设置有一个全局 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)
])
}
最后我按照提出的建议得到了最终答案。
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
协议更改注册。