Home IOS Development How one can use a Swift library in C

How one can use a Swift library in C

0
How one can use a Swift library in C

[ad_1]

How one can construct a C suitable Swift library?

So as to create a Swift library that is going to work with C, now we have to mess around with unsafe reminiscence pointers to create a C suitable interface. Thankfully I used to be capable of finding a pleasant instance, which served me as a superb start line, on the Swift boards created by Cory Benfield, so that is what we’ll use on this case. Thanks you. 🙏

remaining 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 crucial 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 novices and I additionally wrote some issues about the Swift compiler, the place I speak concerning the out there flags. This time we’ll use the -module-name choice to specify our module title, we’ll generate the required recordsdata utilizing the -emit-dependencies flag, parse the supply recordsdata as a library (-parse-as-library), since we might wish to generate a Swift library present the mandatory goal and model info and emit a header file.

# macOS
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

# Linux (with out the goal possibility)
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’ll use these recordsdata to construct our remaining executable, however there are just a few extra extra issues I would like to say.

Beneath Linux the header file will not work. It incorporates a line #embrace Basis/Basis.h and naturally there is no such thing as a such header file for Linux. It’s doable 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 concerning the location of the objc.h file. Anyway, I simply eliminated the embrace 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 lessons 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 lessons, however it will 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 depend: Int = 69
}

I desire the second possibility, 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 will use to name the opposite mytype_get_count technique. 🤔

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 need to use all the things outlined in these headers.

#embrace <stdio.h>
#embrace "mytype.h"

int essential() {
    mytype_t *merchandise = mytype_create();

    int i = mytype_get_count(merchandise);
 
    printf("Hiya, World! %dn", i);

    return 0;
}

We will use clang to compile the primary.c file into an object file utilizing the mandatory header recordsdata.

# macOS
clang -x objective-c -include mytype.h -include mytype_struct.h -c essential.c

# Linux
clang -include mytype.h -include mytype_struct.h -c essential.c

This command will construct a essential.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 just a few hours of combating the ld command and different framework instruments I made a decision to present it up and let swiftc handle the job, since it may construct and hyperlink each C and Swift-based executables.

We’ll want a listing of the item 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 skilled, so if extra about this, be at liberty to succeed in out and present me extra data concerning the course of. I’ve to learn this ebook for positive. 📚

# macOS
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

# Linux
swiftc 
    -L /dwelling/ec2-user/swiftfromc 
    -module-name Instance 
    -emit-executable 
    -Xlinker -rpath 
    -Xlinker @loader_path @/dwelling/ec2-user/swiftfromc/LinkFileList

The command above will produce the ultimate linked executable file which you could run by utilizing the ./Instance snippet and hopefully you may see the “Hiya, World! 69” message. 🙈

If you wish to know extra concerning the rpath linker flag, I extremely advocate studying the article by Marcin Krzyzanowski. If you wish to learn extra about Swift / Goal-C interoperability and utilizing the swiftc command, you must take a look at this text by RDerik. Lastly if you wish to name C code from Swift and go the opposite method, you must check out my different weblog put up.

[ad_2]

LEAVE A REPLY

Please enter your comment!
Please enter your name here