How to refactor using Associated Values
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
I want to show you how to refactor Swift code like a pro, using something called Associated Values 🤨
So let’s take a look at some code!
You can see that I have a struct
that represents a blog post.
But that blog post can be one of two types: it can be either an article or a video.
And each of these types comes with different metadata:
an article requires a
wordCount
and a video requires a
duration
and aURL
to the video file
This is, of course, a simplified example, but it’s still close enough to real world code that we can take some useful learnings from it!
So what’s the problem with this code?
The issue is that all the properties that are specific to one of the two possible types of blog post are all marked as optional.
That’s a problem because we of course expect a blog post to provide only the metadata it needs and have all the other properties set to nil
:
But the issue is that the compiler has currently no way of enforcing this rule 😕
This means that it becomes possible to create blog posts with their metadata set in a way that makes no sense:
It’s pretty clear that this kind of code is unmanageable and that we must find a way to improve it!
A first step could be to store the metadata of each type of blog post into structs of their own:
While this indeed improves the quality of the code a bit, it doesn’t fully solve the issue, because it’s still possible to construct incorrect blog posts 😞
As you can imagine, this is when Associated Values come into play!
First we start by defining an enum
, with a case
for each type of blog post:
And then, we store the metadata of each type using associated values.
Associated values can seem a bit weird the first time you come across one, but you can see them as basically properties associated with a specific case
of an enum
.
What’s great with associated values is that they allow us to model data in a way that reflects the business rules of our app.
For instance, now a blog post must provide either the metadata of an article or the metadata of a video:
But it can no longer provide neither of them or the two of them at once 👌
And the best thing here is that now the compiler will automatically enforce this rule for us, making our code much safer and easier to maintain 😌
That’s all for this article, I hope you’ve enjoyed this nice refactoring trick.
Here’s the code it you want to experiment with it!
enum Metadata {
case article(wordCount: Int)
case video(videoURL: URL, duration: String)
}
struct BlogPost {
var title: String
var metadata: Metadata
}
let correctBlogPost = BlogPost(
title: "My Awesome Video",
metadata: .video(
videoURL: URL(string: "https://mywebsite.com/myVideo.mp4")!,
duration: "4:35"
)
)