9

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)

        // ...
    }
}

3 Answers3

8

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.

CodaFi
  • 43,043
  • 8
  • 107
  • 153
  • CodaFi, so how would this audioQueueHandleBuffer and CFIAudioQueueNewOutput functions in objective c be called to swift? It would have to be set up as class method and be called to Class AudioQueue in swift...can't seem to get this working. will not compile. Would you give more example of it? – CodeOverRide Apr 09 '15 at 22:56
  • @CodeOverRide I have managed to get it work using CodaFi's answer above. Please check my code at my question http://stackoverflow.com/questions/30011730/low-latency-input-output-audioqueue. (Thanks a lot CodaFi :D) – Jens Schwarzer May 04 '15 at 14:24
  • It clearly doesn't have great support at all or it would be able to handle this common case. – Puppy Aug 26 '15 at 14:54
2

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

Community
  • 1
  • 1
Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35
-1

Calling API Function with Callbacks (Blocks) in SWIFT 2

      self.forwardGetRequestToServer(finalUrl!) { (result) -> Void in
        print(result)
    }

function with Returning CallBack Definition in API class

func forwardGetRequestToServer(url: NSURL, completion: (result: NSDictionary) -> Void) {
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "GET"
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in
        if error != nil {
            print("error=\(error)")
            return
        }
        do {

            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! NSDictionary
            completion(result: json)

        } catch {
            print("error serializing JSON: \(error)")

        }

        }

    task.resume() }
Maninderjit Singh
  • 1,419
  • 1
  • 13
  • 23