Better Programming

Advice for programmers.

Follow publication

Swift Protocols With Associated Types and Generics

Advanced protocols

Primoz Cimerman
Better Programming
Published in
3 min readFeb 6, 2020

--

Photo by Chris Ried on Unsplash.

Today, we’re going to look at the infamous Protocol can only be used as a generic constraint because it has Self or associated type requirements error message.

Swift documentation describes protocols as “a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.” Using protocols to define responsibilities of different parts of our code makes it easier to read and helps maintain a clean architecture.

Even though Swift is great for object-oriented programming, it was designed as a protocol-oriented language. Or as Apple’s Dave Abrahams put it at 2015 WWDC, “We have a saying in Swift. Don’t start with a class. Start with a protocol.”

Protocols in Action

Let’s see how protocols work by creating some basic logic for a simple online wallet app. First, we’ll definitely need to fetch some data from the server.

After we fetch the data, we’ll parse it to some object. Since we want to keep our options open, it’s a good idea to use an associated type.

We started with protocols. Now, let’s provide concrete implementations. Since we’re building an online wallet app, we’ll need a User, a Wallet, and their corresponding fetchers and parsers.

The only thing left to implement is a consumer of this logic.

The Problem

As you can see, the UserFetcher and the WalletFetcher implementations are very similar, so it makes sense to refactor the code and use generics.

Declaring our dataParser the way we did results in an error: Protocol 'DataParsing' can only be used as a generic constraint because it has Self or associated type requirements.

Trying to fix our error by specializing the dataParser gets us another error: Cannot specialize non-generic type 'DataParsing'.

This problem arises because the compiler needs to know the concrete types at compile time. Most Swift developers encounter it at some point of their career, and if you’re still reading this article, there’s a decent chance you are among them.

The Solution

A common way of solving this issue is to use type erasure. As the name suggests, we erase the type information by providing a wrapper with concrete implementation.

We can now use our GenericDataFetcher and remove duplicate code from UserFetcher and WalletFetcher.

The only thing that needs to be updated is the DataManager code.

Final Thoughts

The solution I described is used in several places in the Swift standard library. However, it relies on closures which are allocated on the heap. Therefore, I would suggest you use it sparingly. Because it also adds to code complexity, I try to avoid it and prefer refactoring the code in a way that eliminates the need for this kind of workaround. The example we used to demonstrate the problem could definitely be refactored differently, but that’s a topic for another piece.

Thanks for reading and please let me know if you have any comments or questions!

--

--

Primoz Cimerman
Primoz Cimerman

Written by Primoz Cimerman

iOS engineer | Apple enthusiast

No responses yet

Write a response