Result Type: co to je, typ-kontejner Result a jak funguje v mobilním vývoji

Autor: IT Sectr Publikováno: 2026-05-26 Doba čtení: 9 min

Result Type je kontejnerový typ, který představuje výsledek operace, jež může skončit úspěchem (Success) nebo chybou (Failure). Na rozdíl od výjimek předává Result chybu jako běžnou hodnotu bez rozbalování zásobníku, díky čemuž je zpracování očekávaných chyb bezpečnější a lépe komponovatelné. Podle Apple Swift Documentation (2026) umožňuje Result<Success, Failure> ve Swiftu spojovat řetězce operací s automatickým zpracováním chyb přes map a flatMap, aniž by se přerušilo provádění programu.

Klíčové poznatky

  • Result Type je generický kontejner se dvěma stavy: Success (data) a Failure (chyba), bez rozbalování zásobníku
  • Ve Swiftu je Result<Success, Failure> vestavěný typ s metodami map, flatMap, mapError a get
  • V Kotlinu představuje Result<T> úspěch nebo Throwable s funkcemi getOrNull, getOrDefault, fold
  • V Dartu je standardní Result<T> dostupný od Dartu 3.0, k dispozici je také Either z balíčku fpdart
  • Kompozice přes map a flatMap umožňuje spojovat více operací vracejících Result bez vnořených kontrol

Co je Result Type?

Result Type je generický typ, který zapouzdřuje výsledek provedení operace, jež může skončit buď úspěšně, nebo chybou. Na rozdíl od výjimek, kdy chyba přeruší normální tok provádění a vyžaduje rozbalování zásobníku pro nalezení catch, předává Result chybu jako běžnou hodnotu — volající strana vždy obdrží objekt a sama se rozhodne, jak s ním naloží. To je užitečné zejména pro očekávané chyby: nesprávný vstup, obchodní pravidla, selhání serveru, kde by výjimka byla příliš těžkopádným mechanismem.

Koncept Result vychází z funkcionálního programování, kde se podobné typy nazývají Either (nebo Left/Right). Ve Swiftu se Result stal standardním typem ve Swiftu 5.0, v Kotlinu se Result<T> objevil ve standardní knihovně, v Dartu 3.0 je vestavěný Result<T>. Každá implementace poskytuje metody pro práci s kontejnerem: map (transformace úspěšné hodnoty), flatMap (řetězení Result funkcí), mapError (transformace chyby), fold (zpracování obou případů). Result Type je základním kamenem functional error handlingu v mobilním vývoji, alternativa k try-catch pro očekávané scénáře.

Klíčovou výhodou Result je komponovatelnost. Můžete spojit více operací, z nichž každá může vrátit chybu, do jednoho řetězce bez vnořených try-catch. Pokud jakákoli operace v řetězci skončí stavem Failure, celý řetězec se přeruší a vrátí Failure — bez jediné podmínky if nebo bloku catch. Díky tomu je kód lineární a čitelný, zejména ve scénářích s mnoha sekvenčními požadavky na API nebo kontrolami obchodních pravidel.

Result ve Swiftu

Ve Swiftu je Result<Success, Failure> enum se dvěma případy: .success(Success) a .failure(Failure), kde Failure je omezen protokolem Error. Swift Result je plnohodnotný funkcionální typ s metodami map, flatMap, mapError a get. get je speciální metoda: vrací hodnotu Success, pokud je výsledek úspěšný, a vyhazuje chybu, pokud je Failure. To umožňuje použít Result jako most mezi funkcionálním stylem a stylem založeným na výjimkách: zpracujte přes map/flatMap v řetězci a na konci — get přes do-catch pro integraci s throws kódem.

Result enum a vyčerpávající switch

