No he leído demasiado en Swift, pero una cosa que noté es que no hay excepciones. Entonces, ¿cómo hacen el manejo de errores en Swift? ¿Alguien ha encontrado algo relacionado con el manejo de errores?
No he leído demasiado en Swift, pero una cosa que noté es que no hay excepciones. Entonces, ¿cómo hacen el manejo de errores en Swift? ¿Alguien ha encontrado algo relacionado con el manejo de errores?
Respuestas:
Las cosas han cambiado un poco en Swift 2, ya que hay un nuevo mecanismo de manejo de errores, que es algo más similar a las excepciones pero diferente en detalles.
Si la función / método quiere indicar que puede arrojar un error, debe contener una throws
palabra clave como esta
func summonDefaultDragon() throws -> Dragon
Nota: no existe una especificación para el tipo de error que la función realmente puede arrojar. Esta declaración simplemente establece que la función puede lanzar una instancia de cualquier tipo que implemente ErrorType o que no arroje nada.
Para invocar la función, debe usar la palabra clave try, como esta
try summonDefaultDragon()
esta línea normalmente debería estar presente bloque do-catch como este
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
Nota: la cláusula catch utiliza todas las potentes funciones de la coincidencia de patrones Swift para que sea muy flexible aquí.
Puede decidir propagar el error, si está llamando a una función de lanzamiento desde una función que está marcada con la throws
palabra clave:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Alternativamente, puede llamar a la función de lanzamiento usando try?
:
let dragonOrNil = try? summonDefaultDragon()
De esta forma, obtiene el valor de retorno o nulo, si se produce algún error. De esta manera no obtienes el objeto de error.
Lo que significa que también se puede combinar try?
con declaraciones útiles como:
if let dragon = try? summonDefaultDragon()
o
guard let dragon = try? summonDefaultDragon() else { ... }
Finalmente, puede decidir que sabe que el error no ocurrirá realmente (por ejemplo, porque ya lo ha verificado son requisitos previos) y usar la try!
palabra clave:
let dragon = try! summonDefaultDragon()
Si la función realmente arroja un error, obtendrá un error de tiempo de ejecución en su aplicación y la aplicación finalizará.
Para lanzar un error, usa la palabra clave throw como esta
throw DragonError.dragonIsMissing
Puede lanzar cualquier cosa que se ajuste al ErrorType
protocolo. Para empezar, NSError
cumple con este protocolo, pero es probable que desee utilizar un método basado en enumeración ErrorType
que le permita agrupar múltiples errores relacionados, potencialmente con datos adicionales, como este
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Las principales diferencias entre el nuevo mecanismo de error Swift 2 y 3 y las excepciones de estilo Java / C # / C ++ son las siguientes:
do-catch
+ try
+ defer
frente a la try-catch-finally
sintaxis tradicional .do-catch
bloqueo no detectará ninguna NSException, y viceversa, para eso debe usar ObjC.NSError
convenciones del método Cocoa de devolver false
(para Bool
devolver funciones) o nil
(para AnyObject
devolver funciones) y pasar NSErrorPointer
con detalles de error.Como un azúcar sintético adicional para facilitar el manejo de errores, hay dos conceptos más
defer
palabras clave) que le permiten lograr el mismo efecto que finalmente bloquea en Java / C # / etc.guard
palabra clave) que le permite escribir un poco menos de código if / else que en el código normal de comprobación / señalización de erroresErrores de tiempo de ejecución:
Como Leandros sugiere para manejar errores de tiempo de ejecución (como problemas de conectividad de red, análisis de datos, abrir archivos, etc.), debe usar NSError
como lo hizo en ObjC, porque Foundation, AppKit, UIKit, etc. informan sus errores de esta manera. Entonces es más una cuestión de marco que de lenguaje.
Otro patrón frecuente que se está utilizando son los bloques de éxito / falla del separador como en AFNetworking:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
Aún así, el bloque de falla recibió con frecuencia la NSError
instancia, que describe el error.
Errores de programador:
Para los errores del programador (como el acceso fuera de los límites del elemento de matriz, los argumentos no válidos pasados a una llamada de función, etc.), usó excepciones en ObjC. Swift lenguaje no parecen tener ningún soporte de idiomas para las excepciones (como throw
, catch
, etc palabra clave). Sin embargo, como la documentación sugiere, se está ejecutando en el mismo tiempo de ejecución que ObjC y, por lo tanto, aún puede lanzar de NSExceptions
esta manera:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
Simplemente no puede atraparlos en Swift puro, aunque puede optar por capturar excepciones en el código ObjC.
La pregunta es si debe lanzar excepciones para errores de programador, o más bien usar aserciones como Apple sugiere en la guía del lenguaje.
fatalError(...)
es lo mismo también.
Actualización 9 de junio de 2015 - Muy importante
Swift 2.0 viene con try
, throw
y catch
palabras clave y lo más emocionante es:
Swift traduce automáticamente los métodos Objective-C que producen errores en métodos que arrojan un error de acuerdo con la funcionalidad nativa de manejo de errores de Swift.
Nota: Los métodos que consumen errores, como los métodos delegados o los métodos que toman un controlador de finalización con un argumento de objeto NSError, no se convierten en métodos que arrojan cuando Swift los importa.
Extracto de: Apple Inc. "Uso de Swift con Cocoa y Objective-C (Presentación preliminar de Swift 2)". iBooks
Ejemplo: (del libro)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(@"Error: %@", error.domain);
}
El equivalente en swift será:
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
Lanzar un error:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Se propagará automáticamente a la persona que llama:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
De los libros de Apple, The Swift Programming Language parece que los errores deberían manejarse usando enum.
Aquí hay un ejemplo del libro.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
De: Apple Inc. "El lenguaje de programación Swift". iBooks https://itun.es/br/jEUH0.l
Actualizar
De los libros de noticias de Apple, "Uso de Swift con Cocoa y Objective-C". Las excepciones de tiempo de ejecución no se producen con lenguajes rápidos, por eso no tiene try-catch. En su lugar, utiliza el encadenamiento opcional .
Aquí hay un tramo del libro:
Por ejemplo, en la lista de códigos a continuación, la primera y la segunda línea no se ejecutan porque la propiedad de longitud y el método characterAtIndex: no existen en un objeto NSDate. Se deduce que la constante myLength es una Int opcional y se establece en nil. También puede usar una instrucción if – let para desenvolver condicionalmente el resultado de un método al que el objeto puede no responder, como se muestra en la línea tres
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Extracto de: Apple Inc. "Uso de Swift con Cocoa y Objective-C". iBooks https://itun.es/br/1u3-0.l
Y los libros también lo alientan a usar el patrón de error de cacao de Objective-C (NSError Object)
El informe de errores en Swift sigue el mismo patrón que en Objective-C, con el beneficio adicional de ofrecer valores de retorno opcionales. En el caso más simple, devuelve un valor Bool de la función para indicar si tuvo éxito o no. Cuando necesite informar el motivo del error, puede agregar a la función un parámetro de salida NSError de tipo NSErrorPointer. Este tipo es más o menos equivalente al NSError ** de Objective-C, con seguridad de memoria adicional y escritura opcional. Puede usar el prefijo y el operador para pasar una referencia a un tipo NSError opcional como un objeto NSErrorPointer, como se muestra en la lista de códigos a continuación.
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
Extracto de: Apple Inc. "Uso de Swift con Cocoa y Objective-C". iBooks https://itun.es/br/1u3-0.l
No hay excepciones en Swift, similar al enfoque de Objective-C.
En el desarrollo, puede utilizar assert
para detectar cualquier error que pueda aparecer, y debe repararse antes de pasar a producción.
El NSError
enfoque clásico no se modifica, envía un mensaje NSErrorPointer
, que se completa.
Breve ejemplo:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
f();g();
convierta f(&err);if(err) return;g(&err);if(err) return;
durante el primer mes, luego simplemente se conviertaf(nil);g(nil);hopeToGetHereAlive();
El 'Swift Way' recomendado es:
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
Sin embargo, prefiero probar / atrapar ya que me resulta más fácil de seguir porque mueve el manejo de errores a un bloque separado al final, esta disposición a veces se llama "Ruta Dorada". Por suerte, puedes hacer esto con los cierres:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
También es fácil agregar un recurso de reintento:
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
El listado de TryBool es:
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
Puede escribir una clase similar para probar un valor devuelto Opcional en lugar del valor Bool:
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
La versión TryOptional aplica un tipo de retorno no opcional que facilita la programación posterior, por ejemplo, 'Swift Way:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
Usando TryOptional:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
Tenga en cuenta el desempaquetado automático.
Editar: aunque esta respuesta funciona, es poco más que Objective-C transcrito en Swift. Se ha vuelto obsoleto por los cambios en Swift 2.0. La respuesta anterior de Guilherme Torres Castro es una muy buena introducción a la forma preferida de manejar errores en Swift. VOS
Me costó un poco descubrirlo, pero creo que lo he sospechado. Aunque parece feo. Nada más que un skin delgado sobre la versión Objective-C.
Llamar a una función con un parámetro NSError ...
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
Escribiendo la función que toma un parámetro de error ...
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
Contenedor básico alrededor del objetivo C que le brinda la función de captura de prueba. https://github.com/williamFalcon/Swift TryCatch
Usar como:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
Esta es una respuesta de actualización para swift 2.0. Estoy esperando un modelo de manejo de errores rico en funciones como en Java. Finalmente, anunciaron las buenas noticias. aquí
Modelo de manejo de errores: el nuevo modelo de manejo de errores en Swift 2.0 se sentirá instantáneamente natural, con palabras clave conocidas de probar, lanzar y atrapar . Lo mejor de todo es que fue diseñado para funcionar perfectamente con los SDK de Apple y NSError. De hecho, NSError se ajusta al ErrorType de Swift. Definitivamente querrás ver la sesión de la WWDC sobre Novedades en Swift para saber más al respecto.
p.ej :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Como dijo Guilherme Torres Castro, en Swift 2,0, try
, catch
,do
puede ser utilizado en la programación.
Por ejemplo, en el método de recuperación de datos CoreData, en lugar de ponerlo &error
como parámetro en el managedContext.executeFetchRequest(fetchRequest, error: &error)
, ahora solo necesitamos usar managedContext.executeFetchRequest(fetchRequest)
y luego manejar el error con try
, catch
( Apple Document Link )
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
Si ya ha descargado la xcode7 Beta. Intente buscar errores de lanzamiento en Documentaciones y Referencia de API y elija el primer resultado que se muestra, da una idea básica de lo que se puede hacer para esta nueva sintaxis. Sin embargo, todavía no se ha publicado toda la documentación para muchas API.
Se pueden encontrar técnicas más sofisticadas de manejo de errores en
Novedades de Swift (2015 Sesión 106 28m30s)
El manejo de errores es una nueva característica de Swift 2.0. Utiliza los try
, throw
y catch
palabras clave.
Vea el anuncio de Apple Swift 2.0 en el blog oficial de Apple Swift
Agradable y simple lib para manejar la excepción: TryCatchFinally-Swift
Al igual que algunos otros, se ajusta a las características de excepción objetivas de C.
Úselo así:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
Comenzando con Swift 2, como otros ya han mencionado, el manejo de errores se logra mejor mediante el uso de las enumeraciones do / try / catch y ErrorType. Esto funciona bastante bien para los métodos sincrónicos, pero se requiere un poco de inteligencia para el manejo de errores asincrónicos.
Este artículo tiene un gran enfoque para este problema:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
Para resumir:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
entonces, la llamada al método anterior sería la siguiente:
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
Esto parece un poco más limpio que tener una devolución de llamada de errorHandler por separado pasada a la función asincrónica, que era cómo se manejaría esto antes de Swift 2.
Lo que he visto es que, debido a la naturaleza del dispositivo, no desea lanzar un montón de mensajes crípticos de manejo de errores al usuario. Es por eso que la mayoría de las funciones devuelven valores opcionales, entonces solo codifica para ignorar los opcionales. Si una función vuelve nula, lo que significa que falló, puede hacer estallar un mensaje o lo que sea.