Do-Catch: co to jest, konstrukcja i obsługa błędów w Swift

Autor: IT Sectr Opublikowano: 2026-05-26 Czas czytania: 8 min

Do-Catch — konstrukcja języka Swift do obsługi błędów, która przechwytuje wyjątki zgłoszone przez throw i wykonuje odpowiedni blok kodu. W przeciwieństwie do tradycyjnych try-catch w innych językach, Swift wymaga jawnego oznaczenia funkcji, które mogą zgłosić błąd, modyfikatorem throws. Według Swift.org, 2026, Do-Catch to podstawowy mechanizm obsługi błędów w Swift, zapewniający bezpieczeństwo na poziomie typów.

Najważniejsze

  • Do-Catch — konstrukcja Swift przechwytująca błędy z bloków z try i throw.
  • throws — znacznik funkcji informujący, że może ona zgłosić błąd.
  • try — słowo kluczowe do wywołania funkcji throwing wewnątrz bloku do.
  • catch — blok obsługi błędu z możliwością dopasowania według typu.
  • try? i try! — alternatywne formy przekształcające błąd w nil lub powodujące crash.

Co to jest Do-Catch?

Do-Catch — to konstrukcja w Swift składająca się z bloku do, wewnątrz którego wykonywany jest kod mogący zgłosić błąd, oraz jednego lub więcej bloków catch, obsługujących ten błąd. Blok do zawiera wywołania funkcji throwing oznaczone try, a bloki catch dopasowują błąd według typu.

Swift implementuje model strukturalnej obsługi błędów, różniący się od wyjątków w Objective-C. Tutaj błąd to nie exception z rozwijaniem stosu, ale wartość zgodna z protokołem Error. Zgłoszenie błędu przez throw przekazuje sterowanie do najbliższego bloku catch bez narzutu na rozwijanie stosu.

Kluczowa cecha Swift — jawność obsługi. Kompilator nie pozwala wywołać funkcji throwing bez try, do-catch lub alternatywnego mechanizmu (try?, try!). Eliminuje to sytuacje, w których błąd pozostaje niezauważony.

Model błędów w Swift: protokół Error i funkcje throwing

Podstawą obsługi błędów w Swift jest protokół Error. Każdy typ zgodny z tym protokołem może być zgłoszony przez throw. Zazwyczaj błędy definiuje się jako enum z potwierdzeniem Error.

Protokół Error

Error — pusty protokół (znacznikowy). Nie wymaga implementacji metod, tylko potwierdzenia zgodności. Kompilator Swift używa Error do statycznego sprawdzania: funkcja z throws może zgłosić tylko wartość zgodną z Error.

Funkcje throwing

Funkcja oznaczona throws musi być wywoływana z try. Jeśli wewnątrz takiej funkcji nastąpi throw, sterowanie przekazywane jest do wywołującego do-catch. Jeśli błąd nie zostanie obsłużony nigdzie w łańcuchu wywołań, jest propagowany na wyższy poziom.

swift
enum FileError: Error {
    case notFound
    case permissionDenied
    case corrupted(String)
}

func readFile(path: String) throws -> String {
    guard FileManager.default.fileExists(atPath: path) else {
        throw FileError.notFound
    }
    return try String(contentsOfFile: path)
}

Propagacja błędu rethrows

rethrows — modyfikator dla funkcji, które przyjmują throwing-zamknięcie jako parametr. Jeśli przekazane zamknięcie nie zgłasza błędu, funkcję rethrows można wywołać bez try. Pozwala to pisać ogólne funkcje wyższego rzędu, kompatybilne z throwing i non-throwing zamknięciami.

Składnia Do-Catch: wiele bloków catch

Swift obsługuje wielokrotne bloki catch z dopasowaniem według typu błędu. Przypomina to when w Kotlin lub switch według typu: pierwszy pasujący catch jest wykonywany, pozostałe są pomijane.

Dopasowanie według typu

Każdy blok catch może zawierać wzorzec dopasowania do konkretnego typu błędu. Swift sprawdza bloki po kolei, więc bardziej specyficzne typy powinny znajdować się przed ogólnymi. Jeśli żaden wzorzec nie pasuje, wykonywany jest blok catch bez wzorca.

