How do I implement AudioServicesSystemSoundCompletionProc in 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

  • Create a button programmatically and set a background image
  • Restore Purchase working but it is looping constantly
  • Display table view when searchBar (from searchController) begin edited Swift
  • Accessing global const CGFloat defined in an Objective-c .m file from Swift
  • Swift 2 ( executeFetchRequest ) : error handling
  • iOS 8 Orientation change: Keyboard frame does not display correctly
  • 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.

    5 Solutions Collect From Internet About “How do I implement AudioServicesSystemSoundCompletionProc in Swift?”

    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

    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

    let myData = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
    AudioServicesAddSystemSoundCompletion(YOUR_SOUND_ID, CFRunLoopGetMain(), kCFRunLoopDefaultMode,{ (mSound, mVoid) in
            let me = unsafeBitCast(mVoid, YOURCURRENTCLASS.self)
            //me it is your current object so if yo have a variable like
            // var someVar you can do
            print(me.someVar)
        }, myData)
    

    This works in Swift 3:

            let weakSelf = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
            let error = AudioServicesAddSystemSoundCompletion(soundId,
                                                              nil,
                                                              nil,
                                                              {soundId, weakSelfPointer in
    
                                                                guard let weakSelfPointer = weakSelfPointer else {
                                                                    return
                                                                }
    
                                                                let weakSelfValue = Unmanaged<NAME_OF_SELF_CLASS>.fromOpaque(weakSelfPointer).takeUnretainedValue()
    
                                                                // Then you can use `weakSelfValue` as you would do with `self`.
                                                                weakSelfValue.A_METHOD_OF_SELF
            }, weakSelf)