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

SwiftDeprecatedUnsafe Pointers

Swift Problem Overview


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?

Swift Solutions


Solution 1 - Swift

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 https://stackoverflow.com/questions/38023838/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)

Solution 2 - Swift

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.

Solution 3 - Swift

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)
}

Solution 4 - Swift

I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer

Original code from a tutorial I was working on.

--> where input: Data

--> where stream: compression_stream

//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in

//holder
var output = Data()

//Source and destination buffers
stream.src_ptr = srcPointer  //UnsafePointer<UInt8>
stream.src_size = input.count
 etc. 
}

Code with a conversion to make the above code work with a valid method

return input.withUnsafeBytes { bufferPtr in
    
//holder
var output = Data()
    
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
    
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
   return output
}
    
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
    
// Jump back into the original method
stream.src_ptr = typedPointer  //UnsafePointer<UInt8>
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBaran EmreView Question on Stackoverflow
Solution 1 - SwiftMartin RView Answer on Stackoverflow
Solution 2 - SwiftEneko AlonsoView Answer on Stackoverflow
Solution 3 - SwiftRamisView Answer on Stackoverflow
Solution 4 - SwiftMike CritchleyView Answer on Stackoverflow