Result Type: vad är det, Result containertyp och hur fungerar det i mobil utveckling

Författare: IT Sectr Publicerad: 2026-05-26 Lästid: 9 min

Result Type — en containertyp som representerar resultatet av en operation som kan sluta med framgång (Success) eller fel (Failure). Till skillnad från undantag överför Result felet som ett vanligt värde utan att rulla upp stacken, vilket gör hanteringen av förväntade fel säkrare och mer komponerbar. Enligt Apple Swift Documentation (2026), tillåter Result<Success, Failure> i Swift att kombinera operativa kedjor med automatisk felhantering genom map och flatMap, utan att avbryta programmets exekvering.

Huvudpunkter

  • Result Type — generisk container med två tillstånd: Success (data) och Failure (fel), utan att rulla upp stacken
  • I Swift Result<Success, Failure> — inbyggd typ med metoderna map, flatMap, mapError och get
  • I Kotlin Result<T> representerar framgång eller Throwable, med funktionerna getOrNull, getOrDefault, fold
  • I Dart standard Result<T> tillgänglig från Dart 3.0, även Either från paketet fpdart
  • Komposition genom map och flatMap gör det möjligt att kombinera flera operationer som returnerar Result utan nästlade kontroller

Vad är Result Type?

Result Type är en generisk typ som kapslar in resultatet av att utföra en operation som kan sluta både med framgång och med fel. Till skillnad från undantag, där felet avbryter det normala exekveringsflödet och kräver att stacken rullas upp för att hitta catch, överför Result felet som ett vanligt värde — den anropande parten får alltid ett objekt och bestämmer själv hur den ska hantera det. Detta är särskilt användbart för förväntade fel: ogiltig inmatning, affärsregler, serveravvisning, där ett undantag skulle vara en alltför tung mekanism.

Konceptet Result härstammar från funktionell programmering, där liknande typer kallas Either (eller Left/Right). I Swift blev Result en standardtyp i Swift 5.0, i Kotlin dök Result<T> upp i standardbiblioteket, i Dart 3.0 — inbyggt Result<T>. Varje implementation tillhandahåller metoder för att arbeta med containern: map (transformering av framgångsrikt värde), flatMap (kedja av Result-funktioner), mapError (transformering av fel), fold (hantering av båda fallen). Result Type — hörnstenen i funktionell felhantering inom mobil utveckling, ett alternativ till try-catch för förväntade scenarier.

Den viktigaste fördelen med Result är komponerbarhet. Du kan kombinera flera operationer, som var och en kan returnera ett fel, i en enda kedja utan nästlade try-catch. Om någon operation i kedjan slutar med Failure, avbryts hela kedjan och returnerar Failure — utan ett enda if-villkor eller catch-block. Detta gör koden linjär och läsbar, särskilt i scenarier med flera sekventiella API-förfrågningar eller kontroller av affärsregler.

Result i Swift

I Swift är Result<Success, Failure> en enum med två fall: .success(Success) och .failure(Failure), där Failure är begränsat av protokollet Error. Swift Result är en fullvärdig funktionell typ med metoderna map, flatMap, mapError och get. get är en speciell metod: den returnerar Success-värdet om resultatet är framgångsrikt och kastar ett fel om det är Failure. Detta gör det möjligt att använda Result som en bro mellan funktionell och undantagsbaserad stil: hantera genom map/flatMap i en kedja, och i slutet — get via do-catch för integration med throws-kod.

Result enum och uttömmande switch

Swift Result är en enum med generiska parametrar, vilket gör att kompilatorn kan kontrollera uttömmande hantering via switch eller do-catch. Om du lägger till ett nytt fall i enum NetworkError, kommer kompilatorn att ge ett fel i alla switch-uttryck där detta fall inte hanteras. Uttömmande kontroll (exhaustive checking) — den främsta fördelen med Result jämfört med undantag: kompilatorn garanterar att alla möjliga fel beaktas i kompileringsfasen. Till skillnad från detta kontrolleras inte undantag av kompilatorn i Swift (endast throws deklareras, men inte feltypen).

swift
enum NetworkError: Error {
    case badURL
    case requestFailed(String)
    case decodingFailed
}

func fetchUser(id: Int) -> Result<User, NetworkError> {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        return .failure(.badURL)
    }
    let result = performRequest(url: url)
    switch result {
    case let .success(data):
        if let user = try? JSONDecoder().decode(User.self, from: data) {
            return .success(user)
        }
        return .failure(.decodingFailed)
    case let .failure(error):
        return .failure(.requestFailed(error.localizedDescription))
    }
}

