Published on

WebSocket va Combine asoslari

Authors

HTTP — so'rov yuborib javob kutish. Lekin chat, jonli narxlar, o'yin — real-time kerak. WebSocket — doimiy ochiq kanal. Combine — reaktiv ma'lumot oqimi.

WebSocket asoslari

// ═══════════════════════════════════════
//  WEBSOCKET — real-time ulanish
// ═══════════════════════════════════════
class WebSocketMenejer: ObservableObject {
    @Published var xabarlar: [String] = []
    @Published var ulangan = false
    private var webSocketTask: URLSessionWebSocketTask?

    func ulanish() {
        let url = URL(string: "wss://echo.websocket.org")!
        webSocketTask = URLSession.shared.webSocketTask(with: url)
        webSocketTask?.resume()
        ulangan = true

        // Xabar qabul qilishni boshlash
        qabulQilish()
    }

    func xabarYuborish(_ matn: String) {
        let xabar = URLSessionWebSocketTask.Message.string(matn)
        webSocketTask?.send(xabar) { xato in
            if let xato {
                print("Yuborish xatosi: \(xato)")
            }
        }
    }

    private func qabulQilish() {
        webSocketTask?.receive { [weak self] natija in
            switch natija {
            case .success(let xabar):
                switch xabar {
                case .string(let matn):
                    DispatchQueue.main.async {
                        self?.xabarlar.append(matn)
                    }
                case .data(let data):
                    print("Data: \(data.count) bayt")
                    @unknown default:
                    break
                }
                // Keyingi xabarni kutish
                self?.qabulQilish()

            case .failure(let xato):
                print("Qabul xatosi: \(xato)")
            }
        }
    }

    func uzish() {
        webSocketTask?.cancel(with: .goingAway, reason: nil)
        ulangan = false
    }
}

Combine asoslari

import Combine

// ═══════════════════════════════════════
//  PUBLISHER VA SUBSCRIBER
// ═══════════════════════════════════════
class HisoblashMisol {
    var cancellables = Set<AnyCancellable>()

    func boshlash() {
        // Publisher — har 1 soniyada raqam chiqaradi
        let publisher = Timer.publish(every: 1.0, on: .main, in: .common)
        .autoconnect()
        .map { _ in Int.random(in: 1...100) } // Tasodifiy son

        // Subscriber — qiymatlarni qabul qiladi
        publisher
        .filter { $0 > 50 }           // Faqat 50 dan katta
        .map { "Son: \($0)" }          // String ga aylantirish
        .sink { matn in                 // Natijani olish
            print(matn)
        }
        .store(in: &cancellables)       // Subscription ni saqlash
    }
}


// ═══════════════════════════════════════
//  @PUBLISHED VA COMBINE
// ═══════════════════════════════════════
class QidiruvViewModel: ObservableObject {
    @Published var qidiruvMatni = ""
    @Published var natijalar: [String] = []
    private var cancellables = Set<AnyCancellable>()

    init() {
        // $qidiruvMatni — Combine Publisher
        $qidiruvMatni
        // 0.5 soniya kutish (debounce)
        .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
        // Bo'sh matnni filtrlash
        .filter { !$0.isEmpty }
        // Avvalgi qidiruv bilan bir xil bo'lsa o'tkazib yuborish
        .removeDuplicates()
        // Natijani olish
        .sink { [weak self] matn in
            self?.qidirish(matn)
        }
        .store(in: &cancellables)
    }

    private func qidirish(_ matn: String) {
        // Tarmoq so'rovi...
        natijalar = ["Natija: \(matn)"]
    }
}

Combine operatorlar

// ═══════════════════════════════════════
//  ASOSIY OPERATORLAR
// ═══════════════════════════════════════

// map — qiymatni o'zgartirish
[1, 2, 3].publisher
.map { $0 * 10 }           // [10, 20, 30]

// filter — filtrlash
[1, 2, 3, 4, 5].publisher
.filter { $0 > 3 }         // [4, 5]

// debounce — kutish (qidiruv uchun)
$matn.debounce(for: .seconds(0.3), scheduler: RunLoop.main)

// throttle — cheklash (scroll uchun)
$matn.throttle(for: .seconds(1), scheduler: RunLoop.main, latest: true)

// combineLatest — ikki publisher ni birlashtirish
$ism.combineLatest($email)
.map { ism, email in
    !ism.isEmpty && email.contains("@")
}
.assign(to: &$formaTugri)

// flatMap — ichki publisher ga o'tish
$userId
.flatMap { id in
    self.foydalanuvchiYukla(id: id)
}

🎯 Topshiriq

Chat ilovasi yarating: WebSocket bilan ulanish, xabar yuborish/qabul qilish. Combine bilan $qidiruvMatni ga debounce(0.3) va removeDuplicates qo'llab, "yozmoqda..." indikatorini ko'rsating.

Buy mea coffee