Result Type: apa itu, tipe-wadah Result dan cara kerjanya dalam pengembangan mobile

Penulis: IT Sectr Diterbitkan: 2026-05-26 Waktu membaca: 9 mnt

Result Type — tipe-wadah yang merepresentasikan hasil operasi yang dapat berakhir dengan sukses (Success) atau kesalahan (Failure). Berbeda dengan exception, Result meneruskan kesalahan sebagai nilai biasa tanpa membuka stack, yang membuat penanganan kesalahan yang diharapkan lebih aman dan dapat dikomposisikan. Menurut Apple Swift Documentation (2026), Result<Success, Failure> di Swift memungkinkan menggabungkan rantai operasi dengan penanganan kesalahan otomatis melalui map dan flatMap, tanpa menghentikan eksekusi program.

Poin utama

  • Result Type — wadah generik dengan dua keadaan: Success (data) dan Failure (kesalahan), tanpa membuka stack
  • Di Swift Result<Success, Failure> — tipe bawaan dengan metode map, flatMap, mapError dan get
  • Di Kotlin Result<T> merepresentasikan sukses atau Throwable, dengan fungsi getOrNull, getOrDefault, fold
  • Di Dart Result<T> standar tersedia dari Dart 3.0, juga Either dari paket fpdart
  • Komposisi melalui map dan flatMap memungkinkan menggabungkan beberapa operasi yang mengembalikan Result tanpa pemeriksaan bersarang

Apa itu Result Type?

Result Type adalah tipe generik yang mengenkapsulasi hasil eksekusi operasi yang dapat berakhir baik dengan sukses maupun dengan kesalahan. Berbeda dengan exception, di mana kesalahan mengganggu aliran eksekusi normal dan memerlukan pembukaan stack untuk menemukan catch, Result meneruskan kesalahan sebagai nilai biasa — pihak pemanggil selalu menerima objek dan memutuskan sendiri bagaimana menanganinya. Ini sangat berguna untuk kesalahan yang diharapkan: input tidak valid, aturan bisnis, penolakan server, di mana exception akan menjadi mekanisme yang terlalu berat.

Konsep Result berasal dari pemrograman fungsional, di mana tipe serupa disebut Either (atau Left/Right). Di Swift, Result menjadi tipe standar di Swift 5.0, di Kotlin Result<T> muncul di pustaka standar, di Dart 3.0 — Result<T> bawaan. Setiap implementasi menyediakan metode untuk bekerja dengan wadah: map (transformasi nilai sukses), flatMap (rantai fungsi Result), mapError (transformasi kesalahan), fold (penanganan kedua kasus). Result Type — batu penjuru penanganan kesalahan fungsional dalam pengembangan mobile, alternatif untuk try-catch untuk skenario yang diharapkan.

Keuntungan utama Result adalah kemampuan komposisi. Anda dapat menggabungkan beberapa operasi, yang masing-masing dapat mengembalikan kesalahan, dalam satu rantai tanpa try-catch bersarang. Jika operasi apa pun dalam rantai berakhir dengan Failure, seluruh rantai terputus dan mengembalikan Failure — tanpa satu pun kondisi if atau blok catch. Ini membuat kode menjadi linier dan mudah dibaca, terutama dalam skenario dengan beberapa permintaan API berurutan atau pemeriksaan aturan bisnis.

Result di Swift

Di Swift Result<Success, Failure> adalah enum dengan dua kasus: .success(Success) dan .failure(Failure), di mana Failure dibatasi oleh protokol Error. Swift Result adalah tipe fungsional lengkap dengan metode map, flatMap, mapError dan get. get adalah metode khusus: ia mengembalikan nilai Success jika hasilnya berhasil dan melempar kesalahan jika Failure. Ini memungkinkan penggunaan Result sebagai jembatan antara gaya fungsional dan berbasis exception: tangani melalui map/flatMap dalam rantai, dan di akhir — get melalui do-catch untuk integrasi dengan kode throws.

Enum Result dan switch exhaustif

