The right way to construct a C appropriate Swift library?
With a purpose to create a Swift library that is going to work with C, we have now to mess around with unsafe reminiscence pointers to create a C appropriate interface. Happily I used to be capable of finding a pleasant instance, which served me as start line, on the Swift boards created by Cory Benfield, so that is what we’ll use on this case. Thanks you. 🙏
closing class MyType {
var depend: Int = 69
}
@_cdecl("mytype_create")
public func mytype_create() -> OpaquePointer {
let kind = MyType()
let retained = Unmanaged.passRetained(kind).toOpaque()
return OpaquePointer(retained)
}
@_cdecl("mytype_get_count")
public func mytype_get_count(_ kind: OpaquePointer) -> CInt {
let kind = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(kind)).takeUnretainedValue()
return CInt(kind.depend)
}
@_cdecl("mytype_destroy")
public func mytype_destroy(_ kind: OpaquePointer) {
_ = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(kind)).takeRetainedValue()
}
The excellent news is that we do not vital should 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 novices and I additionally wrote some issues about the Swift compiler, the place I speak in regards to the obtainable flags. This time we’ll use the -module-name
choice to specify our module identify, we’ll generate the required information utilizing the -emit-dependencies
flag, parse the supply information as a library (-parse-as-library
), since we would wish to generate a Swift library present the mandatory goal and model data 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 information. We’ll use these information to construct our closing executable, however there are a number of extra extra issues I would like to say.
Below Linux the header file will not work. It incorporates a line #embrace Basis/Basis.h
and naturally there isn’t a such header file for Linux. It’s attainable to put in the GNUstep bundle (e.g. through 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 tougher, as a result of courses will not be included within the generated header file. You might have two choices on this case. The primary one is to show them into Goal-C courses, however this can result in issues when utilizing Linux, anyway, that is how you are able to do it:
import Basis
@objc public closing class MyType: NSObject {
public var depend: Int = 69
}
I favor the second choice, when you do not change the Swift file, however you create a separate header file and outline your object kind as a struct with a customized kind (mytype_struct.h
).
typedef struct mytype mytype_t;
We’ll want this kind (with the corresponding header file), as a result of the mytype_create
operate returns a pointer that we are able to use to name the opposite mytype_get_count
technique. 🤔
Compiling C sources utilizing Swift libraries
So how can we use these uncovered Swift objects in C? Within the C programming language you simply should import the headers after which voilá you should use all the pieces outlined in these headers.
#embrace <stdio.h>
#embrace "mytype.h"
int primary() {
mytype_t *merchandise = mytype_create();
int i = mytype_get_count(merchandise);
printf("Howdy, World! %dn", i);
return 0;
}
We will use clang to compile the primary.c
file into an object file utilizing the mandatory header information.
clang -x objective-c -include mytype.h -include mytype_struct.h -c primary.c
clang -include mytype.h -include mytype_struct.h -c primary.c
This command will construct a primary.o
file, which we are able to use to create the ultimate executable. 💪
Linking the ultimate executable
This was the toughest half to determine, however I used to be capable of hyperlink the 2 object information collectively after a number of hours of battling the ld command and different framework instruments I made a decision to offer it up and let swiftc
care for the job, since it could possibly construct and hyperlink each C and Swift-based executables.
We’ll want a listing of the article information that we’ll hyperlink collectively.
ls *.o > LinkFileList
Then we are able to name swiftc
to do the job for us. I suppose it’s going to invoke the ld
command underneath the hood, however I am not a linker skilled, so if you understand extra about this, be at liberty to succeed in out and present me extra data in regards to the course of. I’ve to learn this ebook for positive. 📚
swiftc
-sdk /Purposes/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk
-F /Purposes/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks
-I /Purposes/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib
-L /Purposes/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 /Purposes/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx
-Xlinker -rpath
-Xlinker /Purposes/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 /Purposes/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib
swiftc
-L /house/ec2-user/swiftfromc
-module-name Instance
-emit-executable
-Xlinker -rpath
-Xlinker @loader_path @/house/ec2-user/swiftfromc/LinkFileList
The command above will produce the ultimate linked executable file that you would be able to run through the use of the ./Instance
snippet and hopefully you will see the “Howdy, 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, it’s best to try this text by RDerik. Lastly if you wish to name C code from Swift and go the opposite method, it’s best to check out my different weblog publish.