I'm trying to interact with an old C terminal app/library from Swift. I've successfully integrated the source code and bridged the headers from C to Swift. The code compiles and runs, I can access all functions from C - library into swift.
There is a structure in C - library which I need to initialize[Function already exists which takes pointer] and assign values to structure variables[Manually].
C-structure:
Struct args{
char ** var1;
unsigned char * var2;
char * var3;
}
and Initialization function call:
init(args * ptr);
How to call the function inside swift and assign values to var1 and var2?
1.Will following snippet successfully initialize the structure?
let Ptr = UnsafeMutablePointer<args>.allocate(capacity: 1)
var args = args()
Ptr.pointee = args
init(Ptr)
2.How to assign values to var1, var2 & var3 assuming we successfully initialize?
They are mapped as:
var1: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!
var2: UnsafeMutablePointer<Uint8>!
var3: UnsafeMutablePointer<Int8>!
For example var1 = {"a", "b"}, var2 = {1,2,3} and var3 = "a"
I've tested following links and did not work:
How to pass an array of Swift strings to a C function taking a char ** parameter : gives 'inout[UnsafeMutablePointer?] to type UnsafeMutablePointer?>!' error
Convert a Swift Array of String to a to a C string array pointer : gives 'inout[UnsafeMutablePointer?] to type UnsafeMutablePointer?>!' error
No built-in support for arrays of C strings : this one needs more efforts and hoping to get easier version
github - Swift wrappers for C functions taking char** arguments : gives 'inout[UnsafeMutablePointer] to type UnsafeMutablePointer?>!' error
This is quite a broad question, so here are some references and observations with a few examples. Hopefully these are helpful.
Please see Apple's documentation for UnsafeMutablePointer
struct and also String
and NSString
:
https://developer.apple.com/documentation/swift/unsafemutablepointer
https://developer.apple.com/documentation/swift/string
https://developer.apple.com/documentation/foundation/nsstring
Another useful reading is Apple's docs about C and Swift interop: https://developer.apple.com/documentation/swift/imported_c_and_objective_c_apis
In this answer I'm also leaving out a lot of memory management aspects as well as things such as keeping track of the size of var1
and var2
arrays, since I don't know the specifics of your library.
Regarding the snippet for initializing the structure, you can't use the type name as the variable name and init
will confuse the Swift compiler because it's reserved for naming class initializers. Let's name the variable myArgs
instead of args
and assume the C library initialization function is named initialize
; if it's indeed init
, one can easily write a wrapper named differently. Another problem with the snippet is that myArgs
will remain unchanged after initialization, Ptr
will actually get initialized, so you would have to use Ptr
to access the initialized args
structure. Thus we can omit Ptr
and use implicit bridging to pass myArgs
to the initialization function. The snippet becomes
var myArgs = args()
initialize(&myArgs)
Now you can access the members as follows:
// Assuming var1 is an array of at least 2 C strings.
// See Swift documentation about optionals on how to deal with
// cases when this assumption breaks down
let s1 = String(cString: myArgs.var1[0]!) // 1st element of var1
let s2 = String(cString: myArgs.var1[1]!) // 2nd element of var1
myArgs.var2.pointee // 1st element of var2
(myArgs.var2 + 1).pointee // 2nd element of var2
let s = String(cString: myArgs.var3) // value of var3
Now let's set var1 to be {"aa", "bbb"}
:
var var1Buffer =
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
var var1a : NSString = "aa"
var var1b : NSString = "bbb"
var var1aBuffer = UnsafeMutablePointer<Int8>.allocate(
capacity: var1a.length + 1)
var var1bBuffer = UnsafeMutablePointer<Int8>.allocate(
capacity: var1b.length + 1)
if (var1a.getCString(var1aBuffer, maxLength: var1a.length + 1,
encoding: String.Encoding.utf8.rawValue)
&& var1b.getCString(var1bBuffer, maxLength: var1b.length + 1,
encoding: String.Encoding.utf8.rawValue)) {
var1Buffer[0] = var1aBuffer
var1Buffer[1] = var1bBuffer
myArgs.var1 = var1Buffer
} else { print("Encoding failed...")}
Here is an example of setting var2 to be an array of 5 elements equal to 200:
var var2Buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 5);
var2Buffer.initialize(repeating: 200, count: 5)
myArgs.var2 = var2Buffer
And setting the value of var3:
let newVar3 : NSString = "This is new variable 3"
var var3Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: newVar3.length + 1)
if (newVar3.getCString(var3Buffer, maxLength: newVar3.length + 1, encoding: String.Encoding.utf8.rawValue)) {
myArgs.var3 = var3Buffer
} else { print("Encoding failed...") }
The above examples assume UTF8 encoding.