Универсальный обработчик завершения Swift

Я пытаюсь понять, как создать универсальный обработчик завершения. Ниже приведен пример, иллюстрирующий пример «внутреннего» универсального обработчика завершения и того же универсального обработчика завершения, который я хотел бы создать, если бы мог сделать это во «внешней» форме. Проблема в том, что я не знаю, как написать эквивалент internalCompletion<T: MyEnum>... в обработчике завершения. Я написал в функции externalCompletion то, что, по моему мнению, она могла бы выглядеть: что-то вроде func externalCompletion(_ completer<T: MyEnum>: ((T) -> Void) where T: Hashable)), но это, очевидно, неверно. Возможно ли то, что я пытаюсь сделать? Я предполагаю, что Swift не позволит обработчику завершения оставаться универсальным, всегда требуя приведения типа на уровне функции, что противоречит цели в моем примере (т.е. func externalCompletetion<T: MyEnum>(_ completer: ((T) -> Void)) where T: Hashable, проблема в том, что мне придется выбирать между EnumA, EnumB и EnumC, не имея возможности запустить завершающий модуль на всех трех.)

typealias MyEnumKeyedData<T: MyEnum> = [T: String] where T: Hashable

// MARK : - MyEnum Protocol

protocol MyEnum {
  static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable
  static var all: [MyEnum] { get }
}
extension MyEnum {
  static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable { return Self.all as! [T] }
}

// MARK : - My enums

enum EnumA: MyEnum {
  case first
  static var all: [MyEnum] { return [EnumA.first]}
}
enum EnumB: MyEnum {
  case first
  static var all: [MyEnum] { return [EnumB.first]}
}
enum EnumC: MyEnum {
  case first
  static var all: [MyEnum] { return [EnumC.first]}
}

// MARK : - MyEnum Data Iterator

class MyDataEnumIterator {
  var dataA: MyEnumKeyedData<EnumA> = [:]
  var dataB: MyEnumKeyedData<EnumB> = [:]
  var dataC: MyEnumKeyedData<EnumC> = [:]

  func updateData<T: MyEnum>(_ key: T, _ value: String) where T: Hashable {
    switch T.self {
    case is EnumA.Type: dataA[key as! EnumA] = value
    case is EnumB.Type: dataB[key as! EnumB] = value
    case is EnumC.Type: dataC[key as! EnumC] = value
    default: fatalError("Enum does not exist")
    }
  }

  // Internal (This works)

  func internalEnumIterator() {
    for key in EnumA.all(EnumA.self) { internalCompletion(key) }
    for key in EnumB.all(EnumB.self) { internalCompletion(key) }
    for key in EnumC.all(EnumC.self) { internalCompletion(key) }
  }

  func internalCompletion<T: MyEnum>(_ key: T) where T: Hashable {
    let value = "\(key)"
    updateData(key, value)
  }

  // External (This obviously doesn't, just sketching the idea)

  func externalEnumIterator(_ completer<T: MyEnum>: ((T) -> Void) where T: Hashable)  {
    for key in EnumA.all(EnumA.self) { completer(key) }
    for key in EnumB.all(EnumB.self) { completer(key) }
    for key in EnumC.all(EnumC.self) { completer(key) }
  }
}

// MARK : - Test cases (internal works, external does not, just sketching example)

let iterator = MyDataEnumIterator()
iterator.externalEnumIterator({ <T: MyEnum> (T) where T: Hashable in
  let value = "\(key)"
  iterator.updateData(key, value)
})
iterator.internalEnumIterator()

person Ethan Kay    schedule 13.11.2016    source источник
comment
Не могли бы вы сделать минимальный пример, чтобы упростить ситуацию?   -  person Alexander    schedule 13.11.2016
comment
Это невероятно сложно. Ни один из этих дженериков не является необходимым.   -  person Alexander    schedule 13.11.2016
comment
Я знаю. Я просто играл с дженериками. Ваше решение явно лучше на практике   -  person Ethan Kay    schedule 13.11.2016
comment
Чтобы добавить к моему заявлению: ни один из этих дженериков на самом деле даже не используется. Я не могу найти ни одного места, где компилятор создает более одной версии любой из этих универсальных функций.   -  person Alexander    schedule 13.11.2016
comment
Однако вам нужно будет это сделать для чего-то вроде func set<T: MyEnum>(_ data: [T: String?]) where T: Hashable { data.forEach{self[$0] = $1} }. Вот тут и началась общая игра, потом заинтересовал вопрос закрытия.   -  person Ethan Kay    schedule 14.11.2016
comment
Да, это может. Даже тогда я не уверен. Однако не гарантирует никакого другого сумасшествия   -  person Alexander    schedule 14.11.2016
comment
Справедливо. Я усыновил твое. Спасибо за помощь!   -  person Ethan Kay    schedule 14.11.2016


