ios - Swift no convierte Objective-C NSError** en lanzamientos
(2)
Tengo un código heredado de Objective-C, que declara el método como
- (void)doSomethingWithArgument:(ArgType)argument error:(NSError **)error
Tal como está escrito aquí https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html
Swift traduce automáticamente los métodos de Objective-C que producen errores en métodos que generan un error de acuerdo con la funcionalidad de manejo de errores nativa de Swift.
Pero en mi proyecto los métodos descritos se llaman así:
object.doSomething(argument: ArgType, error: NSErrorPointer)
Además, lanza una excepción de tiempo de ejecución cuando trato de usarlos como:
let errorPtr = NSErrorPointer()
object.doSomething(argumentValue, error: errorPtr)
¿Necesito algo más para convertir los métodos "NSError **" de Objective-C en métodos "trows" rápidos?
Debe hacer que su método devuelva un BOOL
, para indicar al tiempo de ejecución que se debe o no lanzar un error. También debe agregar __autoreleasing
al parámetro de error, para asegurarse de que ARC no libere accidentalmente el error antes de que tenga la oportunidad de usarlo:
- (BOOL)doSomethingWithArgument:(ArgType)argument error:(NSError * __autoreleasing *)error
Entonces puedes llamarlo desde Swift así:
do {
object.doSomethingWithArgument(someArgument)
} catch let err as NSError {
print("Error: /(err)")
}
Solo los métodos Objective-C que devuelven un objeto BOOL
o un objeto (que puede contener nulos) se convierten a métodos de lanzamiento en Swift.
La razón es que los métodos Cocoa siempre usan un valor de retorno NO
o nil
para indicar el fallo de un método, y no solo para establecer un objeto de error. Esto se documenta en Uso y creación de objetos de error :
Importante: el éxito o el fracaso se indica mediante el valor de retorno del método. Aunque los métodos Cocoa que indirectamente devuelven objetos de error en el dominio de error Cocoa tienen la garantía de devolver tales objetos si el método indica un error devolviendo directamente nil o NO, siempre debe verificar que el valor de retorno sea nil o NO antes de intentar hacer algo con Objeto NSError.
Por ejemplo, la interfaz de Objective-C
@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;
NS_ASSUME_NONNULL_END
@end
se asigna a Swift como
public class OClass : NSObject {
public func doSomethingWithArgument1(x: Int32, error: NSErrorPointer)
public func doSomethingWithArgument2(x: Int32) throws
public func doSomethingWithArgument3(x: Int32, error: NSErrorPointer) -> String
public func doSomethingWithArgument4(x: Int32) throws -> String
}
Si puede cambiar la interfaz de su método, debe agregar un valor de retorno booleano para indicar el éxito o el fracaso.
De lo contrario lo llamarías desde Swift como
var error : NSError?
object.doSomethingWithArgument(argumentValue, error: &error)
if let theError = error {
print(theError)
}
Observación: en
Encontré que Clang tiene un atributo que obliga a una función a lanzar un error en Swift:
-(void)doSomethingWithArgument5:(int) x error:(NSError **)error
__attribute__((swift_error(nonnull_error)));
se asigna a Swift como
public func doSomethingWithArgument5(x: Int32) throws
y parece funcionar "como se esperaba". Sin embargo, no pude encontrar ninguna documentación oficial sobre este atributo, por lo que podría no ser una buena idea confiar en él.