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 协议更改注册。