Published on

SwiftUI crypto app — Combine yordamida tangalar rasmlarini yuklash

Authors

SwiftUI crypto app — Combine yordamida tangalar rasmlarini yuklash

Ushbu darsda biz ro'yxatimizdagi doira shaklidagi vaqtinchalik joylarni (placeholders) har bir tangaga mos keladigan real rasmlar (logotiplar) bilan almashtiramiz.

Buning uchun biz MVVM arxitekturasining yana bir to'liq zanjirini yaratamiz: rasmlarni yuklovchi CoinImageService, uni boshqaruvchi CoinImageViewModel va ekranda ko'rsatuvchi CoinImageView reusable (qayta ishlatiladigan) komponentini tuzamiz.


1. CoinImageService klassini yaratish

services papkasi ichida CoinImageService.swift faylini yaratamiz. Bu klass faqat bitta tanga modelini qabul qiladi va unga tegishli rasm URL manzilidan rasm ma'lumotlarini yuklaydi:

import Foundation
import SwiftUI
import Combine

class CoinImageService {

    // Yuklangan rasmni saqlovchi publisher
    @Published var image: UIImage? = nil

    private var imageSubscription: AnyCancellable?
    private let coin: CoinModel

    init(coin: CoinModel) {
        self.coin = coin
        getCoinImage()
    }

    private func getCoinImage() {
        // Tanganing rasm URL manzilini tekshiramiz
        guard let url = URL(string: coin.image) else { return }

        // NetworkingManager-ning download funksiyasini chaqiramiz
        imageSubscription = NetworkingManager.download(url: url)
            // Yuklangan Data-ni UIImage formatiga o'tkazamiz
            .tryMap({ (data) -> UIImage? in
                return UIImage(data: data)
            })
            // Natijani asosiy oqimda qabul qilamiz
            .sink(receiveCompletion: NetworkingManager.handleCompletion, receiveValue: { [weak self] (returnedImage) in
                // Rasmni o'zgaruvchiga saqlaymiz va obunani yopamiz
                self?.image = returnedImage
                self?.imageSubscription?.cancel()
            })
    }
}

2. CoinImageViewModel yaratish

components/CoinImage papkasi ichida CoinImageViewModel.swift faylini yaratamiz. U yuklanish holatini va servisdan olingan rasmni boshqaradi:

import Foundation
import SwiftUI
import Combine

class CoinImageViewModel: ObservableObject {

    @Published var image: UIImage? = nil
    @Published var isLoading: Bool = false

    private let coin: CoinModel
    private let dataService: CoinImageService
    private var cancelables = Set<AnyCancellable>()

    init(coin: CoinModel) {
        self.coin = coin
        // Har bir tanga uchun alohida rasm xizmatini ishga tushiramiz
        self.dataService = CoinImageService(coin: coin)
        self.isLoading = true
        addSubscribers()
    }

    private func addSubscribers() {
        // Servisdagi image publisher-ga obuna bo'lamiz
        dataService.$image
            .sink { [weak self] (_) in
                // Rasm yuklanib bo'lingach, yuklanish holatini yakunlaymiz
                self?.isLoading = false
            } receiveValue: { [weak self] (returnedImage) in
                // Qabul qilingan rasmni ViewModel-dagi nashr etiluvchi image-ga bog'laymiz
                self?.image = returnedImage
            }
            .store(in: &cancelables)
    }
}

3. CoinImageView reusable komponentini yaratish

components/CoinImage papkasida CoinImageView.swift faylini yaratamiz. Swift-da @StateObject o'zgaruvchisini initializer (init) orqali to'g'ri bog'lash uchun _vm (tag chiziqli variant) orqali ViewModel-ni bog'laymiz:

import SwiftUI

struct CoinImageView: View {

    // ViewModel-ni StateObject sifatida init ichida bog'laymiz
    @StateObject var vm: CoinImageViewModel

    init(coin: CoinModel) {
        // _vm orqali StateObject-ni o'rab turgan o'zgaruvchini (wrapper) o'zimiz yaratamiz
        _vm = StateObject(wrappedValue: CoinImageViewModel(coin: coin))
    }

    var body: some View {
        ZStack {
            if let image = vm.image {
                // Rasm muvaffaqiyatli yuklansa ko'rsatamiz
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
            } else if vm.isLoading {
                // Rasm hali yuklanayotgan bo'lsa
                ProgressView()
            } else {
                // Rasm yuklashda xatolik bo'lsa, savol belgisi chiqadi
                Image(systemName: "questionmark")
                    .foregroundColor(Color.theme.secondaryText)
            }
        }
    }
}

struct CoinImageView_Previews: PreviewProvider {
    static var previews: some View {
        CoinImageView(coin: dev.coin)
            .padding()
            .previewLayout(.sizeThatFits)
    }
}

4. CoinRowView-ga integratsiya qilish

Endi views/CoinRowView.swift fayliga o'tamiz va undagi vaqtinchalik doira belgisini (Circle()) yangi yaratgan CoinImageView bilan almashtiramiz:

// CoinRowView.swift kengaytmasi (Extension) ichida:
private var leftColumn: some View {
    HStack(spacing: 0) {
        Text("\(coin.rank)")
            .font(.caption)
            .foregroundColor(Color.theme.secondaryText)
            .frame(width: 30)

        // Vaqtinchalik Circle o'rniga CoinImageView qo'yamiz
        CoinImageView(coin: coin)
            .frame(width: 30, height: 30)

        Text(coin.symbol.uppercased())
            .font(.headline)
            .padding(.leading, 6)
            .foregroundColor(Color.theme.accent)
    }
}

Loyihani yuklab ishga tushirsak (yoki HomeView Canvas-ni yuklasak), endi barcha tangalarning real logotiplari API-dan yuklanib chiroyli ko'rsatiladi.

Keyingi darsda biz ushbu rasmlarni har safar internetdan qayta yuklab internet trafigini sarflamaslik uchun ularni telefon xotirasiga (FileManager) keshlab qo'yishni o'rganamiz! 🚀

Buy mea coffee