UUID with version 3, 5 and name spaces

在 Swift 裏頭,預設的 UUID 只能從 UUID() 來產生,或者是從另一個 UUID 來產生, 這邊來記錄一下如何從 String 來產生 UUID。 首先,先在 Bridge-Header.h 裡頭加入

#import <CommonCrypto/CommonCrypto.h>

再來寫個 UUID 的 extension:

extension UUID {
    enum UUIDVersion: Int {
        case v3 = 3
        case v5 = 5
    }
    
    enum UUIDv5NameSpace: String {
        case dns  = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
        case url  = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
        case oid  = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
        case x500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
    }
    
    init(version: UUIDVersion, name: String, nameSpace: UUIDv5NameSpace) {
        // Get UUID bytes from name space:
        let spaceUID = UUID(uuidString: nameSpace.rawValue)!.uuid
        var data = withUnsafePointer(to: spaceUID) {
            Data(bytes: $0, count: MemoryLayout.size(ofValue: spaceUID))
        }
        
        // Append name string in UTF-8 encoding:
        data.append(contentsOf: name.utf8)
        
        // Compute digest (MD5 or SHA1, depending on the version):
        var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
        data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> Void in
            switch version {
            case .v3:
                _ = CC_MD5(ptr, CC_LONG(data.count), &digest)
            case .v5:
                _ = CC_SHA1(ptr, CC_LONG(data.count), &digest)
            }
        }
        
        // Set version bits:
        digest[6] &= 0x0F
        digest[6] |= UInt8(version.rawValue) << 4
        // Set variant bits:
        digest[8] &= 0x3F
        digest[8] |= 0x80
        
        // Create UUID from digest:
        self = NSUUID(uuidBytes: digest) as UUID
    }
}

就可以使用新的 init method 來產生新的 UUID:

UUID(version: UUIDVersion, name: String, nameSpace: UUIDv5NameSpace)
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus