How to call objective-c function with double pointer parameter in swift - ios

I have library written on objective c language. It has few methods that take double pointer parameters.
I'm trying to use it in project, that is using swift 2.0
let failedTextFields = AutoreleasingUnsafeMutablePointer<NSArray?>()
UITextField.checkAllFieldsIfIsValidOnView(self.view,
withMatchedFields: nil,
faildedFields: failedTextFields,
updateStates: updateState)
let isValid = failedTextFields.memory?.count > 0
(faildedFields is double pointer parameter)
But when it come to function, it detect, that passed parameter is nil. And because of this, the result is not initialised in function.
Can somebody help me? How can I call this function correctly in 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

CGPDFDictionaryGetString function nil parameter exception

I found away of getting the types of content in a pdf. I built my code on this example where I am trying to read the content types from the dictionary. I found out that the key for type is Type. However I am getting an Error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code:
pdfDoc = CGPDFDocumentCreateWithURL(url)
if (pdfDoc != nil) {
let pdfCatalog=CGPDFDocumentGetCatalog(pdfDoc)
var uriStringRef: UnsafeMutablePointer< CGPDFStringRef>!
if (CGPDFDictionaryGetString(pdfCatalog, "Type", uriStringRef)) { //error
print(uriStringRef)
}
}
The only nil parameter is uriStringRef. However from the example, I am not supposed to set to any value before passing to CGPDFDictionaryGetString, and according to Apple's documentation, this should get the string returned value based on the key "Type". Am I right?
How can I solve this problem?
You are passing an uninitialized pointer to the CGPDFDictionaryGetString method, that is unsafe because you cannot know whether the method being called tries to read from a pointer before writing to it.
From the Swift blog post Interacting with C Pointers:
Pointers as In/Out Parameters
C and Objective-C don’t support multiple return values, so Cocoa APIs frequently use pointers as a way of passing additional data in and out of functions. Swift allows pointer parameters to be treated like inout parameters, so you can pass a reference to a var as a pointer argument by using the same & syntax.
For safety, Swift requires the variables to be initialized before being passed with &. This is because it cannot know whether the method being called tries to read from a pointer before writing to it.
So initialize the CGPDFStringRef 'pointer' like this:
var uriStringRef = CGPDFStringRef.init()
And pass it to the method using the inout syntax:
CGPDFDictionaryGetString(pdfCatalog, "Type", &uriStringRef)

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 call Objective-C "self" from C method [duplicate]

This question already has answers here:
Pass instance method as function pointer to C Library
(1 answer)
How do I give C function a pointer to 'self' (calling obj) in objective-c?
(2 answers)
Closed 9 years ago.
I have Objective-C class in my iOS project that implements Objective-C and C code in the same class. I changed the extension to .mm and this part goes well. Now I want to set a C method that will call the Objective-C method in the same class. The problem I get is when I am trying to call self from the C method.
Here is the code :
void SetNotificationListeners(){
[self fireSpeechRecognition];
}
the error is :
Use of undeclared identifier 'self'
how can I manage this?
You have to pass the instance pointer to the C function:
void SetNotificationListeners(void *uData)
{
MyClass *obj = (__bridge MyClass *)(uData);
[obj fireSpeechRecognition];
}
- (void)myMethod
{
// Call C function from Objective-C method:
myFunction((__bridge void *)(self));
}
(The "brigde" stuff is needed only if you compile with ARC.)
Either give self as an argument to the function call:
void SetNotificationListeners(void *myObj){
[(MyClass*)myObj fireSpeechRecognition];
}
//in objC
SetNotificationListeners(self);
or have a global variable that holds reference
//global variable
static MyClass *myObj;
//in obj c
myObj = self;
SetNotificationListeners(); //use myObj instead of self in function
The first is better in my opinion.
You don’t have to change the file extension. Objective-C is a superset of C, which means you can use plain C in your Objective-C files as you please.
When you write an implementation of an Objective-C method, that method always executes in context of some particular instance, that’s the self part. You get the self in an Objective-C method automagically, but behind the scenes it’s just passed as an argument to the method, see obc_msgsend.
Your plain C function is not a part of the class (plain C functions never are), therefore there’s no instance associated with it when you call it, there’s no implicit self. If you want the function to call some instance, you have to pass the pointer to that instance explicitly. For example some of the plain C APIs have a “context” pointer you can pass when registering for a callback. See the neighboring answers for examples of this.
In Objective-C methods there are two parameters that are implicitly passed into each method, which are self and _cmd. This is why you can access self from within a method.
You could just pass self as an argument to your c function when calling from an Objective-C method, but with that trivial example I'm not sure why you wouldn't just use a method.
You do not need to change the file extension to .mm unless you are using Objective-C++

Resources