Связанный тип Swift во вложенном протоколе не выводится автоматически - PullRequest
1 голос
/ 21 июня 2020

Описание

У меня есть протокол, содержащий связанный тип, который необходимо вывести позже, и он называется UIViewRepresentable

Затем еще один протокол для добавления дополнительных:

protocol UIViewRepresentableHelper: UIViewRepresentable {
    var configuration: (UIViewType) -> () { get set }
}

extension UIViewRepresentableHelper {
    func makeUIView(context: UIViewRepresentableContext<Self>) -> UIViewType { UIViewType() }
    func updateUIView(_ uiView: UIViewType, context: UIViewRepresentableContext<Self>) { configuration(uiView) }
}

Итак, любые типы, соответствующие второму, должны иметь возможность реализовать первый и второй протоколы вместе.

struct LabelView: UIViewRepresentableHelper {
    typealias UIViewType = UILabel // <- Commenting  out this line confuses the Xcode about TheTypeThatShouldBeInferred
    var configuration = { (view: UILabel) in }
}

struct ButtonView: UIViewRepresentableHelper {
    typealias UIViewType = UIButton // <- Commenting  out this line confuses the Xcode about TheTypeThatShouldBeInferred
    var configuration = { (view: UIButton) in }
}

Проблема:

Но, как говорится в комментариях в коде, кажется вроде Swift не может вывести UIViewType из контекста закрытия. Кроме того, Xcode сбивает с толку контекст уже написанного configuration.

Вопрос:

Что можно сделать, чтобы избавиться хотя бы от одной аннотации типа или одной строки код или что-нибудь еще, чтобы сделать его более элегантным и менее закодированным?

Одно из ожиданий:

Я надеялся по крайней мере что-то вроде этого сработает:

struct TextFieldView: UIViewRepresentableHelper {
    var configuration: (UITextField)->() = { _ in }
}

1 Ответ

0 голосов
/ 21 июня 2020

РЕДАКТИРОВАТЬ # 3

Использование одного обобщенного c класса:

public protocol UIViewRepresentableHelper: UIViewRepresentable {
    var configuration: (UIViewType) -> () { get set }
}

public extension UIViewRepresentableHelper {
    func makeUIView(context: UIViewRepresentableContext<Self>) -> UIViewType { UIViewType() }
    func updateUIView(_ uiView: UIViewType, context: UIViewRepresentableContext<Self>) { configuration(uiView) }
}

public struct ViewConfigurator<UIViewType: UIView>: UIViewRepresentableHelper {
    public var configuration: (UIViewType) -> ()
    public init(configuration: @escaping (UIViewType) -> ()) {
        self.configuration = configuration
    }
}

Вы можете использовать его, явно указав тип аргумента закрытия :

let labelConfigurator = ViewConfigurator { (label: UILabel) in
    print(label.text ?? "empty")
}

var label = UILabel()
labelConfigurator.configuration(label) // Optional("")
label.text = "some text"
labelConfigurator.configuration(label) // Optional("some text")

или явно с указанием аргумента generi c:

typealias ButtonConfigurator = ViewConfigurator<UIButton>
let buttonConfigurator = ButtonConfigurator { button in
    print(button.title(for: .normal) ?? "empty")
}

var button = UIButton()
buttonConfigurator.configuration(button) // Optional("")
button.setTitle("some title", for: .normal)
buttonConfigurator.configuration(button) // Optional("some title")

в любом случае вы должны «помочь» компилятору на вызывающем сайте но вы делаете это только один раз.

Я думаю, что это не сработало, когда вы попробовали мой первый ответ, потому что вы не объявили ViewConfigurator вот так:

public struct ViewConfigurator<UIViewType: UIView>: UIViewRepresentableHelper

РЕДАКТИРОВАТЬ # 2

Это сработало, потому что я объявил:

func updateUIView(_ uiView: UITextField, context: Context) {
    
}

, но как только я поставил UIViewType, ошибка вернулась, я думаю, что моя первая мысль была правильно, компилятор плохо справляется со связанными типами и замыканиями

EDIT # 1

Таким образом, проблема на самом деле заключалась в том, что UIViewType и configuration не были объявлены в одном протоколе.

Я мог бы обойти это, создав типалиас UIViewType в UIViewRepresentableHelper

вот так:

protocol UIViewRepresentableHelper: UIViewRepresentable {
    typealias ViewType = UIViewType
    var configuration: (ViewType) -> () { get set }
}

struct TextFieldView: UIViewRepresentableHelper {
    var configuration: (UITextField)->()
    
}

использование :

let textFieldView = TextFieldView { textField in
    print(textField.text)
}

var textField = UITextField()
textFieldView.configuration(textField) // Optional("")
textField.text = "some text"
textFieldView.configuration(textField) // Optional("some text")

...