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.
Related
I have to pass an array of an enum as a function parameter, most of the time I have to pass every single case of the enum. Is there a shorthand way to include all instead of having to pass [.NFL, .NBA, .NHL, .MLB, .NCAAM, .NCAAF] every time? Can I make and all property so that I can just pass .all that included all the cases?
enum LSLeague: String {
case NFL = "NFL"
case NBA = "NBA"
case NHL = "NHL"
case MLB = "MLB"
case NCAAM = "NCAAM"
case NCAAF = "NCAAF"
}
You can use .allCases on the enum type.
So, you might still have to do a bit of work depending on your use case, but you can iterate over each element in an enum if you use:
for league in LSLeague.allCases {
//code here
}
EDIT: #aheze had a great point that I just wanted to add into this answer. He says, "Make sure to also conform the enum to CaseIterable"
So, also make sure that you change the enum you have to:
enum LSLeague: String: CaseIterable {
case NFL = "NFL"
case NBA = "NBA"
case NHL = "NHL"
case MLB = "MLB"
case NCAAM = "NCAAM"
case NCAAF = "NCAAF"
}
if you're able to use this technique.
Swift 5
Use CaseIterable protocol to the enum:
enum CustomTypes: String, CaseIterable {
case location = "Location"
case organization = "Organization"
case dutyUse = "Duty Use"
}
Usage:
let values: [String] = CustomTypes.allCases.map { $0.rawValue }
// values = ["Location", "Organization", "Duty Use"]
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'
Here is a common enum with associated values.
enum MultiplierType {
case width(Double)
case height(Double)
case xxxxx1(Double)
case xxxxx2(Double)
case xxxxx3(Double)
var value: Double {
switch self {
// Normal way.
case let .width(value):
return value
case let .height(value):
return value
case let .xxxxx1(value):
return value
...
}
}
}
My question is how to do like this?
var value: Double {
switch self {
// How to get the value in one case?
case let .width(value), .height(value), .xxx:
return value
}
}
Or
var value: Double {
switch self {
// How to get the value in one case?
case let .width, .height, .xxx (value):
return value
}
}
What is the most elegant way to get the associated value?
You can put multiple enum values in the same case line, but you have to move the let into the ():
var value: Double {
switch self {
case .width(let value), .height(let value), .xxxxx1(let value):
return value
}
}
You might want to put each enum value on a separate line:
var value: Double {
switch self {
case .width(let value),
.height(let value),
.xxxxx1(let value):
return value
}
}
Unfortunately, that's about as elegant as it gets with enums with associated values. There's no way to get the associated value without explicitly listing the enums.
You can only combine values in the same line that have the same type of associated value, and all of the values have to bind to the same variable name. That way, in the code that follows, you know that the value is bound and what its type is no matter which enum pattern matched.
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.
I have a filter that I am trying to use to compare one value to another. Here is the enum that I am using:
enum SomeEnum: String {
case first = "Hey"
case second = "There"
case third = "Peace"
static let values = [first, second, third]
func pickOne() -> String {
switch self {
case .first:
return "value 1"
case .second:
return "value 2"
case .third:
return "value 3"
}
}
Here is where I am attempting to filter and find matching values:
array.append(SomeEnum.values.filter({$0.rawValue == anotherArray["id"] as! String}))
I end up getting an ambiguous error:
Cannot convert value of type '[SomeEnum]' to expected argument type 'String'
Any ideas?
The problem is, that SomeEnum.values return type is [SomeEnum] and not String.
And the append function expects the parameter to be String, instead it is [SomeEnum].
This is, what you need to change:
Change append to appendContentsOf, since filter function returns an array, and not a single value
Change the [SomeEnum] to [String] since you are adding it to a [String] array, like this.
This is the fix:
array.appendContentsOf(SomeEnum.values.filter({ $0.rawValue == "SomeString" }).map({ $0.PickOne() }))