Swift Result adalah enum dengan parameter generik, yang memungkinkan kompiler memeriksa penanganan exhaustif melalui switch atau do-catch. Jika Anda menambahkan kasus baru ke enum NetworkError, kompiler akan memberikan kesalahan di semua ekspresi switch di mana kasus ini tidak ditangani. Pemeriksaan exhaustif (exhaustive checking) — keuntungan utama Result dibandingkan exception: kompiler menjamin bahwa semua kemungkinan kesalahan dipertimbangkan pada tahap kompilasi. Sebaliknya, exception tidak diperiksa oleh kompiler di Swift (hanya throws yang dideklarasikan, tetapi bukan tipe kesalahannya).

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

// Penggunaan dengan 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> memungkinkan mengetik kesalahan pada tingkat tipe: fungsi fetchUser mengembalikan Result<User, NetworkError>, di mana NetworkError adalah enum konkret dengan tiga kasus. Pihak pemanggil menangani setiap kasus melalui switch dengan cakupan exhaustif — kompiler memeriksa apakah semua varian ditangani. Switch exhaustif — keuntungan utama Result dibandingkan exception: kompiler menjamin Anda tidak akan lupa menangani .badURL, .requestFailed atau .decodingFailed. Dalam kasus exception, kompiler tidak memerlukan penanganan, dan catch(.badURL) yang tidak tertangani dapat dengan mudah terlewatkan dalam review.

Result di Kotlin

Di Kotlin Result<T> adalah tipe bawaan dari pustaka standar, yang merepresentasikan sukses (T) atau kesalahan (Throwable). Berbeda dengan Swift, di Kotlin Result tidak mengizinkan menentukan tipe kesalahan spesifik — hanya Throwable. Ini dilakukan untuk kesederhanaan, tetapi memerlukan pemeriksaan tambahan tipe kesalahan melalui ekspresi when. Kotlin Result mendukung fungsi fold (penanganan kedua kasus), getOrNull (sukses atau null), getOrDefault (sukses atau nilai default), map, recover, andThen. Keistimewaan Kotlin: Result tidak dimaksudkan untuk propagasi langsung melintasi batas fungsi — tidak dapat digunakan sebagai tipe kembalian untuk metode Android SDK atau Kotlin Coroutines API tanpa adaptasi tambahan.

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 — pembungkus yang nyaman di Kotlin yang menangkap exception apa pun dan mengembalikan Result.failure. validateEmail mengembalikan Result.success atau Result.failure tergantung pada pemeriksaan. fold menangani kedua kasus secara ringkas: pada Success fungsi validasi berikutnya dipanggil, pada Failure — SignupResult.Error dikembalikan. Penting: Kotlin Result tidak dimaksudkan untuk disimpan di bidang data class atau ditransmisikan langsung melintasi batas fungsi suspend — gunakan sealed class Anda sendiri (Success/Error/Loading) untuk merepresentasikan status UI di Jetpack Compose atau MVVM.

Result di Dart dan Flutter

Sejak Dart 3.0 di pustaka standar telah muncul Result<T> bawaan — sealed class dengan dua konstruktor: T.ok() (sukses) dan Error.error() (kesalahan dengan Object dan StackTrace). Sebelum Dart 3.0, pengembang Flutter menggunakan Either<L, R> dari paket dartz atau sealed class khusus. Result bawaan di Dart minimalis: tidak menyediakan map/flatMap secara langsung — fungsi-fungsi ini harus diimplementasikan melalui when atau menggunakan extension methods. Untuk penanganan fungsional yang serius, Either dari fpdart tetap menjadi solusi yang lebih kuat dengan dukungan untuk map, flatMap, mapLeft, fold, andThen dan operator bind.

Either dari fpdart

Either<L, R> — tipe berasosiasi kiri dari paket fpdart, di mana Left — kesalahan, Right — sukses. Berbeda dengan Result<T> bawaan, Either mengetik kesalahan pada tingkat parameter tipe (L), yang memungkinkan membedakan tipe kesalahan pada tahap kompilasi. Paket fpdart menyediakan set lengkap kombinator fungsional: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — Either bersarang), andThen (rantai tanpa transformasi), fold (keluar dari Either), getOrElse (nilai default). Untuk aplikasi Flutter dengan pendekatan fungsional, Either adalah standar de facto.

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

// Penggunaan dengan fold
final result = await service.fetchUser('42');
result.fold(
    (left)  => showError(left.message),
    (right) => showUser(right),
);

