首页 > 解决方案 > 如何让 2 个泛型使用相同的 T 类型

问题描述

想象一下,我有一个具有以下类型的视图控制器工厂:

protocol ViewFactoryType {
  associatedtype T
  func create(for scene: T) -> UIViewController
}

final class ViewFactory<T>: ViewFactoryType {
  func create(for scene: T) -> UIViewController {
    return .init()
  }
}

而且我还有一个具有以下类型的导航路由器:

protocol RouterType {
  associatedtype T
  func navigate(to target: T, using transition: Transition)
}

我想做的是创建一个实例RouterType并传递它和一个ViewFactory.

两种泛型T类型实际上将使用相同的Scene类型。

我尝试按如下方式创建该路由器:

final class AppRouter<T>: RouterType {

  private let viewControllerFactory: ViewFactoryType
  private let navigationController: UINavigationController

  public init(navigationController: UINavigationController, viewControllerFactory: ViewFactoryType) {
    self.navigationController = navigationController
    self.viewControllerFactory = viewControllerFactory
  }

  public func navigate(to target: T, using transition: Transition) {
    routeTo(viewControllerFactory.create(for: target), transition: transition)
  }

  private func routeTo(_ viewController: UIViewController, transition: Transition) {
    switch transition {
    case .push: navigationController.pushViewController(viewController, animated: true)
    case .present: navigationController.present(viewController, animated: true)
    case .replace: navigationController.setViewControllers([viewController], animated: false)
    }
  }
}

但是,这会产生错误:

Protocol 'ViewFactoryType' can only be used as a generic constraint because it has Self or associated type requirements

Member 'create' cannot be used on value of protocol type 'ViewFactoryType'; use a generic constraint instead

通过使用扩展,我能够稍微解决这个问题:

final class AppRouter<T> {

  private let viewControllerFactory: ViewFactoryType
  private let navigationController: UINavigationController

  public init(navigationController: UINavigationController, viewControllerFactory: ViewFactoryType) {
    self.navigationController = navigationController
    self.viewControllerFactory = viewControllerFactory
  }

  private func routeTo(_ viewController: UIViewController, transition: Transition) {
    switch transition {
    case .push: navigationController.pushViewController(viewController, animated: true)
    case .present: navigationController.present(viewController, animated: true)
    case .replace: navigationController.setViewControllers([viewController], animated: false)
    }
  }
}

extension AppRouter: Router where T == Scene {
  func route(to target: Scene, as transition: Transition) {

  }
}

但是如果我也添加它,工厂方法没有正确的类型func route(to target: Scene, as transition: Transition)

标签: iosswiftgenericsrouter

解决方案


您不能associatedtype以这种方式传递属性。编译器不了解它们应该如何关联或您期望它们如何使用。

您可以重构您AppRouter以接受代表ViewFactoryType它应该期望的类型。

final class AppRouter<T, R> {

  private let viewControllerFactory: R
  private let navigationController: UINavigationController

  public init(navigationController: UINavigationController, viewControllerFactory: R) {
    self.navigationController = navigationController
    self.viewControllerFactory = viewControllerFactory
  }

  private func routeTo(_ viewController: UIViewController, transition: Transition) {
    switch transition {
    case .push: navigationController.pushViewController(viewController, animated: true)
    case .present: navigationController.present(viewController, animated: true)
    case .replace: navigationController.setViewControllers([viewController], animated: false)
    }
  }
}

extension AppRouter: RouterType where T == Scene, R == ViewFactory<T> {
  func navigate(to target: Scene, as transition: Transition) {
    let viewController = viewControllerFactory.create(for: target)
    routeTo(viewController, transition: transition)
  }
}

您现在可以通过传递正确的类型来创建实例

    let viewControllerFactory = ViewFactory<Scene>()
    let router = AppRouter<Scene, ViewFactory<Scene>>(navigationController: navigationController, viewControllerFactory: viewControllerFactory)

推荐阅读