Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

Generating UUIDs in Swift

On this page
  1. Quick start
  2. Parsing
  3. Codable / JSON
  4. v7 with swift-uuidv7
  5. SwiftData
  6. Core Data
  7. CloudKit
  8. Bytes
  9. Comparing
  10. Common pitfalls
  11. Cheat sheet
  12. Try the tools

Apple platforms ship with a native UUID type in Foundation. It covers v4 (random) out of the box. For v7, you need a small dependency.

Quick start

import Foundation

let id = UUID()
print(id.uuidString)  // "0E6F1B8C-2C33-4F1F-9C0B-2A3D4E5F6A7B" (uppercase)

UUID is a struct — value semantics, Hashable, Codable, Sendable. Comparison via == works as expected.

Note: uuidString returns uppercase, unlike most other languages. If you need lowercase for cross-platform consistency:

let id = UUID().uuidString.lowercased()

Parsing

let s = "0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b"
if let id = UUID(uuidString: s) {
    // valid
}

UUID(uuidString:) is a failable initializer that accepts the canonical form (case-insensitive). It does not accept braced or no-hyphens forms.

Codable / JSON

import Foundation

struct User: Codable {
    let id: UUID
    let email: String
}

let user = User(id: UUID(), email: "alice@example.com")
let data = try JSONEncoder().encode(user)
// {"id":"0E6F1B8C-2C33-4F1F-9C0B-2A3D4E5F6A7B","email":"alice@example.com"}

JSONEncoder serializes UUIDs as their uuidString — uppercase by default. To match the lowercase convention used by Postgres / most JS / most APIs, use a custom encoder:

let encoder = JSONEncoder()
encoder.dataEncodingStrategy = .base64
// no built-in UUID strategy, but you can encode manually:

extension UUID {
    var lowercaseString: String { uuidString.lowercased() }
}

struct User: Codable {
    let id: UUID
    let email: String

    enum CodingKeys: CodingKey { case id, email }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id.lowercaseString, forKey: .id)
        try container.encode(email, forKey: .email)
    }
}

For most projects, normalize at the API boundary (server-side) rather than fighting the platform default.

v7 with swift-uuidv7

// Package.swift
.package(url: "https://github.com/wabiverse/swift-uuid-v7", from: "0.1.0")
import UUIDv7

let id = UUIDv7.generate()

Or use a tiny inline implementation if you’d rather avoid the dependency:

import Foundation

extension UUID {
    static func v7() -> UUID {
        let now = UInt64(Date().timeIntervalSince1970 * 1000)
        var bytes = [UInt8](repeating: 0, count: 16)
        // 48-bit timestamp
        for i in 0..<6 {
            bytes[i] = UInt8((now >> (40 - 8 * i)) & 0xff)
        }
        // 74 random bits
        var rand = [UInt8](repeating: 0, count: 10)
        _ = SecRandomCopyBytes(kSecRandomDefault, 10, &rand)
        bytes[6] = (rand[0] & 0x0f) | 0x70    // version 7
        bytes[7] = rand[1]
        bytes[8] = (rand[2] & 0x3f) | 0x80    // variant RFC 4122
        for i in 9..<16 {
            bytes[i] = rand[i - 6]
        }
        return UUID(uuid: (bytes[0], bytes[1], bytes[2], bytes[3],
                          bytes[4], bytes[5], bytes[6], bytes[7],
                          bytes[8], bytes[9], bytes[10], bytes[11],
                          bytes[12], bytes[13], bytes[14], bytes[15]))
    }
}

let id = UUID.v7()

Production code should prefer a maintained library. The above is fine for understanding the structure.

SwiftData

import SwiftData

@Model
final class User {
    @Attribute(.unique) var id: UUID
    var email: String

    init(email: String) {
        self.id = UUID()
        self.email = email
    }
}

SwiftData uses UUID as a primary-key-compatible type natively. For v7, replace UUID() with your v7 generator.

Core Data

For Core Data entities, mark the attribute as a UUID type in the data model. Use a default value or generate in awakeFromInsert():

import CoreData

class User: NSManagedObject {
    @NSManaged var id: UUID
    @NSManaged var email: String

    override func awakeFromInsert() {
        super.awakeFromInsert()
        id = UUID()
    }
}

CloudKit

CloudKit auto-generates record IDs as UUIDs. If you need to control the ID:

let recordID = CKRecord.ID(recordName: UUID().uuidString)
let record = CKRecord(recordType: "User", recordID: recordID)

Bytes

let id = UUID()
withUnsafeBytes(of: id.uuid) { bytes in
    let data = Data(bytes: bytes.baseAddress!, count: 16)
    // RFC 4122 byte order
}

UUID’s uuid property is a 16-tuple in RFC byte order — matches every non-Microsoft platform. SQL Server uniqueidentifier raw bytes need byte-swapping; see UUID vs GUID.

Comparing

let a = UUID()
let b = a
a == b           // true
a.hashValue       // works in Sets and Dictionary keys

UUIDs are Comparable since Swift 5.5 — you can sort an array of UUIDs:

let sorted = uuids.sorted()

For v7 UUIDs this gives chronological order. For v4 it’s a stable but meaningless order.

Common pitfalls

Cheat sheet

GoalCode
Random UUIDUUID()
Time-ordered (v7)UUID.v7() (custom or library)
ParseUUID(uuidString: s)
Lowercase stringid.uuidString.lowercased()
ByteswithUnsafeBytes(of: id.uuid) { ... }

Try the tools