How do I implement AudioServicesSystemSoundCompletionProc in Swift? - c

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.

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)

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)

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)

Related

Swift Array holding elements weakly

I am taking some inspiration from
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.
I would like the implementation to be as type safe as possible, however should be reusable.
The strategy that I am using is declaring a Weak Reference container as so.
class WeakRefContainer<T> where T: AnyObject {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
Then I want to maintain an array of these WeakRefContainers, so I create an array extension:
extension Array where Element: WeakRefContainer<AnyObject> {
func compact() -> [WeakRefContainer<AnyObject>] {
return filter { $0.value != nil }
}
}
When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.
I am now having some compilation issues which am having trouble understanding.
Lets suppose I have a sample class
class SampleClass {
}
And I try to use everything as follows:
var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)
weakReferencesArray.compact()
When I try to call compact I get the following error message:
MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'
Can anyone unblock me please? Thanks
Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.
There is a workaround for this, via a protocol:
protocol WeakHolder {
var hasRef: Bool { get }
}
extension WeakRefContainer: WeakHolder {
var hasRef: Bool { return value != nil }
}
extension Array where Element: WeakHolder {
func compacted() -> [Element] {
return filter { $0.hasRef }
}
mutating func compact() {
self = compacted()
}
}
I also renamed compact to compacted, for better Swift semantics, and replaced the original compact by a mutating version.
You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject.
extension Array where Element: WeakRefContainer<T> {
However, currently, parameterised extensions are not possible. See this proposal.
You can kind of work around this by making compact generic:
extension Array{
func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
return filter { $0.value != nil }
}
}

Does the warning "Simultaneous accesses to parameter 'self' ..." really apply here?

I wrote an extension to Array that allows me to pop the last element and instantly add it to another array:
extension Array {
mutating func popLast(to otherArray: inout [Element]) -> Element? {
guard self.count > 0 else { return nil }
return otherArray.appendAndReturn(self.popLast()!)
}
mutating func appendAndReturn(_ element: Element) -> Element {
self.append(element)
return element
}
}
This simple example in playground works like a charm:
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
newNumbers.popLast(to: &usedNumbers)
print(usedNumbers) // [9]
for _ in newNumbers {
newNumbers.popLast(to: &usedNumbers)
}
print(usedNumbers) // [9, 8, 7, 6, 5, 4, 3, 2, 1]
But using the extension inside of a struct (code after the warning) gives me this warning:
Simultaneous accesses to parameter 'self', but modification requires
exclusive access; consider copying to a local variable
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
return newNumbers.popLast(to: &usedNumbers)
}
}
It is only a warning and my app runs just fine with the expected behavior, but I'm curious if there really is a danger here. Looking at SE-0176, I understand the goal of the warning, if I were to use it to pop the last element from the same array I append it to, because copy-on-write could mess that up. And so I guess it's related to the struct. But using it on two different arrays inside the same struct, I see no danger. Am I missing something and is there a way to write the extension that would circumvent the potential problem?
Update:
Your code now works without modification.
As #Hamish noted in the comments below, this was a bug that is now fixed in the version of Swift which shipped with Xcode 9 beta 3. I also verified that it works at the IBM Swift Sandbox which is using Linux x86_64 build Swift Dev. 4.0 (Jul 13, 2017).
As you figured out in the comments, the problem is that you need exclusive access to the struct in order to mutate it, but you're passing a reference to part of the struct to the inout parameter. This is apparently supposed to work since you're accessing different parts of the structure, but due to a bug the compiler is too strict here.
The warning suggests copying to a local variable. Since your return is complicated, I have used a defer statement to return the copy of newNumbers to newNumbers to avoid having to store the result of the call in a temporary variable:
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
var newNumbersCopy = newNumbers
defer { newNumbers = newNumbersCopy }
return newNumbersCopy.popLast(to: &usedNumbers)
}
}
You could also choose to fix it by making a copy of the usedNumbers:
mutating func getNewNumber() -> Int? {
var usedNumbersCopy = usedNumbers
defer { usedNumbers = usedNumbersCopy }
return newNumbers.popLast(to: &usedNumbersCopy)
}

Trouble formatting a path for the Linphone SDK in Swift

Thanks in advance for the help!
I am trying to record calls using the Linphone SDK in Swift on Mac OS, and am having trouble passing a path into the function:
func linphone_call_params_set_record_file(_ cp: OpaquePointer!, _ path: UnsafePointer<Int8>!)
that works correctly (the SDK is written in C, though I am accessing it using Swift and a bridging header). The Linphone SDK works properly, and I can make and receive calls programmatically, with full audio support.
In trying to invoke the call recorder, I pass this function a path (pathtofile), such as:
let pathtofile = "/Users/Alex/Safety/1.wav"
where I would like to store the recording file.
func SafetyNetAVRecorderInitializer(pathtofile: String) -> Bool {
// Convert pathtofile to UnsafePointer<Int8>.
let cpathtofile = (pathtofile as NSString).utf8String
let path = UnsafeMutablePointer<Int8>(mutating: cpathtofile)
// Actually begin call recording.
if currentcall != nil {
let currentcallparameters = linphone_call_get_current_params(currentcall)
linphone_call_params_set_record_file (currentcallparameters, path)
linphone_call_start_recording(currentcall)
return true
}
return false
}
No runtime errors are encountered on linphone_call_params_set_record_file(), but when I try to invoke linphone_call_start_recording(), the recording does not begin, and an error is printed in the console that reads:
ortp-error-linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().
How can I correctly pass a valid path to linphone_call_params_set_record_file()? I have tried directly passing a plain Swift String instead of an UnsafePointer<Int8> to no avail. Am I just misunderstanding how paths are formatted in C?
For reference, the SDK method source is:
void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
if (cp->record_file){
ms_free(cp->record_file);
cp->record_file=NULL;
}
if (path) cp->record_file=ms_strdup(path);
}
Thanks again!
Try this:
let cpathtofile = (pathtofile as NSString).utf8String! // Unwraps!
...
inphone_call_params_set_record_file(currentcallparameters, cpathtofile)

