UUIDs are great, you can use them everywhere… but when you do so,
it starts being hard to recognize where a UUID comes from.
A common pattern is to add readable prefixes to the UUIDs.
This way, you can have user_ee505428-a17b-4935-b81c-266b81a63515
and post_d58a3f88-0493-458d-bd4b-a6ca4a2cc98c for example,
making it easy to find which is which.
Writing this library, I wanted to keep it very simple and straight-forward.
I also wanted the PrefixedUUIDs to be type-safe, meaning a UUID prefixed with user_
could not be used instead of a UUID prefixed by post_.
After writing PrefixedUUID, I realized prefixed UUIDs are just a subset
of all the types we could want to prefix. That's why I made it more generic,
by creating the Prefixed struct.
These are the features of
PrefixedUUIDs, but you can say the same with any other prefixed type.
- Type safety:
User.ID != Post.ID, even though they are stored the same way Codable: (Encodable & Decodable) conformance- Practical:
PrefixedUUIDs allow you to access the underlyingUUIDor the prefixedString, without storing anything more in memory or requiring expensive computations. - Convenient: Foundation's
UUIDextensions make it easy to work withPrefixedUUIDs - Type inference: Most of the time, you don't need to make associated types explicit
- Lightweight:
PrefixedUUIDs take (almost) as little space in memory asUUIDs do.Stringprefixes are stored once ever and dashes in theUUIDare not stored. The only thing that's stored for eachPrefixedUUIDis aUUID(which directly stores bytes). - Case (in)sensitive: By default, prefixes are case sensitive, but with a simple option, you can choose for a prefix to be case insensitive.
- Debugging: Failing to decode a
Prefixedtype shows a human-readable error:"'usr_abcdef' is not prefixed by 'user_'""'USR_MONPQR' is not prefixed by 'user_' (case insensitive)""user_ABCD"with aPrefixedInt=>"'ABCD' is not a valid `Int`""user_56C68C54"with aPrefixedUUID=>"'56C68C54' is not a valid `UUID`"
To use this package in a project, import the "prefixed-uuid" package like so:
dependencies: [
// Other dependencies
.package(
name: "prefixed",
url: "https://github.com/RemiBardon/swift-prefixed-type",
.upToNextMajor(from: "2.0.0")
),
],Then add it to your target like so:
targets: [
// ...
.target(
name: "<YourTarget>",
dependencies: [
// Other dependencies
.product(name: "Prefixed", package: "prefixed"),
]
),
// ...
]To add Prefixed to your project, go to your project's configuration,
then go to the Swift Packages tab, click + and follow XCode's instructions
(Assuming you use XCode). When you're asked for a URL, enter
https://github.com/RemiBardon/swift-prefixed-type.
The following uses
PrefixedUUIDs, but you can usePrefixed<Prefix, Base>instead if you want.
First, create a type conforming to PrefixProtocol:
struct UserIDPrefix: PrefixProtocol {
static var prefix: String { "user_" }
}If you want the prefix to be case-insensitive, you can override the default (true) value:
struct CaseInsensitiveUserIDPrefix: PrefixProtocol {
static var prefix: String { "lower_user_" }
static var isCaseSensitive: Bool { false }
}Then, use your new type as you would normally do:
struct User: Codable, Identifiable {
typealias ID = PrefixedUUID<UserIDPrefix>
let id: ID
}
Identifiableis not necessary, it's just here as a good practice.Codableis not necessary either, but you can use it:PrefixedUUIDconforms toCodable.
There are many ways to create a PrefixedUUID:
-
Using the verbose initializer:
let prefixedUUID = PrefixedUUID<UserIDPrefix>(uuid: UUID())
-
Using the default value (
UUID()):let prefixedUUID = PrefixedUUID<UserIDPrefix>()
-
Using the convenient
UUIDextensions:let prefixedUUID = UUID().prefixed(by: UserIDPrefix.self)
-
The compiler can infer the type of your
UUIDPrefix, so most of the time you just have to use.prefixed():let prefixedUUID: PrefixedUUID<UserIDPrefix> = UUID().prefixed()
- Do not make an existing
structconform toPrefixProtocol:PrefixProtocolsets customStringand debug descriptions which would override the one you expect.
- Package has been renamed from
prefixed-uuidtoprefixed - Package library has been renamed from
PrefixedUUIDtoPrefixed UUIDPrefixhas been renamed toPrefixProtocolUUIDPrefix.uuidPrefixhas been renamed toPrefixProtocol.prefixUUIDPrefix.uuidPrefixIsCaseSensitivehas been renamed toPrefixProtocol.isCaseSensitive
As you would expect from a package this small, it is not actively developed. However, I still have a few things to add:
- Generic
Prefixedstruct, to beUUID-independent - Swift documentation comments, to make the package more understandable
- Continuous Integration, to be sure the package runs in all environments
- Documentation page
- (Installation guides for older Swift Package Manager versions)
- (CONTRIBUTING guidelines)
This package is provided under MIT License. For more information, see LICENSE.