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

    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, чтобы избежать дублирования оды?

4
Phantom59 22 Сен 2018 в 21:59

1 ответ

Лучший ответ

Возможное решение - найти текущее значение в коллекции 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 соответствие.

6
Martin R 22 Сен 2018 в 19:30