Enum Types: get raw value from key - ios

How to get the raw value from a enum passing the key value? Must work for any enum types working like an extension of enum types.
Any mirror reflection, mappable or RawRepresentable solutions are welcome.
I would like something like this:
enum Test : String {
case One = "1"
}
Test.rawValueFromKey(keyValue: "One") // Must return "1"
// I don't want the solution below, I must get the rawValue through the key name as String.
Test.One.rawValue
I do need to get the rawValue passing the name of the key as a String. Otherwise, I will need to make a switch or many if/else conditions. I have a big Enum and I don't want to check the string passed in a switch for example and pass Test.One.rawValue. I would like to get the rawValue directly through the key as String, just like in a dictionary.
I also don't want the code below, because I have a Enum of 40 items and I think it is ugly to make a switch of 40 items.
switch keyValue {
case "One":
return Test.One.rawValue
break
}
I want something like this:
func rawValueFromKey (keyValue: String) -> Test {
// code here to get the rawValue through the keyValue as String
// return the proper Test Enum
}
I tried some possible solutions using Mirror reflection and enum iterations to find the rawValue through the keyValue but didn't work.
Please give the solution in both Swift 2 and 3.
Thanks

As far as I know, you can't reflect on types so I think you will be able to achieve this only with CustomReflectable or maybe using localized strings for mapping the values/keys. Invariably you'll have to map somehow.

Why not something like this? Sure you are just comparing Strings so it's potentially unsafe (if your enum would conform to CustomStringConvertible, etc), but you can't really avoid that.
I think CaseIterable is available only from Swift 4 though...
protocol KeyRepresentable: CaseIterable {
}
extension KeyRepresentable {
static func fromKey(key: String) -> Self? {
return Self
.allCases
.first { "\($0)" == key }
}
}
Usage:
enum Option: Int, KeyRepresentable {
case a = 1, b = 2
}
print(Option.fromKey(key: "a")?.rawValue)

Related

Trying to parse a string value from an API a JSON object into a struct as an Int

I am new to Swift and I have data I'd like to call from an API through JSON. For some strange reason some of the names are written with whitespaces making it difficult for me to declare variables that'll show the integer type value they hold. Here is a screenshot . I am trying to parse the Int value attached to the type "Anti-social behaviour" but don't seem to know how to declare it appropriately.
You can define your own CodingKeys inside the file. Note that the name should be CodingKeys.
struct Types: Codable {
let buglary: Int
let shoplifting: Int
let drugs: Int
let robbery: Int
let antiSocialBehavior: Int
// Other properties
enum CodingKeys: String, CodingKey {
case buglary = "Buglary"
case shoplifting = "Shoplifting"
case drugs = "Drugs"
case robbery = "Robbery"
case antiSocialBehavior = "Anti-social behavior"
// other coding keys
}
}
Also, note that properties in Swift are always camelcased and not capitalized. So, I also changed the name of your properties. Check the enum inside the struct which actually defines the mapping between property name and their encoding / decoding keys.

Can we create variables with '-' like JS in swift?

