iOS Swift, Enum CaseIterable extension - ios

Im trying to write an extension for an enum that is CaseIterable so that i can get an array of the raw values instead of the cases, Im not entirely sure how to do that though
extension CaseIterable {
static var allValues: [String] {
get {
return allCases.map({ option -> String in
return option.rawValue
})
}
}
}
i need to add a where clause somehow, if i dont have a where clause i get an error saying 'map' produces '[T]', not the expected contextual result type '[String]'
Anyone know if there is a good way of going about this?
my enums i want this function to work on look a bit like this
enum TypeOptions: String, CaseIterable {
case All = "all"
case Article = "article"
case Show = "show"
}

Not all enumeration types have an associated RawValue, and if they have then it is not necessarily a String.
Therefore you need to restrict the extension to enumeration types which are RawRepresentable, and define the return value as an array of RawValue:
extension CaseIterable where Self: RawRepresentable {
static var allValues: [RawValue] {
return allCases.map { $0.rawValue }
}
}
Examples:
enum TypeOptions: String, CaseIterable {
case all
case article
case show
case unknown = "?"
}
print(TypeOptions.allValues) // ["all", "article", "show", "?" ]
enum IntOptions: Int, CaseIterable {
case a = 1
case b = 4
}
print(IntOptions.allValues) // [1, 4]
enum Foo: CaseIterable {
case a
case b
}
// This does not compile:
print(Foo.allValues) // error: Type 'Foo' does not conform to protocol 'RawRepresentable'

Related

Struct Value of type 'ViewController' has no member 'Continents'?

Struct can find in ViewController.
The error show Value of type 'ViewController' has no member 'Continents'?
this massage.
struct Continents {
var continents_name_list : String
}
This is a drop-down menu code:
func getDataToDropDown(cell: UITableViewCell, indexPos: Int, makeDropDownIdentifier: String) {
if makeDropDownIdentifier == "DROP_DOWN_NEW"{
let customCell = cell as! DropDownTableViewCell
customCell.countryNameLabel.text = self.Continents[indexPos]
print("test")
}
}
func numberOfRows(makeDropDownIdentifier: String) -> Int {
return self.Continents.count
}
func selectItemInDropDown(indexPos: Int, makeDropDownIdentifier: String) {
self.continente_text_label.text = "\(self.Continents[indexPos])"
self.dropDown.hideDropDown()
}
Frist:
Last:
Continents Is a type (like Int, String ecc...).
This means you can create arrays, variables etc. of type Continents.
Calling Continents.count is like calling Int.count which means nothing.
This is why it won't work. If you want to get the count of your array, you'll have to write the name of your array.count. In your case continents_list.count.
Anyway there are a few semantical errors in your code so I'll give you a few suggestions.
I assume you want the struct Continents to contain an array of strings. At the moment it will only receive a string. Try this instead:
struct Continents {
var continents_name_list : [String]
}
Furthermore, the continents_list array you created is of type String, you should declare it like this if you want it to be of type Continents:
var continents_list = Continents(continents_name_list: ["c1", "c2", "cn"])
My last advice would be to use an enum instead of a struct to store your continents.
enum Continents {
case Europe
case America
case Africa
case Antartica
case Asia
case Oceania
}

Enum Types: get raw value from key

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)

Swift - how to declare variable/functon of/with enums of different type?

