Manejo de errores en lenguaje rápido

190

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?

peko
fuente
1
Encontré mensajes de error al igual que con Obj-C: o
Arbitur
13
@Arbitur, ¿la buena forma de segfault?
peko
Creé un NSTimer en Swift y cuando escribí mal la función se bloqueó y me dio un error diciendo que no podía encontrar el método :)
Arbitur
3
Puede agregar soporte try-catch para Swift siguiendo las instrucciones en este artículo: medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
William Falcon
@peko ¿Cómo manejas una segfault en Swift? No creo que sea posible a partir de ahora, lo que lamentablemente hace que algunos errores sean irrecuperables
Orlin Georgiev

Respuestas:

148

Swift 2 y 3

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.

1. Indicando posibilidad de error

Si la función / método quiere indicar que puede arrojar un error, debe contener una throwspalabra 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.

2. Función de invocación que puede arrojar errores

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 throwspalabra 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á.

3. Lanzar un error

Para lanzar un error, usa la palabra clave throw como esta

throw DragonError.dragonIsMissing

Puede lanzar cualquier cosa que se ajuste al ErrorTypeprotocolo. Para empezar, NSErrorcumple con este protocolo, pero es probable que desee utilizar un método basado en enumeración ErrorTypeque 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:

  • La sintaxis es un poco diferente: do-catch+ try+ deferfrente a la try-catch-finallysintaxis tradicional .
  • El manejo de excepciones generalmente incurre en un tiempo de ejecución mucho mayor en la ruta de excepción que en la ruta de éxito. Este no es el caso con los errores de Swift 2.0, donde la ruta de éxito y la ruta de error cuestan aproximadamente lo mismo.
  • Todos los códigos de error deben ser declarados, mientras que las excepciones pueden haberse lanzado desde cualquier lugar. Todos los errores son "excepciones marcadas" en la nomenclatura de Java. Sin embargo, a diferencia de Java, no especifica errores potencialmente lanzados.
  • Las excepciones rápidas no son compatibles con las excepciones ObjC. Su do-catchbloqueo no detectará ninguna NSException, y viceversa, para eso debe usar ObjC.
  • Las excepciones rápidas son compatibles con las NSErrorconvenciones del método Cocoa de devolver false(para Booldevolver funciones) o nil(para AnyObjectdevolver funciones) y pasar NSErrorPointercon detalles de error.

