Il modo più semplice per generare un errore / un'eccezione con un messaggio personalizzato in Swift 2?


136

Voglio fare qualcosa in Swift 2 che sono abituato a fare in più altre lingue: generare un'eccezione di runtime con un messaggio personalizzato. Ad esempio (in Java):

throw new RuntimeException("A custom message here")

Capisco che posso lanciare tipi di enum conformi al protocollo ErrorType, ma non voglio definire enum per ogni tipo di errore che lancio. Idealmente, vorrei essere in grado di imitare l'esempio sopra il più vicino possibile. Ho cercato di creare una classe personalizzata che implementasse il protocollo ErrorType, ma non riesco nemmeno a capire che cosa richiede quel protocollo (vedi documentazione ). Idee?


2
Il lancio / presa di Swift 2 non fa eccezione.
zaph,

Risposte:


194

L'approccio più semplice è probabilmente quello di definire uno personalizzato enumcon una sola caseche ha un Stringcollegato ad esso:

enum MyError: ErrorType {
    case runtimeError(String)
}

Oppure, a partire da Swift 4:

enum MyError: Error {
    case runtimeError(String)
}

Esempio di utilizzo sarebbe qualcosa del tipo:

func someFunction() throws {
    throw MyError.runtimeError("some message")
}
do {
    try someFunction()
} catch MyError.runtimeError(let errorMessage) {
    print(errorMessage)
}

Se desideri utilizzare Errortipi esistenti , quello più generale sarebbe un NSErrore potresti creare un metodo di fabbrica per crearne uno e inviarlo con un messaggio personalizzato.


Ciao, so che è stato un anno che hai pubblicato questa risposta, ma vorrei sapere se è possibile ottenere l' Stringinterno del tuo errorMessage, in tal caso, come posso farlo?
Renan Camaforte,

1
@RenanCamaforte Mi dispiace, non capisco la domanda? Qui Stringè associato a MyError.RuntimeError(impostato al momento della throw) e si accede ad esso a catch(con let errorMessage).
Arkku,

1
Ti è stata chiesta la soluzione più semplice. La soluzione quando si creano enumerazioni personalizzate, funzioni ed ecc. Non è semplice. Conosco almeno un modo ma non lo
posterò

3
@VyachaslavGerchicov Se non conosci un modo più semplice per Swift, che è stato anche specificato nella domanda, questo sarebbe il modo più semplice, anche se non lo consideri semplice in un contesto più generale che includa Objective-C . (Inoltre, questa risposta è fondamentalmente una definizione una tantum di una riga di un enum, la funzione e la sua chiamata è un esempio di utilizzo, non parte della soluzione.)
Arkku