Swift Result je enum s generickými parametry, což kompilátoru umožňuje ověřit vyčerpávající zpracování přes switch nebo do-catch. Pokud do enum NetworkError přidáte nový případ, kompilátor vyhlásí chybu ve všech switch výrazech, kde tento případ není zpracován. Exhaustive checking je hlavní výhodou Result oproti výjimkám: kompilátor zaručuje, že všechny možné chyby jsou zohledněny ve fázi sestavení. Na rozdíl od toho výjimky kompilátor ve Swiftu nekontroluje (deklaruje se pouze throws, ale ne typ chyby).

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))
    }
}

// Použití se 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> umožňuje typovat chybu na úrovni typu: funkce fetchUser vrací Result<User, NetworkError>, kde NetworkError je konkrétní enum se třemi případy. Volající strana zpracovává každý případ přes switch s vyčerpávajícím pokrytím — kompilátor ověřuje, že jsou zpracovány všechny varianty. Exhaustive switch je klíčovou výhodou Result oproti výjimkám: kompilátor zaručuje, že nezapomenete zpracovat .badURL, .requestFailed nebo .decodingFailed. V případě výjimek kompilátor zpracování nevyžaduje a snadno se v review přehlédne nezachycený catch(.badURL).

Result v Kotlinu

V Kotlinu je Result<T> vestavěný typ ze standardní knihovny, který představuje úspěch (T) nebo chybu (Throwable). Na rozdíl od Swiftu nelze v Kotlinu u Result určit konkrétní typ chyby — pouze Throwable. To je provedeno kvůli jednoduchosti, ale vyžaduje dodatečnou kontrolu typu chyby přes when výraz. Kotlin Result podporuje funkce fold (zpracování obou případů), getOrNull (úspěch nebo null), getOrDefault (úspěch nebo výchozí hodnota), map, recover a andThen. Zvláštnost Kotlinu: Result není určen k přímému šíření přes hranice funkcí — nelze jej použít jako návratový typ pro metody Android SDK nebo Kotlin Coroutines API bez dodatečné adaptace.

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 je pohodlný obal v Kotlinu, který zachytí jakoukoli výjimku a vrátí Result.failure. validateEmail vrací Result.success nebo Result.failure podle kontroly. fold zpracovává oba případy kompaktně: při Success se volá další validační funkce, při Failure se vrací SignupResult.Error. Důležité: Kotlin Result není určen k ukládání do polí data class ani k přímému předávání přes hranice suspend funkcí — použijte vlastní sealed class (Success/Error/Loading) pro reprezentaci stavů UI v Jetpack Compose nebo MVVM.

Result v Dartu a Flutteru

Od Dartu 3.0 je ve standardní knihovně k dispozici vestavěný Result<T> — sealed class se dvěma konstruktory: T.ok() (úspěch) a Error.error() (chyba s Object a StackTrace). Před Dartem 3.0 používali vývojáři Flutteru Either<L, R> z balíčku dartz nebo vlastní sealed class. Vestavěný Result v Dartu je minimalistický: neposkytuje map/flatMap přímo — tyto funkce je třeba implementovat přes when nebo použít extension methods. Pro seriózní funkcionální zpracování zůstává Either z fpdart silnějším řešením s podporou map, flatMap, mapLeft, fold, andThen a operátorů bind.

Either z fpdart

Either<L, R> je levě-asociovaný typ z balíčku fpdart, kde Left je chyba a Right je úspěch. Na rozdíl od vestavěného Result<T> typuje Either chybu na úrovni typu parametru (L), což umožňuje rozlišovat typy chyb ve fázi kompilace. Balíček fpdart poskytuje kompletní sadu funkcionálních kombinatorů: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — vnořené Either), andThen (řetězec bez transformace), fold (výstup z Either), getOrElse (výchozí hodnota). Pro Flutter aplikace s funkcionálním přístupem je Either de facto standardem.

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));
        }
    }
}

// Použití s fold
final result = await service.fetchUser('42');
result.fold(
    (left)  => showError(left.message),
    (right) => showUser(right),
);