Either<L, R> dari fpdart — tipe berasosiasi kiri: Left — kesalahan, Right — sukses. fetchUser mengembalikan Either<AppError, User>, di mana AppError — sealed class dengan tipe kesalahan spesifik (serverError, networkError). fold menangani kedua kasus: callback pertama untuk Left (kesalahan), kedua untuk Right (sukses). Di Dart 3.0 Result bawaan juga mendukung fold, tetapi tidak menyediakan map/flatMap. Untuk rantai, Either dari fpdart menyediakan map, flatMap (bind), mapLeft, andThen — set lengkap kombinator fungsional untuk komposisi kesalahan.

Komposisi Result melalui map dan flatMap

Keuntungan utama Result dibandingkan exception adalah komposisi. Jika Anda memiliki beberapa operasi, yang masing-masing dapat mengembalikan kesalahan, Anda dapat menggabungkannya dalam rantai melalui map dan flatMap tanpa satu pun if bersarang atau try-catch. map mentransformasi nilai sukses: Result.success(x) -> Result.success(f(x)). flatMap (juga disebut bind atau andThen) — untuk kasus di mana transformasi itu sendiri mengembalikan Result: Result.success(x) -> f(x) -> Result<Y>. Jika pada langkah apa pun terjadi Failure, operasi selanjutnya tidak dijalankan — rantai terputus.

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

// Komposisi melalui flatMap (andThen di fpdart)
val result = validateToken("abc123")
    .flatMap { fetchProfile("42") }
    .map { it.name }
    .getOrElse { "Guest" }

println(result) // "Alice"

Rantai: validateToken -> fetchProfile -> map name -> getOrElse “Guest”. Jika validateToken mengembalikan Left (InvalidToken), rantai terputus dan “Guest” dikembalikan. Jika fetchProfile mengembalikan Left (UserNotFound) — juga “Guest”. Jika kedua operasi berhasil — nama profil. flatMap memungkinkan menggabungkan fungsi Either, yang masing-masing dapat gagal, dalam satu rantai linier. Dalam gaya exception tradisional, kode yang sama akan memerlukan dua try-catch bersarang atau pemeriksaan null. getOrElse di akhir — titik keluar dari komposisi, yang menyediakan nilai default untuk kasus Failure.

Result vs Exception: kapan memilih apa

Result dan exception — bukan pendekatan yang saling eksklusif. Masing-masing memiliki area penerapannya, dan dalam aplikasi mobile yang dirancang dengan baik, keduanya digunakan. Pilihan tergantung pada apakah kesalahan itu diharapkan (expected) atau tidak diharapkan (unexpected). Result — untuk kesalahan yang diharapkan yang merupakan bagian dari logika bisnis: email tidak valid, saldo tidak mencukupi, melebihi batas permintaan. Exception — untuk kesalahan sistem yang tidak diharapkan: kehilangan jaringan, OutOfMemoryError, NullPointerException (yang seharusnya tidak terjadi, tetapi terjadi).

KriteriaResult TypeException (Exception/Error)
Tipe kesalahanDiharapkan (logika bisnis)Tidak diharapkan (sistem)
KinerjaBiaya rendah (tanpa membuka stack)Biaya tinggi (stack unwinding, penangkapan StackTrace)
KomposisiMelalui map/flatMap — rantai linierTry-catch bersarang — sulit dibaca
KompilerPemeriksaan exhaustif (switch/when)Hanya checked exceptions di Java
Aliran eksekusiTidak terputus — kesalahan sebagai nilaiTerputus hingga catch terdekat
PengujianMudah: periksa hasil, assert isSuccess/isErrorMemerlukan assertThrows dan objek mock
Kapan menggunakanValidasi bisnis, rantai permintaan, formulirKehilangan jaringan, kesalahan I/O, kerusakan sistem

Aturan praktis: jika kesalahan adalah bagian dari aliran kerja normal aplikasi (pengguna memasukkan email tidak valid, hak akses tidak mencukupi) — gunakan Result. Jika kesalahan adalah situasi luar biasa (server tidak merespons, memori habis) — gunakan exception. Dalam pengembangan mobile Result di batas lapisan (UseCase -> ViewModel) dan exception di dalam lapisan (API -> Repository) — pola umum yang menggabungkan keuntungan kedua pendekatan.

