Swift Language
Foutafhandeling
Zoeken…
Opmerkingen
Zie De snelle programmeertaal voor meer informatie over fouten.
Fout bij het omgaan met basisprincipes
Functies in Swift kunnen waarden, foute fouten of beide retourneren:
func reticulateSplines() // no return value and no error
func reticulateSplines() -> Int // always returns a value
func reticulateSplines() throws // no return value, but may throw an error
func reticulateSplines() throws -> Int // may either return a value or throw an error
Elke waarde die voldoet aan het ErrorType-protocol (inclusief NSError-objecten) kan als een fout worden gegenereerd. Opsommingen bieden een handige manier om aangepaste fouten te definiëren:
enum NetworkError: ErrorType {
case Offline
case ServerError(String)
}
enum NetworkError: Error {
// Swift 3 dictates that enum cases should be `lowerCamelCase`
case offline
case serverError(String)
}
Een fout duidt op een niet-fatale fout tijdens de uitvoering van het programma en wordt afgehandeld met de gespecialiseerde control-flow-constructies do
/ catch
, throw
en try
.
func fetchResource(resource: NSURL) throws -> String {
if let (statusCode, responseString) = /* ...from elsewhere...*/ {
if case 500..<600 = statusCode {
throw NetworkError.serverError(responseString)
} else {
return responseString
}
} else {
throw NetworkError.offline
}
}
Fouten kunnen worden gevangen met do
/ catch
:
do {
let response = try fetchResource(resURL)
// If fetchResource() didn't throw an error, execution continues here:
print("Got response: \(response)")
...
} catch {
// If an error is thrown, we can handle it here.
print("Whoops, couldn't fetch resource: \(error)")
}
Elke functie die een fout kan veroorzaken, moet worden opgeroepen met try
, try?
of try!
:
// error: call can throw but is not marked with 'try'
let response = fetchResource(resURL)
// "try" works within do/catch, or within another throwing function:
do {
let response = try fetchResource(resURL)
} catch {
// Handle the error
}
func foo() throws {
// If an error is thrown, continue passing it up to the caller.
let response = try fetchResource(resURL)
}
// "try?" wraps the function's return value in an Optional (nil if an error was thrown).
if let response = try? fetchResource(resURL) {
// no error was thrown
}
// "try!" crashes the program at runtime if an error occurs.
let response = try! fetchResource(resURL)
Verschillende soorten fouten opvangen
Laten we voor dit voorbeeld ons eigen fouttype maken.
enum CustomError: ErrorType {
case SomeError
case AnotherError
}
func throwing() throws {
throw CustomError.SomeError
}
enum CustomError: Error {
case someError
case anotherError
}
func throwing() throws {
throw CustomError.someError
}
De syntaxis van Do-Catch maakt het mogelijk een gegooide fout op te vangen en maakt automatisch een constante benoemde error
beschikbaar in het catch
:
do {
try throwing()
} catch {
print(error)
}
Je kunt ook zelf een variabele declareren:
do {
try throwing()
} catch let oops {
print(oops)
}
Het is ook mogelijk om verschillende catch
koppelen. Dit is handig als verschillende soorten fouten in het Do-blok kunnen worden veroorzaakt.
Hier zal de Do-Catch eerst proberen de fout als een CustomError
, en vervolgens als een NSError
als het aangepaste type niet overeenkomt.
do {
try somethingMayThrow()
} catch let custom as CustomError {
print(custom)
} catch let error as NSError {
print(error)
}
In Swift 3 is het niet nodig om expliciet te downcasten naar NSError.
do {
try somethingMayThrow()
} catch let custom as CustomError {
print(custom)
} catch {
print(error)
}
Patroon vangen en schakelen voor expliciete foutafhandeling
class Plane {
enum Emergency: ErrorType {
case NoFuel
case EngineFailure(reason: String)
case DamagedWing
}
var fuelInKilograms: Int
//... init and other methods not shown
func fly() throws {
// ...
if fuelInKilograms <= 0 {
// uh oh...
throw Emergency.NoFuel
}
}
}
In de klantklasse:
let airforceOne = Plane()
do {
try airforceOne.fly()
} catch let emergency as Plane.Emergency {
switch emergency {
case .NoFuel:
// call nearest airport for emergency landing
case .EngineFailure(let reason):
print(reason) // let the mechanic know the reason
case .DamagedWing:
// Assess the damage and determine if the president can make it
}
}
Foutpropagatie uitschakelen
De makers van Swift hebben veel aandacht besteed aan het expressief maken van de taal en foutafhandeling is precies dat, expressief. Als u probeert een functie op te roepen die een fout kan veroorzaken, moet de functieaanroep worden voorafgegaan door het sleutelwoord try. Het try-trefwoord is niet magisch. Het enige wat het doet, is de ontwikkelaar bewust maken van het werpvermogen van de functie.
De volgende code gebruikt bijvoorbeeld een functie loadImage (atPath :), die de afbeeldingsresource op een bepaald pad laadt of een foutmelding geeft als de afbeelding niet kan worden geladen. In dit geval wordt er tijdens het uitvoeren van de afbeelding geen fout gegenereerd omdat de afbeelding wordt meegeleverd met de toepassing. Het is daarom raadzaam de foutpropagatie uit te schakelen.
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
Maak een aangepaste fout met gelokaliseerde beschrijving
Maak enum
aangepaste fouten
enum RegistrationError: Error {
case invalidEmail
case invalidPassword
case invalidPhoneNumber
}
Maak een extension
van RegistrationError
om de gelokaliseerde beschrijving af te handelen.
extension RegistrationError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidEmail:
return NSLocalizedString("Description of invalid email address", comment: "Invalid Email")
case .invalidPassword:
return NSLocalizedString("Description of invalid password", comment: "Invalid Password")
case .invalidPhoneNumber:
return NSLocalizedString("Description of invalid phoneNumber", comment: "Invalid Phone Number")
}
}
}
Omgaan met fout:
let error: Error = RegistrationError.invalidEmail
print(error.localizedDescription)