Obsługa wielu typów

Bezpieczeństwo typów Do-Catch pozwala obsługiwać każdy błąd osobno lub łączyć wiele typów w jednym bloku przez przecinek w pattern-matching. Eliminuje to rozwlekłe łańcuchy if-else charakterystyczne dla Objective-C, gdzie analiza błędu odbywała się przez kod powrotu lub NSError z sprawdzaniem domain i code.

Swift obsługuje również catch where — dopasowanie z dodatkowym warunkiem. Można na przykład filtrować błędy według konkretnego kodu lub tekstu: catch URLError where error.code == .notConnectedToInternet. Zmniejsza to liczbę bloków catch przy zachowaniu precyzji obsługi.

Do debugowania funkcji throwing Swift udostępnia różnicę między błędami a wyjątkami. Błąd Swift — to zwracana wartość, która nie wymaga rozwijania stosu. Wyjątki Objective-C (@try/@catch) działają na poziomie runtime i są używane tylko do krytycznych awarii. Do-Catch obsługuje właśnie błędy, a nie wyjątki, co czyni go przewidywalnym i wydajnym.

Przy tworzeniu bibliotek i SDK ważne jest, aby pamiętać, że funkcje throwing są częścią publicznego kontraktu. Typ błędu nie jest określony w sygnaturze, dlatego dokumentuj, jakie błędy może zgłosić funkcja, w komentarzu lub przez enum podobny do Result. Pomaga to użytkownikom API pisać poprawne bloki catch bez zaglądania do implementacji.

W kontekście programowania asynchronicznego Do-Catch z async/await rozwiązuje problem callback hell. Wcześniej w Swift obsługa błędów w kodzie asynchronicznym wymagała zagnieżdżonych zamknięć ze sprawdzaniem Error? w completion handler. Dzięki async/await i Do-Catch asynchroniczny kod throwing wygląda jak synchroniczny, co upraszcza czytanie i utrzymanie złożonych łańcuchów zapytań z obsługą błędów na każdym kroku.

swift
do {
    let content = try readFile(path: "/data/config.json")
    process(content)
} catch FileError.notFound {
    createDefaultConfig()
} catch FileError.permissionDenied {
    requestAccess()
} catch FileError.corrupted(let detail) {
    logCorruption(detail)
} catch {
    print("Nieznany błąd: \(error)")
}

Warianty try: try, try?, try! i kiedy ich używać

Swift oferuje trzy warianty wywołania funkcji throwing: try, try? i try!. Każdy wariant rozwiązuje konkretne zadanie i ma swoje ograniczenia.

try — standardowe wywołanie

try jest używane wewnątrz bloku do, a błąd jest obsługiwany w catch. To podstawowy sposób wywoływania funkcji throwing. Kompilator wymaga, aby try znajdowało się w kontekście, w którym błąd może zostać przechwycony.

try? — konwersja na Optional

try? przekształca wynik funkcji throwing na Optional. Jeśli funkcja zgłosi błąd, try? zwraca nil, w przeciwnym razie — Optional z wartością powodzenia. Przydatne w wywołaniach, gdzie błąd można zignorować, ale potrzebny jest wskaźnik sukcesu.

try! — wymuszone wykonanie

try! pomija obsługę błędów. Jeśli funkcja throwing zgłosi błąd, aplikacja kończy się błędem runtime. Używaj try! tylko gdy jesteś absolutnie pewien, że błąd jest niemożliwy — na przykład przy ładowaniu wbudowanego zasobu.

swift
// try? — ignorujemy błąd, otrzymujemy nil przy awarii
if let data = try? Data(contentsOf: url) {
    processData(data)
}

// try! — tylko jeśli błąd jest gwarantowanie niemożliwy
let bundled = try! String(contentsOfFile: "Assets/default.txt")

// try — standardowa opcja w do-catch
do {
    let result = try performNetworkRequest()
    updateUI(result)
} catch {
    showError(error)
}

Do-Catch w rozwoju iOS: praktyczne przykłady

