Misunderstanding with enums in Swift - swift

I have enum with cities that looks like
enum Cities: String {
case la = "Los Angeles"
case ta = "Tel Aviv"
case ny = "New York"
}
And I need to pass sting with city name to one of functions that is waiting for NSString
so this code is not working and I don't understand why
let selectedCity = Cities.ny
myFunc(city : selectedCity)
I get error "Cannot convert value of type 'Cities' to expected argument type 'String!'"
Xcode is suggesting me to use Cities.ny.rawValue as selectedCity.

Swift is strongly typed
So when you pass a value to a function argument the compiler checks that it is of the correct type.
Example
Let's declare a function that receive an Int as parameter
func giveMeYourAge(n: Int) {
}
And now let's try to invoke that function passing something that is NOT an Int
giveMeYourAge(n: "Hello World")
Now the compiler gets angry with us ☠️
error: cannot convert value of type 'String' to expected argument type 'Int'
giveMeYourAge(n: "Hello World")
And it is right (of course). Infact it is telling us that we cannot pass a String to a function that expects an Intindeed.
Your code
When you declare you enum (let's call it City as properly suggested by #Honey) you create a new type City.
enum City: String {
case la = "Los Angeles"
case ta = "Tel Aviv"
case ny = "New York"
}
City is something TOTALLY DIFFERENT from a String.
And we cannot use City in place of a String or viceversa. (Just like in the previous example Int was totally different from String).
So if you have a function that expects a String as parameter
func myFunc(city : String) {
// ...
}
of course you won't be able to pass an value of type City to it.
Wait! What about Polymorphism?
Now let's look at this code
class Animal { }
class Dog: Animal { }
func feed(animal: Animal) {
}
let dog = Dog()
feed(animal: dog)
Why does this code compile??? 😨
This is called polymorphism. When we write
class Dog: Animal { }
we define a new type (Dog) that is more specific than Animal.
Polymorphism does allow us to pass to a function that expects an Animal as param, something that is more specific (like a Dog).
However polymorphism has nothing to do with your example. Even if the syntax is similar.
When you define an enum using this syntax
enum City: String { ... }
You are NOT defining a subtype of String.
You are NOT creating something more specific than a String.
You are NOT using polymorphism.
Instead you are simply creating a new type City that has an instance property (rawValue) of type String.

You need to specify raw value to use the string literal, because without that you are just referring to the enum itself.
let selectedCity = Cities.ny.rawValue

I have better solution, which is much more reasonable... therefore you do this:
enum Cities {
case la
case ta
case ny
var cityString: String{
switch self{
case la:
return "Los Angeles"
case ta:
return "Tel Aviv"
case ny:
return "New York"
}
}
}
Now you need to call just this:
let selectedCity = Cities.ny.cityString
the reason behind me voting over other ideas is that rawValues should be used for recognizing the case, not for getting the values of them... But that's just my opinion :)

It's like you're trying to access a class instance, rather that the property of the instance. It won't work. Specifically what you're trying here is accessing the enum itself which is incorrect. You should access its rawValue property.
From Apple:
You access the raw value of an enumeration case with its rawValue
property:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3

Related

avoid implicit enum raw value conversion

I am attempting to avoid the implicit conversion of an enum into a string in a case like this:
enum Animal {
case cat
}
func getTag(forAnimal animal: Animal) -> String {
// fails with "Cannot convert value of type 'Animal' to specified type 'String' which is good!!!"
// return animal
// does return "cat" which I don't want developers to be able to do!!!
return "\(animal)"
}
print(getTag(forAnimal: .cat)) // prints "cat"
The 2nd return works because print calls .description on the object.
Swift apparently adopts CustomStringConvertible for enums automatically and returns the case name as a String. Edit: Apparently , print does not call .description.
I thought about conforming to the protocol and alert developers (with something that throws an error perhaps).
But what I am wanting to do really is to have the enum NOT conform to said protocol, or have a compile-time error. Is this possible?
You can extend StringInterpolation and implement an appendInterpolation method for that specific type. This way you can simply determine how your enumeration would interpolate its value. If you don't want any string to be generated you just need to create a dummy method:
enum Animal {
case cat
}
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Animal) { }
}
Testing:
"\(Animal.cat)" // "" it results in an empty string
If you want a compile-time error you can add a deprecated availability warning:
#available(swift, deprecated: 2)
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Animal) { }
}

Swift: returning a runtime random opaque type generates an error

