Result Type — isang uri-ng-lalagyan na kumakatawan sa resulta ng isang operasyon na maaaring magtapos sa tagumpay (Success) o pagkakamali (Failure). Hindi tulad ng exceptions, ang Result ay nagpapasa ng error bilang isang ordinaryong halaga nang hindi binubuksan ang stack, na ginagawang mas ligtas at mas composable ang paghawak ng inaasahang mga error. Ayon sa Apple Swift Documentation (2026), ang Result<Success, Failure> sa Swift ay nagbibigay-daan sa pagsasama-sama ng mga chain ng operasyon na may awtomatikong paghawak ng error sa pamamagitan ng map at flatMap, nang hindi naaantala ang pagpapatupad ng programa.
Mga pangunahing punto
Result Type ay isang generic na uri na nag-ee encapsulate ng resulta ng pagpapatupad ng isang operasyon na maaaring magtapos pareho sa tagumpay at sa error. Hindi tulad ng exceptions, kung saan ang error ay pumutol sa normal na daloy ng pagpapatupad at nangangailangan ng pagbubukas ng stack upang makahanap ng catch, ang Result ay nagpapasa ng error bilang isang ordinaryong halaga — ang tumatawag na partido ay palaging tumatanggap ng isang bagay at nagpapasya mismo kung paano ito haharapin. Ito ay lalong kapaki-pakinabang para sa inaasahang mga error: hindi wastong input, mga panuntunan sa negosyo, pagtanggi ng server, kung saan ang exception ay magiging masyadong mabigat na mekanismo.
Ang konsepto ng Result ay nagmula sa functional programming, kung saan ang mga katulad na uri ay tinatawag na Either (o Left/Right). Sa Swift, ang Result ay naging isang standard na uri sa Swift 5.0, sa Kotlin Result<T> ay lumitaw sa standard library, sa Dart 3.0 — built-in na Result<T>. Ang bawat implementasyon ay nagbibigay ng mga pamamaraan para sa pagtatrabaho sa lalagyan: map (pagbabago ng matagumpay na halaga), flatMap (chain ng Result function), mapError (pagbabago ng error), fold (paghawak ng parehong kaso). Result Type — ang pundasyon ng functional error handling sa mobile development, isang alternatibo sa try-catch para sa inaasahang mga senaryo.
Ang pangunahing bentahe ng Result ay ang pagkakabuo nito. Maaari mong pagsamahin ang maraming operasyon, na ang bawat isa ay maaaring magbalik ng error, sa isang chain nang walang nested na try-catch. Kung ang anumang operasyon sa chain ay magtatapos sa Failure, ang buong chain ay maaantala at magbabalik ng Failure — nang walang kahit isang condition na if o block na catch. Ginagawa nitong linear at nababasa ang code, lalo na sa mga senaryo na may maraming sunod-sunod na kahilingan sa API o pagsusuri ng mga panuntunan sa negosyo.
Sa Swift ang Result<Success, Failure> ay isang enum na may dalawang kaso: .success(Success) at .failure(Failure), kung saan ang Failure ay limitado ng Error protocol. Ang Swift Result ay isang ganap na functional na uri na may mga pamamaraang map, flatMap, mapError at get. Ang get ay isang espesyal na pamamaraan: ito ay nagbabalik ng Success value kung ang resulta ay matagumpay at nagtatapon ng error kung ito ay Failure. Ito ay nagpapahintulot sa paggamit ng Result bilang isang tulay sa pagitan ng functional at exception-based na mga estilo: hawakan sa pamamagitan ng map/flatMap sa chain, at sa dulo — get sa pamamagitan ng do-catch para sa integrasyon sa throws code.
Ang Swift Result ay isang enum na may generic na mga parameter, na nagpapahintulot sa compiler na suriin ang exhaustive na paghawak sa pamamagitan ng switch o do-catch. Kung magdagdag ka ng bagong kaso sa enum NetworkError, ang compiler ay magbibigay ng error sa lahat ng switch expression kung saan ang kasong ito ay hindi hinahawakan. Exhaustive na pagsusuri (exhaustive checking) — ang pangunahing bentahe ng Result kaysa sa exceptions: ginagarantiyahan ng compiler na ang lahat ng posibleng error ay isinasaalang-alang sa yugto ng compilation. Sa kabaligtaran, ang exceptions ay hindi sinusuri ng compiler sa Swift (tanging throws ang idineklara, ngunit hindi ang uri ng error).
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))
}
}
// Paggamit sa 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> ay nagpapahintulot sa pag-type ng error sa antas ng uri: ang function na fetchUser ay nagbabalik ng Result<User, NetworkError>, kung saan ang NetworkError ay isang konkretong enum na may tatlong kaso. Ang tumatawag na partido ay humahawak ng bawat kaso sa pamamagitan ng switch na may exhaustive na saklaw — sinusuri ng compiler kung ang lahat ng variant ay hinahawakan. Exhaustive switch — ang pangunahing bentahe ng Result kaysa sa exceptions: ginagarantiyahan ng compiler na hindi mo malilimutang hawakan ang .badURL, .requestFailed o .decodingFailed. Sa kaso ng exceptions, ang compiler ay hindi nangangailangan ng paghawak, at ang hindi nahuling catch(.badURL) ay madaling makaligtaan sa review.
Sa Kotlin ang Result<T> ay isang built-in na uri mula sa standard library, na kumakatawan sa tagumpay (T) o error (Throwable). Hindi tulad ng Swift, sa Kotlin ang Result ay hindi nagpapahintulot na tukuyin ang isang tiyak na uri ng error — tanging Throwable. Ito ay ginawa para sa pagiging simple, ngunit nangangailangan ng karagdagang pagsusuri ng uri ng error sa pamamagitan ng when expression. Ang Kotlin Result ay sumusuporta sa mga function na fold (paghawak ng parehong kaso), getOrNull (tagumpay o null), getOrDefault (tagumpay o default na halaga), map, recover, andThen. Ang katangian ng Kotlin: ang Result ay hindi inilaan para sa direktang pagpapalaganap sa mga hangganan ng function — hindi ito maaaring gamitin bilang return type para sa mga pamamaraan ng Android SDK o Kotlin Coroutines API nang walang karagdagang adaptasyon.
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 — isang maginhawang wrapper sa Kotlin na humahawak ng anumang exception at nagbabalik ng Result.failure. validateEmail ay nagbabalik ng Result.success o Result.failure depende sa pagsusuri. fold ay humahawak ng parehong kaso nang compact: sa Success ang susunod na validation function ay tinatawag, sa Failure — ang SignupResult.Error ay ibinabalik. Mahalaga: ang Kotlin Result ay hindi inilaan para sa pag-imbak sa mga field ng data class o direktang pagpapadala sa mga hangganan ng suspend function — gamitin ang iyong sariling sealed class (Success/Error/Loading) para sa representasyon ng mga UI state sa Jetpack Compose o MVVM.
Mula Dart 3.0 sa standard library ay lumitaw ang built-in na Result<T> — isang sealed class na may dalawang constructor: T.ok() (tagumpay) at Error.error() (error na may Object at StackTrace). Bago ang Dart 3.0, ang mga developer ng Flutter ay gumamit ng Either<L, R> mula sa dartz package o custom na sealed classes. Ang built-in na Result sa Dart ay minimalist: hindi ito direktang nagbibigay ng map/flatMap — ang mga function na ito ay kailangang ipatupad sa pamamagitan ng when o paggamit ng extension methods. Para sa seryosong functional na paghawak, ang Either mula sa fpdart ay nananatiling mas malakas na solusyon na may suporta para sa map, flatMap, mapLeft, fold, andThen at bind operators.
Either<L, R> — isang kaliwa-nauugnay na uri mula sa fpdart package, kung saan ang Left — error, Right — tagumpay. Hindi tulad ng built-in na Result<T>, ine-type ng Either ang error sa antas ng parameter ng uri (L), na nagpapahintulot na makilala ang mga uri ng error sa yugto ng compilation. Ang fpdart package ay nagbibigay ng isang kumpletong set ng functional combinators: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — nested na Either), andThen (chain nang walang pagbabago), fold (paglabas mula sa Either), getOrElse (default na halaga). Para sa Flutter applications na may functional approach, ang Either ay ang de facto standard.
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));
}
}
}
// Paggamit sa fold
final result = await service.fetchUser('42');
result.fold(
(left) => showError(left.message),
(right) => showUser(right),
);
Either<L, R> mula sa fpdart — kaliwa-nauugnay na uri: Left — error, Right — tagumpay. fetchUser ay nagbabalik ng Either<AppError, User>, kung saan ang AppError — sealed class na may tiyak na mga uri ng error (serverError, networkError). fold ay humahawak ng parehong kaso: ang unang callback para sa Left (error), ang pangalawa para sa Right (tagumpay). Sa Dart 3.0 ang built-in na Result ay sumusuporta rin sa fold, ngunit hindi nagbibigay ng map/flatMap. Para sa mga chain, ang Either mula sa fpdart ay nagbibigay ng map, flatMap (bind), mapLeft, andThen — isang kumpletong set ng functional combinators para sa komposisyon ng error.
Ang pangunahing bentahe ng Result kaysa sa exceptions ay komposisyon. Kung mayroon kang maraming operasyon, na ang bawat isa ay maaaring magbalik ng error, maaari mong pagsamahin ang mga ito sa isang chain sa pamamagitan ng map at flatMap nang walang kahit isang nested if o try-catch. map ay nagbabago ng matagumpay na halaga: Result.success(x) -> Result.success(f(x)). flatMap (tinatawag din na bind o andThen) — para sa mga kaso kung saan ang pagbabago mismo ay nagbabalik ng Result: Result.success(x) -> f(x) -> Result<Y>. Kung sa anumang hakbang ay nangyari ang Failure, ang mga susunod na operasyon ay hindi isinasagawa — ang chain ay naaantala.
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))
// Komposisyon sa pamamagitan ng flatMap (andThen sa fpdart)
val result = validateToken("abc123")
.flatMap { fetchProfile("42") }
.map { it.name }
.getOrElse { "Guest" }
println(result) // "Alice"
Chain: validateToken -> fetchProfile -> map name -> getOrElse “Guest”. Kung ang validateToken ay magbalik ng Left (InvalidToken), ang chain ay maaantala at “Guest” ay ibabalik. Kung ang fetchProfile ay magbalik ng Left (UserNotFound) — “Guest” din. Kung ang parehong operasyon ay matagumpay — ang pangalan ng profile. flatMap ay nagpapahintulot sa pagsasama-sama ng Either functions, na ang bawat isa ay maaaring mabigo, sa isang linear chain. Sa tradisyonal na exception style, ang parehong code ay mangangailangan ng dalawang nested try-catch o null checks. getOrElse sa dulo — ang exit point mula sa komposisyon, na nagbibigay ng default na halaga para sa Failure case.
Result at exceptions — hindi mga mutually exclusive na approach. Ang bawat isa ay may kanya-kanyang lugar ng aplikasyon, at sa isang mahusay na dinisenyo na mobile application, pareho ang ginagamit. Ang pagpili ay depende sa kung ang error ay inaasahan (expected) o hindi inaasahan (unexpected). Result — para sa inaasahang mga error na bahagi ng business logic: hindi wastong email, hindi sapat na pondo sa account, paglampas sa limitasyon ng kahilingan. Exceptions — para sa hindi inaasahang system error: pagkawala ng network, OutOfMemoryError, NullPointerException (na hindi dapat mangyari, ngunit nangyayari).
| Kriterya | Result Type | Exceptions (Exception/Error) |
|---|---|---|
| Uri ng error | Inaasahan (business logic) | Hindi inaasahan (system) |
| Pagganap | Mababang gastos (nang hindi binubuksan ang stack) | Mataas na gastos (stack unwinding, pagkuha ng StackTrace) |
| Komposisyon | Sa pamamagitan ng map/flatMap — linear na mga chain | Nested na try-catch — mahirap basahin |
| Compiler | Exhaustive na pagsusuri (switch/when) | Tanging checked exceptions sa Java |
| Daloy ng pagpapatupad | Hindi naaantala — error bilang halaga | Naaantala hanggang sa pinakamalapit na catch |
| Pagsubok | Madali: suriin ang resulta, assert isSuccess/isError | Nangangailangan ng assertThrows at mock object |
| Kailan gagamitin | Business validation, chain ng kahilingan, form | Pagkawala ng network, I/O error, pag-crash ng system |
Praktikal na panuntunan: kung ang error ay bahagi ng normal na daloy ng trabaho ng application (ang user ay nagpasok ng hindi wastong email, hindi sapat na mga karapatan sa pag-access) — gamitin ang Result. Kung ang error ay isang pambihirang sitwasyon (hindi tumutugon ang server, naubos ang memorya) — gamitin ang exceptions. Sa mobile development ang Result sa mga hangganan ng layer (UseCase -> ViewModel) at exceptions sa loob ng mga layer (API -> Repository) — isang karaniwang pattern na pinagsasama ang mga bentahe ng parehong approach.
Ang migrasyon ng umiiral na exception-based code sa Result ay dapat na gradual. Magsimula sa mga hangganan ng layer: balutin ang mga tawag ng throws function sa Result { try ... } (Swift) o runCatching { ... } (Kotlin). Pagkatapos ay palitan ang return type ng mga pamamaraan ng Repository at UseCase ng Result/Either, iwanan ang panloob na implementasyon sa exceptions. Sa huling yugto, i-migrate ang ViewModel: sa halip na UiState na may exceptions, gamitin ang sealed class UiState<T> (Loading, Success, Error), kung saan ang Error ay nag-iimbak ng domain error, hindi Throwable. Ang gradual na migrasyon ay nagpapahintulot sa pagsubok ng bawat layer nang hiwalay nang walang global refactoring.
Mga madalas itanong
Optional (T?) ay kumakatawan sa pagkakaroon o kawalan ng halaga — ang nil ay nangangahulugang “walang data”, ngunit hindi nagsasabi kung bakit. Result (Success/Failure) ay naglalaman hindi lamang ng tagumpay, kundi pati na rin ang sanhi ng error na may tiyak na uri. Gamitin ang Optional kapag ang kawalan ng halaga ay normal (halimbawa, opsyonal na field ng profile), Result — kapag kailangan mo ng impormasyon tungkol sa error.
Sa Swift gamitin ang Result { try throwingFunc() } — ang constructor ng Result ay tumatanggap ng throws closure. Sa Kotlin — runCatching { throwingFunc() }, na nagbabalik ng Result<T>. Sa Dart — Result<T>.tryCatch(() => throwingFunc()). Ito ay nagpapahintulot sa madaling integrasyon ng exception-based code sa Result chains.
Gamitin ang mapError (Swift) o mapLeft (Either sa Dart/Kotlin) para sa pagbabago ng uri ng error nang hindi binabago ang matagumpay na halaga. Kung kailangan mong hawakan ang parehong kaso at magbalik ng iisang halaga — gamitin ang fold. Para sa pag-log nang hindi pinuputol ang chain, gamitin ang onFailure (Kotlin) o prefix inspection point.
Oo, ngunit maingat. Ang Kotlin Result<T> ay hindi inirerekomenda bilang direktang return type ng suspend functions dahil sa mga katangian ng K2 compiler at reflection. Gamitin ang iyong sariling sealed class na NetworkResult<T> (Success, Error, Loading) para sa representasyon ng mga estado sa coroutine. Para sa inaasahang mga error sa business logic, ang Either mula sa Arrow ay isang mas malakas na alternatibo.
fold — isang pamamaraan na tumatanggap ng dalawang callback: onSuccess (para sa matagumpay na kaso) at onFailure (para sa kaso ng error), at nagbabalik ng iisang halaga ng anumang uri. Ito ay katumbas ng switch expression, ngunit sa anyo ng isang higher-order function. Ang fold — ang pangunahing exit point mula sa Result chains, kung saan mo ginagawang UiState, string para sa user o ibang Result ang Success/Failure.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din