Do-Catch jest aktywnie używane w rozwoju iOS do pracy z systemowymi API. Rozważmy typowe scenariusze: praca z systemem plików, Core Data i zapytania sieciowe.

System plików

Wiele metod FileManager to throwing. Do-Catch pozwala poprawnie obsłużyć brak pliku, zajętość zasobu lub brak uprawnień.

Zapytania sieciowe z Codable

Parsowanie JSON przez JSONDecoder — operacja throwing. Do-Catch przechwytuje błędy dekodowania i awarie sieciowe oddzielnie, dostarczając użytkownikowi dokładny tekst błędu.

swift
struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Wywołanie z obsługą
do {
    let user = try await fetchUser(id: 42)
    showUser(user)
} catch let error as URLError {
    showNetworkAlert(error)
} catch let error as DecodingError {
    showParseError(error)
} catch {
    showGenericError(error)
}

Typowe błędy podczas pracy z Do-Catch

Deweloperzy, szczególnie przechodzący z innych języków, często popełniają charakterystyczne błędy podczas korzystania z Do-Catch. Rozważmy najczęstsze z nich.

  • Zapomniany try — wywołanie funkcji throwing bez try powoduje błąd kompilacji. Swift nie pozwala niejawnie ignorować błędów.
  • Pusty catch — blok catch bez obsługi ukrywa błąd. Zawsze loguj lub obsługuj błąd, nawet jeśli wydaje się niemożliwy.
  • Catch bez dopasowania — jeden ogólny catch dla wszystkich błędów pozbawia korzyści bezpieczeństwa typów. Rozdzielaj obsługę według typów przez wiele bloków.
  • Nadmierne użycie try! — try! w kodzie produkcyjnym to źródło nieoczekiwanych crashy. Używaj tylko dla wbudowanych zasobów lub testów.

Często zadawane pytania

Czym Do-Catch w Swift różni się od try-catch w innych językach?

W Swift błąd to wartość zgodna z protokołem Error, a nie exception z rozwijaniem stosu. Funkcje throwing muszą być jawnie oznaczone throws, a kompilator wymaga try przy wywołaniu — eliminuje to nieobsłużone błędy na etapie kompilacji.

Czy można używać Do-Catch z async/await?

Tak, async funkcje również mogą być throws i działa dla nich do-catch. Wywołanie async throwing funkcji wymaga try await wewnątrz bloku do. To standardowy sposób obsługi błędów w asynchronicznym kodzie Swift.

Jak obsłużyć błąd bez do-catch?

Są alternatywy: try? przekształca błąd w nil, try! powoduje crash przy błędzie, a rethrows pozwala propagować błąd z zamknięcia. Do-catch pozostaje podstawowym sposobem jawnej obsługi.

Czy jedna funkcja może zgłaszać różne typy błędów?

Swift nie typuje zgłaszanych błędów — każda funkcja throwing może zgłosić dowolny typ zgodny z Error. Rozdzielaj obsługę przez dopasowanie w catch do konkretnych typów błędów.

Czy warto opakowywać wszystkie błędy we własny typ?

Tak, to najlepsza praktyka. Zdefiniuj AppError jako enum z potwierdzeniem Error i konwertuj błędy systemowe na domenowe. To ujednolica obsługę i izoluje warstwę aplikacji od szczegółów systemowych.

Podsumowanie

  • Do-Catch — konstrukcja Swift do obsługi błędów z obowiązkowym oznaczeniem throws na funkcjach i try przy wywołaniu.
  • Błędy Swift — wartości zgodne z protokołem Error, a nie wyjątki z rozwijaniem stosu, co daje zerowy narzut przy braku awarii.
  • Wielokrotne catch pozwalają obsługiwać różne typy błędów oddzielnie z pattern-matching, analogicznym do switch.
  • try? przekształca błąd w nil dla prostych scenariuszy, try! — wymuszone wywołanie z ryzykiem crashy, używane tylko dla gwarantowanie udanych operacji.
  • Do-Catch z async/await — standardowy sposób obsługi błędów w asynchronicznym kodzie Swift z użyciem try await.
  • Unikaj pustych catch i nadmiernego użycia try! — to ukrywa błędy i prowadzi do nieoczekiwanych awarii w production.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również