'NSError' is not convertible to 'AutoreleasingUnsafePointer<NSError?>' - error-handling

In my iOS application I need to play sound. I have the following code:
var audioFileLocationUrl = NSBundle.mainBundle().pathForResource("midnight", ofType: "m4a")
var error: NSError
var audioPlayer = AVAudioPlayer()
audioPlayer = AVAudioPlayer(contentsOfURL: audioFileLocationUrl, error: error)
audioPlayer.prepareToPlay()
On line:
audioPlayer = AVAudioPlayer(contentsOfURL: audioFileLocationUrl, error: error)
I have error: Could not find overload for 'init' that accepts the supplied arguments

Edit (it was pointed out to me that there is a clear difference between when to use NSErrorPointer and when to use NSError?. Here is the official word:
From “Using Swift with Cocoa and Objective-C.”
“When you need to report the reason for the error, you can add to the function an NSError out parameter of type NSErrorPointer. This type is roughly equivalent to Objective-C’s NSError **, with additional memory safety and optional typing. You can use the prefix & operator to pass in a reference to an optional NSError type as an NSErrorPointer object, as shown in the code listing below.
var writeError : NSError?
AVAudioPlayer(contentsOfURL: audioFileLocationUrl, error: &writeError)

Try
var error :NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: audioFileLocationUrl, error: &error)
Failing that check you're calling the right method and passing in the right object types.
The reason you get this warning is because the compiler can't find that init method with those parameters.

Update: Error handling has changed drastically in Swift 2. See the documentation for the latest info.
For me, this code produces the error 'NSError' is not convertible to 'AutoreleasingUnsafePointer<NSError?>'. This is because the method is trying to take an error by reference so it can "write back" to your local variable. In Obj-C this looks like (NSError **). (Fun fact: AutoreleasingUnsafePointer<NSError?> is aliased to NSErrorPointer in Swift.)
In fact, if you ⌘-click on AVAudioPlayer to see its declaration, you'll find the init method is defined as
init(contentsOfURL url: NSURL!, error outError: AutoreleasingUnsafePointer<NSError?>)
For this reason, you must:
use an NSError? (i.e. Optional<NSError>) variable, since the error may be nil.
Pass the error in by reference with the prefix & operator.
var error: NSError?
...
audioPlayer = AVAudioPlayer(contentsOfURL: audioFileLocationUrl, error: &error)
See Error Reporting in "Using Swift with Cocoa and Objective-C" for more information.

Related

Swift perform selector with inout argument

