I am using an existing C library in a Swift app and trying to convert a C character buffer to a Swift String.
Bridging.h
typedef struct { char mfg[8]; char model[8]; } motorcycle;
void GetMotorcycle(motorcycle *m);
Example.swift
var cycle = motorcycle(mfg: (0,0,0,0,0,0,0,0), model: (0,0,0,0,0,0,0,0));
GetMotorcycle(&cycle)
var manufacturer : String = String.fromCString(cycle.mfg) // Error
This produces "Could not find a overload for 'fromCString' that accepts the supplied arguments"
Since Swift treats the C character array as a tuple, I cannot find a way to convert it to a Swift String.
Well, at least that C character array is only eight characters long, since there's currently no way to iterate over a tuple. Here's how you can convert it:
func char8ToString(tuple: (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar)) -> String {
let arr: unichar[] = [unichar(tuple.0), unichar(tuple.1),
unichar(tuple.2), unichar(tuple.3),
unichar(tuple.4), unichar(tuple.5),
unichar(tuple.6), unichar(tuple.7)]
let len = arr.reduce(0) { $1 != 0 ? $0 + 1 : $0 }
return NSString(characters: arr, length: len)
}
var manufacturer: String = char8ToString(cycle.mfg)
Hopefully we get language support for a better way to handle this case soon!
Swift 5.2 — this solution can accepts arbitrary-length tuples, doesn’t do any extra copying, and uses the latest conventions for safe memory binding.
extension String {
init<T>(tupleOfCChars: T, length: Int = Int.max) {
self = withUnsafePointer(to: tupleOfCChars) {
let lengthOfTuple = MemoryLayout<T>.size / MemoryLayout<CChar>.size
return $0.withMemoryRebound(to: UInt8.self, capacity: lengthOfTuple) {
String(bytes: UnsafeBufferPointer(start: $0, count: Swift.min(length, lengthOfTuple)), encoding: .utf8)!
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With