// Användning med switch
let result = fetchUser(id: 42)
switch result {
case .success(let user):
    showUser(user)
case .failure(.badURL):
    logError("Invalid URL")
case .failure(.requestFailed(let msg)):
    showAlert(msg)
case .failure(.decodingFailed):
    logError("Decoding error")
}

Result<Success, Failure> gör det möjligt att typa felet på typnivå: funktionen fetchUser returnerar Result<User, NetworkError>, där NetworkError är en specifik enum med tre fall. Den anropande parten hanterar varje fall via switch med uttömmande täckning — kompilatorn kontrollerar att alla varianter är hanterade. Uttömmande switch — den viktigaste fördelen med Result jämfört med undantag: kompilatorn garanterar att du inte glömmer att hantera .badURL, .requestFailed eller .decodingFailed. När det gäller undantag kräver kompilatorn ingen hantering, och ett ouppfångat catch(.badURL) kan lätt missas i en granskning.

Result i Kotlin

I Kotlin är Result<T> en inbyggd typ från standardbiblioteket, som representerar framgång (T) eller fel (Throwable). Till skillnad från Swift tillåter Result i Kotlin inte att en specifik feltyp anges — endast Throwable. Detta gjordes för enkelhetens skull, men kräver ytterligare kontroll av feltypen via ett when-uttryck. Kotlin Result stöder funktionerna fold (hantering av båda fallen), getOrNull (framgång eller null), getOrDefault (framgång eller standardvärde), map, recover, andThen. En egenhet med Kotlin: Result är inte avsett för direkt spridning över funktionsgränser — det kan inte användas som returtyp för metoder i Android SDK eller Kotlin Coroutines API utan ytterligare anpassning.

kotlin
fun parseJson(input: String): Result<JsonObject> {
    return runCatching {
        JsonParser.parseString(input).asJsonObject()
    }
}

fun validateEmail(email: String): Result<String> {
    return if (email.contains("@")) {
        Result.success(email.trim())
    } else {
        Result.failure(IllegalArgumentException("Invalid email"))
    }
}

data class SignupData(val name: String, val email: String)

fun processSignup(name: String, email: String): SignupResult {
    return validateEmail(email).fold(
        onSuccess = { validateName(name) },
        onFailure = { SignupResult.Error("Invalid email") }
    )
}

runCatching — en bekväm wrapper i Kotlin som fångar upp alla undantag och returnerar Result.failure. validateEmail returnerar Result.success eller Result.failure beroende på kontrollen. fold hanterar båda fallen kompakt: vid Success anropas nästa valideringsfunktion, vid Failure — returneras SignupResult.Error. Viktigt: Kotlin Result är inte avsett för lagring i data class-fält eller direkt överföring över gränserna för suspend-funktioner — använd dina egna sealed class (Success/Error/Loading) för att representera UI-tillstånd i Jetpack Compose eller MVVM.

Result i Dart och Flutter

Från Dart 3.0 i standardbiblioteket har en inbyggd Result<T> dykt upp — en sealed class med två konstruktorer: T.ok() (framgång) och Error.error() (fel med Object och StackTrace). Före Dart 3.0 använde Flutter-utvecklare Either<L, R> från dartz-paketet eller anpassade sealed classes. Det inbyggda Result i Dart är minimalistiskt: det tillhandahåller inte map/flatMap direkt — dessa funktioner måste implementeras via when eller med hjälp av extension methods. För seriös funktionell hantering förblir Either från fpdart en kraftfullare lösning med stöd för map, flatMap, mapLeft, fold, andThen och bind-operatorer.

Either från fpdart

Either<L, R> — en vänsterassocierad typ från fpdart-paketet, där Left — fel, Right — framgång. Till skillnad från det inbyggda Result<T>, typar Either felet på typparameternivå (L), vilket gör det möjligt att skilja feltyper åt i kompileringsfasen. fpdart-paketet tillhandahåller en komplett uppsättning funktionella kombinatorer: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — nästlade Either), andThen (kedja utan transformering), fold (utgång från Either), getOrElse (standardvärde). För Flutter-applikationer med funktionell metod är Either de facto-standard.

dart
import 'dart:convert';
import 'package:fpdart/fpdart.dart';

