Протокол соответствия стандарту ios swift

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

допустим, у меня есть Outlet, который является текстовым полем. Я хочу, чтобы текстовое поле соответствовало протоколу, подобному протоколу ValidatesName. Есть ли способ сделать это? Я не хочу делать новый класс, который подкласс UITextField и соответствует протоколу. Я хочу использовать для этого конкретного свойства.

@IBOutlet weak var nameTextField:UITextField!<Conforms ValidatesName>
@IBOutlet weak var emailTextField:UITextField!<Conforms ValidatesEmail>
@IBOutlet weak var passwordTextField:UITextField!<Conforms ValidatesPassword>

Спасибо

2 ответа

Решение

Ваша проблема заключается в том, что хотя вы можете добавить соответствие протокола через расширение, расширение применяется к классу, а не к экземпляру этого класса. Это означает, что вы можете сказать что-то вроде:

extension UITextField: ValidatesName {...}

Но это заставит все экземпляры UITextField соответствовать ValidatesName

Точно так же вы могли бы также сказать

extension UITextField: ValidatesEmail{...}

Но теперь все экземпляры UITextField будут соответствовать ValidatesName а также ValidatesEmail,

Имея отдельный Validates... протоколы не кажутся подходящими в любом случае. Основная подпись вашего протокола - что-то вроде var isValid: Bool; это не меняется между именем и электронной почтой. Что меняется, так это логика проверки, и это должно где-то жить. Это, в сочетании с тем, что вам нужны подклассы для работы с Интерфейсным Разработчиком, предполагает, что единый протокол Validatable это может быть принято вашими подклассами - более разумный подход.

protocol Validatable  {
    var isValid: Bool { get }
}

Теперь вы можете определить подклассы UITextField, которые соответствуют этому протоколу (вы можете добавить соответствие через расширение к вашему подклассу, если хотите, я просто хотел сэкономить здесь)

class NameTextField: UITextField, Validatable {

    var isValid: Bool {
        get {
            guard let text = self.text else {
                return false
            }

            return !text.isEmpty
        }
    }
}

class EmailTextField: UITextField, Validatable {
    var isValid: Bool {
        get {
            guard let text = self.text else {
                return false
            }

            return text.contains("@")
        }
    }
}

Теперь вы можете добавить свои текстовые поля в массив и получить что-то вроде:

@IBOutlet weak var nameTextField:NameTextField!
@IBOutlet weak var emailTextField:EmailTextField!

var validatableFields:[Validatable]!

override func viewDidLoad() {
    super.viewDidLoad()

    self.validatableFields = [nameTextField,emailTextField]
}

...

for field in validateableFields {
    if !field.isValid() {
        print("A field isn't valid")
    }
}

К сожалению, есть несколько ограничений, препятствующих этому:

  1. IBOutlets должен ссылаться на классы, которые наследуются от NSObject, возможно, для включения архивирования / декодирования, поэтому вы не можете использовать только протокол для типа IBOutlet

  2. В Swift нет способа объявить тип переменной как комбинацию конкретного соответствия типа и протокола, как вы делаете в своем примере.

  3. В Objective-C вы можете объявить конкретное соответствие протокола типа +, но протоколы IBOutlets все равно игнорируют протоколы, возможно потому, что соответствие протокола проверяется во время выполнения, и Xcode / Interface Builder не обязательно знает, будет ли объект в конечном итоге соответствовать протоколу.

Другие вопросы по тегам