I need to declare variable which will store array of enums of different type, eg.:
var enums = [EnumTypeA.Option1, EnumTypeB.Option2]
Compiler states:
Type of expression is ambiguous without more context
This will be necessary to pass any enum or other object as a function parameter. However I discovered that I can pass generics to achieve this, eg.:
func f1<T>(enum: T)
but having protocol with optional methods (prefixed with #objc) it is impossible.
You can use a protocol...
protocol MyEnums {}
enum MyEnum1: MyEnums {
case first, second
}
enum MyEnum2: MyEnums {
case red, green
}
let myArray: [MyEnums] = [MyEnum1.first, MyEnum2.green]
func myFunc(myEnum: MyEnums) {
print(myEnum)
}
for value in myArray {
myFunc(myEnum: value)
}
This was fun. Instead of generics, I just went with Any, since that is the base of everything.
enum TypeA {
case Option1
case Option2
}
enum TypeB {
case Option1
case Option2
}
func acceptDifferentEnums(value: Any) {
switch value {
case let typeA as TypeA:
print("This is TypeA")
case let typeB as TypeB:
print("This is typeB")
default:
print("This is something else")
}
}
acceptDifferentEnums(TypeA.Option1) // This is TypeA
acceptDifferentEnums(TypeB.Option2) // This is TypeB
acceptDifferentEnums("Foo") // This is something else
You then use the switch statement to downcast the value property into your various enums, and process them accordingly.

Get enum from value

I have a question about enum in Swift.
I declared my enum like this:
enum FirstEnum : CustomStringConvertible {
case VALUE1
case VALUE2
case VALUE3
var description: String {
switch self {
case .VALUE1:
return "First value"
case .VALUE2:
return "Second value"
case .VALUE3:
return "Third value"
}
}
func getFromCode(value:String) -> FirstEnum? {
switch value {
case "v1":
return FirstEnum.VALUE1
case "v2":
return FirstEnum.VALUE2
case "v3" :
return FirstEnum.VALUE3
}
}
I need to get enum from a string (like a dictionary) so I expect this line should work:
let foo = FirstEnum.getFromCode("v1")
But XCode (7) expects a FirstEnum parameter for method getFromCode instead a String as declared in method definition, saying:
Cannot convert value of type "String" to expected argument type
"FirstEnum"
Why this?...what I'm doing wrong?
Use Failable Initializers for Enumerations
You can use a failable initializer to select an appropriate
enumeration member based on one or more parameters. The initializer
can then fail if the provided parameters do not match an appropriate
enumeration member.
The example below defines an enumeration called TemperatureUnit, with
three possible states (Kelvin, Celsius, and Fahrenheit). A failable
initializer is used to find an appropriate enumeration member for a
Character value representing a temperature symbol:
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
In Swift, you can derive your enum from String.
enum FirstEnum : String {
case VALUE1 = "First value"
case VALUE2 = "Second value"
case VALUE3 = "Third value"
static func getFromCode(value:String) -> FirstEnum? {
switch value {
case "v1":
return FirstEnum.VALUE1
case "v2":
return FirstEnum.VALUE2
case "v3" :
return FirstEnum.VALUE3
default:
return nil
}
}
}
Also, your getFromCode method should be static and needs a default clause.
But you don't need this method when you derive from String. To construct your enum value , you can use the rawValue like following:
let value = FirstEnum(rawValue: "First value")
You need a static func so that you can call it without an instance of the enum:
static func getFromCode(value:String) -> FirstEnum? {
switch value {
case "v1":
return FirstEnum.VALUE1
case "v2":
return FirstEnum.VALUE2
case "v3" :
return FirstEnum.VALUE3
default:
return nil
}
}
You were getting the error because there is a hidden parameter when calling a method that is the instance of the enum. Admittedly, the error message could be better.
Without static, you need an instance of the enum to call it. Note that this works with the function as you had it defined:
let foo = FirstEnum.VALUE3.getFromCode("v1")
Also, your switch needs to be exhaustive, which is why I added the default case.

How to adhere to protocol method with a Raw type in method argument?

protocol Measurement {
mutating func convert(#toUnit: String)
}
enum MassUnit : String {
case Milligram = "mg"
}
enum VolumeUnit : String {
case Milliliter = "ml"
}
struct Mass : Measurement {
mutating func convert(#toUnit: MassUnit)
// Build error: Does not adhere to 'Measurement'
}
struct Volume : Measurement {
mutating func convert(#toUnit: VolumeUnit)
// Build error: Does not adhere to 'Measurement'
}
func +<T: Measurement> (left:T, right:T) -> Measurement {
let newRightValue = right.convert(toUnit: left.unit)
return T(quantity: left.quantity + newRightValue.quantity , unit: left.unit)
}
How can I make Mass adhere correctly to Measurement? What change in the Measurement protocol is needed to get it to work with enum of type String?
Question updated with more information about why the convert method signature should say something about the argument given. The code is part of an open source Unit framework i'm building called Indus Valley
You probably confuse enum MassUnit : String with inheritance.
Contrary to class ChildClass : ParentClass which denotes that ChildClass inherits from ParentClass, enum MassUnit : String has a slightly different meaning, telling that the rawType of the enum is String, not that the enum inherits the String type.
So MassUnit is not of type String. You MassUnit's rawValues are of type String, but to access that you need to call the rawValue property of the enum to get that String equivalent.
Therefore, mutating func convert(#toUnit: String) and mutating func convert(#toUnit: MassType) are not compatible, as MassType is not a String itself. Only its rawValue is.
When dealing with your unit conversions, I advise not trying to use Strings to represent the units when converting the way you've got setup above. That's going to make your code complicated with checking the String can be converted to its respective enum every time you want to make a conversion. Also, what if you want to use the a MassUnit/VolumeUnit instead of a String?
I would recommend using the a setup similar to what I've outlined below. It references my previous answer - How to represent magnitude for mass in Swift?
(Note - I've excluded anything to do with the volume because it's basically the same as the implementation for the mass)
I'd make the units like so:
protocol UnitProtocol {
var magnitude: Int { get }
init?(rawValue: String)
}
// Taken from my previous answer.
enum MassUnit: String, UnitProtocol, Printable {
case Milligram = "mg"
case Gram = "g"
var magnitude: Int {
let mag: Int
switch self {
case .Milligram: mag = -3
case .Gram : mag = 0
}
return mag
}
var description: String {
return rawValue
}
}
// Not making this a method requirement of `UnitProtocol` means you've only got to
// write the code once, here, instead of in every enum that conforms to `UnitProtocol`.
func ordersOfMagnitudeFrom<T: UnitProtocol>(unit1: T, to unit2: T) -> Int {
return unit1.magnitude - unit2.magnitude
}
Then I'd make the masses/volumes like so:
protocol UnitConstruct {
typealias UnitType: UnitProtocol
var amount: Double { get }
var unit : UnitType { get }
init(amount: Double, unit: UnitType)
}
struct Mass : UnitConstruct {
let amount: Double
let unit : MassUnit
}
Now for the converting function! Using a global function means you don't need to rewrite the code for every type than conforms to UnitConstruct.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: T.UnitType) -> T {
let x = Double(ordersOfMagnitudeFrom(lhs.unit, to: unit))
return T(amount: lhs.amount * pow(10, x), unit: unit)
}
// This function is for converting to different units using a `String`,
// as asked in the OP.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: String) -> T? {
if let unit = T.UnitType(rawValue: unit) {
return convert(lhs, toUnits: unit)
}
return nil
}
You can then use the previous code like so:
let mass1 = Mass(amount: 1.0, unit: .Gram)
let mass2 = convert(mass1, toUnits: .Milligram) // 1000.0 mg
// Or, converting using Strings:
let right = convert(mass1, toUnits: "mg") // Optional(1000.0 mg)
let wrong = convert(mass1, toUnits: "NotAUnit") // nil

Resources