1
@Otar Sì, ma ... stai parlando try!, che non è usato qui. In effetti non puoi nemmeno effettuare la chiamata potenzialmente lanciante senza un qualche tipo di try. (Anche quella parte del codice è l'uso di esempio, non la soluzione effettiva.)
Arkku

136

Il modo più semplice è di Stringconformarsi a Error:

extension String: Error {}

Quindi puoi semplicemente lanciare una stringa:

throw "Some Error"

Per rendere la stringa stessa localizedStringl'errore dell'errore, puoi invece estendere LocalizedError:

extension String: LocalizedError {
    public var errorDescription: String? { return self }
}

Questo è intelligente, ma c'è un modo per far sì che localizedDescriptionsia la stringa stessa?
villapossu,

1
Modo molto elegante!
Vitaliy Gozhenko,

1
Elegante davvero! Ma si rompe per me nei target di prova con il seguente messaggio Redundant conformance of 'String' to protocol 'Error':(
Alexander Borisenko il

2
Per qualche motivo questo non funziona per me. Dice che non può completare l'operazione quando analizza error.localizedDescriptiondopo aver lanciato una stringa.
Noah Allen,

1
Attenzione: questa estensione mi ha causato problemi con le librerie esterne. Ecco il mio esempio . Questo è possibile per qualsiasi libreria di terze parti che gestisce gli errori; Eviterei le estensioni che rendono String conforme a Error.
Bryan W. Wagner,

20

La soluzione di @ nick-keets è molto elegante, ma si è rotta per me nel target di test con il seguente errore di compilazione:

Redundant conformance of 'String' to protocol 'Error'

Ecco un altro approccio:

struct RuntimeError: Error {
    let message: String

    init(_ message: String) {
        self.message = message
    }

    public var localizedDescription: String {
        return message
    }
}

E usare:

throw RuntimeError("Error message.")

19

Dai un'occhiata a questa fantastica versione. L'idea è di implementare i protocolli String ed ErrorType e utilizzare il RawValue dell'errore.

enum UserValidationError: String, Error {
  case noFirstNameProvided = "Please insert your first name."
  case noLastNameProvided = "Please insert your last name."
  case noAgeProvided = "Please insert your age."
  case noEmailProvided = "Please insert your email."
}

Uso:

do {
  try User.define(firstName,
                  lastName: lastName,
                  age: age,
                  email: email,
                  gender: gender,
                  location: location,
                  phone: phone)
}
catch let error as User.UserValidationError {
  print(error.rawValue)
  return
}

Sembra che ci sia un piccolo vantaggio in questo approccio, in quanto è ancora necessario as User.UserValidationErroril .rawValue. Tuttavia, se invece lo hai implementato CustomStringConvertiblecome var description: String { return rawValue }, potrebbe essere utile ottenere le descrizioni personalizzate usando la sintassi enum senza dover passare rawValuein ogni posto dove lo stampi.
Arkku,

1
implementare meglio il metodo localizedDescription per restituire .rawValue
DanSkeel il

16

Swift 4:

Secondo:

https://developer.apple.com/documentation/foundation/nserror

se non si desidera definire un'eccezione personalizzata, è possibile utilizzare un oggetto NSError standard come segue:

import Foundation

do {
  throw NSError(domain: "my error description", code: 42, userInfo: ["ui1":12, "ui2":"val2"] ) 
}
catch let error as NSError {
  print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
  let uis = error.userInfo 
  print("\tUser info:")
  for (key,value) in uis {
    print("\t\tkey=\(key), value=\(value)")
  }
}

stampe:

Caught NSError: The operation could not be completed, my error description, 42
    User info:
        key=ui1, value=12
        key=ui2, value=val2

Ciò consente di fornire una stringa personalizzata, oltre a un codice numerico e un dizionario con tutti i dati aggiuntivi necessari, di qualsiasi tipo.

NB: questo è stato testato su OS = Linux (Ubuntu 16.04 LTS).


12

Soluzione più semplice senza ulteriori estensioni, enumerazioni, classi ed ecc .:

NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()

2
ri. i tuoi commenti sulla mia risposta, questo è semplice solo nel senso che hai in qualche modo arbitrariamente deciso che la definizione, enum o estensione una volta è complicato. Quindi, sì, la tua risposta ha zero righe di "setup", ma a costo di avere ogni eccezione generata è un incantesimo complicato e non Swiftlike ( raise()invece di throw) che è difficile da ricordare. Confronta la tua soluzione con throw Foo.Bar("baz")o throw "foo"moltiplicata per il numero di luoghi in cui viene generata un'eccezione: la tariffa unica dell'estensione o dell'enum di una riga è di gran lunga preferibile a cose del genere NSExceptionName.
Arkku,

@Arkku Ad esempio postNotificationrichiede 2-3 parametri e il suo selettore è simile a questo. Sostituisci Notificatione / o NotificationCenterin ciascun progetto per consentirgli di accettare meno parametri di input?
Vyachaslav Gerchicov,

1
No, e non avrei nemmeno usato la soluzione nella mia risposta; L'ho pubblicato solo per rispondere alla domanda, non perché è qualcosa che farei da solo. Ad ogni modo, questo è un altro punto: sono convinto che la tua risposta sia di gran lunga più complicata da usare resto convinto sia della mia che di Nick Keets. Ovviamente ci sono altri punti validi da considerare, come se l'estensione Stringper conformarsi Errorè troppo sorprendente, o se un MyErrorenum è troppo vago (personalmente risponderei di sì a entrambi, e invece farei un caso enum separato per ogni errore, cioè, throw ThisTypeOfError.thisParticularCase).
Arkku,

6

Sulla base della risposta di keets @Nick, ecco un esempio più completo:

extension String: Error {} // Enables you to throw a string

extension String: LocalizedError { // Adds error.localizedDescription to Error instances
    public var errorDescription: String? { return self }
}

func test(color: NSColor) throws{
    if color == .red {
        throw "I don't like red"
    }else if color == .green {
        throw "I'm not into green"
    }else {
        throw "I like all other colors"
    }
}

do {
    try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
    Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
    Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}

Originariamente pubblicato sul mio blog rapido: http://eon.codes/blog/2017/09/01/throwing-simple-errors/


1
TBH: Io ora solo farethrow NSError(message: "err", code: 0)
eonist

Quindi non usi nemmeno il tuo esempio? : D Oh, e il primo argomento dovrebbe essere domain, no message, giusto?
NRitH

1
Il tuo diritto, dominio. E no, aggiunge troppo zucchero nel codice. Di solito faccio molti piccoli quadri e moduli e cerco di mantenere basso lo zucchero delle estensioni. In questi giorni provo ad usare un mix tra Result e NSError
eonist il

6

Nel caso in cui non sia necessario rilevare l'errore e si desideri interrompere immediatamente l'applicazione, è possibile utilizzare un errore irreversibile: fatalError ("Custom message here")


3
Si noti che ciò non genererà un errore che può essere rilevato. Questo causerà l'arresto anomalo dell'app.
Adil Hussain,

4

Mi piace la risposta di @ Alexander-Borisenko, ma la descrizione localizzata non è stata restituita quando rilevata come errore. Sembra invece che tu debba usare LocalizedError:

struct RuntimeError: LocalizedError
{
    let message: String

    init(_ message: String)
    {
        self.message = message
    }

    public var errorDescription: String?
    {
        return message
    }
}

Vedi questa risposta per maggiori dettagli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.