Swift doesn't convert Objective-C NSError** to throws
Only Objective-C
methods are translated to throwing Swift
methods, which do return a BOOL
(not lower-cased bool
), or a nullable-object.
(Tested with Xcode 11.7, and Swift 5 language.)
The reason is that Cocoa methods always use a return value NO
or nil
to indicate the failure of a method, and not just set an error object.
This is documented in
Using and Creating Error Objects:
Important: Success or failure is indicated by the return value of the method.
Although Cocoa methods that indirectly return error objects in the Cocoa error
domain are guaranteed to return such objects if the method indicates failure
by directly returning nil or NO, you should always check that the return
value is nil or NO before attempting to do anything with the NSError object.
For example, the Objective-C interface
@interface OClass : NSObject
NS_ASSUME_NONNULL_BEGIN
-(void)doSomethingWithArgument1:(int) x error:(NSError **)error;
-(BOOL)doSomethingWithArgument2:(int) x error:(NSError **)error;
-(NSString *)doSomethingWithArgument3:(int) x error:(NSError **)error;
-(NSString * _Nullable)doSomethingWithArgument4:(int) x error:(NSError **)error;
-(BOOL)doSomething:(NSError **)error;
NS_ASSUME_NONNULL_END
@end
is mapped to Swift as
open class OClass : NSObject {
open func doSomethingWithArgument1(x: Int32, error: NSErrorPointer)
open func doSomethingWithArgument2(x: Int32) throws
open func doSomethingWithArgument3(x: Int32, error: NSErrorPointer) -> String
open func doSomethingWithArgument4(x: Int32) throws -> String
open func doSomething() throws
}
If you can change the interface of your method then you should add a boolean
return value to indicate success or failure.
Otherwise you would call it from Swift as
var error : NSError?
object.doSomethingWithArgument(argumentValue, error: &error)
if let theError = error {
print(theError)
}
Remark: At
- https://github.com/apple/swift/blob/master/test/Inputs/clang-importer-sdk/usr/include/errors.h
I found that Clang has an attribute which forces a function to throw an error in Swift:
-(void)doSomethingWithArgument5:(int) x error:(NSError **)error
__attribute__((swift_error(nonnull_error)));
is mapped to Swift as
public func doSomethingWithArgument5(x: Int32) throws
and seems to work "as expected". However, I could not find any official documentation
about this attribute, so it might not be a good idea to rely on it.
How can I throw an NSError from a Swift class and catch it in an Objective-C class?
The problem is that a Swift Error / Objective-C NSError is not an NSException. You are configured to catch NSExceptions but that is irrelevant.
The way to "catch" an NSError in Objective-C when Swift throws an Error is by indirection with the NSError**
parameter, just as it always has been.
NSError* err = nil;
BOOL ok = [self.netServiceManager requestHelloPageAndReturnError:&err];
if (ok) {
// proceed normally
} else {
// you got an error, it is sitting in `err`
}
(Notice how Swift supplies a BOOL result exactly so you can implement the correct pattern.)
How to write swift 3 method with throws statement and return value that can be used in Objective C?
The standard way of reporting success or failure in Objective-C is
to return the boolean value NO
or a nil
object pointer,
as documented in
Using and Creating Error Objects:
Important: Success or failure is indicated by the return value of the method.
Although Cocoa methods that indirectly return error objects in the Cocoa error
domain are guaranteed to return such objects if the method indicates failure
by directly returning nil or NO, you should always check that the return
value is nil or NO before attempting to do anything with the NSError object.
So you can return a instance of an object
func doSomething(param: String) throws -> NSNumber
which is translated to
- (NSNumber * _Nullable)doSomethingWithParam:(NSString * _Nonnull)param error:(NSError * _Nullable * _Nullable)error;
and returns nil
if an error is thrown, or return a boolean and
pass other values back by reference
func doSomething(param: String, value: UnsafeMutablePointer<Int>) throws
which is mapped to
- (BOOL)doSomethingWithParam:(NSString * _Nonnull)param value:(NSInteger * _Nonnull)value error:(NSError * _Nullable * _Nullable)error;
Is it possible to pass a NULL NSError** to a Swift function that throws?
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.
@throws or NSError which one is appropriate in for objective C api kind of methods
Unlike Java, in Objective-C exceptions are just used to handle unrecoverable states. This means that for example, you don't catch EXC_BAD_ACCESS, rather you debug your application and correct the problem.
The Objective-C equivalent of Java exceptions is NEError and the null pointer pattern. For example if a formatter fails to format a string to a number because the string does not represent a number, it returns nil. There is also a method to format the number that takes a NSError double pointer to return the error description.
So to handle errors you typically implement a method like this:
- (id) object : (out NSError**) error
{
BOOL ok= ...
if(!ok)
{
if(error)
*error= ...
return nil;
}
return someObject;
}
Related Topics
This Action Could Not Be Completed. Try Again (-22421)
Uigesturerecognizer on Uiimageview
How to Turn a Cvpixelbuffer into a Uiimage
Firebase Cloud Messaging - Check Existing or Available Topics
How to Create Custom Mkannotationview and Custom Annotation Title and Subtitle
Sharing Code Between Original iOS App and App Extension
Autolayout - Make Height of View Relative to Half Superview Height
What's the Difference Between "Architectures" and "Valid Architectures" in Xcode Build Settings
Load Local HTML into Uiwebview Using Swift
Uitableview - Scroll to the Top
Creating a Uiimage from a Uicolor to Use as a Background Image for Uibutton
How to Reload Tableview from Another View Controller in Swift
How to Add Multiple Collection Views in a Uiviewcontroller in Swift
How to Present iOS Uiactionsheet in Swift
How to Access File Included in App Bundle in Swift