Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement AudioServicesSystemSoundCompletionProc in Swift?

Tags:

c

xcode

swift

I'm trying and failing to create an instance of AudioServicesSystemSoundCompletionProc for an argument in AudioServicesAddSystemSoundCompletion using Swift in Xcode.

Here's what I've got so far

func completionCallback(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void {

}

var foo:(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void = completionCallback;

AudioServicesAddSystemSoundCompletion(soundID, nil, nil, foo, nil);

I wrote this with the help of some guides explaining how to write equivalent C Function Pointers in Swift, but this throws this error:

'(ssID: SystemSoundID, clientData: UnsafeMutablePointer<Void>) -> Void' is not convertible to 'AudioServicesSystemSoundCompletionProc'

The documentation shows the Objective-C declaration:

typedef void (*AudioServicesSystemSoundCompletionProc) ( SystemSoundID ssID, void *clientData );

This is declaration shown when using Xcode:

typealias AudioServicesSystemSoundCompletionProc = CFunctionPointer<((SystemSoundID, UnsafeMutablePointer<Void>) -> Void)>

I'm not sure how to implement AudioServicesSystemSoundCompletionProc correctly in Swift.

like image 496
David Michael Harrison Avatar asked Dec 06 '22 23:12

David Michael Harrison


2 Answers

You could do it as a closure, as of Swift 2.0.

AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { (soundID, clientData) -> Void in

    // Your completion callback...                            
    AudioServicesDisposeSystemSoundID(soundID)

}, nil) 

Further info from Apple (scroll down to Function Pointers):

C function pointers are imported into Swift as closures

like image 60
Rygen Avatar answered Dec 31 '22 02:12

Rygen


David's answer is correct. But just to clarify, AudioServicesSystemSoundCompletionProc needs to be done in objective-c, then bridged across to swift. You can either write an implementation yourself, or... it is already done for you here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0 (as mentioned by David)

You need to go to that link, download the files FunctionPointer.h and FunctionPointer.c, put it in your project folder, then link it to swift using bridging header.

To do that:

  • Go to your project's build settings, scroll down to where it says Objective-C Bridging Header, edit the content to "YourProject/Bridging-Header.h" (where YourProject is the name of your project)
  • Create an objective-c header file in your project, and name it Bridging-Header.h
  • Inside the header file, add #import "FunctionPointer.h"

Now you can access FunctionPointer anywhere inside your swift project... sweet

Back to the compeletion handler, in your code, where you want to use AudioServicesAddSystemSoundCompletion, do something like this:

    // at the top of the class
    var functionPointer: AudioServicesCompletionFunctionPointer?

    // in the code
    var me = self
    let userData = withUnsafePointer(&me) { ptr in
        return unsafeBitCast(ptr, UnsafeMutablePointer<Void>.self)
    }
    self.functionPointer = AudioServicesCompletionFunctionPointer(systemSoundID: soundId, block: {(systemSoundID: SystemSoundID, userData: UnsafeMutablePointer<Void>) -> () in
        // sound has ended, do your stuff here
        }, userData: userData)
    AudioServicesAddSystemSoundCompletion(soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, AudioServicesCompletionFunctionPointer.completionHandler(), userData)

where you'll have to change NSObject to TheClassYou'reUsingThisIn and soundId to your soundId

like image 32
lzl Avatar answered Dec 31 '22 02:12

lzl