eee0910680
Funde 1.5.1 en 1.6.0. Parte 1 dedup (prevención): StableID (UUIDv5 namespace+nombre), Account.defaultAccountStableID, Category.stableID(for:) + asignación en createDefaultCategories. Pendiente: asignar en repos + limpieza de duplicados existentes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
33 lines
1.6 KiB
Swift
33 lines
1.6 KiB
Swift
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]))
|
|
}
|
|
}
|