Search code examples
iosswiftuuid

Generate UUID from name space?


Trying to generate a client Id, based on 2 unique strings. This should end up being the same UUID as generated in the server, from same I'ds.

With Javascript it looks something like this:

uuidv5(id1 + id2 , uuidv5.DNS);

Can't seem to find a way of generating this on Swift, NSUUID only can generate a UUID from nothing

NSUUID().uuidString

I am looking for something like this:

NSUUID(namespace: id1 + id2).uuidString

Edit

Example:

let sorted = ["5a23dbfb2626b400190998fc", "5pCAvA7h8k9JuErRn"]
let appended = sorted.seperaredStringBy("-")
let result = uuidv5(appended , uuidv5.DNS)

//results:
2522b097-8532-548e-a18b-9366c6511b5e

Solution

  • The Swift standard library or the Foundation framework have no built-in method for version 5 UUIDs, as far as I know.

    Here is a possible implementation for version 3 and version 5 UUIDs, taken from the description at Generating v5 UUID. What is name and namespace? and the reference implementation in RFC 4122.

    (Updated for Swift 4 and later.)

    import Foundation
    import CommonCrypto
    
    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:
            var spaceUID = UUID(uuidString: nameSpace.rawValue)!.uuid
            var data = withUnsafePointer(to: &spaceUID) { [count =  MemoryLayout.size(ofValue: spaceUID)] in
                Data(bytes: $0, count: count)
            }
    
            // 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: UnsafeRawBufferPointer) -> Void in
                switch version {
                case .v3:
                    _ = CC_MD5(ptr.baseAddress, CC_LONG(data.count), &digest)
                case .v5:
                    _ = CC_SHA1(ptr.baseAddress, 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
        }
    }
    

    Example 1 (Your case):

    let uuid = UUID(version: .v5, name: "5a23dbfb2626b400190998fc-5pCAvA7h8k9JuErRn", nameSpace: .dns)
    print(uuid) // 2522B097-8532-548E-A18B-9366C6511B5E
    

    Example 2 (From Appendix B in RFC 4122, as corrected in the Errata):

    let uuid = UUID(version: .v3, name: "www.widgets.com", nameSpace: .dns)
    print(uuid) //3D813CBB-47FB-32BA-91DF-831E1593AC29