class UserService {
    Either<AppError, User> fetchUser(String id) {
        try {
            final response = await http.get(
                Uri.parse('https://api.example.com/users/$id')
            );
            if (response.statusCode == 200) {
                final user = User.fromJson(
                    json.decode(response.body)
                );
                return Either.of(user);
            }
            return Either.left(
                AppError.serverError(response.statusCode)
            );
        } on SocketException catch (e) {
            return Either.left(AppError.networkError(e.message));
        }
    }
}

// Användning med fold
final result = await service.fetchUser('42');
result.fold(
    (left)  => showError(left.message),
    (right) => showUser(right),
);

Either<L, R> från fpdart — vänsterassocierad typ: Left — fel, Right — framgång. fetchUser returnerar Either<AppError, User>, där AppError — sealed class med specifika feltyper (serverError, networkError). fold hanterar båda fallen: den första callbacken för Left (fel), den andra för Right (framgång). I Dart 3.0 stöder det inbyggda Result också fold, men tillhandahåller inte map/flatMap. För kedjor tillhandahåller Either från fpdart map, flatMap (bind), mapLeft, andThen — en komplett uppsättning funktionella kombinatorer för felkomposition.

Komposition av Result genom map och flatMap

Den främsta fördelen med Result jämfört med undantag är komposition. Om du har flera operationer, som var och en kan returnera ett fel, kan du kombinera dem i en kedja genom map och flatMap utan en enda nästlad if eller try-catch. map transformerar det framgångsrika värdet: Result.success(x) -> Result.success(f(x)). flatMap (även kallad bind eller andThen) — för fall där transformeringen själv returnerar Result: Result.success(x) -> f(x) -> Result<Y>. Om Failure inträffar vid något steg, utförs inte efterföljande operationer — kedjan avbryts.

kotlin
data class UserRequest(val userId: String, val token: String)

sealed class AuthError {
    data object InvalidToken : AuthError()
    data class UserNotFound(val id: String) : AuthError()
}

typealias Outcome<T> = Either<AuthError, T>

fun validateToken(token: String): Outcome<String> =
    if (token.isNotBlank()) Either.right(token)
    else Either.left(AuthError.InvalidToken)

fun fetchProfile(userId: String): Outcome<Profile> =
    if (userId == "42") Either.right(Profile("Alice"))
    else Either.left(AuthError.UserNotFound(userId))

// Komposition genom flatMap (andThen i fpdart)
val result = validateToken("abc123")
    .flatMap { fetchProfile("42") }
    .map { it.name }
    .getOrElse { "Guest" }

println(result) // "Alice"

Kedja: validateToken -> fetchProfile -> map name -> getOrElse “Gäst”. Om validateToken returnerar Left (InvalidToken), avbryts kedjan och “Gäst” returneras. Om fetchProfile returnerar Left (UserNotFound) — också “Gäst”. Om båda operationerna är framgångsrika — profilnamnet. flatMap gör det möjligt att kombinera Either-funktioner, som var och en kan misslyckas, i en enda linjär kedja. I traditionell undantagsstil skulle samma kod kräva två nästlade try-catch eller null-kontroller. getOrElse i slutet — utgångspunkten från kompositionen, som tillhandahåller ett standardvärde för Failure-fallet.

Result vs Undantag: när man ska välja vad

Result och undantag — är inte ömsesidigt uteslutande tillvägagångssätt. Var och en har sitt tillämpningsområde, och i en väl designad mobilapplikation används båda. Valet beror på om felet är förväntat (expected) eller oväntat (unexpected). Result — för förväntade fel som är en del av affärslogiken: ogiltig e-post, otillräckligt saldo på kontot, överskriden begränsning av förfrågningar. Undantag — för oväntade systemfel: nätverksförlust, OutOfMemoryError, NullPointerException (som inte bör inträffa, men som inträffar).

KriteriumResult TypeUndantag (Exception/Error)
FeltypFörväntat (affärslogik)Oväntat (system)
PrestandaLåg kostnad (utan att rulla upp stacken)Hög kostnad (stack unwinding, insamling av StackTrace)
KompositionGenom map/flatMap — linjära kedjorNästlade try-catch — svårläst
KompilatorUttömmande kontroll (switch/when)Endast checked exceptions i Java
ExekveringsflödeAvbryts inte — fel som värdeAvbryts till närmaste catch
TestningEnkelt: kontrollera resultatet, assert isSuccess/isErrorKräver assertThrows och mock-objekt
När ska man användaAffärsvalidering, kedjor av förfrågningar, formulärNätverksförlust, I/O-fel, systemkrascher