Strategi migrasi dari exception ke Result

Migrasi kode berbasis exception yang ada ke Result harus bertahap. Mulailah dari batas lapisan: bungkus panggilan fungsi throws dalam Result { try ... } (Swift) atau runCatching { ... } (Kotlin). Kemudian ganti tipe kembalian metode Repository dan UseCase dengan Result/Either, biarkan implementasi internal pada exception. Pada tahap terakhir, migrasikan ViewModel: alih-alih UiState dengan exception, gunakan sealed class UiState<T> (Loading, Success, Error), di mana Error menyimpan kesalahan domain, bukan Throwable. Migrasi bertahap memungkinkan menguji setiap lapisan secara terpisah tanpa refactoring global.

Pertanyaan yang sering diajukan

Apa perbedaan Result Type dengan Optional/Option?

Optional (T?) merepresentasikan ada atau tidaknya nilai — nil berarti “tidak ada data”, tetapi tidak mengatakan mengapa. Result (Success/Failure) tidak hanya berisi sukses, tetapi juga penyebab kesalahan dengan tipe yang spesifik. Gunakan Optional ketika ketiadaan nilai adalah normal (misalnya, bidang profil opsional), Result — ketika Anda membutuhkan informasi tentang kesalahan.

Bagaimana mengonversi fungsi throws ke Result?

Di Swift gunakan Result { try throwingFunc() } — konstruktor Result menerima closure throws. Di Kotlin — runCatching { throwingFunc() }, yang mengembalikan Result<T>. Di Dart — Result<T>.tryCatch(() => throwingFunc()). Ini memungkinkan integrasi mudah kode berbasis exception ke dalam rantai Result.

Bagaimana menangani kesalahan di Result tanpa kehilangan informasi?

Gunakan mapError (Swift) atau mapLeft (Either di Dart/Kotlin) untuk transformasi tipe kesalahan tanpa mengubah nilai sukses. Jika perlu menangani kedua kasus dan mengembalikan nilai tunggal — gunakan fold. Untuk pencatatan tanpa memutus rantai, gunakan onFailure (Kotlin) atau inspection point prefiks.

Bisakah Result di Kotlin digunakan dengan coroutine?

Ya, tetapi dengan hati-hati. Kotlin Result<T> tidak direkomendasikan sebagai tipe kembalian langsung fungsi suspend karena fitur K2 compiler dan refleksi. Gunakan sealed class khusus NetworkResult<T> (Success, Error, Loading) untuk merepresentasikan status di coroutine. Untuk kesalahan yang diharapkan dalam logika bisnis, Either dari Arrow adalah alternatif yang lebih kuat.

Apa itu fold dalam konteks Result?

fold — metode yang menerima dua callback: onSuccess (untuk kasus sukses) dan onFailure (untuk kasus kesalahan), dan mengembalikan nilai tunggal dari tipe apa pun. Ini adalah padanan dari ekspresi switch, tetapi dalam bentuk fungsi tingkat tinggi. fold — titik keluar utama dari rantai Result, di mana Anda mengubah Success/Failure menjadi UiState, string untuk pengguna, atau Result lainnya.

Ringkasan

  • Result Type — wadah generik (Success/Failure) untuk penanganan aman kesalahan yang diharapkan tanpa membuka stack
  • Di Swift Result<Success, Failure> dengan Failure: Error memastikan pemeriksaan exhaustif melalui switch dengan kesalahan type-safe
  • Di Kotlin Result<T> membungkus sukses atau Throwable, runCatching — konstruktor nyaman dari kode throws
  • Di Dart Result<T> bawaan (Dart 3.0) dan Either<L, R> dari fpdart untuk komposisi lanjutan dengan map/flatMap
  • Komposisi melalui map (transformasi sukses) dan flatMap (rantai fungsi Result) menggantikan try-catch bersarang
  • Result vs exception: Result untuk kesalahan bisnis yang diharapkan, exception untuk kegagalan sistem yang tidak diharapkan
  • Gunakan Result di batas lapisan untuk penanganan kesalahan yang eksplisit dan dapat diuji tanpa mengganggu aliran eksekusi

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga