Last active
July 25, 2025 19:21
-
-
Save niw/9d3a201b165495fd3ccde48e1b75a49c to your computer and use it in GitHub Desktop.
A simple UUIDv7 implementation in Swift.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UUIDv7.swift | |
// UUIDv7 | |
// | |
// Created by Yoshimasa Niwa on 7/25/25. | |
// | |
import Foundation | |
import Security | |
extension UUID { | |
public static func v7() -> Self { | |
v7(timestamp: Date()) | |
} | |
// A simple UUIDv7 implementation. | |
// It is only provides monotonicity up to milliseconds granularity. | |
// Note that RFC 9415 defines optional implementation about increased monotonicity, | |
// which can be implemented. | |
// See section 5.7 and 6.2 in <https://www.rfc-editor.org/rfc/rfc9562.txt>. | |
static func v7(timestamp: Date) -> Self { | |
// 48-bit UNIX Epoc timestamp in milliseconds | |
let timestamp = UInt64(timestamp.timeIntervalSince1970 * 1000) | |
// 80-bit cryptographically secure pseudo random number. | |
var randomBytes = [UInt8](repeating: 0, count: 10) | |
if SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) != errSecSuccess { | |
fatalError("Failed to generate secure random bytes") | |
} | |
let uuid = ( | |
UInt8((timestamp >> 40) & 0xFF), | |
UInt8((timestamp >> 32) & 0xFF), | |
UInt8((timestamp >> 24) & 0xFF), | |
UInt8((timestamp >> 16) & 0xFF), | |
UInt8((timestamp >> 8) & 0xFF), | |
UInt8(timestamp & 0xFF), | |
randomBytes[0] & 0x0F | 0x70, // version | |
randomBytes[1], | |
randomBytes[2] & 0x3F | 0x80, // variant | |
randomBytes[3], | |
randomBytes[4], | |
randomBytes[5], | |
randomBytes[6], | |
randomBytes[7], | |
randomBytes[8], | |
randomBytes[9], | |
) | |
return UUID(uuid: uuid) | |
} | |
var version: UInt8 { | |
(uuid.6 & 0xF0) >> 4 | |
} | |
public var timestamp: Date? { | |
guard version == 0x07 else { | |
return nil | |
} | |
let timestamp = | |
UInt64(uuid.0) << 40 | | |
UInt64(uuid.1) << 32 | | |
UInt64(uuid.2) << 24 | | |
UInt64(uuid.3) << 16 | | |
UInt64(uuid.4) << 8 | | |
UInt64(uuid.5) | |
return Date(timeIntervalSince1970: Double(timestamp) / 1000.0) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment