Belajar Swift - Error Handling & Debugging
Episode 6 of 23

Belajar Swift - Error Handling & Debugging

Episode ini membahas error handling di Swift dengan throws, try, catch, dan defer, pembuatan custom error type, serta pola error yang umum. Kalian juga belajar debugging di Xcode dengan breakpoint dan LLDB, ditambah assertions dan unit tests untuk memvalidasi perilaku program.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Program yang baik tidak hanya berjalan saat semuanya benar — ia juga berperilaku elegan saat terjadi kesalahan. Episode 6 membahas error handling dan debugging: bagaimana Swift memodelkan kegagalan secara eksplisit dengan throws, try, dan catch, serta bagaimana kalian melacak dan memperbaiki bug dengan breakpoint, LLDB, dan test.

Satu prinsip yang akan kalian pegang terus: error di Swift tidak disembunyikan. Fungsi yang bisa gagal menyatakan kemampuannya lewat tipe, dan compiler memaksa pemanggil menanganinya. Ini kontras dengan bahasa yang membiarkan kegagalan tak terlihat sampai crash di produksi.

Error Handling Dasar

throws, try, dan catch

Fungsi yang bisa melempar error ditandai throws, dan pemanggil menanganinya dengan do-catch:

do-catch dengan throws
enum KesalahanBagi: Error {
    case pembagiNol
}
 
func bagi(_ a: Int, _ b: Int) throws -> Int {
    guard b != 0 else {
        throw KesalahanBagi.pembagiNol
    }
    return a / b
}
 
do {
    let hasil = try bagi(10, 0)
    print(hasil)
} catch KesalahanBagi.pembagiNol {
    print("Pembagi tidak boleh nol")
}

try bagi(10, 0) memanggil fungsi berpotensi error di dalam blok do. Saat error dilempar, eksekusi pindah ke blok catch yang cocok. Penanganan yang tepat mengubah kegagalan menjadi alur yang terkendali.

defer: Pembersihan yang Dijamin

Saat error bisa melempar di tengah fungsi, pembersihan sumber daya harus tetap terjadi. Blok defer menjalankan kodenya ketika keluar dari scope, apa pun jalannya:

defer untuk pembersihan
func bacaFile(path: String) throws -> String {
    let file = open(path)
    defer {
        close(file)
        print("File ditutup")
    }
    // Operasi yang bisa melempar
    return "Isi file"
}

defer { close(file) } memastikan close(file) dipanggil entah fungsi mengembalikan nilai atau melempar error. Beberapa blok defer dieksekusi dalam urutan terbalik dari deklarasi — ingat ini saat menyusun inisialisasi bertingkat.

Custom Error dan Pola Umum

Membuat Error Type Sendiri

Error di Swift adalah tipe apa pun yang mengadopsi protocol Error. Praktik terbaik: gunakan enum dengan associated values untuk membawa konteks:

Error dengan konteks
enum ErrorJaringan: Error {
    case tidakAdaKoneksi
    case statusBuruk(code: Int)
    case payloadTidakValid(pesan: String)
}
 
func kirimRequest() throws {
    throw ErrorJaringan.statusBuruk(code: 503)
}
 
do {
    try kirimRequest()
} catch ErrorJaringan.statusBuruk(let code) where code >= 500 {
    print("Server bermasalah: \(code)")
} catch {
    print("Error lain: \(error)")
}

catch ErrorJaringan.statusBuruk(let code) membongkar associated value dan menggabungkannya dengan where untuk filter. Menangkap semua error lain lewat catch polos memastikan tidak ada kegagalan yang luput.

Pola Umum: Result

Untuk operasi asynchronous, pola klasik throws tidak berlaku karena error tidak bisa dilempar melewati batas closure. Gantinya, Swift menyediakan enum Result:

Result sebagai hasil operasi
func proses(data: String) -> Result<Int, Error> {
    guard let angka = Int(data) else {
        return .failure(ErrorJaringan.payloadTidakValid(pesan: data))
    }
    return .success(angka * 2)
}
 
let hasil = proses(data: "21")
print(hasil)

Result<Int, Error> membungkus sukses atau kegagalan dalam satu nilai. Episode 8 dan 9 akan memakai pola ini bersama async/await untuk networking dan concurrency.

Debugging di Xcode

Breakpoint dan LLDB

Xcode menyediakan debugger LLDB yang terintegrasi. Kalian bisa menghentikan eksekusi dengan breakpoint, lalu memeriksa state dari konsol. Satu set perintah yang wajib dihafal:

Perintah dasar LLDB
breakpoint set --file main.swift --line 10
run
print skor
step
next
continue

breakpoint set --file main.swift --line 10 memasang breakpoint sebelum menjalankan program. Di konsol LLDB, print skor menampilkan nilai variabel saat itu, next menjalankan baris berikutnya, dan continue melanjutkan eksekusi sampai breakpoint berikutnya.

Membaca Stack Trace

Saat crash, stack trace menunjukkan rantai pemanggilan fungsi. Mulailah membaca dari frame paling atas (lokasi crash), lalu turun mencari frame milik kode kalian — frame dari framework Apple biasanya aman untuk dilewati. Symbolication otomatis Xcode menerjemahkan alamat memori kembali menjadi nama fungsi dan baris.

Assertions dan Unit Tests

Assertions untuk Kontrak Internal

assert dan precondition menegakkan invariant yang jika dilanggar berarti bug:

assert dan precondition
func prosesNilai(_ nilai: Int) {
    precondition(nilai >= 0, "nilai harus non-negatif")
    assert(nilai < 1000, "nilai terlalu besar untuk diproses")
    print("Memproses \(nilai)")
}
 
prosesNilai(42)

precondition(nilai >= 0) selalu aktif, bahkan di build produksi — cocok untuk kondisi yang tidak boleh dilanggar. assert hanya aktif di build debug dan dihapus saat release, berguna untuk pemeriksaan yang mahal. Gunakan keduanya untuk menemukan bug lebih awal.

Verifikasi Cepat dengan Unit Test

Meskipun episode 15 membahas testing mendalam, kalian bisa mulai memverifikasi perilaku sekarang dengan XCTest:

Test sederhana
import XCTest
 
final class BagiTests: XCTestCase {
    func testPembagianNormal() throws {
        let hasil = try bagi(10, 2)
        XCTAssertEqual(hasil, 5)
    }
}

XCTAssertEqual(hasil, 5) memeriksa hasil fungsi. Test menjadi jaring pengaman saat kalian mengubah kode — setiap kali fitur berubah, test memberi tahu apakah perilaku lama rusak.

Tip

Mulailah membiasakan menulis test berdampingan dengan fitur, bukan sesudahnya. Test yang ditulis setelah bug ditemukan sering kali sekadar mengunci perilaku yang salah.

Penutup

Inti yang harus dibawa pulang:

  • Fungsi berpotensi error ditandai throws, dipanggil dengan try, dan ditangani di do-catch.
  • Blok defer menjamin pembersihan sumber daya apa pun jalannya eksekusi.
  • Error type terbaik dibuat dengan enum dan associated values.
  • Result membungkus sukses dan gagal untuk operasi asynchronous.
  • LLDB dan breakpoint Xcode memungkinkan inspeksi state secara interaktif.
  • precondition dan assert menegakkan invariant internal sejak dini.

Di episode 7 selanjutnya kita akan membahas data persistence dan file I/O — bekerja dengan FileManager dan URL untuk membaca serta menulis file lokal, serialisasi dengan protocol Codable untuk JSON dan plist, penyimpanan ringan UserDefaults dan Keychain, serta gambaran Core Data untuk persistence yang kompleks. Data kalian akan mulai disimpan dengan aman!