Methods to construct a C appropriate Swift library?
In an effort to create a Swift library that is going to work with C, we’ve got to mess around with unsafe memory pointers to create a C compatible interface. Fortuitously I used to be capable of finding a pleasant instance, which served me as an excellent place to begin, on the Swift forums created by Cory Benfield, so that is what we’ll use on this case. Thanks you. 🙏
remaining class MyType
var rely: Int = 69
@_cdecl("mytype_create")
public func mytype_create() -> OpaquePointer
let sort = MyType()
let retained = Unmanaged.passRetained(sort).toOpaque()
return OpaquePointer(retained)
@_cdecl("mytype_get_count")
public func mytype_get_count(_ sort: OpaquePointer) -> CInt
let sort = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(sort)).takeUnretainedValue()
return CInt(sort.rely)
@_cdecl("mytype_destroy")
public func mytype_destroy(_ sort: OpaquePointer)
_ = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(sort)).takeRetainedValue()
The excellent news is that we do not needed need to create a separate header file for our interfaces, however the Swift compiler can generate it for us if we offer the -emit-objc-header
flag.
I’ve an article about the swiftc command for beginners and I additionally wrote some issues about the Swift compiler, the place I speak in regards to the accessible flags. This time we’ll use the -module-name
choice to specify our module identify, we’ll generate the required recordsdata utilizing the -emit-dependencies
flag, parse the supply recordsdata as a library (-parse-as-library
), since we would prefer to generate a Swift library present the required goal and model info and emit a header file.
swiftc
-module-name mytype
-emit-dependencies
-parse-as-library
-c mytype.swift
-target arm64-apple-macosx12.0
-swift-version 5
-emit-objc-header
-emit-objc-header-path mytype.h
swiftc
-module-name mytype
-emit-dependencies
-parse-as-library
-c mytype.swift
-swift-version 5
-emit-objc-header
-emit-objc-header-path mytype.h
This could generate a mytype.h
and a mytype.o
file plus some extra Swift module associated output recordsdata. We will use these recordsdata to build our remaining executable, however there are a couple of extra extra issues I would like to say.
Beneath Linux the header file will not work. It comprises a line #embrace Basis/Basis.h
and naturally there isn’t any such header file for Linux. It’s doable to put in the GNUstep package (e.g. by way of yum: sudo yum set up gnustep-base gnustep-base-devel gcc-objc
, however for me the clang command nonetheless complained in regards to the location of the objc.h
file. Anyway, I simply eliminated the iclude Basis assertion from the header file and I used to be good to go. 😅
The second factor I would like to say is that if you wish to export a category for Swift, that is going to be a bit more durable, as a result of courses will not be included within the generated header file. You will have two choices on this case. The primary one is to show them into Goal-C courses, however this may result in issues when utilizing Linux, anyway, that is how you are able to do it:
import Basis
@objc public remaining class MyType: NSObject
public var rely: Int = 69
I desire the second choice, when you do not change the Swift file, however you create a separate header file and outline your object sort as a struct with a customized sort (mytype_struct.h
).
typedef struct mytype mytype_t;
We will want this kind (with the corresponding header file), as a result of the mytype_create
perform returns a pointer that we will use to name the opposite mytype_get_count
methodology. 🤔
Compiling C sources utilizing Swift libraries
So how will we use these uncovered Swift objects in C? Within the C programming language you simply need to import the headers after which voilá you should use all the things outlined in these headers.
#embrace <stdio.h>
#embrace "mytype.h"
int predominant()
mytype_t *merchandise = mytype_create();
int i = mytype_get_count(merchandise);
printf("Good day, World! %dn", i);
return 0;
We will use clang to compile the predominant.c
file into an object file utilizing the required header recordsdata.
clang -x objective-c -include mytype.h -include mytype_struct.h -c predominant.c
clang -include mytype.h -include mytype_struct.h -c predominant.c
This command will construct a predominant.o
file, which we will use to create the ultimate executable. 💪
Linking the ultimate executable
This was the toughest half to determine, however I used to be in a position to hyperlink the 2 object recordsdata collectively after a couple of hours of fighting the ld command and other framework tools I made a decision to provide it up and let swiftc
deal with the job, since it could actually construct and hyperlink each C and Swift-based executables.
We will want a listing of the thing recordsdata that we’ll hyperlink collectively.
ls *.o > LinkFileList
Then we will name swiftc
to do the job for us. I suppose it’s going to invoke the ld
command beneath the hood, however I am not a linker knowledgeable, so if extra about this, be happy to succeed in out and provide me more info in regards to the course of. I’ve to learn this book for certain. 📚
swiftc
-sdk /Functions/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk
-F /Functions/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks
-I /Functions/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib
-L /Functions/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib
-L /Customers/tib/swiftfromc/
-module-name Instance
-emit-executable
-Xlinker -rpath
-Xlinker @loader_path @/Customers/tib/swiftfromc/LinkFileList
-Xlinker -rpath
-Xlinker /Functions/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx
-Xlinker -rpath
-Xlinker /Functions/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx
-target arm64-apple-macosx12.1
-Xlinker -add_ast_path
-Xlinker /Customers/tib/swiftfromc/mytype.swiftmodule
-L /Functions/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib
swiftc
-L /residence/ec2-user/swiftfromc
-module-name Instance
-emit-executable
-Xlinker -rpath
-Xlinker @loader_path @/residence/ec2-user/swiftfromc/LinkFileList
The command above will produce the ultimate linked executable file you could run through the use of the ./Instance
snippet and hopefully you may see the “Good day, World! 69” message. 🙈
If you wish to know extra in regards to the rpath linker flag, I extremely suggest studying the article by Marcin Krzyzanowski. If you wish to learn extra about Swift / Goal-C interoperability and utilizing the swiftc command, you must try this article by RDerik. Lastly if you wish to name C code from Swift and go the opposite manner, you must check out my other blog post.