Published on

Combine: Publishers va Subscribers (Murakkab daraja)

Authors

1. @Published aslida qanday ishlaydi?

Biz shu paytgacha barcha ma'lumotlarni e'lon qilish uchun (publish) @Published dan foydalanib keldik:

class CombineViewModel: ObservableObject {
    @Published var data: [String] = []
}

Biroq, bu asosan qulaylik (wrapper) hisoblanadi. Combine'da ikkita asosiy Publisher turi bor: CurrentValueSubject va PassthroughSubject.

CurrentValueSubject

CurrentValueSubject o'zida har doim joriy (oxirgi marta berilgan) qiymatni saqlab turadi. Demak u initsializatsiya paytida boshlang'ich qiymat (initial value) talab qiladi.

import Combine

class CombineDataService {
    // String turidagi ma'lumot, boshlang'ich qiymati: "Boshlang'ich"
    let currentValuePublisher = CurrentValueSubject<String, Never>("Boshlang'ich")

    func publishData() {
        // Yangi qiymat yuborish
        currentValuePublisher.send("Yangi xabar 1")
    }
}

PassthroughSubject

PassthroughSubject esa o'zida hech qanday qiymat saqlamaydi. U faqat kanal vazifasini o'taydi (ma'lumot undan o'tib ketadi). Agar unga kimdir obuna (subscribe) bo'lmagan bo'lsa, yuborilgan ma'lumot shunchaki yo'qoladi. U xotiradan joy tejash uchun zo'r vosita.

class CombineDataService {
    // Boshlang'ich qiymat kiritilmaydi
    let passthroughPublisher = PassthroughSubject<String, Error>()

    func publishData() {
        passthroughPublisher.send("O'tib ketuvchi xabar")
    }
}

2. Publisher zanjirlarini (Pipelines) boshqarish

Biz passthroughPublisher orqali ketma-ket raqamlarni (1, 2, 3...) vaqt oralig'i bilan yuboryapmiz deylik. Qabul qiluvchi tomonda ularni qanday filtrlaymiz?

class CombineViewModel: ObservableObject {
    @Published var data: [String] = []
    var cancellables = Set<AnyCancellable>()
    let dataService = CombineDataService()

    init() {
        addSubscribers()
    }

    func addSubscribers() {
        dataService.passthroughPublisher
            // 1. Zanjirdagi birinchi amal (Masalan, kelgan int ni string ga o'tkazish)
            .map { String($0) }

            // 2. Ketma-ketlik funksiyalari (Sequence Operations)
            // .first() // Faqat birinchi ma'lumotni oladi va zanjirni to'xtatadi

            // .first(where: { $0.count > 5 }) // Shartga mos kelgan birinchisini oladi

            // tryFirst() yordamida agar kutilmagan qiymat kelsa, zanjirda Xato (Error) keltirib chiqarish mumkin
            .tryFirst(where: { value in
                if value == "3" {
                    throw URLError(.badServerResponse)
                }
                return value == "4"
            })
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print("XATOLIK YUZ BERDI: \(error.localizedDescription)")
                }
            }, receiveValue: { [weak self] returnedValue in
                self?.data.append(returnedValue)
            })
            .store(in: &cancellables)
    }
}

Xulosa: SwiftUI da shunchaki array ni yangilash o'rniga, Combine zanjirlari orqali xabarlarni filtrlash, o'zgartirish, xatoliklarni boshqarish (.map, .first, .tryFirst) kabi amallarni bitta uzoq oqim (pipeline) doirasida mukammal boshqarishingiz mumkin.

Buy mea coffee