Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass C function callback in Swift

Tags:

swift

How do you pass a C callback in Swift? Consider this example:

class AudioQueue {

    var desc : AudioStreamBasicDescription
    var queue : AudioQueue?

    func audioQueueHandleBuffer(ctx : UnsafeMutablePointer<()>,
                                inAQ : AudioQueue!,
                                inBuffer : AudioQueueBufferRef) {
       // do stuff
    }

    func initialize() {
        // this does not work!
        var err = AudioQueueNewOutput(&desc, audioQueueHandleBuffer,
                                      nil, nil, nil, 0, queue)

        // ...
    }
}
like image 524
Era Avatar asked Aug 16 '14 15:08

Era


2 Answers

Swift closures and functions are decidedly not C functions. They exist in a weird halfway state between C++ members and Objective-C blocks, so you'll have to move any callbacks into an Objective-C file and take a block or have some other means of calling back to Swift. For example you might have your own version of the relevant AVFoundation functions take a block:

void audioQueueHandleBuffer(void *ctx, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    NSCAssert(ctx != NULL, @"Cannot execute NULL context block.");

    void(^block)(AudioQueueRef, AudioQueueBufferRef) = (__bridge void(^)(AudioQueueRef, AudioQueueBufferRef))ctx;

    return block(inAQ, inBuffer);
}

OSStatus CFIAudioQueueNewOutput(AudioStreamBasicDescription *desc, void(^callback)(AudioQueueRef, AudioQueueBufferRef), AudioQueueRef *queue) {
    return AudioQueueNewOutput(desc, audioQueueHandleBuffer, (__bridge_retained void *)([callback copy]), nil, nil, 0, queue);
}

Then just pass your function through like you would expect in Swift.

class AudioQueue {

    var desc : AudioStreamBasicDescription
    var queue : AudioQueueRef

    func audioQueueHandleBuffer(inAQ : AudioQueueRef, inBuffer : AudioQueueBufferRef) {
            // do stuff
    }

    func initialize() {
        var err = CFIAudioQueueNewOutput(&desc, audioQueueHandleBuffer, &queue)

        // ...
    }
}

It's an incredibly painful workaround for a problem that you shouldn't be trying to express in Swift. Code that must manipulate pointers, especially function pointers, is best left in a C or Objective-C file. Otherwise, you're just fighting an unnecessary battle against the language -especially because it has such great support for C and Objective-C interoperability.

like image 52
CodaFi Avatar answered Nov 28 '22 11:11

CodaFi


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 :)

like image 25
Jens Schwarzer Avatar answered Nov 28 '22 11:11

Jens Schwarzer