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: SwiftUIda 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