Swift callbacks

In some cases, we need to call C/C++ code from Swift and even Swift from C/C++. Code in Swift is only compatible with C, so you cannot call C++ from it. You must use bridge and C-API to call C++ methods.

Calling C++ from Swift

This is very similar to calling C++ from DLL
1) We create C++ class in a file CppClass.h:
// Declaration + Implementation: CppClass.h
#ifndef CppClass_h
#define CppClass_h

class CppClass{
public:
	CppClass() {}
	
	void HelloWorld() { printf("Hello World"); }

}
#endif //CppClass_h

2) Now, to expose C API for external use CApi.h. We cannot use forward declaration with class for return types, so we create “typedef struct” and named it “CCppClass” (C for C API prefix - better readibility).
// Declaration: CApi.h

#ifndef CApi_h
#define CApi_h

typedef struct CppClass CCppClass;

#ifdef __cplusplus
extern "C" {
#endif
    CCppClass * CreateCppClass();
	void ReleaseCppClass(CCppClass * c);
	void CallHelloWorld(CCppClass * c);
	
#ifdef __cplusplus
}
#endif
#endif // CApi_h

3) In the next step, we create actuall API implementation in CApi.c:
// Implementation: CApi.cpp
#include "./CApi.h"

#include "./CppClass.h"

CCppClass * CreateCppClass(){
	return new CppClass();
}

void ReleaseCppClass(CCppClass * c){
	delete c;
}

void CallHelloWorld(CCppClass * c){
	c->HelloWorld();
}

4) In Swift, we cannot directly include C code, we must create "a bridge". This is a simple file, that contains included files and is auto-loaded by Swift compiler. However, we have to add this file to compiler settings.

How to create a Swift Bridging Header Manually

  1. Add a new file to Xcode (File > New > File), then select “Source” and click “Header File“.
  2. Name your file “XXX-Bridging-Header.h”.
  3. Create the file.
  4. Navigate to your project build settings and find the “Swift Compiler – Code Generation” section. You may find it faster to type in “Swift Compiler” into the search box to narrow down the results. Note: If you don’t have a “Swift Compiler – Code Generation” section, this means you probably don’t have any Swift classes added to your project yet. Add a Swift file, then try again.
  5. Next to “Objective-C Bridging Header” you will need to add the name/path of your header file. If your file resides in your project’s root folder simply put the name of the header file there. Examples: “ProjectName/ProjectName-Bridging-Header.h” or simply “ProjectName-Bridging-Header.h”.
  6. Open up your newly created bridging header and add includes for C++ or imports for Objective-C. Any class listed in this file will be able to be accessed from your swift classes.

 


5) Now, we can finally use our C++ class from Swift
var myCppClass:OpaquePointer? = nil;

myCppClass = CreateCppClass();
CallHelloWorld(myCppClass);
ReleaseCppClass(myCppClass);

Source:

Calling Swift from C/C++

Calling Swift from C/C++ is a bit complicated. It can be divided into two parts - calling simple method or calling class member method. The first type is quite easy, because Swift methods are directly mapable to C function pointers.
Both approaches are combined with the first part of this tutorial - C methods have to be made visible to Swift via bridge. So I asume that is is already done, so I skip this in this samples.

C code:
void CallSwiftFromC(void(*f)()){
	f();
}
Swift code:
func TestMethod(){
    //Some Swift code
}

class SwiftClass{
	func SomeFunc(){
		CallSwiftFromC(TestMethod);
	}
}

Now, the second type of callback is more complicated. We want to call Swift class member method from C/C++. Is is not directly possible, so we have to pass pointer to Swift class along. We have created Callbacks struct, that holds pointer to callback method and clas pointer. This is because we want to store callback in “std::function” and we dont want to modify some underlying code. For example, “std::function” can be in some other class and we dont have source code for it and without modifying the class, it wont be possible to call the Swift method. We have used lambda function to wrap modified callback and each time the actuall “std::function” is called, our lambda is called first and loads data from Callbacks struct.
//In file CApi.cpp
typedef struct Callbacks
{
    void * classPtr;
    void(*callback)(void *);
           
}Callbacks;

//can be inited in some method. Must also be released somewhere. Or can be used with shared_ptr
static Callbacks * callbacks = new Callbacks(); 


void CallSwiftMemberFromC(void * classPtr, void(*callback)(void *))
{
    callbacks->classPtr = classPtr;
    callbacks->callback = callback;
    
    std::function<void()> actaulCallback = [&](){
        callbacks->callback(callbacks->classPtr);
    };
    actaulCallback();
}
In Swift, we have to obtain pointer to class. We use a helper function “bridge” to get it. We can use closure passed to CallSwiftMemberFromC to "shorten" code a bit. The closure conforms to “void(*callback)(void *)”.

func bridge(_ obj : T) -> UnsafeMutableRawPointer {
    return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}

func bridge(_ ptr : UnsafeMutableRawPointer) -> T? {
    return Unmanaged.fromOpaque(ptr).takeUnretainedValue()
}

class SwiftClass{

	func SomeFunc(){
		CallSwiftMemberFromC(bridge(self), {(observer) -> Void in
			// Extract pointer to `self` from void pointer:
			let mySelf = Unmanaged.fromOpaque(observer!).takeUnretainedValue()
			// Call instance method:
			mySelf.TestMethod();
		});
	}
	
	func TestMethod(){
	
	}
}

	
Source:
Martin Prantl 2017