Swinject将self属性注入新的UIViewController

时间:2017-06-04 17:16:05

标签: ios swift dependency-injection swinject

让我们假设UITableViewControllerdidSelectRowAtSection上加载了一个名为ie ClassToInject的类的实例,它想通过属性注入注入它,因为我们的ViewControllerToBePushed的属性为ClassToInject,后来(因为它是UITabBarViewControllerdidSet回调,它会搜索其所有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{ $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)
    }
}

2 个答案:

答案 0 :(得分:2)

在不知道申请细节的情况下很难推荐适当的解决方案,但这里有一些建议:

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)
    ])
}

答案 1 :(得分:0)

最后,我按照提出的建议得到了最终答案。

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
)!

然后,使用所需的参数Activity和使用.transient上下文的选项卡本身类型来实例化TabBarController中的每个选项卡。它解析如下:

let activitySummary = Containers.activityPresentableContainer.resolve(
   ActivityPresentable.self, arguments: ActivityPresentableTabs.summary, activity!
) as! UIViewController

这样我可以根据他们使用的信息来概括标签栏的标签。如果其中一个标签随时发生变化,我可以按照ActivityPresentable协议更改注册。

相关问题