Get Data from UUID in Swift 3

2020-07-10 09:56发布

问题:

I have the following code written in Objective C that I am trying to get working in Swift 3. Some of the functions equivalents do not appear to be available in Swift 3. Here is the code is the code in Objective C

NSUUID *vendorIdentifier = [[UIDevice currentDevice] identifierForVendor];
uuid_t uuid;
[vendorIdentifier getUUIDBytes:uuid];
NSData *vendorData = [NSData dataWithBytes:uuid length:16];

and my current effort in Swift 3 which compiles and runs but is not giving the correct answer.

let uuid = UIDevice.current.identifierForVendor?.uuidString
let uuidData = uuid?.data(using: .utf8)
let uuidBytes = uuidData?.withUnsafeBytes { UnsafePointer<UInt8>($0) }
let vendorData : NSData  = NSData.init(bytes: uuidBytes, length: 16)
let hashData = NSMutableData()
hashData.append(vendorData as Data)

回答1:

The uuid property of UUID is a C array with is imported to Swift as a tuple. Using the fact that Swift preserves the memory layout of imported C structures, you can pass a pointer to the tuple to the Data(bytes:, count:) constructor:

if let vendorIdentifier = UIDevice.current.identifierForVendor {
    var uuid = vendorIdentifier.uuid
    let data = withUnsafePointer(to: &uuid) {
        Data(bytes: $0, count: MemoryLayout.size(ofValue: uuid))
    }

    // ...
}

As of Swift 4.2 (Xcode 10) your don't need to make a mutable copy first:

if let vendorIdentifier = UIDevice.current.identifierForVendor {
    let data = withUnsafePointer(to: vendorIdentifier.uuid) {
        Data(bytes: $0, count: MemoryLayout.size(ofValue: vendorIdentifier.uuid))
    }

    // ...
}


回答2:

Here's one possible way. Note that identifierForVendor returns UUID in Swift 3. UUID has a uuid property which gives you a uuid_t. uuid_t is a tuple of 16 UInt8 values.

So the trick is converting the tuple of bytes into an array of bytes. Then it's trivial to create the Data from the array.

if let vendorIdentifier = UIDevice.current.identifierForVendor {
    let uuid = vendorIdentifier.uuid // gives a uuid_t
    let uuidBytes = Mirror(reflecting: uuid).children.map({$0.1 as! UInt8}) // converts the tuple into an array
    let vendorData = Data(bytes: uuidBytes)
}

If anyone knows a better way to convert a tuple of UInt8 into an array of UInt8, please speak up.



回答3:

This extension I made seems to work great without using reflection, nor pointers. It depends on the fact that UUID in Swift is represented as a tuple of 16 UInt8s which can simply be unwrapped like so:

extension UUID{
    public func asUInt8Array() -> [UInt8]{
        let (u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16) = self.uuid
        return [u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16]
    }
    public func asData() -> Data{
        return Data(self.asUInt8Array())
    }
}


回答4:

To translate a UUID to Data in Swift 4.2, I used this:

let uuid = UUID()
withUnsafeBytes(of: uuid.uuid, { Data($0) })


回答5:

Swift 4.2 Extension

public extension UUID {

    var data: Data {
        return withUnsafeBytes(of: self.uuid, { Data($0) })
    }

}