What is the best way to call into Swift from C?
Asked Answered
S

3

25

Calling into C from Swift is pretty simple, however I'm looking into making a bi-directional wrapper in C, so my C has to call Swift functions.

Right now, I can do this by declaring function pointers in C, and having my C functions call them after the Swift side has set them up to call code in Swift.

My C header file:

typedef void (*callback_t)(void);

void callBackIntoSwift( callback_t cb );

My C implementation file:

#include "stuff.h"
#include <stdio.h>

void callBackIntoSwift( callback_t cb )
{
    printf( "Will call back into Swift\n" );
    cb();
    printf( "Did call back into Swift\n" );
}

After including my C header file in the bridging header, I can do the following on the Swift side:

let cb: callback_t = {
    someKindOfSwiftFunction()
}

callBackIntoSwift( cb )

Or even:

callBackIntoSwift {
    someKindOfSwiftFunction()
}

Is there a better way to do this, where function pointers and callbacks are not needed? I'd like to let the C-side call someKindOfSwiftFunction directly … but when I try to apply @convention (c) to function declarations I get the message that the attribute can only be applied to types, and not declarations.

Any ideas or codebases in e.g. Github I can take a look at?

Stocktaking answered 30/1, 2016 at 19:29 Comment(0)
D
15

According to Joe Groff:

There’s no official way yet. Aside from name mangling, Swift functions use a different calling convention from C. Unofficially, if you’re willing to deal with more than the usual amount of code breakage and compiler bugs, there’s an unofficial attribute @_cdecl that does this:

@_cdecl("mymodule_foo")
func foo(x: Int) -> Int {
  return x * 2
}

which you can then call from C:

#include <stdint.h>

intptr_t mymodule_foo(intptr_t);

intptr_t invoke_foo(intptr_t x) {
  return mymodule_foo(x);
}
Drugget answered 12/2, 2018 at 17:54 Comment(1)
Altough this answer is correct, I had some troubles getting things to work. That's why I made a little article.Reactor
U
0

Passing values between C and Swift

In C

 extern char *mySwiftFunction(char *valuefromc);
 int checkSwiftFuncation(void )
 {
        char  *retval = mySwiftFunction("samplevalue");
        printf("value %s\n",retval);
        return 0;
}

In Swift

@_silgen_name("mySwiftFunction")  // vital for the function being visible from C
 func mySwiftFunction(valuefromc: UnsafePointer<Int8>) -> UnsafeMutablePointer<Int8>
{

    let value = String(cString: valuefromc, encoding: .utf8)
    print ("mySwiftFUnction called in Swift with \(value!)")
    let retmsg = "return message"
    return retmsg.charpointer
}



extension String {
    var charpointer: UnsafeMutablePointer<Int8> {
        return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
    }}
Unwary answered 3/9, 2021 at 11:23 Comment(1)
Good example, but unless I'm mistaken mySwiftFunction returns a dangling pointer in your example -- retmsg gets de-allocated at the end of the function, but the address of a pointer inside it gets returned. If you're lucky, -[NSString UTF8String] has to create a copy of the string though and uses an autoreleased NSData to store the UTF8 bytes, so the pointer might still be retained by the NSAutoreleasePool. But I don't think one should rely on that behavior never changing.Iddo
G
-1

You can do something like this:

FileSwift.swift

public class SwiftTest: NSObject {
    @objc public static func testMethod() {
        print("Test")
    }
}

FileSwiftWrapper.h

void SwiftFunctionWrapper();

FileSwiftWrapper.m

#import "ProductModuleName-Swift.h"

void SwiftFunctionWrapper() {
      [SwiftTest testMethod];
}
Gilli answered 30/1, 2016 at 20:36 Comment(5)
OP asked about calling C functions in swift, not how to call an Objective-C methodYand
Author want to call Swift code from C. "Calling into C from Swift is pretty simple, however I'm looking into making a bi-directional wrapper in C, so my C has to call Swift functions." @YandGilli
Probably I can extend my comment. SwiftFunctionWrapper() function can be called from any c file.Gilli
@Gilli is right … doing it that way though requires an extra layer of wrapping to bridge C to Swift via Objective-C … using callbacks at least skips that layer. Might there be some good resources out there?Stocktaking
This answer is not cross-platform and would only work on Darwin platforms where Objective-C interop is available, while it won't work on Linux or Windows. The question did not specify a specific platform. A correct answer should be usable on all platform that Swift supports.Drugget

© 2022 - 2024 — McMap. All rights reserved.