Swinject注入自身的财产到新的UIViewController

问题描述:

让我们假设我们有一个UITableViewControllerdidSelectRowAtSection加载一个类的实例,即命名为:ClassToInject,并希望通过物业注入注入,因为我们ViewControllerToBePushedClassToInject的属性,即随后在didSet回调(因为它是一个UITabBarViewController),它搜索符合ClassToInjectPresentable简单,因为它所有的财产viewControllersSwinject注入自身的财产到新的UIViewController

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

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

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

这是很难推荐合适的解决方案,而知道你的应用程序的细节,但这里有一些建议:

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

'tabBar.property = injector'从哪里来? –

+0

对不起,它的意思是'tabBar.property = property'。我已经更新了一个答案 –