Ответы (1)


Вот рабочая версия кода с минимальными изменениями, необходимыми для его работы.

typealias MyEnumKeyedData<T: MyEnum> = [T: String] where T: Hashable

// MARK : - MyEnum Protocol

protocol MyEnum {
    static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable
    static var all: [MyEnum] { get }
}
extension MyEnum {
    static func all<T: MyEnum>(_:T.Type) -> [T] where T: Hashable { return Self.all as! [T] }
}

// MARK : - My enums

enum EnumA: MyEnum {
    case first
    static var all: [MyEnum] { return [EnumA.first]}
}
enum EnumB: MyEnum {
    case first
    static var all: [MyEnum] { return [EnumB.first]}
}
enum EnumC: MyEnum {
    case first
    static var all: [MyEnum] { return [EnumC.first]}
}

// MARK : - MyEnum Data Iterator

class MyDataEnumIterator {
    var dataA: MyEnumKeyedData<EnumA> = [:]
    var dataB: MyEnumKeyedData<EnumB> = [:]
    var dataC: MyEnumKeyedData<EnumC> = [:]

    func updateData(_ key: MyEnum, _ value: String) {
        switch key {
        case let key as EnumA: dataA[key] = value
        case let key as EnumB: dataB[key] = value
        case let key as EnumC: dataC[key] = value
        default: fatalError("Enum does not exist")
        }
    }

    // Internal (This works)

    func internalEnumIterator() {
        for key in EnumA.all(EnumA.self) { internalCompletion(key) }
        for key in EnumB.all(EnumB.self) { internalCompletion(key) }
        for key in EnumC.all(EnumC.self) { internalCompletion(key) }
    }

    func internalCompletion<T: MyEnum>(_ key: T) where T: Hashable {
        let value = "\(key)"
        updateData(key, value)
    }

    func EnumIterator(_ compeltitionHandler: (MyEnum) -> Void) {
        for key in EnumA.all(EnumA.self) { compeltitionHandler(key as MyEnum) }
        for key in EnumB.all(EnumB.self) { compeltitionHandler(key as MyEnum) }
        for key in EnumC.all(EnumC.self) { compeltitionHandler(key as MyEnum) }
    }
}


let iterator = MyDataEnumIterator()

iterator.EnumIterator{ key in
    let value = "\(key)"
    iterator.updateData(key, value)
}
iterator.internalEnumIterator()

Вот вменяемая версия кода, которая убирает всю ерунду и добавляет синтаксис нижнего индекса:

// MARK : - MyEnum Protocol

protocol MyEnum {
    static func all() -> [MyEnum]
}

// MARK : - My enums

enum EnumA: MyEnum {
    case first
    static func all() -> [MyEnum] { return [EnumA.first] }
}
enum EnumB: MyEnum {
    case first
    static func all() -> [MyEnum] { return [EnumB.first] }
}
enum EnumC: MyEnum {
    case first
    static func all() -> [MyEnum] { return [EnumC.first] }
}

// MARK : - MyEnum Data Iterator

class MyDataEnumIterator {
    var dataA = [EnumA: String]()
    var dataB = [EnumB: String]()
    var dataC = [EnumC: String]()


    subscript(key: MyEnum) -> String? {
        get {
            switch key {
            case let key as EnumA: return dataA[key]
            case let key as EnumB: return dataB[key]
            case let key as EnumC: return dataC[key]
            default: fatalError("Enum does not exist")
            }
        }
        set {
            switch key {
            case let key as EnumA: dataA[key] = newValue
            case let key as EnumB: dataB[key] = newValue
            case let key as EnumC: dataC[key] = newValue
            default: fatalError("Enum does not exist")
            }
        }
    }

    func EnumIterator(_ body: (MyEnum) -> Void) {
        EnumA.all().forEach(body);
        EnumB.all().forEach(body);
        EnumC.all().forEach(body);
    }
}


let iterator = MyDataEnumIterator()
iterator.EnumIterator{
    iterator[$0] = "\($0)"
}
person Alexander    schedule 13.11.2016