Can associated values and raw values coexist in Swift enumeration?
There are examples on Swift book demonstrating associated values and raw values separately, is there a way to define enums with the two features together?
I have tried to combine them, but got errors:
enum Barcode :String {
case UPCA(Int, Int, Int) = "Order 1" // Enum with raw type cannot have cases with arguments
case QRCode(String) = "Order 2" // Enum with raw type cannot have cases with arguments
}
Yes this is possible. An enum may contain both associated values and raw values. (Swift 5 & 4)
Shorthand notation
The issue with your code is that you are using the shorthand notation for RawRepresentable
and defining associated types.
Let's look at how to define these separately:
1) RawRepresentable
(shorthand notation):
enum Barcode: String {
case UPCA = "order 1"
case QRCode = "order 2"
}
2) Associated types:
enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}
Each of these is great, but what if you need both as your code snippet shows.
Solution
Define the enum with associated values and then implement conformance to RawRepresentable
separately in an extension (i.e. not using shorthand notation).
Example:
enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}
extension Barcode: RawRepresentable {
public typealias RawValue = String
/// Failable Initalizer
public init?(rawValue: RawValue) {
switch rawValue {
case "Order 1": self = .UPCA(1,1,1)
case "Order 2": self = .QRCode("foo")
default:
return nil
}
}
/// Backing raw value
public var rawValue: RawValue {
switch self {
case .UPCA: return "Order 1"
case .QRCode: return "Order 2"
}
}
}
Minor Detail
In this solution, defaults for the associated values, e.g. .UPCA(1,1,1)
must be supplied when constructing the enum from the rawValue argument. You can get fancy and use the associated types as part of the backing raw value — which is more powerful, but adds some complexity.
References
For more info on the topic see Ole Begemann's excellent write up.
The answers here are great, but don't provide an alternative, so here is one:
I'm trying to write a convenient wrapper for Parse.com's rest API, and honestly this restriction imposed by swift made me write a bit more code, but the end result is more readable:
class Parse {
enum Endpoint {
case signUp(ParseHTTPBody)
case login(ParseHTTPBody)
}
}
extension Parse.Endpoint {
var httpMethod: String {
switch self {
case .signUp, .login:
return "POST"
}
}
var path: String {
switch self {
case .signUp:
return "/1/users"
case .login:
return "/1/login"
}
}
}
Notice, now I httpMethod
and path
instead of rawValue
, which is more readable in my case:
func setParseEndpoint(endpoint: Parse.Endpoint) -> Self {
URL = NSURL(string: baseURL + endpoint.path)
HTTPMethod = endpoint.httpMethod
return self
}