How to get the parameter name in the enum?

Viewed 304

My code is like this:

enum API {
    case login(phone:String, password:String, deviceID:String)
}

extension API:TargetType {
    var task: Task {
        switch self {
        case let .login(phone, password, deviceID):
            ///How to get the parameter name here?
            ///For example:"phone", "password", "deviceID"
            ///Can this be generated automatically?
            let parameters = 
                ["phone":phone,
                 "password:":password,
                 "deviceID":deviceID]
            return .requestParameters(parameters, encoding: JSONEncoding.default);
        }
    }
}

How to get the parameter name in Switch case? For example:"phone", "password", "deviceID" Can this be generated automatically?

How to avoid writing "phone" and the other dictionary keys literally, and make the compiler generate them from the associated value labels.

Maybe after the completion is like this

func parameters(_ api:API) -> [String, Any] {

}

switch self {
case .login:
    return .requestParameters(parameters(self), encoding: JSONEncoding.default);
}

It seems that it is impossible to complete temporarily.

Who is the hero?

4 Answers

You can assign all associated values of the enum case to a single variable and then access the separate values using their labels.

enum API {
    case login(phone:String, password:String, deviceID:String)
}

extension API:TargetType {
    var task: Task {
        switch self {
        case let .token(params)
            let parameters = 
                ["phone":params.phone,
                 "password:":params.password,
                 "deviceID":params.deviceID]
            return .requestParameters(parameters, encoding: JSONEncoding.default);
        }
    }
}

Btw shouldn't that .token be .login? There's no .token case in your API enum defined.

If you want to generate the Dictionary keys to match the String representation of the associated value labels, that cannot be done automatically, but as a workaround, you can define another enum with a String raw value and use that for the Dictionary keys.

enum API {
    case login(phone:String, password:String, deviceID:String)

    enum ParameterNames: String {
        case phone, password, deviceID
    }
}

extension API:TargetType {
    var task: Task {
        switch self {
        case let .token(params)
            let parameters = 
                ["\(API.ParameterNames.phone)" : params.phone,
                 "\(API.ParameterNames.phone)" : params.password,
                 "\(API.ParameterNames.deviceID)" : params.deviceID]
            return .requestParameters(parameters, encoding: JSONEncoding.default);
        }
    }
}

There is code where you could get the label plus all the value(s) of an enum.

public extension Enum {
    public var associated: (label: String, value: Any?, values: Dictionary<String,Any>?) {
        get {
            let mirror = Mirror(reflecting: self)
            if mirror.displayStyle == .enum {
                if let associated = mirror.children.first {
                    let values = Mirror(reflecting: associated.value).children
                    var dict = Dictionary<String,Any>()
                    for i in values {
                        dict[i.label ?? ""] = i.value
                    }
                    return (associated.label!, associated.value, dict)
                }
                print("WARNING: Enum option of \(self) does not have an associated value")
                return ("\(self)", nil, nil)
            }
            print("WARNING: You can only extend an enum with the EnumExtension")
            return ("\(self)", nil, nil)
        }
    }
}

You will then be able to get the .associated.label and .associated.value of your enum. In your case your .value will be a tupple. Then you would need to use the .associated.values. Unfortunately you won't get the field names for these values. Because it's a tupple you will get field names like .0, .1 and .2. As far as I know there is no way to get the actual field names.

So in your case your code will be something like this:

enum API {
    case login(phone:String, password:String, deviceID:String)
}

extension API:TargetType {
    var task: Task {
        return .requestParameters(self.associated.values, encoding: JSONEncoding.default);
    }
}

But then you still need some functionality for going from the self.associated.values where the keys are .0, .1 and .2 to the names you like. I think the only option is for you to do this mapping yourself. You could extend your enum with a function for that.

If you want to see some more enum helpers, then have a look at Stuff/Enum

Your switch should look like this:

switch self {
        case .login(let phone, let password, let deviceID)
            let parameters = 
                ["phone":phone,
                 "password:":password,
                 "deviceID":deviceID]
            return .requestParameters(parameters, encoding: JSONEncoding.default);
        }

Swift automatically generates the declared variables for you

You can take a look about Reflection in Swift And you can make it automatic generate parameter like this:

class ParameterAble {
    func getParameters() -> [String: Any] {
        var param = [String: Any]()

        let childen = Mirror(reflecting: self).children
        for item in childen {
            guard let key = item.label else {
                continue
            }
           param[key] = item.value
        }
        return param
    }
}

class LoginData: ParameterAble {
    var phone: String
    var password: String
    var deviceID: String

    init(phone: String, password: String, deviceID: String) {
        self.phone = phone
        self.password = password
        self.deviceID = deviceID
    }
}

enum API {
    case login(data: LoginData)
}

extension API {
    var task: [String: Any] {
        switch self {
        case let .login(data):

            return data.getParameters()
        }
    }
}
let loginData = LoginData(phone: "fooPhone", password: "fooPass", deviceID: 
"fooId")
let login = API.login(data: loginData)
print(login.task)

This is output: ["phone": "fooPhone", "deviceID": "fooId", "password": "fooPass"]

You can try it in Playground

Related