Расширение свойства Protocol для обеспечения реализации по умолчанию в Swift - PullRequest
0 голосов
/ 22 сентября 2018

У меня есть протокол, который я использую для нескольких перечислений, который включает в себя CaseIterable Swift 4.2

    public protocol CycleValue: CaseIterable {

  /// Computed property that returns the next value of the property.
  var nextValue:Self { get }
}

Один из моих вариантов использования для CycleValue - свойство Theme:

@objc public enum AppThemeAttributes: CycleValue  {

  case classic, darkMode // etc.

  public var nextValue: AppThemeAttributes {
    guard self != AppThemeAttributes.allCases.last else {
      return AppThemeAttributes.allCases.first!
    }

    return AppThemeAttributes(rawValue: self.rawValue + 1)!
  }
}

У меня есть другие случаи использования;например, типы кнопок.CaseIterable делает реализацию nextValue легкой, но одинаковой для всех типов CycleValue.

Я хотел бы реализовать расширение CycleValue, которое обеспечивает реализацию по умолчанию для свойства nextValue и избегает дублирования кода (т. Е. DRY!).

Я боролся с PAT (связанными с протоколом типами).Кажется, не получается получить правильный синтаксис.

Это должно быть возможно, верно?Как я могу предоставить реализацию по умолчанию для nextValue, чтобы избежать дублирования од?

1 Ответ

0 голосов
/ 22 сентября 2018

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

public protocol CycleValue: CaseIterable, Equatable {
    var nextValue: Self { get }
}

public extension CycleValue {
    var nextValue: Self {
        var idx = Self.allCases.index(of: self)!
        Self.allCases.formIndex(after: &idx)
        return idx == Self.allCases.endIndex ? Self.allCases.first! : Self.allCases[idx]
    }
}

(обратите внимание, что оба принудительных развертывания развернутыбезопасны!) Пример:

public enum AppThemeAttributes: CycleValue  {
    case classic, darkMode // etc.
}

let a = AppThemeAttributes.classic
print(a) // classic
let b = a.nextValue
print(b) // darkMode
let c = b.nextValue
print(c) // classic

Протокол должен соответствовать Equatable, чтобы компилироваться, но это не является реальным ограничением: протокол CaseIterable не может быть связанзначения, так что компилятор всегда может синтезировать соответствие Equatable.

...