Software Engineer at Capital One UK
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.
In the tutorial, I ran into this problem.
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.
As the error suggests, we need to make String, as in the String class itself, conforms to Identifiable
.
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.
As far as I concern, there are two main ways.
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.
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 🎉