Lekce 14 - Ošetřování chyb ve Swift
V předešlém cvičení, Řešené úlohy k 10.-13. lekci OOP ve Swift, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V dnešním Swift tutoriálu si vysvětlíme poslední důležité téma, než přejdeme na formulářové aplikace. Tímto tématem jsou chyby. Jelikož je ve formulářových aplikacích budeme potkávat, naučíme se je dnes ošetřovat.
Chyby
Při programování se pravidelně objevují chyby, jak jste si již určitě stihli všimnout Některé vzniknou naší chybou, proti vzniku jiným se můžeme jen těžko bránit. Typickým příkladem může být čtení dat ze souboru nebo třeba webové služby, která zrovna nemusí být dostupná (protože jiný programátor udělal chybu), nefunguje internet a tak podobně.
V tomto tutoriálu si ukážeme, jak se s problémy za běhu programu vypořádat tak, aby celý nespadl.
Swift podobně jako další moderní jazyky, používá koncept tzv. výjimek, které označují chybový stav aplikace. Pokud přecházíte z jiného jazyka, bude vám povědomý, jen se zde termín výjimka nepoužívá a několik věcí funguje trochu jinak.
Spuštění nebezpečného kódu
Nejdříve si ukážeme, jak se zachovat, když nějaký kód může chybu
vyvolat. Swift je v tomto směru přísnější a funkce, které mohou vyvolat
chybu, musí být označeny klíčovým slovem throws
, které se
píše před návratový typ. Podobné pravidlo funguje např. také v Javě. S takovou funkcí poté nemůžeme pracovat tak, jak jsme
zvyklí.
Ukážeme si jednoduchý příklad se zápisem do souboru. Založte si nový
Command Line Tool projekt s názvem ChybaSoubor
a do
main.swift
vložte následující kód:
// Tento kód zatím nefunguje let dokumentyUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL let souborUrl = dokumentyUrl.appendingPathComponent("info.txt")! "ITnetwork.cz je fajn web".write(to: souborUrl, atomically: true, encoding: String.Encoding.unicode)
Kód výše možná vypadá hrozivě, ale o nic složitého nejde. Pojďme si jej popsat.
- První řádek získá od systému cestu ke složce Dokumenty
- Druhý k cestě připojí název souboru, do kterého budeme zapisovat.
- Na třetím řádku využijeme metodu na
String
u pro zápis do souboru. Zbylé parametry řeší zamknutí souboru a kódování češtiny.
Tento kód nám zatím ale nebude fungovat. Je to proto, že může vyvolat
chybu když do souboru nepůjde zapsat a my jsme tuto chybu nijak neošetřili.
Mechanismus chyb je ve Swift podobný mechanismu Optionals
, kde
jsme také museli řešit všechny neočekávané stavy. Metoda
write()
je v našem případě označena klíčovým slovem
throws
, což znamená, že jakmile ji použijeme, musíme se
postarat o možné chybové stavy.
Try
Problém vyřešíme pomocí klíčového slova try
, které
napíšeme na začátek třetího řádku. Samotné ale nestačí.
Nejjednodušší je napsat try?
, což je opět velmi podobné
Optional
konceptu. Pokud se něco nepovede, tak se prostě nic
nestane a program pokračuje dál. Kdybychom try?
použili s
metodou, která vrací hodnotu, tak získáme Optional
, který bude
v případě chyby prázdný.
Použít můžeme také try!
. Zde opět vykřičník značí
nebezpečnou situaci a prakticky Swiftu říkáme, že k chybě prostě nemůže
dojít a proto nechceme řešit možné chybové stavy. Pokud k chybě dojde,
tak náš program spadne.
do-catch blok
Asi nejčastější je doplnění try
o blok
do-catch
. Viz zápis níže:
do { try "ITnetwork.cz je fajn web".write(to: souborUrl, atomically: true, encoding: String.Encoding.unicode) } catch { print("Do souboru se nepovedlo zapsat") }
Blok do
nám dovolí použít samotné slovo try
.
Pokud dojde k chybě, provede se blok catch
. Zde máme rovněž k
dispozici proměnnou error
, ze které můžeme získat konkrétní
chybu, pokud se něco pokazí. Z chyby poté můžeme získat dodatečné
informace. V dalších lekcích si ukážeme, jak pomocí více
catch
bloků reagovat na různé druhy chyb. Po vykonání kódu
výše najdete ve své složce Dokumenty nový soubor s textem, který jsme
zapsali.
Blok defer
Blok defer
slouží k vykonání kódu, který se má provést
po ukončení bloku, ve kterém je try
vložené.
To znamená v případě, že se try
povedlo, ale i v případě,
že nám program skončil v catch
bloku apod.
Ještě než si ukážeme jak se tento blok používá, vytvoříme z našeho
dřívějšího kódu metodu. Můžeme ji nechat v souboru
main.swift
. Takové metodě se potom říká funkce, jelikož
metoda patří vždy do nějaké třídy.
func zapisDoSouboru(text: String) { let dokumentyUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL let souborUrl = dokumentyUrl.appendingPathComponent("info.txt")! do { try text.write(to: souborUrl, atomically: true, encoding: String.Encoding.unicode) } catch { print("Do souboru se nepovedlo zapsat, protože: \(error)") } }
Abychom nevymýšleli zbytečně složitou ukázku, tak si napíšeme
jednoduché logování zavolání naší metody. Doplníme její začátek o
defer
blok:
func zapisDoSouboru(text: String) { defer { print("Provedena metoda zapisDoSouboru()") } let dokumentyUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL let souborUrl = dokumentyUrl.appendingPathComponent("info.txt")! do { try text.write(to: souborUrl, atomically: true, encoding: String.Encoding.unicode) } catch { print("Do soubor se nepovedlo zapsat, protože: \(error)") } // Zde dojde k vykonání defer bloku }
Ačkoliv blok defer
uvádíme jako první, tak se provede
jakmile program opustí scope metody (blok složených závorek), ve kterém je
defer
použito. V našem případě tedy když se metoda ukončí.
Tento blok se vykoná vždy, tedy když metoda skončí
doběhnutím do jejího konce, i když skončí tím, že něco vrátí
returnem. Můžeme jej používat i např. v cyklu, kdy se spustí po
vyskočení z cyklu pomocí break
nebo po jeho přirozeném
doběhnutí a podobně. Zkrátka se nemůže stát, že by se daný kód
nevyvolal. Od toho defer, což znamená odložit na později. Typicky se do
tohoto bloku umisťují tzv. úklidové práce jako vyčištění paměti a
podobně, které se mají vykonat bez ohledu na to, zda se nebezpečná operace
povedla či nikoli. S tímto mechanismem se ještě setkáme v pokročilejších
kurzech.
Nyní již víme jak používat cizí funkce, které mohou vyvolat chybový stav.
I když budeme mnohem častěji zpracovávat "cizí" chyby, naučíme se i vyvolávat vlastní. To nás ale čeká zas příště, v lekci Enumy a vlastní Errory ve Swift.