Associated Values in Enums in Swift 1.2

Swift’s Associated Values for Enums are Awesome.

When I first started writing Swift full time about 6 months ago, I only had a simple understanding of what they actually were. For example, say we’re building template builder for a UITextView that has differently styled parts (think HTML). I’d start with something like this:

enum TextTemplateType {
    case Header
    case Subheader
    case Paragraph
    case Inline
}
 
let headerAttributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(20)]
let subheaderAttributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(16)]
let paragraphAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(14)]
 
var attributedString: NSMutableAttributedString = NSMutableAttributedString()
 
func addTemplateText(text: String, withType templateType: TextTemplateType) {
    switch templateType {
    case .Header:
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(text)\n", attributes: headerAttributes))
        break
    case .Subheader:
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(text)\n", attributes: subheaderAttributes))
        break
    case .Paragraph:
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(text)\n", attributes: paragraphAttributes))
        break
    case .Inline:
        attributedString.appendAttributedString(NSAttributedString(string: text, attributes: paragraphAttributes))
        break
    }
}
 
addTemplateText("My Header", withType: .Header)
addTemplateText("A Subheader", withType: .Subheader)
addTemplateText("Paragraph text that  is long. ", withType: .Paragraph)
addTemplateText("Append this sentence on to the paragraph", withType: .Inline)

Nothing crazy. We want to be able to easily build a styled body of text. And it works, but it feels a little clunky. What are a couple ways we could clean this up?

  1. Create a protocol that has a property of attributes and make a bunch of classes that implement said protocol.
  2. Use inheritence
  3. Utilize associated values in Enums!

What Exactly is an Associated Value?

Associated values allow our cases to have actual inputs. Instead of the enum case returning a rawValue, we instead can allow for any number of properties to be associated (get it?) with it.

I had a difficult time (more so than I should have) initially figuring out what this actually meant, but I think the example above will prove to be super clear. Let’s change up our Enum to look like this:

enum TextTemplateType {
    case Header(String)
    case Subheader(String)
    case Paragraph(String)
    case Inline(String)
}

What we’re saying is that (for example) in order to specify something as a Header, you must specify a string to go along with it. So what does this look like in application? We can now do the following:

enum TextTemplateType {
    case Header(String)
    case Subheader(String)
    case Paragraph(String)
    case Inline(String)
}
 
let headerAttributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(20)]
let subheaderAttributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(16)]
let paragraphAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(14)]
 
var attributedString: NSMutableAttributedString = NSMutableAttributedString()
 
func appendToTemplate(templateType: TextTemplateType)
{
    switch templateType {
    case .Header(let headerString):
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(headerString)\n", attributes: headerAttributes))
        break
    case .Subheader(let subheaderString):
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(subheaderString)\n", attributes: subheaderAttributes))
        break
    case .Paragraph(let paragraphText):
        attributedString.appendAttributedString(NSAttributedString(string: "\n\(paragraphText)\n", attributes: paragraphAttributes))
        break
    case .Inline(let inlineText):
        attributedString.appendAttributedString(NSAttributedString(string: inlineText, attributes: paragraphAttributes))
        break
    }
}
 
appendToTemplate(.Header("My Header"))
appendToTemplate(.Subheader("My subheader"))
appendToTemplate(.Paragraph("My long paragraph text."))
appendToTemplate(.Inline("Let's append this text on"))

 

What’s even nicer is that we can specify multiple associate values. Want to include an image along with a paragraph? Add it as another value. Maybe you’d like the ability to override a default styling? Have it included as an optional!

case Header(String, [String: AnyObject]?)

One final thing that I really like is that it really allows you to write some pretty easy to read code:

func buildTemplate(template: [TextTemplateType])
{
    for textTemplate in template
    {
        appendToTemplate(textTemplate)
    }
}
 
let myArticle: [TextTemplateType] = [
    .Header("My Article"),
    .Subheader("First section"),
    .Paragraph("This is the first section of my article. Isn't it great?"),
    .Subheader("Second section"),
    .Paragraph("This is the second section. I think it's better than the first.")
]
 
buildTemplate(myArticle)

This is only scratching the surface of what they can do. We can get super tricky with implementing various protocols on the Enums. Swift 2 will also be adding in the ability to do where clause matching on the enum case’s and their associated values, so we can get even more explicit in how handle different situations.

Leave a Reply

Your email address will not be published. Required fields are marked *