IOS developer based in barcelona.

Strong typed Ids in Swift

In one of my current projects, I’m working with mobile payments. I have some data types to store important data, in this post I will show you only two: Requests and Transactions.

struct Request {
  let id: String
  let name: String
}

In order to have a transaction, first of all you need to accept a requests, and automatically a transaction is created, and this transaction can have multiple states. When I get request or transaction data from our backend, I must not mess Request Ids with Transaction Ids and vice versa, at first we were using Strings to store these Ids but it was a poor solution.

To use the best of Swift, instead of using a poor typed Id with Strings we created a strong typed Ids for every type, so there is a RequestUUID and a TransactionUUID.

I’m gonna show you my solution to avoid boilerplate creating every time a XXXUUID, so, the first we need is a protocol:

protocol Identifier: Hashable, ExpressibleByStringLiteral, CustomStringConvertible {
    var uuid: String { get set }
    init(uuid: String)
    init(_ uuid: String)
    init?(_ uuid: String?)
    init(stringLiteral value: String)
}

The Identifier protocol has a uuid var, to store a RequestUUID or TransactionUUID, and three different initializers.

The next step is adding a default implementation of Identifier, so:

extension Identifier {    
    init?(_ uuid: String?) {
        guard let uuid = uuid else { return nil }
        self.init(uuid: uuid)
    }
    
    init(_ uuid: String) {
        self.init(uuid: uuid)
    }
    
    init(stringLiteral value: String) {
        self.init(uuid: value)
    }
    
    var description: String {
        return uuid
    }
}

Here, I’m gonna focus only on the two first inits, I have added an init and a failable init. So, whether we create a new type conforming Identifier contract, we only need to define the uuid var, but let’s see an example creating the RequestUUID and TransactionUUID mentioned before.

struct RequestUUID: Identifier {
    var uuid: String
}

struct TransactionUUID: Identifier {
    var uuid: String
}

Now, if you have a RequestUUID you can use

let request = RequestUUID("123456")

The default implementation has another init, the init(stringLiteral value: String), it’s used to init a type that represents a string literal. So, you could init RequestUUID and TransactionUUID like:

let requestUUID: RequestUUID = "123456789"
let transactionUUID: TransactionUUID = "123456789"

And finally, notice that we have added CustomStringConvertible, in case you want to use/print the uuid, you can do the next snippet and the Id will be printed.

print("Request UUID \(requestUUID)")

I'm all ears to know your feedback, thanks for reading! 😃

UIButton with Right Image