23

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?

Dan
  • 1,258
  • 1
  • 10
  • 22

3 Answers3

13

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);
}
Max Desiatov
  • 5,087
  • 3
  • 48
  • 56
  • 4
    Altough this answer is correct, I had some troubles getting things to work. [That's why I made a little article](https://gist.github.com/HiImJulien/c79f07a8a619431b88ea33cca51de787). –  Feb 28 '18 at 02:37
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];
}
Rolan
  • 62
  • 4
  • 2
    OP asked about calling C functions in swift, not how to call an Objective-C method – pbush25 Jan 30 '16 at 20:44
  • 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." @pbush25 – Rolan Jan 30 '16 at 20:53
  • Probably I can extend my comment. `SwiftFunctionWrapper()` function can be called from any c file. – Rolan Jan 30 '16 at 20:55
  • @Rolan 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? – Dan Jan 31 '16 at 01:59
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!)
    }}
DAC84
  • 421
  • 1
  • 8
  • 20
  • 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. – uliwitness Dec 08 '22 at 18:05