6

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.

  • Any progress on this? I can't find a solution that doesn't use Obj-C code, including David's answer. – Sean Feb 01 '15 at 21:14
  • possible duplicate of [Using Swift CFunctionPointer to pass a callback to CoreMIDI API](http://stackoverflow.com/questions/25514176/using-swift-cfunctionpointer-to-pass-a-callback-to-coremidi-api) – rickster Feb 15 '15 at 17:21

5 Answers5

6

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

Rygen
  • 309
  • 4
  • 15
5

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

zs2020
  • 53,766
  • 29
  • 154
  • 219
lzl
  • 469
  • 4
  • 10
4
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)
Raul Izq
  • 41
  • 1
2

The answer for this can be seen here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0

Solution by Julius Parishy (https://stackoverflow.com/users/1164263/julius-parishy)

Community
  • 1
  • 1
  • 2
    This answer uses Obj-C. Also, linking to answers is bad practice-- links disappear. – Sean Feb 01 '15 at 21:16
0

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)
Artheyn
  • 1,042
  • 10
  • 7