Pass C Function Callback in Swift

How to properly pass a callback-function from swift to c++?

First of all, you need to declare the function type properly in .cpp/.hpp .

.cpp:

void run_func(void (* funcpntr)()) {
(*funcpntr)();
}

.hpp:

void run_func(void (* funcpntr)());

(You can omit the latter funcpntr as shown in Jarod42's answer. And this needs to be enclosed in extern "C" {...} as shown in the first "cpp-class.hpp:".)

And Swift side:

Function definition:

//This needs to be a toplevel function, which means it cannot be a method.
func test_func() {
//...
}

To pass it to run_func():

    run_func(test_func)

Or you can pass a closure to run_func:

    //This closure cannot have captured variables.
run_func {
//...
}

how implement callback from C++ function to Swift

Eventually I realized it possible to pass 'closure' to c++ as argument

So at first I created a Closure in Swift class:

typealias closureCallback = (Int32) -> ()

Then passed it to cpp side :

  1. In header.h‍‍

    int callback(void (^closureCallback)(int));
  2. In callbackClass.hpp :

    class callbackClass.hpp
    {
    .
    .
    .

    public:
    int run(void (^closureCallback)(int));
    };

    #endif
  3. And in callbackClass.cpp

    int callback::run(void (^closureCallback)(int))
    {
    for (unsigned int i = 0; i < 100; i++)
    {
    closureCallback(i);
    }
    return 0;
    }

At last handle it in Swift :

typealias closureCallback = (Int32) -> ()

func callbackFunc(){

var value : String!

let closureValue: closureCallback = {valueFromclosureCallback in
value = String(valueFromclosureCallback)
}

callback(closureValue) //call cpp function

}

Swift - pass escaping closure to C API callback

You need a wrapper class so that a void pointer to the instance can be tunneled through the C function to the callback. The combination of passRetained() and takeRetainedValue() ensures that the wrapper instance is released only after the completion function has been called.

func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {

class Wrapper {
let completion: (GetSnapshotResult) -> Void
init(completion: @escaping (GetSnapshotResult) -> Void) {
self.completion = completion
}
}

let wrapper = Wrapper(completion: completion)
let observer = UnsafeMutableRawPointer(Unmanaged.passRetained(wrapper).toOpaque())

CAPIGetSnapshot(observer) { theObserver in
let theWrapper = Unmanaged<Wrapper>.fromOpaque(theObserver!).takeRetainedValue()
theWrapper.completion(
.success( snapshot: UIImage(), "test")
)
}
}

Some remarks:

  • I am assuming that the C function passes the ptr argument to the callback.

  • passRetained(wrapper) retains the object. That ensures that the wrapper instance is not released when the getSnapshot() function returns.

  • takeRetainedValue() in the closure consumes the retain. As a consequence, the wrapper instance is released when the closure returns.

  • completion is a closure and closures are reference types. wrapper.completion holds a reference to that closure as long as the wrapper instance exists.

  • Of course you can use the same variable names (“observer”, “wrapper”) inside the closure. I chose different names here (“theObserver”, “theWrapper”) only to emphasize that they are different variables, i.e. that the closure does not capture context anymore.

  • observer needs to be a mutable raw pointer only because the first argument of the C function is declared as void * ptr. If you can change the function declaration to

    void CAPIGetSnapshot(const void * ptr, void(*callbackOnFinish)(const void *))

    then let observer = UnsafeRawPointer(...) works as well.

  • For more information about conversion between object references to void pointers see for example How to cast self to UnsafeMutablePointer<Void> type in swift.

Instead of a custom wrapper class you can also take advantage of the fact that arbitrary Swift values are automatically boxed in a class type when cast to AnyObject (see for example AnyObject not working in Xcode8 beta6?).

func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {

let wrapper = completion as AnyObject
let observer = UnsafeRawPointer(Unmanaged.passRetained(wrapper).toOpaque())

CAPIGetSnapshot(observer) { theObserver in
let theCompletion = Unmanaged<AnyObject>.fromOpaque(theObserver!).takeRetainedValue()
as! ((GetSnapshotResult) -> Void)
theCompletion(
.success( snapshot: UIImage(), "test")
)
}
}

The forced unwraps and forced casts are safe here because you know what it passed to the function. A failure to unwrap or cast would indicate a programming error. But I would prefer the first version instead of relying on this “magic”.

How to pass an Objective-C with callback to a Swift method?

This part is impossible as your spec stands:

productsRequestCompletionHandler?(true, products)

You cannot hand back two values if a ProductsRequestCompletionHandler takes only one value. So you will have to revise your definition of a ProductsRequestCompletionHandler.

We may then imagine that on the Swift side we have a class like this:

@objc class IAPBridge : NSObject {
public typealias ProductsRequestCompletionHandler = (Bool, [SKProduct]?) -> Void
@objc func requestProducts(_ ch:ProductsRequestCompletionHandler) {
let products : [SKProduct]? = // whatever
ch(true, products)
}
}

Over on the Objective-C side, your class's .m file must import the implicitly generated bridging header:

#import "MyApp-Swift.h" // or whatever it is called

In the eyes of your Objective-C class, an IAPBridge has this method:

- (void)requestProducts:(SWIFT_NOESCAPE void (^ _Nonnull)(BOOL, NSArray<SKProduct *> * _Nullable))ch;

So now you just call it:

IAPBridge* b = [[IAPBridge alloc] init];
[b requestProducts:^(BOOL success, NSArray<SKProduct *> * products) {
if (success) {
NSLog(@"Thank you for the products! They are %@", products);
} else {
NSLog(@"%@", @"Darn, something went wrong");
}
}];

Using a C API function in Swift 3 that has a callback function pointer as an argument

Passing a global function as callback should work, but the last parameter
must be an optional UnsafeMutableRawPointer?:

func callbackCalled(handle: HSYNC, channel: DWORD, data: DWORD, user: UnsafeMutableRawPointer?) -> Void {
print("callback called")
}

Syncer(h, c, d, callbackCalled, u)

Alternatively, pass a closure expression (and let compiler infer
the types of the parameters):

Syncer(h, d, c, {
(handle, channel, data, user) in
// ...
}, u)

You can not pass an instance method as a callback, compare
How to use instance method as callback for function which takes only func or literal closure.

If NULL is not a valid value for the user parameter then you can
add
"Nullability annotations" to the C declaration, e.g.

typedef void (CALLBACK SYNCPROC)(HSYNC h, DWORD c, DWORD d, void * _Nonnull user);

and then it will be represented as a non-optional UnsafeMutableRawPointer in Swift.

Pass C function callback in Swift

With Swift 2.0 it is now possible to set up the callback in pure Swift! Please check http://oleb.net/blog/2015/06/c-callbacks-in-swift/ and Swift 2 Cannot invoke 'FSEventStreamCreate' with an argument list of type for inspiration :)

how to pass a callback function in another function in swift?

The parameter you have declared is not correct. Replace it with something like this:

func classBFunction(_ completion: (String) -> Void) {
completion("all working")
}

Like shared by regina_fallangi in the comments, callbacks are usually called completion handlers, which is why I replaced the names accordingly.

Extra Credit:

If you only want to optionally pass a function, you could do this:

func classBFunction(_ completion: ((String) -> Void)? = nil) {
completion?("all working")
}

Now you could also just call classBFunction().

Function callback from C to Swift

Apple confirmed me, on the dev forum, that it is not supported now, and requested me to fill a new request on the bugreporter.

Moreover, I give to the readers another detail:

It seems that in the compiled binary the symbols for all swift functions are already available and bridged to be accessible from C (even in a swift-only app)

I made an app called FunctionTest, iPhone App with this function in a swift file

func thisIsATestFunction()
{
println("test")
}

compiled, and then from Terminal:

nc /Users/xxx/Library/Developer/Xcode/DerivedData/FunctionTest-hhrbtzsuyrdoftfnbakosvenaiak/Build/Products/Debug-iphonesimulator/FunctionTest.app/FunctionTest

     U _NSStringFromClass
U _OBJC_CLASS_$_NSString
U _OBJC_CLASS_$_UIResponder
U _OBJC_CLASS_$_UIViewController
U _OBJC_CLASS_$_UIWindow
000088c8 S _OBJC_CLASS_$__TtC12FunctionTest11AppDelegate
00008888 S _OBJC_CLASS_$__TtC12FunctionTest14ViewController
.........
.........
00003840 T __TF12FunctionTest19thisIsATestFunctionFT_T_ <--- this is my test function

Calling from c the address 00003840 executed the function

void (* func)() = 0x00003840;
func(); // the swift function is executed

So I think that this is already work-in-progress...hoping that they will implement this functionality in the next releases :-)



Related Topics



Leave a reply



Submit