Como un azúcar sintético adicional para facilitar el manejo de errores, hay dos conceptos más

  • acciones diferidas (usando deferpalabras clave) que le permiten lograr el mismo efecto que finalmente bloquea en Java / C # / etc.
  • declaración de protección (usando una guardpalabra 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 errores

Swift 1

Errores 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 NSErrorcomo 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 NSErrorinstancia, 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 NSExceptionsesta 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.

MDJ
fuente
20
"problemas de conectividad de red" y "abrir archivos" utilizando las API de Cocoa (NSFileHandle) pueden generar excepciones que deben detectarse. Sin excepciones en Swift, debe implementar esta parte de su programa en Objective-C o realizar todo su trabajo utilizando las API de BSD C (ambas son soluciones alternativas). Consulte la documentación de NSFileHandle.writeData para obtener más información ... developer.apple.com/library/ios/documentation/Cocoa/Reference/… :
Matt Gallagher
55
Nuevamente, el manejo sin excepción significa la construcción de objetos en dos etapas con todos sus problemas inherentes. Ver stroustrup.com/except.pdf .
Phil
2
el fatalError(...)es lo mismo también.
holex
8
Por mucho que me guste Swift, creo que esta es una elección catastrófica, y después de haber probado algunas de las consecuencias, están jugando con fuego con esta omisión ...
Rob
2
Sí, las excepciones comprobadas ahora se usan con moderación, porque se descubrió que al obligar al programador a detectar excepciones, tienen pocas esperanzas de recuperarse del código de contaminantes en formas de ruptura del principio de responsabilidad única. Una clase de nivel de dominio no quiere tener que lidiar con las excepciones de la capa de infraestructura. Entonces, ahora, las excepciones no marcadas tienden a ser favorecidas, y la parte interesada puede detectarlas, si corresponde. . Marcado = definitivamente recuperable. Sin marcar = no / potencialmente recuperable.
Jasper Blues
69

Actualización 9 de junio de 2015 - Muy importante

Swift 2.0 viene con try, throwy catchpalabras 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

Guilherme Torres Castro
fuente
Para la última declaración, debería ser: do {try myString.writeToFile (path, atomically: true, encoding: NSUTF8StringEncoding)} catch let error as NSError {print (error)}
Jacky
1
@Jacky Sí, eso es cierto para swift 2.0, aunque esta respuesta se escribió antes del lanzamiento de swift 2.0, actualicé la respuesta para mostrar la nueva forma de manejar los errores en swift 2.0. Estaba pensando en dejar este camino como referencia, pero consideraré actualizar la respuesta completa para usar solo swift 2.0
Guilherme Torres Castro
12

No hay excepciones en Swift, similar al enfoque de Objective-C.

En el desarrollo, puede utilizar assertpara detectar cualquier error que pueda aparecer, y debe repararse antes de pasar a producción.

El NSErrorenfoque 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)")
}
Leandros
fuente
66
Esto plantea dos preguntas: ¿qué sucede cuando el código ObjC que llamamos desde Swift en realidad arroja una excepción, y si NSError es nuestro objeto de error universal como en ObjC?
MDJ
1
¿Es solo un hecho de la vida con Swift que los inicializadores no fallan o no?
Phil
11
El manejo de excepciones parece bastante sucio
Tash Pemhiwa
27
Sí, ¿quién necesita excepciones cuando puedes simplemente estrellarte? ¿O poner un NSError ** como argumento en todas las funciones que declaras? para que todo se 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();
hariseldon78
2
Esta respuesta está desactualizada (Swift ahora admite excepciones) y es incorrecta (Objective-C admite excepciones.
Rog
11

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.

Howard Lovatt
fuente
7

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...
}
Vince O'Sullivan
fuente
5

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
})
William Falcon
fuente
Buena idea. Pero quienes decidan usar esto deben tener en cuenta que los objetos asignados en el bloque try no se desasignan cuando se lanza una excepción. Esto puede causar problemas con los objetos zombies y cada uso de RAII se ve comprometido (desbloqueo automático, auto-sql-commit, auto-sql-rollback ...). ¿Quizás c ++ podría ayudarnos con alguna forma de "runAtExit"?
hariseldon78
Actualización: acabo de encontrar que hay una bandera en el sonido metálico para permitir la liberación de objetos en el lanzamiento de excepciones: -fobjc-arc-exceptions. Debo probar si todavía funciona con la versión envuelta (creo que debería)
hariseldon78
Si utiliza esta opción, tenga en cuenta que el tamaño del código aumenta ya que el compilador debe generar un código seguro de semi-excepción. Además: confiar en una función de compilador de este tipo puede no ser la mejor idea. Las excepciones son solo para errores de programador, por lo que no vale la pena usar esa opción del compilador solo para ahorrar un poco de memoria durante el desarrollo. Si tiene excepciones en su código de producción, debe tratar con la cosa que causa esas excepciones en primer lugar.
Christian Kienle
1
Puede haber situaciones fuera de su control. Por ejemplo, analizar json en el formato incorrecto.
William Falcon
3

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)
}}
Paraneetharan Saravanaperumal
fuente
3

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 &errorcomo 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)

Zingoer
fuente
1

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")
}
Morten Holmgaard
fuente
He agregado una muestra :)
Morten Holmgaard
Probablemente valga la pena mencionar la opinión de los autores: "Advertencia: este es un truco para la diversión y el mal. Resiste la tentación de usarlo".
jbat100
1

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.

Gene Loparco
fuente
0

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.

cheborneck
fuente
1
Devolver un valor nulo no devuelve información sobre la naturaleza del error. Si se devuelve un objeto de error cuando se produce un error, entonces, dependiendo del error, el programador puede elegir ignorarlo, manejarlo, dejar que aparezca o "hacer estallar un mensaje o lo que sea". El conocimiento es poder.
Vince O'Sullivan