GLFW callback signature in Swift 3

I'm trying to setup a simple GLFW application in Swift 3. Currently I'm stuck trying to set a callback function.
func setupGLFW() {
...
glfwSetErrorCallback(errorCallback)
...
}
func errorCallback(error: Int32, description: UnsafePointer<Int8>) {
...
}
This worked in Swift 2 but now I get this error:
cannot convert value of type '(Int32, UnsafePointer) -> ()' to expected argument type 'GLFWerrorfun!'
After consulting the section Interacting with C APIs in the documentation, I also tried this signature:
func errorCallback(error: CInt, description: UnsafePointer<CChar>) {
...
}
This leads to the same error.
The C signature for GLFWerrorfun is:
typedef void(* GLFWerrorfun) (int, const char *)
Try with:
func errorCallback(error: Int32, description: UnsafePointer<Int8>?) {
(Please do not miss the ? after UnsafePointer<Int8>.)
In Swift 3, nullable pointer are imported as Optional pointer, so you need ?.
You could have the callback inline where you call glfwSetErrorCallback:
glfwSetErrorCallback { (error, description) in
//
}
Or, use a closure stored in a variable:
let callback: GLFWerrorfun = { (error, description) in
//
}
glfwSetErrorCallback(callback)
You can see how this typedef is bridged to Swift by Option + Left Clicking on the symbol:
In this case I didn't have the actual GLFW headers, so I just dropped the declaration in a Bridging Header. Your generated interface may show more information.

How do I pass an array of custom objects in Swift

I am trying to make a main menu for an Mac application as a beginning task to learn about Swift. Here is my code, which does not work.
In the AppDelegate
import Foundation
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
{
#IBOutlet weak var window: NSWindow!
//private let mainWindow = NSWindow(frame: NSWindow.mainScreen().bounds)
//let mainWindow = NSWindow()
//let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSSquareStatusItemLength)
func applicationDidFinishLaunching(aNotification: NSNotification)
{
// Insert code here to initialize your application
let menuData = [getMainMenuItems]
//[makeMainMenu, menuData]
}
}
And then in a different project file, named Swift_FileManager
import Foundation
import Cocoa
class menuArrayObject
{
var title: String = ""
var subMenuTitles: [String] = []
}
func getMainMenuItems (menuData:Array<AnyObject>) -> AnyObject
{
//Make a new menu array
var menuData = [AnyObject]()
let arrayObject1 = menuArrayObject()
arrayObject1.title = "Project"
arrayObject1.subMenuTitles = ["New Project","Open Project","Save Project", "Quit Project"]
menuData.append(arrayObject1)
return menuData
}
The code compiles but the function getMainMenuItems is never called.
Can somebody shed some light on the (probably very simple) issue here? Thanks in advance
let menuData = [getMainMenuItems]
This line (probably) isn't doing what you think it's doing. What it's doing is creating an array of [(Array<AnyObject>)->(AnyObject)] type (an array of functions that take an array of AnyObjects as input, and return an AnyObject) – and adding your getMainMenuItems function to it.
It's not calling your function at all.
In order to call a function, you need to use a pair of brackets (), containing the required input. In this case, your getMainMenuItems method expects an array of AnyObjects (although it never appears to actually use them).
For example:
let menuData = getMainMenuItems([/* Insert your objects here */])
Although that all being said, I'm not entirely sure that your getMainMenuItems function actually needs an input, as you never use it. I think you're looking for something like this:
func getMainMenuItems() -> [MenuArrayObject] {
// create an empty array of MenuArrayObjects
var menuData = [MenuArrayObject]()
let arrayObject1 = MenuArrayObject()
arrayObject1.title = "Project"
arrayObject1.subMenuTitles = ["New Project","Open Project","Save Project", "Quit Project"]
menuData.append(arrayObject1)
return menuData
}
Note that the function no longer has an input, and I've replaced AnyObject with [MenuArrayObject]. It's best to be as type specific as you can in Swift (and pretty much every other OOP language), so you should really steer away from AnyObject unless you have good reason to use it.
Now you can just invoke your function like this:
let menuData = getMainMenuItems()
and it will return an array of MenuArrayObjects.
If you're trying to make the function so that it'll add an object to an already existing array, then you can use an inout parameter in your function. This will let you pass an array into your function, which it can modify.
For example:
func getMainMenuItems(inout menuData:[MenuArrayObject]) {
let arrayObject1 = MenuArrayObject()
arrayObject1.title = "Project"
arrayObject1.subMenuTitles = ["New Project","Open Project","Save Project", "Quit Project"]
menuData.append(arrayObject1)
}
You can then call it like so:
// an existing array of MenuArrayObjects
var menuDataArray = [MenuArrayObject]()
// call function with array – which it will append an element to
getMainMenuItems(&menuDataArray)
The code let menuData = [getMainMenuItems] is just creating an array containing the function. I'm guessing that you mean to call the function instead. That looks something like:
let menuData = getMainMenuItems([])
In applicationDidFinishLaunching you need to instantiate menuArrayObject:
let myMenuArray = menuArrayObject()
And then call it:
let menuData = myMenuArray.getMainMenuItems()
Your getMainMenuItems method, it seems looking at what you wrote in the method, should be defined as:
func getMainMenuItems() -> [AnyObject]

Resources