62

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?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
Baran
  • 2,710
  • 1
  • 23
  • 23

4 Answers4

79

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)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 4
    Can you tell me how go with this one: data.withUnsafeBytes { CC_SHA256($0, CC_LONG(data.count), &buffer) } ?? – Michal Zaborowski Mar 27 '19 at 16:00
  • 3
    @mientus: Have a look at the Swift 5 update in https://stackoverflow.com/a/25762128/1187415. – Martin R Mar 27 '19 at 16:12
  • 1
    @MartinR when there is nil/unreal then i got `Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)` how can we handle it ? – Jack Mar 30 '21 at 11:00
26

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.

Eneko Alonso
  • 18,884
  • 9
  • 62
  • 84
  • I used another way to implement the second example. This avoids using `withUnsafeMutableBytes`: `var derivedKeyData = [UInt8](repeating: 0, count: keyByteCount);let derivationStatus = CCKeyDerivationPBKDF(CCPseudoRandomAlgorithm(kCCPBKDF2),passphrase,passphrase.utf8.count,Array(salt),salt.count,hash,rounds,&derivedKeyData,derivedKeyData.count)`. – Baran Apr 04 '19 at 08:04
  • 1
    True, in some cases a pointer (indirection) to an allocated integer array works quite easer. Converting back and forth from Data to array of `UInt8` is also quite straightforward. – Eneko Alonso Apr 04 '19 at 14:10
  • I get error : Value of type 'UnsafePointer<_>' has no member 'baseAddress' – iosbegindevel Oct 18 '19 at 07:11
7

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)
}
Ramis
  • 13,985
  • 7
  • 81
  • 100
  • Instead of `rawBytes.bindMemory(to: UInt8.self).baseAddress` I think you want to use `rawBytes.baseAddress`, as the later one is always just a pointer to the very first data byte, ignoring any type or alignment requirement. – Mecki Jun 29 '22 at 12:52
3

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>
}
Mike Critchley
  • 1,643
  • 15
  • 20