Objective-C Wrapper for Cfunctionpointer to a Swift Closure

Objective-C Wrapper for CFunctionPointer to a Swift Closure

I needed to define this callback:

typedef void (*MIDIReadProc) ( const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon );

and I wanted to use Objective-C as least as possible.

This was my approach:

MIDIReadProcCallback.h

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

typedef void (^OnCallback)(const MIDIPacketList *packetList);

@interface MIDIReadProcCallback : NSObject

+ (void (*)(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon))midiReadProc;
+ (void)setOnCallback:(OnCallback)onCallback;

@end

MIDIReadProcCallback.m

#import "MIDIReadProcCallback.h"

static OnCallback _onCallback = nil;

static void readProcCallback(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
if (_onCallback) {
_onCallback(pktlist);
}
}

@implementation MIDIReadProcCallback

+ (void (*)(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon))midiReadProc {
return readProcCallback;
}

+ (void)setOnCallback:(OnCallback)onCallback {
_onCallback = onCallback;
}

@end

Then you can register MIDIReadProcCallback.midiReadProc as callback and set handler MIDIReadProcCallback.setOnCallback({ (packetList: MIDIPacketList) in ... })

Swift call Objective-C wrapper function containing blocks

This is what I ended up doing

let username = "username" //usernameField.text
let password = "password" //passwordField.text

ObjCWrapper.login(username, andPassword: password,
andErrorBlock:
{
(map) -> Void in
// stuff
})
{
(map) -> Void in
// stuff
}

How to implement C-Style callback functions using swift?

You cannot create C function like callbacks in Swift as closures are not compatible with CFunctionPointer. You can implement some workaround in Objective-C or C. Example is describe in Objective-C Wrapper for CFunctionPointer to a Swift Closure

EXC_BAD_ACCESS exception when calling a Swift closure from C

Thank you for the interesting question.

I see a few options.

Option 1:

If you have control over the C API, make (some part of) it Objective-C by just changing the extension from .c to .m. Then your Swift code should interoperate with it just fine. At least the part of it that directly interacts with Swift code should be made Objective-C. Objective-C portion will hopefully be interoperable with the remaining C parts (the ones in .c files).

Option 2:

Write an Objective-C wrapper (in a .m file) around the C API and use the wrapper in Swift code. Sample wrapper based on your example:

// This is where we store the block passed in from Swift
void (^cb_block_cb2)(int, int) = NULL;

// This wrapper will be used in the block passed to C API
void block_wrapper(int i1, int i2) {
cb_block_cb2(i1, i2);
}

// Obj-C wrapper around C API block setter
// Make this available to Swift, e.g. via bridging header.
void api_set_callback_block2(void (^cb_block)(int, int))
{
if (cb_block == NULL)
{
puts("error: when setting callback block: cb_block is null");
return;
}
cb_block_cb2 = cb_block;
api_set_callback_block(^(int a, int b){ block_wrapper(a, b);});
}

// Obj-C wrapper around C API block trigger
// Make this available to Swift, e.g. via bridging header.
void api_trigger_block2()
{
puts("Entered api_trigger_block2()");
if (cb_block_cb2 == NULL)
{
puts("error: when triggering callback block: cb_block_cb2 is null");
return;
}
api_trigger_block();
puts("Returned from callback in api_trigger_block2()!!!!");
}

Conceptually, the above options are similar. You may be able to come up with some other options along the same lines. At this point I can't give a good technical explanation as to why the above works. As far as I can tell from experimenting, Swift functions/closures can't always be passed to C functions compiled with a C compiler and taking compatible blocks. However, it works fine if the same functions are compiled as Objective-C. A block defined in an Objective-C file can be passed to C code just fine.

For the record, I also tried using @objc and @convention(c) annotations in Swift code trying to fix this, but to no avail.

Hope this helps.

Adding Swift closure object to NSMutableArray and then removing it doesn't work - LEAK

To re-emphasize, our codebase uses Swift that interops with ObjC, therefore, in my case its not possible to go pure Swift trivially.

I changed our API to use an NSDictionary that maps from NSUInteger handle to the closure and that integer is then used for removal of the closure from the dictionary.

The purpose of that API is to register listener callbacks and have a facility to unregister them. So that NSUInteger handle satisfies the removal bit.

Trying to find a Swift MIDI callback workaround

Here is a blog post and github repo with a working example. The Swift code uses objc as a trampoline for the function pointers.

Using CGEventTapCreate Trouble with parameters in Swift

Bit of a while ago, but I think when you look at the type of CGEventTapCallback, you see that it is a CFunctionPointer:

typealias CGEventTapCallBack = CFunctionPointer<((CGEventTapProxy, 
CGEventType, CGEvent!, UnsafeMutablePointer<Void>) -> Unmanaged<CGEvent>!)>

At the moment you can't create these in Swift, so you have to dip into objective-c, create a function there, which can then call an equivalent function in Swift, see here:

Objective-C Wrapper for CFunctionPointer to a Swift Closure

I ended up writing the whole of this stuff in Objective-C...



Related Topics



Leave a reply



Submit