Try-Catch — en konstruktion för att fånga undantag som gör det möjligt att utföra potentiellt farlig kod i ett skyddat block och korrekt hantera fel utan att programmet avslutas oväntat. Try-blocket innehåller kod som kan kasta ett undantag, catch fångar det och utför återställningslogik. Enligt Apple Swift Documentation (2026) körs finally-blocket oavsett om undantaget kastades eller inte, vilket garanterar frigöring av resurser.
Huvudpunkter
Try-Catch — en grundläggande konstruktion för strukturerad undantagshantering, närvarande i de flesta moderna programmeringsspråk. Den består av tre block: try (försök att utföra farlig kod), catch (fånga och hantera undantaget) och valfritt finally (slutförande). Idén med konstruktionen är att separera logiken för affärsoperationer från logiken för felhantering, vilket gör koden mer läsbar och förutsägbar.
Konceptet implementerades först i språket C++ som try/catch, sedan övertogs av Java, C#, Swift, Kotlin, Dart, Python, JavaScript och andra språk. Varje språk lägger till sina egna egenskaper: i Swift måste catch-blocket vara uttömmande, i Kotlin kan try-catch vara ett uttryck (expression), i Dart är finally obligatoriskt för flödesresurser. Trots skillnaderna är grundprincipen densamma: felet hanteras så nära uppkomstplatsen som möjligt, inte globalt.
Användningen av Try-Catch är särskilt relevant inom mobilutveckling, där externa faktorer — nätverksförlust, felaktigt serversvar, minnesbrist — ständigt uppstår. Korrekt undantagshantering förhindrar appkrascher och säkerställer korrekt UX: användaren får ett felmeddelande istället för att appen plötsligt stängs. Enligt Google Android Kotlin Style Guide (2026) bör varje funktion som kan kasta ett undantag antingen hantera det via try-catch eller deklarera throws i signaturen.
Exekveringsmekanismen för try-catch baseras på stack unwinding. När ett undantag kastas inuti try-blocket via operatorn throw (eller som ett resultat av ett systemfel), avbryts det normala exekveringsflödet omedelbart. Exekveringen flyttas till en högre nivå i anropsstacken för att hitta ett lämpligt catch-block. Moderna språk söker efter en catch med en typ som matchar typen av det kastade undantaget, med hjälp av typmatchningsmekanism (type matching).
Om lämplig catch hittas, körs dess kropp, varefter exekveringen fortsätter efter hela try-catch-finally-konstruktionen. Om ingen catch hittas, stiger undantaget vidare i stacken och kan hanteras på en högre nivå — ända till den globala hanteraren som i en mobilapp visar en feldialog för användaren. Om undantaget inte hanteras någonstans kraschar appen. Det är därför korrekt hantering av alla möjliga undantagstyper är avgörande för appens stabilitet.
fun readUserData(): User {
return try {
val response = api.fetchUser()
parseUser(response)
} catch (e: IOException) {
logError("Nätverksfel", e)
throw AppException("Det gick inte att ladda data")
} catch (e: JsonParseException) {
logError("Tolkningsfel", e)
return User.default()
} finally {
closeLoadingIndicator()
}
}
Koden försöker först utföra en API-förfrågan och tolka svaret. Om IOException (nätverksproblem) uppstår, loggas undantaget och skickas vidare som AppException. Om JsonParseException — returneras standardanvändaren. Finally-blocket döljer garanterat laddningsindikatorn och förhindrar läckage av UI-komponenter på skärmen.
I Swift implementeras felhantering via protokollet Error (tidigare ErrorType). Alla typer som överensstämmer med Error kan kastas via operatorn throw. En funktion som kan kasta ett fel markeras med nyckelordet throws i signaturen. Anrop av en sådan funktion kräver prefixet try (för explicit try-catch), try? (valfritt resultat) eller try! (tvångsexekvering utan felhantering).
enum NetworkError: Error {
case noConnection
case serverError(code: Int)
case timeout
}
func fetchUser(id: Int) throws -> User {
guard isConnected() else {
throw NetworkError.noConnection
}
let data = try performRequest(path: "/users/\(id)")
return try decodeUser(from: data)
}
do {
let user = try fetchUser(id: 42)
updateUI(user)
} catch NetworkError.noConnection {
showOfflineAlert()
} catch let error as NetworkError {
showError("Nätverk " + error.localizedDescription)
} catch {
showGenericError()
}
Enum NetworkError implementerar Error-protokollet och definierar tre fall: noConnection, serverError med kod och timeout. Funktionen fetchUser är markerad med throws: först kontrollerar den anslutningen, sedan utför den förfrågan och tolkning. I do-catch-blocket hanterar tre catch olika scenarier: det specifika fallet noConnection, den allmänna typen NetworkError och alla andra fel. Detta gör det möjligt att visa olika meddelanden för användaren beroende på problemtyp.
Kotlin ärvde try-catch-finally från Java, men lade till en viktig skillnad: i Kotlin är try-catch ett uttryck (expression), inte en sats (statement). Detta innebär att resultatet av try-blocket eller catch-blocket kan tilldelas en variabel. Det sista uttrycket i try-blocket blir resultatet vid framgång, det sista uttrycket i catch — vid fel. Om felet inte hanteras av någon catch, skickas undantaget vidare uppåt i stacken.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
fun loadData(): Result<List<Item>> {
return try {
val response = api.getItems()
Result.Success(response.toList())
} catch (e: HttpException) {
Log.e("HTTP ", e)
Result.Error(e)
} catch (e: IOException) {
Log.e("Nätverk ", e)
Result.Error(e)
}
}
I exemplet omsluter sealed class Result ett lyckat svar eller ett fel. Funktionen loadData använder try-catch som uttryck: vid framgång returnerar den Result.Success, vid undantaget HttpException eller IOException — Result.Error med loggning. Detta tillvägagångssätt gör att den anropande parten kan hantera fel utan undantag — via when-uttryck baserat på Result-typen. Detta är särskilt praktiskt i Jetpack Compose för att visa olika UI-tillstånd (Loading, Success, Error) via StateFlow och collectAsState.
Dart stöder try-catch-finally med syntax som liknar Java, men med tillägg av on-satsen för filtrering efter undantagstyp utan att ange en variabel. Detta är praktiskt när själva undantaget inte behövs — bara faktumet av dess typ är viktigt. Dart stöder också catch-blocket med två parametrar: undantagsobjektet och StackTrace, vilket är användbart för att logga hela anropskedjan.
import 'dart:io';
import 'dart:convert';
class UserRepository {
Future<User> fetchUser(String id) async {
try {
final client = HttpClient();
final request = await client.getUrl(
Uri.parse('https://api.example.com/users/$id')
);
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
return User.fromJson(json.decode(body));
} on SocketException catch (e, stackTrace) {
log("No internet", e, stackTrace);
throw AppException("Connection failed");
} on FormatException {
throw AppException("Invalid response format");
} finally {
client.close();
}
}
}
SocketException fångas med objekt och StackTrace för detaljerad loggning, och skickas sedan vidare som AppException. FormatException fångas utan variabel — det räcker att veta att svarsformatet är felaktigt. Finally-blocket stänger garanterat HttpClient och förhindrar socketläckage. I Flutter är detta tillvägagångssätt särskilt viktigt för Widget-testning, där ohanterade undantag i State.initState leder till att hela testsessionen kraschar.
Även erfarna utvecklare gör misstag vid arbete med try-catch som leder till minnesläckage, dolda buggar eller olämpligt appbeteende. Låt oss titta på fem vanliga problem inom mobilutveckling.
Tomt catch — en av de sämsta metoderna. Undantaget sväljs, appen fortsätter att fungera i felaktigt tillstånd och utvecklaren får inte veta om problemet. Logga alltid åtminstone undantaget. I Kotlin använd catch(e: Exception) { Log.e(...) }, i Swift — catch { print($0) }. I Dart bör en minimalt acceptabel catch anropa debugPrint eller skriva till Crashlytics.
Att fånga alla undantag via catch (Exception e) utan att särskilja efter typ döljer oväntade fel — NullPointerException, OutOfMemoryError, StackOverflowError. Fånga bara de typer du förväntar dig och kan hantera. För resten, tillåt spridning uppåt. Inom mobilutveckling ger specifika catch för IOException, TimeoutException, AuthException mer meningsfulla meddelanden till användaren.
Resurser — filer, sockets, DB-kursorer, animationer — bör frigöras i finally eller i use-block (AutoCloseable). Utvecklare glömmer ofta att stänga resurser vid undantag, vilket leder till läckage. I Kotlin använd .use { } för Closeable-resurser, i Swift — defer { }, i Dart — await using från async-paketet. Finally-blocket garanterar frigöring även vid kastande av undantag inuti catch.
I asynkron kod fångar try-catch inte undantag från andra trådar. I Kotlin Coroutines använd CoroutineExceptionHandler eller SupervisorJob. I Swift async/await — do-catch inuti Task. I Flutter — runZonedGuarded för global fångst. Att ignorera denna regel är orsaken till svåra att reproducera krascher i produktion.
Felhantering bör inte blockera användargränssnittet på obestämd tid. Visa användaren ett specifikt meddelande och ge möjlighet att upprepa operationen. Snackbar med Retry-knapp i Kotlin/Compose, UIAlertController med action i Swift, SnackBar med action i Flutter — minimalt tillräcklig UX för nätverks- eller serverfel. Undvik allmänna dialoger „Ett fel har inträffat" utan återställningsmöjlighet.
Vanliga frågor
Try-catch använder undantag och stack unwinding för felhantering, vilket kan vara prestandakrävande vid många fel. Result Type är en behållartyp (Success eller Failure) som hanteras via pattern matching utan stack unwinding, vilket är effektivare för förväntade fel.
Finally är obligatoriskt om try-blocket öppnar resurser (filer, sockets, kursorer) som måste stängas. Om inga resurser öppnas krävs inte finally. I moderna språk använd AutoCloseable/use/defer för automatisk stängning av resurser utan finally. Use-blocket i Kotlin och Swift ersätter finally för Closeable-objekt.
I normalt flöde (utan undantag) påverkar try-catch praktiskt taget inte prestandan — JVM och Swift-kompilatorn optimerar detta fall. Men vid kastande av undantag sker stack unwinding, vilket kan ta 10–100 µs beroende på stackdjup. Använd inte undantag för flödeskontroll — det är ett antimönster.
I korutiner använd try-catch inuti coroutineScope eller CoroutineExceptionHandler för global fångst. SupervisorJob förhindrar annullering av föräldrakorutinen vid fel i en barnkorutin. För launch använd CoroutineExceptionHandler, för async — try-catch runt await().
Flera catch är att föredra: koden läses linjärt, varje block hanterar en undantagstyp. Ett catch med if-else är svårare att underhålla, det är lätt att missa en ny undantagstyp. I Swift är flera catch obligatoriska för uttömmande hantering av enum Error, i Kotlin finns inga begränsningar, men bästa praxis är separat catch per typ.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också