Praktisk regel: om felet är en del av applikationens normala arbetsflöde (användaren har angett en ogiltig e-post, har inte tillräckliga åtkomsträttigheter) — använd Result. Om felet är en exceptionell situation (servern svarar inte, minnet är slut) — använd undantag. Inom mobil utveckling är Result på gränserna av lager (UseCase -> ViewModel) och undantag inom lager (API -> Repository) — ett vanligt mönster som kombinerar fördelarna med båda tillvägagångssätten.

Migreringsstrategi från undantag till Result

Migrering av befintlig undantagsbaserad kod till Result bör vara gradvis. Börja med gränserna av lager: slå in anrop av throws-funktioner i Result { try ... } (Swift) eller runCatching { ... } (Kotlin). Byt sedan ut returtypen för Repository- och UseCase-metoder till Result/Either, medan den interna implementeringen lämnas på undantag. I det sista steget, migrera ViewModel: istället för UiState med undantag, använd sealed class UiState<T> (Loading, Success, Error), där Error lagrar domänfelet, inte Throwable. Gradvis migrering gör det möjligt att testa varje lager separat utan global omfaktorisering.

Vanliga frågor

Vad skiljer Result Type från Optional/Option?

Optional (T?) representerar närvaron eller frånvaron av ett värde — nil betyder “inga data”, men säger inte varför. Result (Success/Failure) innehåller inte bara framgång, utan också orsaken till felet med en specifik typ. Använd Optional när frånvaron av ett värde är normalt (t.ex. ett valfritt profilfält), Result — när du behöver information om felet.

Hur konverterar man en throws-funktion till Result?

I Swift använd Result { try throwingFunc() } — Result-konstruktorn accepterar en throws-slutning. I Kotlin — runCatching { throwingFunc() }, som returnerar Result<T>. I Dart — Result<T>.tryCatch(() => throwingFunc()). Detta möjliggör enkel integrering av undantagsbaserad kod i Result-kedjor.

Hur hanterar man ett fel i Result utan förlust av information?

Använd mapError (Swift) eller mapLeft (Either i Dart/Kotlin) för transformering av feltypen utan att ändra det framgångsrika värdet. Om du behöver hantera båda fallen och returnera ett enda värde — använd fold. För loggning utan att avbryta kedjan, använd onFailure (Kotlin) eller en prefix-inspektionspunkt.

Kan Result i Kotlin användas med korutiner?

Ja, men försiktigt. Kotlin Result<T> rekommenderas inte som direkt returtyp för suspend-funktioner på grund av egenskaper hos K2-kompilatorn och reflektion. Använd din egen sealed class NetworkResult<T> (Success, Error, Loading) för att representera tillstånd i korutiner. För förväntade fel i affärslogiken är Either från Arrow ett kraftfullare alternativ.

Vad är fold i kontexten av Result?

fold — en metod som accepterar två callbackar: onSuccess (för det framgångsrika fallet) och onFailure (för det felaktiga fallet), och returnerar ett enda värde av vilken typ som helst. Det är motsvarigheten till ett switch-uttryck, men i form av en högre ordningens funktion. fold — den huvudsakliga utgångspunkten från Result-kedjor, där du omvandlar Success/Failure till UiState, en sträng för användaren eller en annan Result.

Sammanfattning

  • Result Type — generisk container (Success/Failure) för säker hantering av förväntade fel utan att rulla upp stacken
  • I Swift Result<Success, Failure> med Failure: Error säkerställer uttömmande kontroll genom switch med type-säkra fel
  • I Kotlin Result<T> slår in framgång eller Throwable, runCatching — bekväm konstruktor från throws-kod
  • I Dart inbyggt Result<T> (Dart 3.0) och Either<L, R> från fpdart för avancerad komposition med map/flatMap
  • Komposition genom map (transformering av framgång) och flatMap (kedja av Result-funktioner) ersätter nästlade try-catch
  • Result vs undantag: Result för förväntade affärsfel, undantag för oväntade systemfel
  • Använd Result på gränserna av lager för explicit och testbar felhantering utan att avbryta exekveringsflödet

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.

Diskutera projektet

Läs också