I have a service that returns this JSON structure "actual-price": {,
I want to know if it is possible to create a variable like JS with Codable on swift.
PS: I can't change the JSON since the service is not mine
You will need to use a CodingKeys enumeration to map the jSON properties to valid Swift properties. Note that once you introduce a CodingKeys enumeration it must contain all of the properties you wish to map, not just the properties where you want to change the name.
Something like
struct MyStruct: Codable {
var actualPrice: Double
var quantity: Int
enum CodingKeys: String, CodingKey {
case actualPrice = "actual-price"
case quantity = "quantity"
}
}

Enum: Count and list all cases (from outside!)

I've got this enum (Xcode 10/Swift 5) in its own file ("MyEnum.swift"):
enum MyEnum: Int, CaseIterable {
case A = 0
case B = 1
case C = 2
case D = 3
case E = 4
//Each case needs its own number and description!
var description: String {
switch self {
case .A:
return "abc"
case .B:
return "bcd"
case .C:
return "cde"
case .D:
return "def"
case .E:
return "efg"
}
}
}
... and want to add the descriptions to a PickerView. I know how to set up the functions for the view but I'm now stuck on counting the enum cases and adding the descriptions.
According to the documentation and different questions, adding CaseIterable is supposed to make it possible to call
MyEnum.allCases.count
... but I can only access allCases from within the enum file. From outside (so from my ViewController class) I can only call MyEnum.AllCases, which hasn't got count and, to be honest, I'm not even sure what AllCases returns exactly (it's not a normal array you can use count on).
Adding this code (source) to the enum file at least makes it possible to count the cases:
static let count: Int = {
var max: Int = 0
while MyEnum(rawValue: max) != .none { max += 1 }
return max
}()
...but isn't there supposed to be an easier way to do this with Swift 4.2+?
How do I get a list of the case descriptions to fill my PickerView with (so "abc", "bcd",... - preferably without hardcoding it)?
I tried in 1 file:
enum TestEnum: CaseIterable {
case test1
case test2
}
in another file of another class I wrote:
let count = TestEnum.allCases.count
And it works, but I noticed that when I was typing "allCases" wasn't shown
I manually needed to write it
allCases is declared public, so you should be able to access it from another file:
/// A collection of all values of this type.
public static var allCases: Self.AllCases { get }
like this:
let descriptions = MyEnum.allCases.map { $0.description }
Try cleaning the project and rebuild.
Also, if something is not in Xcode's autocomplete list, it doesn't mean that you can't access that thing. Xcode's complete list has a ton of bugs according to my own experience.
For the sake of completeness and in case allCases doesn't work at all (even after a restart) add this to the enum file:
static let allValues = [MyEnum.A, MyEnum.B, MyEnum.C, MyEnum.D, MyEnum.E]
It's hardcoded but at least it gives you access to everything:
count: MyEnum.allValues.count
description (e.g. "abc"): MyEnum.allValues[0].description
rawValue (e.g. 0):MyEnum.allValues[0].rawValue
The same issue happened to me. What ended up working is restarting xcode.

Swift 4: Array for all types of Ints and Floating point numbers

I have a program that working with all kind of Ints and Floating pointer value types
Is there is a way to create array that will holds all kind of those values in it?
I have tried to do this via protocol and extend Int and Double with them but no luck because of protocol equability limitation in Swift(or something like this)
protocol ArrayStructValue: Comparable,Equatable { }
extension Double: ArrayStructValue{}
extension Int:ArrayStructValue {}
If you want two different types in one array, why don't you use a union, e.g. using an enum:
enum MyNumber {
case integer(Int)
case double(Double)
}
let numbers: [MyNumber] = [.integer(10), .double(2.0)]
However, in most situations it would be probably better to just convert all Int into Double and just have a [Double] array.
As already mentioned by #vadian both types Int and Double already conforms to Strideable which inherits from Comparable which inherits from Equatable so there is no need to constrain your protocol to them. So all you need is to make Int and `Double inherit from your protocol:
protocol ArrayStructValue { }
extension Double: ArrayStructValue { }
extension Int: ArrayStructValue { }
var f: [ArrayStructValue] = []
f.append(1)
f.append(2.0)
type(of: f[0]) // Int.Type
type(of: f[1]) // Double.Type

Swift: get self.description in self.description

I'm trying to modify description output for ErrorProtocol. But this snippet, gives me infinite loop.
enum GeneralError: ErrorProtocol, CustomStringConvertible {
case NoMemory
var description: String {
return String(self).lowercased()
}
}
Changing to self.dynamic type gives me a "generalerror".
Is there any way how to get just "nomemory"? Without using conditionals.
Swift: Convert enum value to String? isn't an exact match for your question, but it really covers the guts of it. In your case, you'll want to change it up a little bit:
enum Error : String, ErrorProtocol, CustomStringConvertible {
case NoMemory
var description : String {
return self.rawValue.lowercaseString
}
}
This works because the default rawValue for enum cases where the associated type is String is the name of the case.
When you call String(self) in GeneralError.description, you're making the String initializer use your object (self) as a CustomStringConvertible to make a new string. Internally, it does this by calling description on it, hence the recursion
What exactly are you trying to achieve?

Resources