How to write safer code using the Lock ๐Ÿ”’ and Key ๐Ÿ”‘ pattern


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 ๐Ÿฟ


Do you see whatโ€™s the issue with this code?

Here, we have an actor that implements a network client.

We have a first method called authenticate(), which allows us to get an authentication token.

And then we have another method called fetchNewsFeed(), which uses the authentication token to retrieve some actual data.

This code will run perfectly fine, however thereโ€™s one annoying issue: the code assumes that the method authenticate() will always be called before the method fetchNewsFeed().

However, the compiler has no way to enforce this rule!

This is a problem because not only does it open up the way to potential bugs, but it also forces every call site of the method fetchNewsFeed() to deal with a potential error case!

So how could we improve?

We would like to have a mechanism that makes it impossible to call the method fetchNewsFeed() as long as the property authToken is still set to nil.

This mechanism can be implemented by using something called the Lock ๐Ÿ”’ and Key ๐Ÿ”‘ pattern.

Hereโ€™s how it works.

First, we split our NetworkClient into two parts:

On one side an UnauthenticatedNetworkClient, and on the other an AuthenticatedNetworkClient.

Then, in the AuthenticatedNetworkClient, we make the authToken non-optional.

This means that in order to initialize an AuthenticatedNetworkClient, it will now be mandatory to provide an authToken:

Finally, we also update the UnauthenticatedNetworkClient, so that the method authenticate() now returns an AuthenticatedNetworkClient:

Thanks to these changes, we now have the compile time guarantee that when a method is called on an AuthenticatedNetworkClient, an authToken will always be available ๐Ÿ˜Œ

The initializer of the AuthenticatedNetworkClient has become a Lock ๐Ÿ”’ around its methods, and the token has become the Key ๐Ÿ”‘ which opens that Lock ๐Ÿ”’.

Hence the name of the Lock ๐Ÿ”’ and Key ๐Ÿ”‘ pattern!

Thatโ€™s all for this article!

I hope youโ€™ve enjoyed discovering this new pattern to make your code safer.

Hereโ€™s the code if you want to experiment with it:

// Before
actor NetworkClient {
    var authToken: String?

    func authenticate(
        login: String,
        password: String
    ) async {
        // ...
        self.authToken = authToken
    }

    func fetchNewsFeed() async throws -> NewsFeed {
        guard let authToken else {
            throw NetworkError.noToken
        }

        // ...
    }
}

// After
actor UnauthenticatedNetworkClient {
    func authenticate(
        login: String,
        password: String
    ) async -> AuthenticatedNetworkClient {
        // ...
        return AuthenticatedNetworkClient(authToken: authToken)
    }
}

actor AuthenticatedNetworkClient {
    var authToken: String

    init(authToken: String) {
        self.authToken = authToken
    }

    func fetchNewsFeed() async throws -> NewsFeed {
        // ...
    }
}
Previous
Previous

Bad practice: not using .isMultiple(of:)

Next
Next

Bad practice: using .lowercased() to compare strings