Using Type-Safe Identifiers in Swift

Photo by Agung Raharja on Unsplash

Before we start

Difficulty: Beginner | Easy | **Normal** | Challenging

This article has been developed using Xcode 12.4, and Swift 5.3.2

This is supported by the YouTube video

Prerequisites:

Why

You need to uniquely identify both values and objects — and this is often brought into focus when you are using Core Data in your project.

If you want to keep track of instances of your value types, you can use an `Identifier` in much the same way that you might use ObjectIdentifier for class-types (well, objects).

You may well be able to do this through UUID, but using your own Identifier gives you more flexibility, and allows you to make your code type-safe which may well be particularly important when using databases (for example CoreData abstractions).

My previous solution

I’d usually be satisfied creating trackable instances of value types using a simple String as the identifier (or even an Integer).

This would look something like the following:

struct Person {
var name: String
var id: String
}
let dang = Person(name: “Đặng”, id: “1”)

which is quite a fragile way of working, we don’t have any of Swift’s type safety.

More than this, items is a set or a dictionary need to conform to the `Hashable` protocol (as detailed in my article )— and there is a way to get this…wait for it!

Create an Identifier

By creating an identifier type we can create a `hashable` instance of our types, that can then be put into dictionaries or sets.

In order to do so it makes sense to create an `Identifier` type, something like:

struct Identifier: Hashable {
let string: String
}

which can then be used in the class

struct Identifier: Hashable {
let string: String
}
struct Person {
var name: String
var id: Identifier
}
let dang = Person(name: “Đặng”, id: Identifier(string: “1”))

Using Type-Safety

We need to also store what type of Identifier we are using, and we can do so by storing a Object value in the identifier.

struct Identifier<Object>: Hashable {
let string: String
}
struct Person {
var name: String
var id: Identifier<Person>
}
let dang = Person(name: “Đặng”, id: Identifier(string: “1”))

this means that we get Swift’s type-safety for the Identifier- it has to be the right **type** of Identifier in order to compile now.

Using different type of Identifier

Instead of using just Strings, which let’s face it are not great for all uses, we can use any type of Id:

struct Identifier<Object, Id: Hashable & Codable>: Hashable {
let rawId: Id
}
struct Person {
var name: String
var id: Identifier<Person, String>
}
let person = Person(name: “Đặng”, id: Identifier(rawId: “1”))

Conclusion

There is certainly use for using UUID in your project. But there is also use for using identifiers in your project.

This is one of those issues where you need to think about your project, and what is the best solution for the problem that you are solving.

So I’m saying the classic “it depends” solution. Sorry about that. But yes, both these type of identifiers are great ways for using DiffableDataSource (but that is another story completely).

If you’ve any questions, comments or suggestions please hit me up on Twitter

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store