Either<L, R> z fpdart je levě-asociovaný typ: Left je chyba, Right je úspěch. fetchUser vrací Either<AppError, User>, kde AppError je sealed class s konkrétními typy chyb (serverError, networkError). fold zpracovává oba případy: první callback pro Left (chyba), druhý pro Right (úspěch). V Dartu 3.0 podporuje vestavěný Result také fold, ale neposkytuje map/flatMap. Pro řetězení poskytuje fpdart u Either map, flatMap (bind), mapLeft, andThen — kompletní sadu funkcionálních kombinatorů pro kompozici chyb.

Kompozice Result přes map a flatMap

Hlavní výhodou Result oproti výjimkám je kompozice. Pokud máte několik operací, z nichž každá může vrátit chybu, můžete je spojit do řetězce přes map a flatMap bez jediného vnořeného if nebo try-catch. map transformuje úspěšnou hodnotu: Result.success(x) -> Result.success(f(x)). flatMap (nazývaný také bind nebo andThen) — pro případy, kdy transformace sama vrací Result: Result.success(x) -> f(x) -> Result<Y>. Pokud na kterémkoli kroku nastane Failure, následující operace se neprovedou — řetězec se přeruší.

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))

// Kompozice přes flatMap (andThen v fpdart)
val result = validateToken("abc123")
    .flatMap { fetchProfile("42") }
    .map { it.name }
    .getOrElse { "Guest" }

println(result) // "Alice"

Řetězec: validateToken -> fetchProfile -> map name -> getOrElse „Guest“. Pokud validateToken vrátí Left (InvalidToken), řetězec se přeruší a vrátí „Guest“. Pokud fetchProfile vrátí Left (UserNotFound) — také „Guest“. Pokud jsou obě operace úspěšné — jméno profilu. flatMap umožňuje spojit Either funkce, z nichž každá může selhat, do jednoho lineárního řetězce. V tradičním exception stylu by stejný kód vyžadoval dva vnořené try-catch nebo kontroly null. getOrElse na konci je výstupním bodem kompozice, který poskytuje výchozí hodnotu pro případ Failure.

Result vs výjimky: kdy co zvolit

Result a výjimky nejsou vzájemně se vylučující přístupy. Každý má svou oblast použití a v dobře navržené mobilní aplikaci se používají oba. Volba závisí na tom, zda je chyba očekávaná (expected) nebo neočekávaná (unexpected). Result — pro očekávané chyby, které jsou součástí obchodní logiky: nesprávný e-mail, nedostatek prostředků na účtu, překročený limit požadavků. Výjimky — pro neočekávané systémové chyby: ztráta připojení, OutOfMemoryError, NullPointerException (které by nastat neměly, ale stávají se).

KritériumResult TypeVýjimky (Exception/Error)
Typ chybyOčekávané (obchodní logika)Neočekávané (systémové)
VýkonNízká náročnost (bez rozbalování zásobníku)Vysoká náročnost (stack unwinding, zachycení StackTrace)
KompozicePřes map/flatMap — lineární řetězceVnořené try-catch — obtížně čitelné
KompilátorExhaustive checking (switch/when)Pouze checked exceptions v Javě
Tok prováděníNepřerušuje se — chyba jako hodnotaPřerušuje se až k nejbližšímu catch
TestováníSnadné: ověření výsledku, assert isSuccess/isErrorVyžaduje assertThrows a mock objekty
Kdy použítObchodní validace, řetězce požadavků, formulářeZtráta připojení, chyby I/O, pády systému

Praktické pravidlo: pokud je chyba součástí normálního toku činnosti aplikace (uživatel zadal nesprávný e-mail, chybí přístupová práva) — použijte Result. Pokud je chyba výjimečnou situací (server neodpovídá, došla paměť) — použijte výjimky. V mobilním vývoji je Result na hranicích vrstev (UseCase -> ViewModel) a výjimky uvnitř vrstev (API -> Repository) rozšířeným vzorem, který kombinuje výhody obou přístupů.

