2016-11-30 5 views
0

Пытаясь достичь что-то вроде следующего Objective C кода в быстрой UIViewController<Routable>, но жалуется, что не может вывести общий типпараметр swift метода типа, который реализует протокол?

Я думаю, что он хочет, чтобы я как-то определить, что подкласс UIViewController использовать, но я не Не волнуйся. Я просто хочу UIViewController, который реализует маршрутизируемо

public protocol Routable { 
    static func provideInstance(id: String?) -> Self? 
} 

public class Router { 
    public func getDestinationViewController<T: UIViewController>(url: URL) -> T? where T: Routable { 
     return nil 
    } 
} 

// Generic parameter `T` could not be inferred 
let vc = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!) 

Я знаю, что я мог бы использовать литье, чтобы решить эту проблему и просто метод возврата либо UIViewController или маршрутизируемо, но я предпочел бы сделать это правильный путь

ответ

3

Что точный тип вы ожидаете vc быть в этом случае? (Я имею в виду тип, определяемый во время компиляции, смотря на код выше, а не «некоторый тип, который мы не знаем до времени исполнения».) Если вы не можете ответить на этот вопрос, компилятор тоже не сможет этого сделать.

Правильный путь здесь - «вернуть UIViewController или Routable», как вы говорите. Это все, что вы знаете на данный момент. Попытка сказать что-то более точное было бы неверным.

Фактом, что vc является тип-вывод, является просто удобством. Он все еще должен иметь определенный, известный тип во время компиляции. Если вы не можете записать его в источнике, компилятор не может это сделать.

+0

действительно ли невозможно вернуть UIViewController, который также является маршрутизируемым? Это возможно в objc '- (UIViewController * ) gimeeSomething' – aryaxt

+1

Это невозможно выразить в Swift. Вам нужно будет добавить любые специальные методы, которые вы хотите от UIViewController, к протоколу «Routable». Это известное ограничение и, вероятно, будет улучшено в будущих версиях Swift, но в настоящее время это невозможно. –

1

Когда вы используете общие функции, тип универсального параметра выводится из типа переменной, которой вы назначаете результат (или, в случае функции, возвращающей Void, из конкретного типа параметра, который вы передаете аргументу тип T).

В вашем примере вы не сообщаете компилятору, какой тип переменной vc, чтобы вы получили сообщение об ошибке. Вы можете исправить это легко, как так:

class MyViewController: UIViewController, Routable { 
    public static func provideInstance(id: String?) -> Self? { 
     return self.init() 
    } 
} 

// give `vc` a concrete type so `T` can be inferred: 
let vc: MyViewController? = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!) 

EDIT:

Поскольку ваш вопрос, кажется, больше вдоль линий «Как реплицировать Objective-C понятие по UIViewController *<Routable>», который вы не можете сделайте в Swift, я упомянул, что вам, возможно, стоит рассмотреть ваш дизайн. Когда я перешел из Objective-C в Swift, я думал, что не использовать что-то вроде UIViewController *<Routable> было бы трудно обойти (и даже подал отчет об ошибке с Apple об этом), но на практике это не было проблемой.

Ваш Router класс необходим информация на вашем контроллере, чтобы иметь возможность правильно маршрутизировать. В вашем примере вы ищете контроллер представления, связанный с определенным URL-адресом. Смысл в том, что ваш UIViewController имеет свойство, который содержит этот URL, так, а не возвращать протокол Routable, правильный подход к подклассу UIViewController как так:

class RoutableViewController: UIViewController { 
    let url: URL = URL(string: "...") 
} 

Теперь маршрутизатор выглядит следующим образом:

class Router { 
    var routables = [RoutableViewController]() 

    func viewControllerWithURL(_ url: URL) -> RoutableViewController? { 
     return routables.first 
    } 
} 

Теперь вам не нужно делать литье под давлением, а безопасность типа (очевидно) поддерживается.

EDIT 2:

Вот решение, которое делает каждый контроллер вид соответствует Routable (я не думаю, что вы получите много против решения я предложил в моем предыдущем редактировать, но здесь это в любом случае) :

protocol Routable { 
    var url: URL? { get } 
} 

// now every UIViewController conforms to Routable 
extension UIViewController: Routable { 
    var url: URL? { return nil } 
} 

class MyViewController: UIViewController { 
    private let myURL: URL 

    override var url: URL? { return myURL } 

    init(url: URL) { 
     myURL = url 
     super.init(nibName: nil, bundle: nil) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 
} 

class Router { 
    var viewControllers: [UIViewController] = [ 
     MyViewController(url: URL(string: "foo")!), 
     UIViewController() 
    ] 

    func viewController(for url: URL) -> UIViewController? { 
     for viewController in viewControllers { 
      if viewController.url == url { return viewController } 
     } 

     return nil 
    } 
} 
+0

Это не работает, потому что я не знаю, что vc я собираюсь вернуться, я хочу, чтобы он был динамичным. Маршрутизатор проходит через мои зарегистрированные маршруты и находит совпадение и возвращает его, не зная, какой vc это – aryaxt

+0

. Затем вы хотите получить функцию, которая возвращает 'UIViewController? ', И вам придется прибегать к типу, используя либо' как?', Либо несколько 'case' s в выражении 'switch' (например,' case is MyViewController: '). Дженерики используются для обеспечения реализации методов для пока неизвестных аргументов, а не для произвольного понижения. – par

+0

«Я знаю, что могу использовать кастинг для решения проблемы и просто вернуть метод как UIViewController или Routable, но я скорее сделаю это правильно». Это было возможно в объективе c, я удивлен, что Swift не поддерживает это – aryaxt

Смежные вопросы