Swift: установка необязательного свойства протокола

Как установить необязательное свойство протокола? Например, UITextInputTraits имеет ряд дополнительных свойств чтения/записи. Когда я пытаюсь выполнить следующее, я получаю ошибку компиляции (не могу назначить "keyboardType" в "textInputTraits" ):

func initializeTextInputTraits(textInputTraits: UITextInputTraits) {
  textInputTraits.keyboardType = .Default
}

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

textInputTraits.keyboardType? = .Default

Протокол выглядит так:

protocol UITextInputTraits : NSObjectProtocol {
  optional var keyboardType: UIKeyboardType { get set }
}

Ответы

Ответ 1

Это невозможно в Swift (еще?). Ссылка из потока ADF:

Дополнительные требования к свойствам и необязательные требования к методам, возвращающие значение, будут всегда возвращать необязательное значение соответствующего типа при их доступе или вызове, чтобы отразить тот факт, что необязательное требование возможно, не были реализованы.

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

Ответ 2

Я бы посоветовал сделать расширение, возвращающее ваше значение по умолчанию для keyboardType, и заставить установщик ничего не делать.

extension UITextInputTraits {
   var keyboardType: UIKeyboardType { 
      get { return .default }
      set { /* do nothing */ } 
   }
}

Таким образом, вы можете удалить optional из вашей декларации свойства.

protocol UITextInputTraits : NSObjectProtocol {
  var keyboardType: UIKeyboardType { get set }
}

Или, если вы предпочитаете, вы можете сделать его необязательным типом возврата, var keyboardType: UIKeyboardType? и верните nil вместо .Default в вашем расширении.

Таким образом, вы можете проверить myObject.keyboardType == nil чтобы увидеть, реализовано ли ваше свойство.

Ответ 3

Обходным путем является создание другого протокола, который соответствует первому, но который объявляет это необязательное свойство обязательным. Затем, чтобы попытаться передать объект в качестве этого нового протокола, и если приведение выполнено успешно, мы можем получить доступ к этому свойству напрямую.

protocol UITextInputTraitsWithKeyboardType : UITextInputTraits {
  // redeclare the keyboardType property of the parent protocol, but this time as mandatory
  var keyboardType: UIKeyboardType { get set }
}

func initializeTextInputTraits(textInputTraits: UITextInputTraits) {
  // We try to cast ('as?') to the UITextInputTraitsWithKeyboardType protocol
  if let traitsWithKBType = textInputTraits as? UITextInputTraitsWithKeyboardType {
    // if the cast succeeded, that means that the keyboardType property is implemented
    // so we can access it on the casted variable
    traitsWithKBType.keyboardType = .Default
  } 
  // (and if the cast failed, this means that the property is not implemented)
}

Я также продемонстрировал, что внизу этого SO-ответа, если вы хотите еще один пример идеи.

Ответ 4

Ниже приведено то, что я придумал для совместимости Swift 2.2:

@objc protocol ComponentViewProtocol: class {
    optional var componentModel: ComponentModelProtocol? { get set }
    optional var componentModels: [String:ComponentModelProtocol]? { get set }
}

class ComponentView: NSObject, ComponentViewProtocol {
    var componentModel: ComponentModelProtocol?
    ...
    ..
    .
}

class SomeCodeThatUsesThisStuff {

    func aMethod() {
        if var componentModel = componentView.componentModel {
            componentModel = .... (this succeeds - you can make assignment)
        } else if var componentModels = componentView.componentModels {
            componentModels = .... (this failed - no assignment possible)
        }
    }
}

В сущности, вам нужно выполнить "if/let binding" для необязательного свойства, чтобы убедиться, что оно действительно реализовано? Если он равен нулю, инкапсулирующий объект не реализовал его, но в противном случае вы можете теперь назначать ему, и компилятор больше не будет жаловаться.