Hi, I’m an expert C/C++ coder, and mostly comfortable in Swift, but I often end up needing to bridge to C APIs, and this always gets me confused and frustrated due to all the all the different unsafe pointer and buffer types and “withUnsafe…” methods.
Today’s saga: All I want to do is initialize a C struct (an array of 32 bytes) from a Swift Data object. (”All I wanted was a Pepsi! Just one Pepsi! And she wouldn’t give it to me!”)
I’ve got a C declaration like this:
typedef struct { uint8_t bytes[32]; } SHSPublicKey; ///< A 256-bit Ed25519 public key.
It gets mapped to Swift as the rather silly-looking
public struct SHSPublicKey {
public init() ///< A 256-bit Ed25519 public key.
public init(bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8))
///< A 256-bit Ed25519 public key.
public var bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
}
My handmade Swift wrapper is:
struct PublicKey {
init?(_ data: Data) {
guard data.count == MemoryLayout<SHSPublicKey>.size else {return nil}
_key = SHSPublicKey() # Ugh, compiler forces redundant init
withUnsafeMutablePointer(to: &_key) { ptr in
data.copyBytes(to: ptr, count: MemoryLayout<SHSPublicKey>.size) // ERROR
}
}
fileprivate var _key: SHSPublicKey // HACK: Should be `let`
}
This fails with: error: cannot convert value of type 'UnsafeMutablePointer<SHSPublicKey>' to expected argument type 'UnsafeMutablePointer<UInt8>' because instance method 'copyBytes(to:count:)' was not imported from C header
… which makes no sense to me because copyBytes
is a method of the standard Data
class, not something from my C header. … Help?
(Also note that Swift forced me to zero-initialize _key
even though it’s redundant, saying I can’t pass it as inout
if it’s uninitialized. And _key
really should be declared as let
since it’s supposed to be immutable, but if I do that, Swift won’t let me pass it as an inout parameter to initialize it )
PS: If there is any good book / article about C interop and pointers, I’d love to know. The Swift book says nothing, the Apple API docs just describe individual methods without showing how to use them, and the blog posts I’ve found are pretty shallow and don’t discuss pointers.