I'm calling some old Objective-C code from Swift, and it will often throw this error, even if it seems nothing went wrong:
do {
try objCObject.someMethod()
}
catch {
print(error)
// Trying to handle the error here
}
Where that method's Objective-C signature is like this:
- (BOOL) someMethodWithError: (NSError **) outError;
Putting a breakpoint inside that catch
I can see this by using the LLDB console.
(lldb) po error
Foundation._GenericObjCError.nilError
(lldb) po error as NSError
Error Domain=Foundation._GenericObjCError Code=0 "(null)"
What is happening here and how do I handle this? When I try to write a special case for this in Swift, I get this:
/Path/To/My Code.swift:200:27: error: module 'Foundation' has no member named '_GenericObjCError'
catch Foundation._GenericObjCError.nilError {
^~~~~~~~~~ ~~~~~~~~~~~~~~~~~
This is caused when an Objective-C method uses the standard Cocoa approach to error throwing: take an NSError **
as the last parameter and return a BOOL
with YES
indicating success. If this works as intended, the return value will only ever be NO
if an error has occurred, and will then set the NSError **
object accordingly.
Swift expects this to be how all Objective-C methods with this signature work.
What you're seeing is what happens when one of these methods misbehaves for some reason and returns NO
without setting the NSError **
parameter to anything (or, explicitly setting it to nil
).
This could be due to a number of factors, such as returning an error code that is implicitly cast to the BOOL
(so, the 0
success code is translated to the NO
failure code), or writing its return line in such a way that its logic doesn't always return YES
on success, or because there actually was an error but the author didn't know what to set the NSError **
to, etc.
As for dealing with this, here is what I would do:
In this case, I think it's safest to assume the author simply made an error and returned the wrong value. Best to ignore the thrown error entirely.
do {
try objCObject.someMethod()
}
catch {
let nsError = (error as NSError)
if nsError.code == 0,
nsError.domain == "Foundation._GenericObjCError" {
print("Got invalid error from Objective-C")
}
else {
// Actually handle your error here
}
}
In this case, treat it as the error that the author documented. You may tweak the above example code to treat this error specially if desired.
This is easy. Simply change your method so that it only ever returns NO
when you are in a real error state, and always make sure that the NSError **
object has been set to a valid error object if the caller requested that.
- (BOOL) someMethodWithError: (NSError **) outError {
[self.something attempt];
if (!self.something.succeeded) {
if (nil != outError) {
*outError = [self makeSomeDescriptiveErrorFromSomething: self.something];
}
return NO;
}
else {
return YES;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With