Using Swift Enums for Hard-Coded Strings

Datetime:2016-08-23 04:15:09          Topic: Swift           Share

Swift has made quite an improvement to the enum type compared to what you could do in C. In this post, I will show you how you can use the new features of enum to manage hard-coded strings in your application.

Using Enums for Segue Identifiers

In Swift, you can change the raw value type of an enum to be a string. This will allow us to define an enum that has segue identifiers as its raw value. Let’s look at how we can transform our prepareForSegue function from the following example using hard-coded strings:

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if let identifier = segue.identifier {
        if identifier == "Login" {
            ...
        } else if identifier == "Main" {
            ...
        } else if identifier == "Options" {
            ...
        }
    }
}

Using the string type as the raw value of the enum, we can define our segue identifiers in an enum like this:

enum SegueIdentifier: String {
    case Login = "Login"
    case Main = "Main"
    case Options = "Options"
}

Now we can change our prepareForSegue to take advantage of the enum we just created.

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if let identifier = segue.identifier, segueIdentifier = SegueIdentifier(rawValue: identifier) {
        switch segueIdentifier {
        case .Login:
            ...
        case .Main:
            ...
        case .Options:
            ...
        }
    }
}

The hard-coded segue identifiers may appear in several of your view controllers, so it is much better to centralize them in an enum that can be shared throughout your application.

Parameterized Messages

Another place where hard-coded strings may be repeated in your application is in error messages. Good error messages will be parameterized, so that you have more context to understand what is wrong. Unfortunately, we cannot use the same trick as we did above (changing the raw value type of the enum to a string). The raw value in an enum has to be constant.

// This will not compile
enum Alert: String {
    case DuplicateUserNameMessage = "The username \(userName) already exists"
}

One option is to use Objective-C format specifiers and the string initializer. This solution is not bad, but we can do better and make the code more readable.

enum Alert: String {
    case DuplicateUserNameMessage = "The username %@ already exists"
}

let message = String(format: Alert.DuplicateUserNameMessage.rawValue, "Mike")
print(message)

Protocols, Methods, and Properties

For a better solution, let me introduce you to a couple new features. Swift enums can implement functions, computed properties, and protocols. The CustomStringConvertible protocol is a good one to implement for our parameterized messages, allowing us to pass the enum to a print function so it will know how to print it. All we have to do is implement the description property.

public protocol CustomStringConvertible {
    /// A textual representation of `self`.
    var description: String { get }
}

You implement a protocol on an enum just as you would implement a struct or class. The description property will print out the message using the enum’s associated value parameters.

enum Alert: CustomStringConvertible {
    case DuplicateUserNameMessage(userName: String)
    case NetworkFailureMessage(url: String)
    case OverheatingMessage(degrees: Float)
    var description: String {
        switch self {
        case DuplicateUserNameMessage(let userName): return "The username \(userName) already exists"
        case NetworkFailureMessage(let url): return "Unable to connect to \(url)"
        case OverheatingMessage(let degrees): return String(format: "Warning: The water is %.1f degrees", degrees)
        }
    }
}

let userName = "Mike"
let url = "www.google.com"
let degrees: Float = 103.4587254

print(Alert.DuplicateUserNameMessage(userName: userName))
print(Alert.NetworkFailureMessage(url: url))
print(Alert.OverheatingMessage(degrees: degrees))

This code will print out the following:

The username Mike already exists

Unable to connect to www.google.com

Warning: The water is 103.5 degrees

This solution is much more readable and Swift-like. Now all of your error messages are defined in one enum instead of being duplicated throughout your code.

Hope this is helpful for you.





About List