Published on

Swift'da Do, Try, Catch va Throws

Authors

Do, Try, Catch va Throws

Swift Concurrency bo'limidagi birinchi texnik darsimizni dasturlash tilidagi to'rtta asosiy kalit so'zlarni o'rganishdan boshlaymiz: do, try, catch va throws.

Garchand bu so'zlar Swift uchun yoki Swift Concurrency uchun yangilik bo'lmasa-da, kelajakda yozadigan deyarli barcha asinxron kodlarimizda ulardan foydalanamiz. Ushbu maqolada faqatgina ularni qanday ishlatishni emas, balki nima uchun aynan shu usulni ishlatishimizni — kod evolyutsiyasini ko'rib chiqamiz.

Xatoliklarni boshqarish evolyutsiyasi

Tasavvur qiling, bizda ma'lumotlarni tarmoqdan yoki bazadan yuklab oluvchi menejer klassi (DataManager) bor. Undan qanday qilib ma'lumotni ishonchli tarzda qabul qilib olishimiz mumkin? Keling, turli xil usullarni ko'rib chiqamiz.

1-usul: Optional qaytarish

Eng sodda usul funksiyadan ma'lumotni ixtiyoriy (Optional) sifatida qaytarishdir:

class DataManager {
    let isActive: Bool = false

    func getTitle() -> String? {
        if isActive {
            return "Yangi matn"
        } else {
            return nil
        }
    }
}

Muammo: Agar ma'lumot kelmasa (nil qaytsa), ishlab chiquvchi nima uchun xato bo'lganini bilmaydi. Bu xatolik tarmoq yo'qligimi, server ishlamayotganimi yoki boshqa sababdanmi?

2-usul: Tuple qaytarish

Keyingi qadam — Tuple yordamida ham qiymat, ham xatolikni qaytarish:

func getTitle2() -> (title: String?, error: Error?) {
    if isActive {
        return ("Yangi matn", nil)
    } else {
        return (nil, URLError(.badURL))
    }
}

Muammo: Bu biroz yaxshiroq, ammo dasturchi uchun noqulay. Chunki u har safar natijani tekshirayotganda ham titleni, ham errorni tekshirib chiqishi kerak bo'ladi. Real holatda esa har ikkala qiymat bir vaqtda kelmaydi.

3-usul: Result turidan foydalanish

Bu usul Combine framework'ida juda ko'p ishlatiladi:

func getTitle3() -> Result<String, Error> {
    if isActive {
        return .success("Yangi matn")
    } else {
        return .failure(URLError(.badURL))
    }
}

Muammo: Result yaxshi yechim bo'lsa-da, har bir funksiya chaqirilganda natijani switch orqali success va failure holatlariga ajratib ishlash majburiydir. Bu kodni biroz uzun qilib yuboradi.

4-usul: Throws yordamida xatoliklarni uloqtirish (Eng yaxshi usul)

Yuqoridagi muammolardan qochish va yechimni osonlashtirish uchun biz funksiyalarni throws qilib belgilashimiz mumkin. Bu "ushbu funksiya qandaydir xatolik yuz bersa, uni uloqtiradi" degan ma'noni beradi.

class DataManager {
    let isActive: Bool = false

    func getTitle4() throws -> String {
        if isActive {
            return "Yangi matn"
        } else {
            // Xato yuz bersa uni throw (uloqtirish) qilamiz
            throw URLError(.badURL)
        }
    }
}

Bu funksiya endi oddiy String qaytaradi. Agar xato yuz bersa-chi? Unda u to'g'ridan-to'g'ri xatolik qaytaradi.

Do, Try va Catch'dan foydalanish

Keling endi ViewModel'imizda yuqoridagi getTitle4() funksiyasidan qanday foydalanishimizni ko'ramiz.

Modomiki funksiya throws (xato uloqtiruvchi) ekanligini e'lon qildikmi, biz uni sinab ko'rishimiz (try) kerak bo'ladi. Agar xato yuz bersa, biz uni tutib olishimiz (catch) uchun do-catch blokidan foydalanamiz.

class ViewModel: ObservableObject {
    @Published var text: String = "Boshlang'ich matn"
    let manager = DataManager()

    func fetchTitle() {
        do {
            // manager.getTitle4() ni "try" (sinab) ko'ramiz.
            // Agar muvaffaqiyatli ishlasa, pastdagi qatorga o'tadi
            let newTitle = try manager.getTitle4()
            self.text = newTitle

        } catch {
            // Agar try dagi kod xato "throw" qilsa, do bloki darhol to'xtaydi
            // va kod ushbu catch blokiga sakraydi.
            self.text = error.localizedDescription
        }
    }
}

Do blokining asosiy qoidasi

do bloki ichida bir nechta try chaqiruvlari bo'lishi mumkin. Ammo eng muhim qoida shuki: agar birinchi try xatolik (error) qaytarsa, qolgan kodlar o'qilmaydi va dastur to'g'ridan-to'g'ri catch blokiga tushib ketadi.

do {
    let title1 = try manager.getTitle4() // Agar shu yerda xato bo'lsa...
    let title2 = try manager.getTitle4() // ...bu qator umuman ishlamaydi!
} catch {
    // xatolik ushlanadi
}

Try? (Optional Try)

Ba'zida bizga xatolikning turi muhim emas bo'lishi mumkin. Masalan, "Agar ishlatsa natijani ber, xato bo'lsa shunchaki nil ber" demoqchi bo'lsak, try? (ixtiyoriy try) dan foydalanamiz.

func fetchOptionalTitle() {
    // do-catch blokiga ehtiyoj yo'q
    let newTitle = try? manager.getTitle4() // newTitle endi String? turida bo'ladi

    if let title = newTitle {
        self.text = title
    }
}

Buning afzalligi shundaki, biz kodni keraksiz do-catch bloklari bilan to'ldirib tashlamaymiz, ayniqsa biz xatoni Foydalanuvchiga ko'rsatishga yoki uni ro'yxatga olishga ehtiyoj sezmasak.

Xulosa

Swift Concurrency'da deyarli barcha asinxron funksiyalar (internetdan rasm yuklash, ma'lumotlar bazasi bilan ishlash) qandaydir xatolikni qaytarishi mumkin bo'lgan holatda bo'ladi va ular asosan throws so'zi bilan belgilanadi. Shuning uchun ularni o'qishda do, try, va catch usullaridan erkin foydalana olish asinxron kodlarni mukammal yozishingiz uchun eng muhim kalit hisoblanadi.

Buy mea coffee