I noticed some unusual behaviour when working with a C library which took strings in as const char *
(which is converted to Swift as UnsafePointer
As mentioned in the comments, this is a clear bug in Swift.
Here's a workaround I'm using. If you can't trust Swift to convert the strings to pointers for you, then you've got to do it yourself.
Assuming C function defined as:
void multiString(const char *arg0, const char *arg1, const char *arg2);
Swift code:
func callCFunction(arg0: String?, arg1: String?, arg2: String?) {
let dArg0 = arg0?.data(using: .utf8) as NSData?
let pArg0 = dArg0?.bytes.assumingMemoryBound(to: Int8.self)
let dArg1 = arg1?.data(using: .utf8) as NSData?
let pArg1 = dArg1?.bytes.assumingMemoryBound(to: Int8.self)
let dArg2 = arg2?.data(using: .utf8) as NSData?
let pArg2 = dArg2?.bytes.assumingMemoryBound(to: Int8.self)
multiString(pArg1, pArg2, pArg3)
}
Warning:
Don't be tempted to put this in a function like:
/* DO NOT USE -- BAD CODE */
func ocstr(_ str: String?) -> UnsafePointer<Int8>? {
guard let str = str else {
return nil
}
let nsd = str.data(using: .utf8)! as NSData
//This pointer is invalid on return:
return nsd.bytes.assumingMemoryBound(to: Int8.self)
}
which would remove repeated code. This doesn't work because the data object nsd
gets deallocated at the end of the function. The pointer is therefore not valid on return.
Just to be clear, there is a workaround until Apple fixes this. Unwrap your optional Strings before passing them and everything will work fine.
var anOptional: String?
var anotherOptional: String?
func mySwiftFunc() {
let unwrappedA = anOptional!
let unwrappedB = anotherOptional!
myCStringFunc(unwrappedA, unwrappedB)
}
I didn't find anything useful on if this is desired behaviour or just a bug.
The pragmatic solution would probably be to just have a proxy method like this, but you probably did something similar already.
func proxy(_ str: String?, _ functionToProxy: (UnsafePointer<UInt8>?) -> ()) {
if let str = str {
functionToProxy(str)
} else {
functionToProxy(nil)
}
}
proxy(input, test)
Did you test if it was working in Swift 2? They changed something maybe related in Swift 3:
https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md