SwiftUI: Requires String conform to Identifiable error in List - Tanin's blog

Tanin's blog

App Development | Productivity

SwiftUI: Requires String conform to Identifiable error in List

Posted at — Aug 31, 2019

Intro

I was following along with this awesome SwiftUI Tutorial by Paul Hudson. Paul was super eager about SwiftUI that his video was published on the 11th of June, just about a week after SwiftUI was announced! And as we know, SwiftUI was still in its infancy at that time. A few things have changed since then.

The Problem

In the tutorial, I ran into this problem.

string-conform-identifiable

Initializer ‘init(_:rowContent)’ requires that ‘String’ conform to ‘Identifiable’

dataSource.images is a list of strings (image names). List is basically a SwiftUI UIKit’s UITableView counterpart. SwiftUI makes it easy for us to put a collection of items into a List by passing it in as a parameter of List’s initializers. In Paul’s Tutorial, the beta he was using didn’t have this error.

The Reason

As the error suggests, we need to make String, as in the String class itself, conforms to Identifiable.

What is Identifiable?

It’s a protocol. In order to display dynamically in a List, a type must conform to the Identifiable protocol. It makes objects of the type to be displayed in a List uniquely Identifiable. Implementing Identifiable forces a type to have an id field, which SwiftUI will use to distinguish between objects in the List. The field can be any type as long as it is unique: String, UUID, Int, etc.

The Fixes

As far as I concern, there are two main ways.

1. Make String conforms to Identifiable

So the String class needs an id field, if we are certain the content of strings we’re going to put into the List is unique, we could just return self for the id field. This means, if you have the array ["img1", "img2", "img3"], the identifier of the items will be img1, img2, and img3 respectively.

extension String: Identifiable {
    public var id: String {
        self
    }
}

However, I don’t quite like this approach as it changes the functionality of String in the whole project. Going forward, we might come across the same scenario but with duplicate strings in a collection. And we might forget we have added this extension to String.

We need to return something truly unique to each String object. How about UUID? Returning UUID() for the id field? This would solve the non-unique problem but it would raise the issue of reusability as discussed in this StackOverflow thread. A new UUID object will get generated every time you get the object.

What else is unique in a String object? Memory address! I came up with this idea on my own and found a way to get a memory address of an object to return in the id field.

extension String: Identifiable {
    public var id: String {
        Unmanaged.passUnretained(self as AnyObject).toOpaque()
    }
}

This works fine. But I agree, it’s not nice.

2. Tell SwiftUI on the spot which field to use for id in this List

I found this probably the best way in this scenario.

List(dataSource.images, id: \.self) { image in
    // display image
}

There is a variant of List’s initializer which also takes in the id key path along with the data. This allows us to specify the key path to a field we need to use to uniquely identify elements in a collection. In this case, since all the images' names are unique, we just use \.self. This is different from the previous approach as it only applies to strings this List only, not in the whole project, but similar in a way that it’ll use the content of the strings as their unique identifiers.


Feel free to have a look at my ContentView.swift for this tutorial. Hope you’ve found it useful and thanks for reading 🎉