Dispatch queue specific usage for Swift and Objective C inter-op - ios

I am migrating my project from ObjC to Swift. I have this code:
const char *specific = "my_queue"
dispatch_queue_set_specific(myQueue, specific, (void *)specific, NULL);
Later in my unit test, I have:
assertTrue(dispatch_get_specific(specific))
I noticed that in Swift, the API has changed, and we can't use any key type anymore
let value = String(cString: specific, encoding: .utf8)
myQueue.setSpecific(DispatchSpecificKey<String>(), value)
The 2 APIs already diverged. In ObjC, key and values are both c strings; but in Swift the key is a special type DispatchSpecificKey which is not available in ObjC.
So how can I set the specific in Swift, and get the correct specific from ObjC?
Another way around seems problematic too: how can I set the specific in ObjC, and get the correct specific from Swift?

Related

Access object created in Objective C

We have an Objective C library that creates objects that are meant to be used in Swift code.
The function returns the object as an id. This is the declaration in Objective C header file (Clazz.h). The header file has been exposed in bridging header as required for interoperability.
+ (id)getObject;
The pointer returned is an instance of AVAssetWriter*. To access this API in Swift, I followed the steps in below post:
Objective-C pointer and swift
This is the Swift 3 code consumer code:
let obj = (Clazz.getObject() as! UnsafeMutableRawPointer).assumingMemoryBoundTo(to: AVAssetWriter.self).pointee
It built fine. However, when this code executes, the app seems to crash.
In Clazz.h, I declared the function as:
+ (AVAssetWriter*)getObject();
and tried to use it in Swift as,
let obj = Clazz.getObject() as AVAssetWriter
Code built fine but it failed when it was executed.
How do we access Objective C created objects in Swift?
Please note that I was able to inspect the value in swift code and the problem is not in bridging. Also, the memory location is not dangling.
By declaring obj variable as below, things worked
let obj:AVAssetWriter = Clazz.getObject() as AVAssetWriter

Objective-C pointer and swift

