In Swift 2, the C function
size_t embeddedSize ( const char *_Nullable string );
is mapped to Swift as
func embeddedSize(string: UnsafePointer<Int8>) -> Int
and you can pass a (non-optional) Swift string as the argument,
as documented in
Interacting with C APIs in the
"Using Swift with Cocoa and Objective-C" reference:
Constant Pointers
When a function is declared as taking a UnsafePointer<Type>
argument, it can accept any of the following:
- ...
- A
String
value, if Type
is Int8
or UInt8
. The string will automatically be converted to UTF8 in a buffer, and a pointer to that buffer is passed to the function.
- ...
You can also pass nil
because in Swift 2, nil
is an allowed value
for UnsafePointer
.
As @zneak pointed out, the "automatic conversion" to UTF-8 does not work for optional strings in Swift 2, so you have to (conditionally)
unwrap the string:
let someString: String? = "Some String"
let s2: size_t
if let str = someString {
s2 = embeddedSize(str)
} else {
s2 = embeddedSize(nil)
}
Using the map
method of Optional
and the nil-coalescing
operator ??
, this can be written more compactly as
let someString: String? = "Some String"
let s2 = someString.map { embeddedSize($0) } ?? embeddedSize(nil)
One generic solution was suggested by @zneak.
Here is another possible solution. String
has a method
func withCString<Result>(@noescape f: UnsafePointer<Int8> throws -> Result) rethrows -> Result
which calls the closure with a pointer to the UTF-8 representation
of the string, life-extended through the execution of f
. So
for a non-optional string, the following two statements are
equivalent:
let s1 = embeddedSize("Test")
let s1 = "Test".withCString { embeddedSize($0) }
We can define a similar method for optional strings. Since
extensions of generic types can restrict the type placeholder only
to protocols and not to concrete types, we have to define a
protocol that String
conforms to:
protocol CStringConvertible {
func withCString<Result>(@noescape f: UnsafePointer<Int8> throws -> Result) rethrows -> Result
}
extension String: CStringConvertible { }
extension Optional where Wrapped: CStringConvertible {
func withOptionalCString<Result>(@noescape f: UnsafePointer<Int8> -> Result) -> Result {
if let string = self {
return string.withCString(f)
} else {
return f(nil)
}
}
}
Now the above C function can be called with the optional
string argument as
let someString: String? = "Some String"
let s2 = someString.withOptionalCString { embeddedSize($0) }
For multiple C string arguments, the closure can be nested:
let string1: String? = "Hello"
let string2: String? = "World"
let result = string1.withOptionalCString { s1 in
string2.withOptionalCString { s2 in
calculateTotalLength(s1, s2)
}
}
Apparently, the problem has been solved in Swift 3.
Here the C function is mapped to
func embeddedSize(_ string: UnsafePointer<Int8>?) -> Int
and passing a String?
compiles and works as expected, both for
nil
and non-nil
arguments.