Result Type — egy konténer típus, amely egy olyan művelet eredményét reprezentálja, amely sikerrel (Success) vagy hibával (Failure) végződhet. A kivételekkel ellentétben a Result a hibát mint közönséges értéket adja át a verem kibontása nélkül, ami a várható hibák kezelését biztonságosabbá és kompozícióra alkalmasabbá teszi. A Apple Swift Documentation (2026) szerint a Result<Success, Failure> Swift-ben lehetővé teszi műveleti láncok összekapcsolását automatikus hibakezeléssel map és flatMap segítségével, anélkül hogy megszakítaná a program végrehajtását.
Főbb pontok
Result Type egy generikus típus, amely egy művelet végrehajtásának eredményét kapszulázza, amely mind sikerrel, mind hibával végződhet. A kivételekkel ellentétben, ahol a hiba megszakítja a normál végrehajtási folyamatot és a verem kibontását igényli a catch megtalálásához, a Result a hibát mint közönséges értéket adja át — a hívó fél mindig kap egy objektumot, és maga dönti el, hogyan kezelje. Ez különösen hasznos a várható hibákhoz: érvénytelen bemenet, üzleti szabályok, szerver elutasítása, ahol a kivétel túl nehézkes mechanizmus lenne.
A Result koncepciója a funkcionális programozásból ered, ahol hasonló típusokat Either-nek (vagy Left/Right-nak) neveznek. Swift-ben a Result Swift 5.0-ban vált standard típussá, Kotlin-ban a Result<T> a standard könyvtárban jelent meg, Dart 3.0-ban — beépített Result<T>. Minden implementáció metódusokat biztosít a konténerrel való munkához: map (a sikeres érték transzformációja), flatMap (Result függvények lánca), mapError (a hiba transzformációja), fold (mindkét eset kezelése). Result Type — a funkcionális hibakezelés sarokköve a mobilfejlesztésben, a try-catch alternatívája várható forgatókönyvekhez.
A Result legfontosabb előnye a kompozícióképesség. Több műveletet, amelyek mindegyike visszaadhat hibát, egyetlen láncba kapcsolhatsz beágyazott try-catch nélkül. Ha bármelyik művelet a láncban Failure-rel végződik, az egész lánc megszakad és Failure-t ad vissza — egyetlen if feltétel vagy catch blokk nélkül. Ez lineárissá és olvashatóvá teszi a kódot, különösen több egymást követő API-kérés vagy üzleti szabályok ellenőrzésének forgatókönyveiben.
Swift-ben a Result<Success, Failure> egy enum két esettel: .success(Success) és .failure(Failure), ahol a Failure az Error protokollal van korlátozva. A Swift Result egy teljes funkcionális típus a map, flatMap, mapError és get metódusokkal. A get egy különleges metódus: visszaadja a Success értéket, ha az eredmény sikeres, és hibát dob, ha Failure. Ez lehetővé teszi a Result használatát hídként a funkcionális és a kivétel-alapú stílusok között: kezeld map/flatMap segítségével a láncban, a végén pedig — get segítségével do-catch-en keresztül a throws kóddal való integrációhoz.
A Swift Result egy enum generikus paraméterekkel, ami lehetővé teszi a fordítónak, hogy ellenőrizze a kimerítő kezelést switch vagy do-catch segítségével. Ha új esetet adsz a NetworkError enum-hoz, a fordító hibát ad minden olyan switch kifejezésben, ahol ez az eset nincs kezelve. Kimerítő ellenőrzés (exhaustive checking) — a Result fő előnye a kivételekkel szemben: a fordító garantálja, hogy az összes lehetséges hiba figyelembe van véve a fordítási szakaszban. Ezzel szemben a kivételeket a fordító nem ellenőrzi Swift-ben (csak a throws van deklarálva, de a hiba típusa nem).
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))
}
}
// Használat switch-csel
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> lehetővé teszi a hiba típus szintű tipizálását: a fetchUser függvény Result<User, NetworkError>-t ad vissza, ahol a NetworkError egy konkrét enum három esettel. A hívó fél minden esetet switch segítségével kezel kimerítő lefedettséggel — a fordító ellenőrzi, hogy minden variáns kezelve van. Kimerítő switch — a Result kulcsfontosságú előnye a kivételekkel szemben: a fordító garantálja, hogy nem felejted el kezelni a .badURL, .requestFailed vagy .decodingFailed eseteket. A kivételek esetében a fordító nem igényel kezelést, és egy el nem kapott catch(.badURL) könnyen észrevétlen marad a review-ban.
Kotlin-ban a Result<T> egy beépített típus a standard könyvtárból, amely sikert (T) vagy hibát (Throwable) reprezentál. A Swift-tel ellentétben Kotlin-ban a Result nem teszi lehetővé egy konkrét hiba típus megadását — csak Throwable. Ez az egyszerűség kedvéért történt, de a hiba típusának további ellenőrzését igényli a when kifejezés segítségével. A Kotlin Result támogatja a fold (mindkét eset kezelése), getOrNull (siker vagy null), getOrDefault (siker vagy alapértelmezett érték), map, recover, andThen függvényeket. A Kotlin sajátossága: a Result nem közvetlen propagálásra szolgál a függvényhatárokon keresztül — nem használható visszatérési típusként az Android SDK vagy Kotlin Coroutines API metódusaihoz további adaptáció nélkül.
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 — egy kényelmes wrapper Kotlin-ban, amely elkap minden kivételt és Result.failure-t ad vissza. validateEmail Result.success vagy Result.failure értéket ad vissza az ellenőrzéstől függően. fold mindkét esetet tömören kezeli: Success esetén a következő validációs függvény hívódik meg, Failure esetén — SignupResult.Error kerül visszaadásra. Fontos: a Kotlin Result nem alkalmas data class mezőkben való tárolásra vagy közvetlen átadásra suspend függvények határain keresztül — használd a saját sealed class-odat (Success/Error/Loading) az UI állapotok reprezentálásához Jetpack Compose-ban vagy MVVM-ben.
Dart 3.0-tól a standard könyvtárban megjelent a beépített Result<T> — egy sealed class két konstruktorral: T.ok() (siker) és Error.error() (hiba Object és StackTrace paraméterekkel). Dart 3.0 előtt a Flutter fejlesztők Either<L, R>-t használtak a dartz csomagból vagy saját sealed class-okat. A beépített Result Dart-ban minimalista: nem biztosítja közvetlenül a map/flatMap függvényeket — ezeket when segítségével vagy extension methods használatával kell implementálni. Komoly funkcionális kezeléshez a fpdart-ból származó Either marad az erősebb megoldás, támogatva a map, flatMap, mapLeft, fold, andThen és bind operátorokat.
Either<L, R> — egy bal-asszociált típus a fpdart csomagból, ahol a Left — hiba, a Right — siker. A beépített Result<T>-vel ellentétben az Either a hibát a típusparaméter (L) szintjén tipizálja, ami lehetővé teszi a hiba típusok megkülönböztetését a fordítási szakaszban. A fpdart csomag teljes funkcionális kombinator készletet biztosít: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — beágyazott Either), andThen (lánc transzformáció nélkül), fold (kilépés Either-ből), getOrElse (alapértelmezett érték). Funkcionális megközelítésű Flutter alkalmazásokhoz az Either a de facto szabvány.
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));
}
}
}
// Használat fold-dal
final result = await service.fetchUser('42');
result.fold(
(left) => showError(left.message),
(right) => showUser(right),
);
Either<L, R> a fpdart-ból — bal-asszociált típus: Left — hiba, Right — siker. fetchUser Either<AppError, User>-t ad vissza, ahol az AppError — sealed class konkrét hiba típusokkal (serverError, networkError). fold mindkét esetet kezeli: az első callback a Left (hiba), a második a Right (siker) számára. Dart 3.0-ban a beépített Result is támogatja a fold-ot, de nem biztosít map/flatMap-et. A láncokhoz a fpdart-ból származó Either map, flatMap (bind), mapLeft, andThen függvényeket biztosít — teljes funkcionális kombinator készletet a hibák kompozíciójához.
A Result fő előnye a kivételekkel szemben a kompozíció. Ha több műveleted van, amelyek mindegyike visszaadhat hibát, összekapcsolhatod őket egy láncba map és flatMap segítségével egyetlen beágyazott if vagy try-catch nélkül. map transzformálja a sikeres értéket: Result.success(x) -> Result.success(f(x)). flatMap (más néven bind vagy andThen) — azokra az esetekre, amikor a transzformáció maga is Result-ot ad vissza: Result.success(x) -> f(x) -> Result<Y>. Ha bármelyik lépésben Failure történik, a következő műveletek nem hajtódnak végre — a lánc megszakad.
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))
// Kompozíció flatMap segítségével (andThen a fpdart-ban)
val result = validateToken("abc123")
.flatMap { fetchProfile("42") }
.map { it.name }
.getOrElse { "Guest" }
println(result) // "Alice"
Lánc: validateToken -> fetchProfile -> map name -> getOrElse „Vendég“. Ha a validateToken Left (InvalidToken) értéket ad vissza, a lánc megszakad és „Vendég“ kerül visszaadásra. Ha a fetchProfile Left (UserNotFound) értéket ad vissza — szintén „Vendég“. Ha mindkét művelet sikeres — a profil neve. flatMap lehetővé teszi az Either függvények összekapcsolását, amelyek mindegyike meghiúsulhat, egyetlen lineáris láncba. A hagyományos kivétel-stílusban ugyanez a kód két beágyazott try-catch-t vagy null ellenőrzést igényelne. getOrElse a végén — a kompozícióból való kilépési pont, amely alapértelmezett értéket biztosít a Failure esetre.
A Result és a kivételek — nem egymást kizáró megközelítések. Mindegyiknek megvan a maga alkalmazási területe, és egy jól tervezett mobil alkalmazásban mindkettőt használják. A választás attól függ, hogy a hiba várható (expected) vagy váratlan (unexpected). Result — a várható hibákhoz, amelyek az üzleti logika részét képezik: érvénytelen e-mail, elégtelen egyenleg a számlán, kérési limit túllépése. Kivételek — a váratlan rendszerhibákhoz: hálózati kapcsolat megszakadása, OutOfMemoryError, NullPointerException (amelyeknek nem kellene előfordulniuk, de előfordulnak).
| Szempont | Result Type | Kivételek (Exception/Error) |
|---|---|---|
| Hiba típusa | Várható (üzleti logika) | Váratlan (rendszer) |
| Teljesítmény | Alacsony költség (verem kibontása nélkül) | Magas költség (stack unwinding, StackTrace rögzítése) |
| Kompozíció | map/flatMap segítségével — lineáris láncok | Beágyazott try-catch — nehezen olvasható |
| Fordító | Kimerítő ellenőrzés (switch/when) | Csak checked exceptions Java-ban |
| Végrehajtási folyamat | Nem szakad meg — a hiba mint érték | Megszakad a legközelebbi catch-ig |
| Tesztelés | Könnyű: eredmény ellenőrzése, assert isSuccess/isError | assertThrows és mock objektumok szükségesek |
| Mikor használd | Üzleti validáció, kérési láncok, űrlapok | Hálózati kapcsolat megszakadása, I/O hibák, rendszerösszeomlások |
Gyakorlati szabály: ha a hiba az alkalmazás normál munkafolyamatának része (a felhasználó érvénytelen e-mailt adott meg, nincs elegendő hozzáférési jogosultsága) — használj Result-ot. Ha a hiba kivételes helyzet (a szerver nem válaszol, elfogyott a memória) — használj kivételeket. A mobilfejlesztésben a Result a rétegek határain (UseCase -> ViewModel) és a kivételek a rétegeken belül (API -> Repository) — egy gyakori minta, amely mindkét megközelítés előnyeit ötvözi.
A meglévő kivétel-alapú kód Result-ra történő migrációjának fokozatosnak kell lennie. Kezdd a rétegek határaival: csomagold be a throws függvények hívásait Result { try ... } (Swift) vagy runCatching { ... } (Kotlin) kifejezésekbe. Ezután cseréld ki a Repository és UseCase metódusok visszatérési típusát Result/Either-re, miközben a belső implementációt kivételeken hagyod. Az utolsó szakaszban migráld a ViewModel-t: a kivételekkel ellátott UiState helyett használj sealed class UiState<T> (Loading, Success, Error) típust, ahol az Error a domain hibát tárolja, nem a Throwable-t. A fokozatos migráció lehetővé teszi az egyes rétegek külön-külön történő tesztelését globális refaktorálás nélkül.
Gyakran ismételt kérdések
Optional (T?) egy érték jelenlétét vagy hiányát reprezentálja — a nil jelentése “nincs adat”, de nem mondja meg, hogy miért. Result (Success/Failure) nem csak a sikert, hanem a hiba okát is tartalmazza egy konkrét típussal. Használd az Optional-t, amikor az érték hiánya normális (pl. opcionális profilmező), Result-ot — amikor információra van szükséged a hibáról.
Swift-ben használd a Result { try throwingFunc() } kifejezést — a Result konstruktora throws lezárást fogad el. Kotlin-ban — runCatching { throwingFunc() }, amely Result<T>-t ad vissza. Dart-ban — Result<T>.tryCatch(() => throwingFunc()). Ez lehetővé teszi a kivétel-alapú kód egyszerű integrálását Result láncokba.
Használd a mapError (Swift) vagy mapLeft (Either Dart-ban/Kotlin-ban) függvényeket a hiba típusának transzformálásához a sikeres érték megváltoztatása nélkül. Ha mindkét esetet kezelned kell és egyetlen értéket visszaadnod — használd a fold-ot. Naplózáshoz a lánc megszakítása nélkül használd az onFailure (Kotlin) vagy egy előtagolt inspection point-ot.
Igen, de óvatosan. A Kotlin Result<T> nem ajánlott közvetlen visszatérési típusként suspend függvényekhez a K2 fordító és a reflexió sajátosságai miatt. Használd a saját sealed class-odat, a NetworkResult<T>-t (Success, Error, Loading) az állapotok reprezentálásához korutinokban. Várható üzleti logikai hibákhoz az Arrow-ból származó Either egy erősebb alternatíva.
fold — egy metódus, amely két callback-et fogad: onSuccess (a sikeres esethez) és onFailure (a hiba esethez), és egyetlen értéket ad vissza bármilyen típusból. Ez a switch kifejezés megfelelője, de magasabb rendű függvény formájában. A fold — a fő kilépési pont a Result láncokból, ahol a Success/Failure-t UiState-vé, felhasználói sztringgé vagy más Result-tá alakítod.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is