5

Being new to Swift this is what I found:

enum HttpMethod {
  static let post = "POST"
  static let get = "GET"
}

// can assign to string property.
request.httpMethod = HttpMethod.post // --> "POST"

The reason to use a caseless enum and not a struct makes sense to me after reading this but is not the thing I'm interested here.

Having a strong C# background this is how I would implement it:

enum HttpMethod: String {
  case post = "POST"
  case get = "GET"
}

// I'd even consider this alternatively:
enum HttpMethod: String {
  case post
  case get
}

// Must retrieve string value
request.httpMethod = HttpMethod.post.rawValue // --> "POST" or "post"

The second version requires the usage of rawValue but it treats the enum as a real enum. Coming from C# I'm used to using .ToString() on enum values.

Is this all just coming down to personal preference and Swift's conventions to use a caseless enum instead of actual cases + rawValue, or is there another (technical) reason to prefer the first over the second version?

Krumelur
  • 32,180
  • 27
  • 124
  • 263
  • With Enums you have a finite number of cases and the cases are known at compile time. The advantage is that switch statements can be made exhaustive. If possible create Enums if you know the values at compile time. The static values are just a work around when you want to extend what has been defined in a framework – user1046037 Mar 22 '18 at 11:26
  • If you use the enum and rawValue, then all of the cases have to be the same type. With the careless enum, every constant can have a different data type. – vacawama Mar 22 '18 at 11:47
  • 2
    @vacawama: *"careless enum"* is nice! – Martin R Mar 22 '18 at 12:16
  • 1
    @MartinR, that was either a Freudian slip or spellcheck. I didn't do it on purpose, LOL! – vacawama Mar 22 '18 at 12:19
  • 1
    "careless" is the result of macOS' auto correction. Happened to me, too. One should never use enums carelessly! :-) – Krumelur Mar 22 '18 at 12:57

3 Answers3

7

Enum with cases

It is better to create an enum with cases in the following scenarios:

  • It is mutually exclusive
  • Finite set of values that you know at compile time
  • You are the one defining it (If an enum is defined in a framework you wouldn't be able to extend it to add more cases)

Advantage of an enum are:

  • Since the values are a finite set, you can write exhaustive switch statements
  • cleaner code

Static values:

When a struct / class is defined in a framework and you want to extend it to add more values.

Example of where this approach is used is Notification.Name in Foundation

Note:

  • enums in swift are quite powerful
  • enums can have associated values
  • enums can have other functions. (if you are defining states like start, inProgress, finished, you could define a function called next, which could return the next state. start.next()
  • if you are in a scenario where values are not mutually exclusive, like it could a combination of values then use OptionSet instead

Conclusion

  • It all depends on your intent

  • If you know the values before hand and they will not change, then create an enum

  • If that is not possible then create static values.

  • If you are creating static values, you are compromising so you don't have to use it in an enum, you could define it as a struct so that the intent is clearer.

  • This is as of now, there is a swift proposal for extendable enums

Community
  • 1
  • 1
user1046037
  • 16,755
  • 12
  • 92
  • 138
1

I do agree that such an implementation is better with cases than not. The other reason caseless enums are useful for is storing constants. It can become tricky deciding how to store constants. Caseless enums provide a type which can not be instantiated/constructed. It is just a means of listing static constant properties which can be accessed like enums.

enum Constants {
    
    static let dateFormat: String = "dd.MM.yy"
    static let timeFormat: String = "hh:mm:ss a"
    static let defaultLocationCoordinates: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-26.204103, 28.047305)
}

class Test {
    static func echo() {
        print("\(Constants.dateFormat)")
        print("\(Constants.timeFormat)")
        print("\(Constants.defaultLocationCoordinates)")
    }
}
dev_exo
  • 164
  • 1
  • 8
0

Whether to use enums with string values depends on the problem you are trying to solve. If you need an unbounded set of string cases, it might be better to declare a struct with a single let rawValue: String property, and for known values, declare static instances of the known values. That is what Apple frameworks do for things like NSNotification.Name.

Sidebar regarding enum rawValue: enums declared with :String are automatically CustomStringConvertible (similar to .toString()), using "\(enum-name)", rather than .rawValue, but that prints it in the case of the enum, not the string. I sometimes implement CustomStringConvertible to print the rawValue instead, when I need that.

Brian Arnold
  • 353
  • 1
  • 4
  • 10
  • `"\(enum-name)"` returns the case name, not the associated String. So OP would get `"post"` instead of `"POST"`. – vacawama Mar 22 '18 at 12:00
  • Right. I forgot, I typically declare CustomStringConvertible when I want the exact string. I edited that part of my response. – Brian Arnold Mar 22 '18 at 13:43