27

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?

3 Answers 3

18

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); } 
Sign up to request clarification or add additional context in comments.

1 Comment

Altough this answer is correct, I had some troubles getting things to work. That's why I made a little article.
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!) }} 

1 Comment

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.
-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]; } 

5 Comments

OP asked about calling C functions in swift, not how to call an Objective-C method
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
Probably I can extend my comment. SwiftFunctionWrapper() function can be called from any c file.
@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?
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.