I am learning opaque types now, still a bit confusing. I tried the following:
protocol Animal {
func introduce()
}
struct Dog: Animal {
func introduce() {
print("Dog")
}
}
struct Cat: Animal {
func introduce() {
print("Cat")
}
}
func random() -> some Animal {
let value = [true, false].randomElement()!
return value ? Cat() : Dog()
}
And in the return line of random I am getting the following error
Result values in '? :' expression have mismatching types 'Cat' and 'Dog'
So, as far as I understand just like Generics the compiler needs to be able to decide at compile time what the concrete return type of the function is.
Am I right? If I am, isn't this message about confusing as both structs implement Animal? And if I'm wrong, what does this error message mean?
Thanks a lot
EDIT: I'm trying to understand, not to make it work :)
Consider this: What is the type of the expression value ? Cat() : Dog()
It isn’t animal. And for a ternary you need one type but you have either a cat or a dog. Type inference isn’t going to figure out that you can erase those two different types back to some common type, even if it’s possible to do so

Swift - Nested enums - how to perform same action on all nested enums with associated values?

I want to print the raw value of nested enums. For example, if I have a top level Food enum with multiple cases (for simplicity let's just say two: fruit, veggie), each of which are string enums (Fruit: String, Vegetable: String, etc), is there a way to print the inner associated values without doing a switch statement one the top level enum?
My current code is below. As you can see, the more cases I add to Food, the more redundant code will end up in var description. I'd like to write one action for all cases that prints the rawValue of the inner enums.
Is that possible without the switch?
enum Foods {
case fruit(Fruit)
case veggie(Vegetable)
var description: String { // this is what I'd like to replace
switch self {
case .fruit(let type):
return type.rawValue // since every case will look like this
case .veggie(let type):
return type.rawValue
}
}
}
enum Fruit: String {
case apple = "Apple"
case banana = "Banana"
}
enum Vegetable: String {
case carrot = "Carrot"
case spinach = "Spinach"
}
Is that possible without the switch?
No, this is not currently possible.
Workaround: I've noticed a pattern with your enum case names. The raw values of each are all capitalized versions of the case name i.e. case apple = "Apple". If this pattern always holds, you can use the following code:
var description: String { ("\(self)".split(separator: ".").last?.dropLast().capitalized)! }
Which will produce:
print(Foods.fruit(.apple).description) // Apple

Swift error. Used undeclared type String

if we insert line "case let dictionary as [String : AnyObject]:" inside struct's method everything works fine. But if use inside nested enums we get error "Used undefined type String"
public struct JSON {
public enum Type : Int {
case Number
case String
case Bool
case Array
case Dictionary
case Null
case Unknown
public static func evaluate(object: AnyObject) -> Type {
switch object {
case let dictionary as [String : AnyObject]: // this lines supply error. Use of undefined type String
return .Dictionary
default:
return .Unknown
}
}
} // enum Type
Could some one explain why I have the error with String type?
It seems enum Type contains case String, and it hides the String what you want. I tried the code in Playground and there is no more error after change String to another name.
EDIT
How about reading the project SwiftyJSON (only single file)
https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift
I does very similar job.(JSON Handling)
It also contains the code looks like this:
public enum Type :Int {
case Number
case String
case Bool
case Array
case Dictionary
case Null
case Unknown
}
I think this project will be very helpful for you.
(and I guess you may end up using this project)
As already said in the other answer, String inside enum Type
refers to the enumeration value. The same problem would occur with
let a : Array<Int> = []
let b : Bool = false
inside methods of enum Type. Renaming the enumeration values is probably the best solution.
But for the sake of completeness: You can solve the problem
by prepending the "Swift" module name explicitly to refer to the
String type:
case let dictionary as [Swift.String : AnyObject]:
If you want to fix this without renaming the enum case, you can change the argument type to Swift.String, i.e.:
case let dictionary as [Swift.String : AnyObject]:
That should work (I had a similar problem and this solved it).

enum of non-literal values in Swift

Is there any way to map a non-literal value like tuple of dictionary to enums? Following code will throw Raw value for enum must be literal.
enum FileType {
case VIDEO = ["name": "Video", "contentTypeMatcher": "video/"]
case IMAGE = ["name": "Image", "contentTypeMatcher": "image/"]
case AUDIO = ["name": "Audio", "contentTypeMatcher": "aduio/"]
case PDF = ["name": "PDF", "contentTypeMatcher":"application/pdf"]
case TEXT = ["name": "Text", "contentTypeMatcher": "text/"]
case FOLDER= ["name": "Folder", "contentTypeMatcher" :"application/x-directory"]
case PLAIN = ["name": "Plain", "contentTypeMatcher": ""]
}
It's the same when I use tuples:
enum FileType {
case VIDEO = (name: "Video", contentTypeMatcher: "video/")
case IMAGE = (name: "Image", contentTypeMatcher: "image/")
case AUDIO = (name: "Audio", contentTypeMatcher: "aduio/")
case PDF = (name: "PDF", contentTypeMatcher:"application/pdf")
case TEXT = (name: "Text", contentTypeMatcher: "text/")
case FOLDER = (name: "Folder", contentTypeMatcher :"application/x-directory")
case PLAIN = (name: "Plain", contentTypeMatcher: "")
}
#Antonio gives workaround but does not answer the actual question.
Define your enum.
enum FileType {
case Image, Video
}
Give cases non-literal values, whatever type you want with conforming to RawRepresentable protocol. Do it by enum extension to have cleaner code.
extension FileType: RawRepresentable {
typealias Tuple = (name: String, contentTypeMatcher: String)
private static let allCases = [FileType.Image, .Video]
// MARK: RawRepresentable
typealias RawValue = Tuple
init?(rawValue: Tuple) {
guard let c = { () -> FileType? in
for iCase in FileType.allCases {
if rawValue == iCase.rawValue {
return iCase
}
}
return nil
}() else { return nil }
self = c
}
var rawValue: Tuple {
switch self {
case .Image: return Tuple("Image", "image/")
case .Video: return Tuple("Video", "video/")
}
}
}
To be able to match Tuple in switch, implement pattern matching operator.
private func ~= (lhs: FileType.Tuple, rhs: FileType.Tuple) -> Bool {
return lhs.contentTypeMatcher == rhs.contentTypeMatcher && lhs.name == rhs.name
}
And thats it...
let a = FileType.Image
print(a.rawValue.name) // "Image"
let b = FileType(rawValue: a.rawValue)!
print(a == b) // "true"
print(b.rawValue.contentTypeMatcher) // "image/"
Let's say I answered the question without questioning. Now... Enums (in Swift at least) are designed to have unique cases. Caveat to this workaround is that you can (I hope by accident) hold same rawValue for more cases. Generally your example code smells to me. Unless you (for very reasonable reason) need to create new enum value from tuple, consider redesign. If you want go with this workaround, I suggest (depends on project) to implement some check if all case raw values are unique. If not, consider this:
enum FileType {
case Video, Image
var name: String {
switch self {
case .Image: return "Image"
case .Video: return "Video"
}
var contentTypeMatcher: String {
switch self {
case .Image: return "image/"
case .Video: return "video/"
}
}
The language reference, when talking about Enumeration Declaration, clearly states that:
the raw-value type must conform to the Equatable protocol and one of the following literal-convertible protocols: IntegerLiteralConvertible for integer literals, FloatingPointLiteralConvertible for floating-point literals, StringLiteralConvertible for string literals that contain any number of characters, and ExtendedGraphemeClusterLiteralConvertible for string literals that contain only a single character.
So nothing else but literals can be used as raw values.
A possible workaround is to represent the dictionary as a string - for example, you can separate elements with commas, and key from value with colon:
enum FileType : String {
case VIDEO = "name:Video,contentTypeMatcher:video/"
case IMAGE = "name:Image,contentTypeMatcher:image/"
...
}
Then, using a computed property (or a method if you prefer), reconstruct the dictionary:
var dictValue: [String : String] {
var dict = [String : String]()
var elements = self.rawValue.componentsSeparatedByString(",")
for element in elements {
var parts = element.componentsSeparatedByString(":")
if parts.count == 2 {
dict[parts[0]] = parts[1]
}
}
return dict
}
My coworkers and I have been debating this topic recently as Swifts enum type is unique from other languages. In a language like Java where an enum is just a class that inherits from Enumeration, you can have static non-literal values assigned to each case.
In swift, we can not find a supported way to do this. From Swift documentation:
If a value (known as a “raw” value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages. You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it.
The second paragraph may seem like it can do what #Antonio asked but it is not. In swift's example:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
But each enum is an instance with different value types (tuple vs string) and the values within them are different based on each instance of the enum created.
I wanted something that allowed more than the limited raw values but each enum contained the same value type (ie tuple, object, etc...) and is static.
With my coworkers input we came up with two options that have different tradeoffs.
The first is a private static dictionary by the enum that holds the value type you desire:
enum FooBarDict {
case foo
case bar
private static let dict = [foo: (x: 42, y: "The answer to life, the universe, and everything"),
bar: (x: 420, y: "Party time")]
var x: Int? { return FooBarDict.dict[self]?.x }
var y: String? { return FooBarDict.dict[self]?.y }
}
Our issue with this implementation is that there's no way at compile time that you can ensure that the developer has exhaustively included all of the enum cases. This means that any properties you right must be optional or return a default.
To resolve that issue we came up with the following:
enum FooBarFunc {
case foo
case bar
typealias Values = (x: Int, y: String)
private func getValues() -> Values {
switch self {
case .foo: return (x: 42, y: "The answer to life, the universe, and everything")
case .bar: return (x: 420, y: "Party time")
}
}
var x: Int { return getValues().x }
var y: String { return getValues().y }
}
Now it is exhaustive due to the switch statement in the getValues! A developer can not add a new case and compile without explicitly adding the value type.
My (perhaps unfounded) fear with this approach is that it may be both slower due to the switch statement lookup - although this may be optimized to be as fast as the dictionary lookup. And I am unsure if it will create a new value each time a enum property is requested. I'm sure I could find answers to both of these concerns but I've already wasted too much time on it.
To be honest, I hope I'm just missing something about the language and this is easily done in another way.