I have an objc method I'd like to call from swift, but the method exists in a third party framework that may or may not exist at runtime.
What I'd like to do is call it dynamically, so I'm looking into interacting with it using selectors.
The method signature looks like this in ObjC
- (NSString * _Nullable)buildData:(NSError * _Nullable __autoreleasing * _Nullable)error;
Removing the nullable annotations, it's the bog standard "return a thing, or error" pattern that ObjC has had forever.
- (NSString*)buildData:(NSError*)error;
If swift can load this at compile time, then it quite happily translates into
func buildData() throws -> String
However, I'd like to call it dynamically. I've worked out I can do this:
let _target:NSObject = // obtain a reference to the underlying value
var error: NSError? = nil
_target.perform(NSSelectorFromString("buildData:"), with: &error)
The problem is, I can't pass a reference to the perform selector method. XCode gives me a compile error of
'&' used with non-inout argument of type 'Any?'
So my question is, how can I call this method using selectors?
Try this:
var error: NSError? = nil
let _target: NSObject = // obtain a reference to the underlying value
withUnsafeMutablePointer(to: &error) {
let selector: Selector = NSSelectorFromString("buildData:")
let methodIMP: IMP! = _target.method(for: selector)
unsafeBitCast(methodIMP,to:(#convention(c)(Any?,Selector,OpaquePointer)->Void).self)(_target,selector,OpaquePointer($0))
}
More info on using convention(c) for invoking selectors in Swift in my answer here

Is it possible to pass a NULL NSError** to a Swift function that throws?

In Objective-C, it's expected that you can pass in NULL to any NSError** parameter to ignore the error. However, when I try to pass NULL to a Swift method that throws an error, it generates a runtime error.
// Thrower.swift
class Thrower: NSObject {
static func throwError() throws {
throw NSError(domain: "bla", code: 0, userInfo: nil)
}
}
...
// AppDelegate.m
BOOL success = [Thrower throwErrorAndReturnError:NULL];
This generates an EXC_BAD_INSTRUCTION error, with this stack:
I'm a little surprised at this behavior. I would expect this to either work, or the compiler to generate a warning when you pass NULL to one of these methods.
Here's what the generated header of the Swift method looks like:
+ (BOOL)throwErrorAndReturnError:(NSError * __nullable * __null_unspecified)error;
If this was not supposed to work, why wouldn't they generate NSError * __nullable * __nonnull, so that a compiler warning is generated when you try to pass in a nullable NSError*?
Is there something I'm missing here, or is this just expected behavior?
On WWDC'15 Session 401 (Swift and Objective-C Interoperability) Doug Gregor said:
This means, 'I thought about it, I couldn't come to an answer.' The best thing to do is keep it implicitly unwrapped optional in Swift, keep it null-unspecified here.
So, basically, your null is mapped to ImplicitlyUnwrappedOptional<ErrorType>.None, that pretty much explains the crash.
On the session above they mention NSError ** is assumed to be nullable on both pointers. Apparently they've changed their mind or the mapping is not symmetrical, anyway it looks wrong to me.
Considering that behavior and not being NSError * __nullable * __nonnull I'd say it's a bug, I'd open a radar. If you do please let us know so we can dupe it.

Unwrapping an Optional value error in Swift

According to the question subject it is very frequent question and lot many answers there on stacks but i am confuse with behaviour of the error assurance cases. I am little new to Swift and i have some codes. In this code some times i getting and error of Unwrapping an Optional value
Bellow is the code
let operation = AFHTTPRequestOperation(request: request)
operation.setCompletionBlockWithSuccess({ (operation:AFHTTPRequestOperation!, responseObject:AnyObject!) -> Void in
var error:NSError?
//Bellow line gives me an error
let responseDict = NSJSONSerialization.JSONObjectWithData(responseObject as! NSData, options:NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
Error: fatal error: unexpectedly found nil while unwrapping an Optional value
Note: This line gives me and error only when my internet connectivity is down. When internet is totally disconnect then this error not comes up and it goes to Error block of AFNetworking. And when internet is fully working then also i did not get this error as well.
Please suggest some modification or replacement in code.
Your responseDict might not be valid JSON in this case. Whenever you get an error like this, look carefully at all your forced unwrapped optionals. In this case:
let responseDict = NSJSONSerialization.JSONObjectWithData(responseObject as! NSData, options:NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
Instead use:
if let responseDict = NSJSONSerialization.JSONObjectWithData(responseObject as! NSData, options:NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary {
// Response was valid JSON
}
You need to unwrap the responseObject which is an optional. Most likely it is nil when the connection is down (which makes perfect sense).
if let response = responseObject as? NSData {
let responseDict = NSJSONSerialization.JSONObjectWithData(response, ...
}

NSError initialization macro stopped working with recent Clang

I am used to using this macro to initialize NSError** arguments:
#define INIT_ERROR_IF_NULL(error) error = error ? error : &(NSError*){ nil }
This checks the passed NSError** argument and if it’s NULL, initializes it to a dummy NSError so that I don’t have to wrap every *error dereference in a conditional. In code, this version:
- (void) doFoo: (NSError**) error
{
if (error) {
*error = [NSError …];
}
}
…turns into this:
- (void) doFoo: (NSError**) error
{
INIT_ERROR_IF_NULL(error);
*error = [NSError …];
}
I like the approach, it saves branching and makes me write less code (of course I don’t bother when the error is being used just once). The problem is that it stopped working in the recent Clang version, as the compiler complains about pointer type mismatch:
Pointer type mismatch ('NSError *__autoreleasing *' and 'NSError **')
If I change the macro to explicitly cast the resulting anonymous object to NSError *__autoreleasing* (the type signatures are getting ridiculous), the compiler complains further, on a different line:
Casting 'NSError **' to type 'NSError *__autoreleasing *' changes retain/release properties of pointer
Again, the variable in question is a method argument. I don’t understand the issue. What can I do to keep my macro?
This compiles without errors/warnings:
#define INIT_ERROR_IF_NULL(error) error = error ? error : &( NSError __autoreleasing *){ nil }

Declaring Functions using NSArray as an Arguments

I have a function as follows:
-(int)ladderCalc:(NSArray*)amounts percentages:(NSArray*)percentages amount:(int)amount
{
// Do some stuff
return foo;
}
I have declared like this in the header file:
-(int)ladderCalc:(NSArray*)amounts percentages:(NSArray*)percentages amount:(int)amount;
But I am getting an error "implicit declaration of function is invalid in c99" when I try to use the int value returned elsewhere in the same file. Am I not declaring the function correctly?
UPDATE
I am realizing that I am not declaring this in the standardized way, I changed my declaration to MarkGranoff's recommendation (see the changes above) but I am still getting it as a warning this time.
Here is the context of how I am calling this function:
-(int)fooTotal: (int)amount
{
int totalFee = 0;
// Declare arguments
NSArray *percentages = [[NSArray alloc] initWithObjects:firstValue, secondValue, thirdValue, fourthValue, fifthValue, nil];
NSArray *amounts = [[NSArray alloc] initWithObjects:sixthValue, seventhValue, eigthValue, ninthValue, nil];
totalFee = ladderCalc(amounts,percentages,amount);
return totalFee;
}
So, I am still getting a warning even though this seems to make sense as far as Obj-C style is concerned.
I am pretty sure I am not calling this function correctly, I am getting an unrecognized symbol error when I compile the project.
Undefined symbols for architecture i386:
"_ladderCalc", referenced from:
-[FeeCalcLibrary getMFModelTotal:] in FeeCalcLibrary-A83D2A7637F57664.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Try this instead:
-(int)ladderCalc:(NSArray*)amounts percentages:(NSArray*)percentages amount:(int)amount;
and change the signature of the implementation to match. Then you have arguments with names you can reference in the code of the method. Namely: amounts, percentages, and amount.
As #MarkGranoff says.
Objective-C has it's arguments interspersed in the method name.
For the method declaration:
-(int)ladderCalcWithAmounts:(NSArray*)amounts percentages:(NSArray*)percentages amount:(int)amount;
the method name is (the colons are part of the name):
ladderCalc:percentages:amount:
Interspersed in the method name the arguments are:
(NSArray*)amounts
(NSArray*)percentages
(int)amount;
This improved readability over a "C" function call which might be:
int ladderCalcPercentagesAmount(NSArray *amounts, NSArray *percentages, amount);
Technically, Objective-C does not have named parameters, rather interspersed parameters. Named parameters tends to imply that position is not important just the associated names, an example are Python's named parameters.