Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 5.0: 'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(...)

I previously used this code in Swift 4.2 to generate an id:

public static func generateId() throws -> UInt32 {
    let data: Data = try random(bytes: 4)
    let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
    return value // + some other stuff 
}

withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?

like image 653
Baran Emre Avatar asked Mar 27 '19 13:03

Baran Emre


3 Answers

In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:

let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }

(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.

Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:

let randomId = UInt32.random(in: .min ... .max)
like image 193
Martin R Avatar answered Nov 17 '22 12:11

Martin R


On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.

Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.

Example reading from the pointer buffer (code is unrelated to the question):

var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
    guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
        return
    }
    reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}

Example writing to the buffer pointer (code is unrelated to the question):

try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
    let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
                                      passphrase,
                                      passphrase.utf8.count,
                                      salt,
                                      salt.utf8.count,
                                      CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
                                      rounds,
                                      outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
                                              kCCKeySizeAES256)
    guard status == kCCSuccess else {
        throw Error.keyDerivationError
    }
}

The code from the question would look like:

let value = data.withUnsafeBytes { 
    $0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}

In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.

like image 22
Eneko Alonso Avatar answered Nov 17 '22 13:11

Eneko Alonso


One more way to fix this warning to use bindMemory(to:).

var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
    guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
        return Int32(kCCMemoryFailure)
    }
    return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
like image 7
Ramis Avatar answered Nov 17 '22 13:11

Ramis