I'm following an apple document, but unfortunately the examples are written on objective-c, but I have confidence with Swift language and can not understand the meaning of some things, in particular, in this example:
void RunLoopSourcesPerformRoutine (void *info){
RunLoopSource* obj = (RunLoopSource*)info;
[obj sourceFired];
}
this line: RunLoopSource* obj = (RunLoopSource*)info;
the parameter: void *info indicates that info is a pointer to void, then I can put the address of any type of data structure, following various apple documents I saw that the translation of this : void *info into swift language is :
info: UnsafeMutableRawPointer?
Now, the RunLoopSource* obj = (RunLoopSource*)info; line indicates that obj is a variable of type: RunLoopSource, and to this is assigned the value of (RunLoopSource *) info, but precisely What does it mean this statement? : (RunLoopSource *) info, and how it translates in swift language ?
Swift really hates pointer. These 2 lines of code can be converted to Swift as
func RunLoopSourcesPerformRoutine(info: UnsafeMutableRawPointer) {
let obj = info.assumingMemoryBound(to: RunLoopSource.self)
obj.pointee.sourceFired()
}
This specific expression is a "typecast": it's saying that info, which is declared to be a pointer-to-unknown-anything (void *) is actually known by the programmer to be a pointer to a RunLoopSource. This forcibly changes the type of the expression to make the compiler happy as it is assigned to obj.
It is equivalent to using as! in Swift and is idiomatic when you know the semantics of a void * but the syntax doesn't capture it.
(This attempts to answer your question as stated but I'm not sure if you are looking for more information. If so, please clarify and me or someone more expert in unsafe pointers in Swift can help out.)
What you are dealing with (void *info) is a C pointer-to-void, which arrives into Swift as a form of UnsafeRawPointer. This means that type info has been cast away and that memory is being managed elsewhere.
In order to work with this thing as what you believe it to be, i.e. a RunLoopSource, you need to characterize it explicitly as a RunLoopSource. In C, you would cast, as in the example code you posted: (RunLoopSource*)info. In Swift, you rebind.
Observe that in your case this whole thing has been made just a little more complicated by the fact that this UnsafeMutableRawPointer has been wrapped in an Optional, and will have to be unwrapped before you can do anything at all.
Assuming, then, in your case, that info is really an UnsafeMutableRawPointer? bound to a RunLoopSource, you can say:
let rlsptr = info!.assumingMemoryBound(to: RunLoopSource.self)
let rls = rlsptr.pointee
Now rls is a RunLoopSource and you can work with it however you like. Keep in mind, however, that the memory is unmanaged, so you should work with it only here and now.
EDIT By the way, Apple has a really nice document on this entire matter: https://swift.org/migration-guide/se-0107-migrate.html

How do I use CFArrayRef in Swift?

I'm using an Objective-C class in my Swift project via a bridging header. The method signature looks something like this:
- (CFArrayRef)someMethod:(someType)someParameter;
I started by getting an instance of the class, calling the method, and storing the value:
var myInstance = MyClassWithThatMethod();
var cfArr = myInstance.someMethod(someValue);
Then try to get a value in the array:
var valueInArrayThatIWant = CFArrayGetValueAtIndex(cfArr, 0);
However I get the error Unmanaged<CFArray>' is not identical to 'CFArray'. What does Unmanaged<CFArray> even mean?
I looked through How to convert CFArray to Swift Array? but I don't need to convert the array to a swift array (however that would be nice). I just need to be able to get values from the array.
I have also tried the method of passing the CFArray into a function outlined in this answer:
func doSomeStuffOnArray(myArray: NSArray) {
}
However I get a similar error when using it:
doSomeStuffOnArray(cfArr); // Unmanaged<CFArray>' is not identical to 'NSArray'
I am using CFArray because I need to store an array of CGPathRef, which cannot be stored in NSArray.
So how am I supposed to use CFArray in Swift?
As explained in
Working with Core Foundation Types, there are two possible solutions when
you return a Core Foundation object from your own function that is imported in Swift:
Annotate the function with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED.
In your case:
- (CFArrayRef)someMethod:(someType)someParameter CF_RETURNS_NOT_RETAINED;
Or convert the unmanaged object to a memory managed object with takeUnretainedValue() or takeRetainedValue() in Swift. In your case:
var cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
An Unmanaged is a wrapper for an actual CF value. (Sort of like an optional.) It's there because ARC can't tell from looking at the declaration of someMethod: whether that method retains the value it returns.
You unwrap an Unmanaged by telling ARC what memory management policy to use for the value inside. If someMethod calls CFRetain on its return value:
let cfArr = myInstance.someMethod(someValue).takeRetainedValue()
If it doesn't:
let cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
After you do that, cfArr is a CFArray, so you can use the bridging tricks from the other questions you linked to for accessing it like a Swift array.
If you own the code for someMethod you can change it a bit to not need this. There's a couple of options for that:
Annotate with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED to tell the compiler what memory behavior is needed
Since it's an ObjC method, bridge to NSArray and return that--it'll automatically become an [AnyObject] array in Swift.

How to use the CoreAudio API in Swift

I am in the process of migrating my streaming audio engine to swift. i am finding it difficult to use the C Audio API in swift.
I have a problem with AudioFileStreamOpen api where it takes 2 C functions as a parameter. I don't know how to use this API is swift.
AudioFileStreamOpen(self as UnsafePointer<()>, propertyProc, packetProc, kAudioFileMP3Type, audioStreamId)
I have defined the callback method as below for this API. But i am getting the compilation error.
func propertyProc(inClientData: UnsafePointer<()>,inFileStreamId: AudioFileStreamID,inPropertyId: AudioFileStreamPropertyID,ioFlags: UnsafePointer<UInt32>) -> Void {
.....
}
func packetProc(inClientData: UnsafePointer<()>,inNumberOfBytes: UInt32,inNumberOfPackets: UInt32, ConstUnsafePointer<()>, inPacketDescriptions: UnsafePointer<AudioStreamPacketDescription>) -> Void {
.....
}
Any help is appreciated to correctly define this C API in swift
You can't (currently) use an API requiring a C callback pointer from pure Swift code. Calling Swift functions or methods using a C function pointer is not supported by the current beta 4 language implementation, according to replies in the Swift forum at devforums.apple.com
UPDATE: The above answer is obsolete as of Swift 2.0
One alternative is to put some small trampoline C callback functions in an Objective C file, which can interoperate with Swift, and have those C functions in turn call a block or closure, which can be in Swift code. Configure the C callbacks with your Swift closures, and then pass those C callbacks to the CoreAudio functions.
I don't know much about Audio API, however, you should replace UnsafePointer by a pointer to an Object. for example:
var clientData : AnyObject?
var listenerProc : AudioFileStream_PropertyListenerProc = AudioFileStream_PropertyListenerProc.convertFromNilLiteral()
var packetsProc : AudioFileStream_PacketsProc = AudioFileStream_PacketsProc.convertFromNilLiteral()
var audioFileTypyeId : AudioFileTypeID = 0
AudioFileStreamOpen(&clientData, listenerProc, packetsProc, audioFileTypyeId, &streamId)
the initialization code for listenerProc, packetsProc or other variables is just to by-pass the compiler error.
To your situation, try to replace 'self as UnsafePointer<>' by '&self'. However 'self' must be something that can be converted to compatible data type.
https://developer.apple.com/library/prerelease/ios/documentation/MusicAudio/Reference/AudioStreamReference/index.html#//apple_ref/c/func/AudioFileStreamOpen

How to access C function from another C function in iOS

I'm trying to assign a function to the AURenderCallback inputProc
int setupRemoteIO(audio unit etc){
inProc.inputProc = playerCallback
}
but it says that playerCallback is not declared in this scope although playerCallback is present in the same file and class as setupRemoteIO.
The player callback is like this
static OSStatus playerCallback(void *inRefCon etc)
What could be the problem?
In C, you need to declare a function before its first use, i.e. higher up in the file than the point where you try to use the function. That's why include files are usually clustered at the top of a file; all of the symbols declared in the headers will be available throughout the code in the including file.
In this case, that means the declaration of your callback:
static OSStatus playerCallback(void *inRefCon etc);
must appear before your setupRemoteIO() function so that the compiler knows the function exists when you come to use it.
As you're on iOS, I'll also make the point that in recent compilers this restriction doesn't apply to Objective-C methods. It used to: you could only use method selectors that had already been seen. But in newer versions of Clang an Objective-C method can make use of a selector declared later in the same file without error.

Resources