How can you reflect the design of the Codable / CodableKeys protocols?

I am trying to achieve something similar to how Swift uses a set of protocols CodableKeysfor the enumeration defined in the class that implements Codable. In my case, the class CommandHandlerand the enumeration are CommandIds, and it does not require a compiler from code-gen, since the enumeration will always be explicitly specified.

Here is a simplified version of what I need ...

protocol CommandId{}
protocol CommandHandler{
    associatedtype CommandIds : CommandId, RawRepresentable
}

class HandlerA : CommandHandler{
    enum CommandIds : String, CommandId{
        case commandA1
        case commandA2
    }
}

class HandlerB : CommandHandler{
    enum CommandIds : String, CommandId{
        case commandB1
        case commandB2
        case commandB3
    }
}

func processHandler<T:CommandHandler>(_ handler:T){
    // Logic to iterate over CommandIds. <-- This is where I get stumped
}

let handlerA = HandlerA()
processHandler(handlerA)

I am struggling with the code inside processHandlerhere because I'm not sure how to reach the enum values ​​from the handler instance.

So what am I missing? What will be the code to get the values ​​of a linked enumeration?

+4
source share
4 answers

, , , , Swift. , , .

Swift 4...

-, , . CodableKeys Codable.

protocol CommandId : EnumerableEnum, RawRepresentable {}

protocol CommandHandler{
    associatedtype CommandIds : CommandId
}

, "case" . EnumerableEnum, 'values'.

CommandId , , EnumerableEnum . , CommandId , .

public protocol EnumerableEnum : Hashable {
    static var values: [Self] { get }
}

public extension EnumerableEnum {

    public static var values: [Self] {

        let valuesSequence = AnySequence { () -> AnyIterator<Self> in

            var caseIndex = 0

            return AnyIterator {
                let currentCase: Self = withUnsafePointer(to: &caseIndex){
                    $0.withMemoryRebound(to: self, capacity: 1){
                        $0.pointee
                    }
                }
                guard currentCase.hashValue == caseIndex else {
                    return nil
                }
                caseIndex += 1
                return currentCase
            }
        }

        return Array(valuesSequence)
    }
}

, CommandHandler/CommandId

class HandlerA : CommandHandler{

    enum CommandIds : Int, CommandId{
        case commandA1
        case commandA2
    }
}

class HandlerB : CommandHandler{
    enum CommandIds : String, CommandId{
        case commandB1 = "Command B1"
        case commandB2
        case commandB3 = "Yet another command"
    }
}

, CommandHandler

func enumerateCommandIds<T:CommandHandler>(_ commandHandlerType:T.Type){

    for value in commandHandlerType.CommandIds.values{
        let caseName     = String(describing:value)
        let caseRawValue = value.rawValue

        print("\(caseName) = '\(caseRawValue)'")
    }
}

, ,

enumerateCommandIds(HandlerA.self)
// Outputs
//     commandA1 = '0'
//     commandA2 = '1'

enumerateCommandIds(HandlerB.self)
// Outputs
//     commandB1 = 'Command B1'
//     commandB2 = 'commandB2'
//     commandB3 = 'Yet another command'

, , , ! !

+1

, , associatedtype , - :

enum Commands:String {
    case default_command = ""
}

protocol CommandDef {
    associatedtype Commands
}

class MyClassA : CommandDef {
    enum Commands : String {
        case commandA1
        case commandA2 = "Explicit A2"
    }
}

class MyClassB : CommandDef {
    enum Commands : String {
        case commandB1
        case commandB2 = "Explicit B2"
        case commandB3
    }
}
0

swift. , . .

, ?

enum Commands:String {
    case compliaceType = ""
}

protocol CommandDef {
    associatedtype Commands
}

class MyClassA : CommandDef {

    enum Commands : String {
        case commandA1 = "hi"
        case commandA2 = "Explicit A2"
    }


}

class MyClassB : CommandDef {
    enum Commands : String {
        case commandB2 = "Explicit B2"
    }

}

print(MyClassA.Commands.commandA1)
0

Swift, Codable, Codables , . , , CodingKeys , , Codable, .

, Swift structs ( "memberwise initializer" ), . Swift, , / , , .

, Sourcery (https://github.com/krzysztofzablocki/Sourcery), . Sourcery script , Command , CommandHandler.

, , , Codable Swift, . Swift, .. , , script, .



, CommandIds, , , CommandId :

protocol CommandId {
    static var all: [Self] { get }
}

:

class HandlerA : CommandHandler {
    enum CommandIds : String, CommandId {
        case commandA1
        case commandA2

        static var all: [CommandIds] { return [.commandA1, .commandA2] }
    }
}

:

func processHandler<T:CommandHandler>(_ handler:T){
    T.CommandIds.all.forEach { // Do something with each command case }
}

, , Codable Swift - . Codable-matchinging init(from decoder: Decoder) , , ,

// This is all the code a developer had to write
struct Example: Codable {
  let name: String
  let number: Int
}

// This is all source code generated by the compiler using compiler reflection into the type properties, including their names and types
extension Example {
  enum CodingKeys: String, CodingKey {
    case name, number
  }

  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    name = try values.decode(String.self, forKey: .name)
    number = try values.decode(Int.self, forKey: .number)
  }
}

Swift - , , (), .

, , Swift . , Core Swift Apple , , WWDC.

, , Swift pull, Swift (, Codable Equatable Hashable), Sourcery, Swift.

0

Source: https://habr.com/ru/post/1690653/


All Articles