Как я могу иметь 2 дженерика использовать один и тот же тип T - PullRequest
2 голосов
/ 06 марта 2020

Представьте, что у меня есть фабрика контроллеров представления со следующим типом:

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.

Оба типа generi c 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)

1 Ответ

0 голосов
/ 06 марта 2020

Вы не можете передать свойства 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)
...