import Foundation import CryptoKit /// Deterministic UUIDs (RFC 4122 v5) for records that every device/install must /// create identically — the default Account and the default Categories. /// /// Why: `awakeFromInsert()` assigns a random UUID, so each device seeded its own /// "Default" account + default categories with *different* UUIDs before the first /// CloudKit import. CloudKit keys on the UUID (record name), so those identical-in- /// meaning records piled up as distinct records (8 accounts, 23 categories in the /// wild). Giving the defaults a STABLE UUID derived from a fixed namespace + name /// means every device produces the SAME record name → CloudKit natively merges /// them into one. No dedup, no deletes, no cascade — the root-cause fix. enum StableID { /// Fixed app namespace (generated once, constant forever — never change it). static let namespace = UUID(uuidString: "F3A1C2D4-5E6F-4A7B-8C9D-0E1F2A3B4C5D")! /// RFC 4122 v5 UUID: SHA-1(namespace bytes ‖ name) with version/variant bits set. static func v5(_ name: String) -> UUID { var hasher = Insecure.SHA1() withUnsafeBytes(of: namespace.uuid) { hasher.update(bufferPointer: $0) } hasher.update(data: Data(name.utf8)) let digest = Array(hasher.finalize()) // 20 bytes; take first 16 var b = Array(digest.prefix(16)) b[6] = (b[6] & 0x0F) | 0x50 // version 5 b[8] = (b[8] & 0x3F) | 0x80 // RFC 4122 variant return UUID(uuid: (b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15])) } }