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 :
In
header.h
int callback(void (^closureCallback)(int));
In
callbackClass.hpp
:class callbackClass.hpp
{
.
.
.
public:
int run(void (^closureCallback)(int));
};
#endifAnd 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 thegetSnapshot()
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 asvoid * ptr
. If you can change the function declaration tovoid 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
How to Reproduce This Xcode Blue Drag Line
Checking If a Double Value Is an Integer - Swift
Swift Package Manager - Uikit Dependency
Get the Last Character of a String Without Using Array
Print() to Console Log with Color
Swiftui Hide Tabbar in Subview
Swift Uialertcontroller with Pickerview Button Action Stay Up
Bold Part of String in Uitextview Swift
Get the Size (In Bytes) of an Object on the Heap
Writing Swift Dictionary to File
Apply Vertical Alpha Gradient to Uitableview
Writing Data to an Nsoutputstream in Swift 3
Showing Cells in Demands in Uicollectionview with Vertical Infinite Scroll
How to Display Realm Results in Swiftui List
How to Change Font Size and Font Name of Uisegmentedcontrol Programmatically on Swift
Swift Combine: How to Create a Single Publisher from a List of Publishers