This came up recently on Swift Forums. Check out this thread, which has a bunch of good advice.
In this case I’m going to specifically recommend the code that I posted there. Using an array for the digest makes sense because your final goal is to create a hex representation (
md5Hex
) and thus there’s no reason to use any of the alternative techniques. These alternatives have benefits (like avoiding an extra allocation), but they won’t benefit you.
OOPer wrote:
The details of type inference in Swift is not clearly documented and hard to predict, especially when closure is involved.
Indeed. In this specific case, however, the difference in behaviour is tied to a well-known Swift oddity. To understand this, you must first realise that Swift 5 introduced a new API on
Data
that uses the
Unsafe[Mutable]RawBufferPointer
. So now you have two methods in play:
func withUnsafe[Mutable]Bytes<ResultType, ContentType>(_ body: (Unsafe[Mutable]Pointer<ContentType>) throws -> ResultType) rethrows -> ResultType
func withUnsafe[Mutable]Bytes<ResultType> (_ body: (Unsafe[Mutable]RawBufferPointer ) throws -> ResultType) rethrows -> ResultType
with the first one being deprecated.
Now consider this code snippet:
digestData.withUnsafeMutableBytes { digestBytes -> Void in
// ^
// 'withUnsafeMutableBytes' is deprecated: use `withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead
messageData.withUnsafeBytes { messageBytes in
_ = CC_MD5(messageBytes.baseAddress, CC_LONG(messageData.count), digestBytes)
}
}
Both closures are a single statement, and Swift will do type inferencing ‘through’ a single-statement closure. Thus, it can use the type of the third parameter of
CC_MD5
to infer the type of
digestBytes
as an
UnsafeMutablePointer<UInt8>
. That does not match
Unsafe[Mutable]RawBufferPointer
, so you end up using the original deprecated API, and hence the deprecation warning.
Contrast this with this snippet:
digestData.withUnsafeMutableBytes { digestBytes -> Void in
messageData.withUnsafeBytes { messageBytes in
_ = CC_MD5(messageBytes.baseAddress, CC_LONG(messageData.count), digestBytes)
// ^
// Cannot convert value of type 'UnsafeMutableRawBufferPointer' to expected argument type 'UnsafeMutablePointer<UInt8>?'
print("Hello Cruel World")
}
}
All I’ve done is add a call to
print
in the inner closure. This makes the closure a multi-statement closure, which disables type inferencing. Swift then chooses the
Unsafe[Mutable]RawBufferPointer
method, resulting in the type mismatch error.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"