Strategie migrace z výjimek na Result

Migrace stávajícího exception-based kódu na Result by měla být postupná. Začněte s hranicemi vrstev: zabalte volání throws funkcí do Result { try ... } (Swift) nebo runCatching { ... } (Kotlin). Poté nahraďte návratový typ metod Repository a UseCase na Result/Either, přičemž vnitřní implementaci ponechte na výjimkách. V poslední fázi migrujte ViewModel: místo UiState s výjimkami použijte sealed class UiState<T> (Loading, Success, Error), kde Error uchovává doménovou chybu, nikoli Throwable. Postupná migrace umožňuje testovat každou vrstvu zvlášť bez globálního refaktoringu.

Často kladené otázky

Čím se Result Type liší od Optional/Option?

Optional (T?) představuje přítomnost nebo nepřítomnost hodnoty — nil znamená „žádná data“, ale neříká proč. Result (Success/Failure) obsahuje nejen úspěch, ale také příčinu chyby s konkrétním typem. Použijte Optional, když je nepřítomnost hodnoty normální (například volitelné pole profilu), a Result, když potřebujete informaci o chybě.

Jak převést throws funkci na Result?

Ve Swiftu použijte Result { try throwingFunc() } — konstruktor Result přijímá throws closure. V Kotlinu — runCatching { throwingFunc() }, který vrací Result<T>. V Dartu — Result<T>.tryCatch(() => throwingFunc()). To umožňuje snadno integrovat exception-based kód do Result řetězců.

Jak zpracovat chybu v Result bez ztráty informací?

Použijte mapError (Swift) nebo mapLeft (Either v Dartu/Kotlinu) pro transformaci typu chyby bez změny úspěšné hodnoty. Pokud potřebujete zpracovat oba případy a vrátit jedinou hodnotu — použijte fold. Pro logování bez přerušení řetězce použijte onFailure (Kotlin) nebo kontrolní bod (inspection point).

Lze použít Result v Kotlinu s korutinami?

Ano, ale opatrně. Kotlin Result<T> se nedoporučuje jako návratový typ suspend funkcí přímo kvůli specifikům K2 compileru a reflexe. Použijte vlastní sealed class NetworkResult<T> (Success, Error, Loading) pro reprezentaci stavů v korutinách. Pro očekávané chyby v obchodní logice je Either z Arrow silnější alternativou.

Co je fold v kontextu Result?

fold je metoda, která přijímá dva callbacky: onSuccess (pro úspěšný případ) a onFailure (pro chybový případ), a vrací jedinou hodnotu libovolného typu. Je to ekvivalent switch výrazu, ale ve formě funkce vyššího řádu. fold je hlavním výstupním bodem Result řetězců, kde převedete Success/Failure na UiState, řetězec pro uživatele nebo jiný Result.

Shrnutí

  • Result Type je generický kontejner (Success/Failure) pro bezpečné zpracování očekávaných chyb bez rozbalování zásobníku
  • Ve Swiftu zajišťuje Result<Success, Failure> s Failure: Error vyčerpávající kontrolu (exhaustive checking) přes switch s type-safe chybami
  • V Kotlinu obaluje Result<T> úspěch nebo Throwable, runCatching je pohodlný konstruktor z throws kódu
  • V Dartu vestavěný Result<T> (Dart 3.0) a Either<L, R> z fpdart pro pokročilou kompozici s map/flatMap
  • Kompozice přes map (transformace úspěchu) a flatMap (řetězení Result funkcí) nahrazuje vnořené try-catch
  • Result vs výjimky: Result pro očekávané obchodní chyby, výjimky pro neočekávané systémové poruchy
  • Použijte Result na hranicích vrstev pro explicitní a testovatelné zpracování chyb bez přerušení toku provádění

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také