Published on

SwiftUI crypto app — yangilangan API ma'lumotlarini ro'yxatga yuklash

Authors

SwiftUI crypto app — yangilangan API ma'lumotlarini ro'yxatga yuklash

Bu darsda ikkita muhim narsa qo'shamiz:

  1. Portfel qiymatini va uning 24 soatlik foiz o'zgarishini haqiqiy hisoblaymiz
  2. Yangilash tugmasini qo'shamiz — bosilganda API dan yangi narxlar yuklab olinadi

1. Portfel qiymatini hisoblab statistikaga qo'shish

HomeViewModel-dagi mapGlobalMarketData funksiyasiga portfel tangalarini ham uzatamiz. Buning uchun subscriberga $portfolioCoins ni ham qo'shamiz:

// Eski:
marketDataService.$marketData
    .map(mapGlobalMarketData)

// Yangi: market data + portfolio coinsni birlashtirish
marketDataService.$marketData
    .combineLatest($portfolioCoins)
    .map(mapGlobalMarketData)
    .sink { [weak self] stats in
        self?.statistics = stats
    }
    .store(in: &cancellables)

Funksiya imzosini yangilaymiz:

private func mapGlobalMarketData(
    data: MarketDataModel?,
    portfolioCoins: [CoinModel]
) -> [StatisticModel] {
    var stats: [StatisticModel] = []
    guard let data = data else { return stats }

    // Portfelning joriy umumiy qiymati (har bir tanganing holdings × narxi)
    let portfolioValue = portfolioCoins
        .map { $0.currentHoldingsValue }
        .reduce(0, +)

    // Portfelning 24 soat oldingi qiymati (formulasi orqali hisoblaymiz)
    let previousValue = portfolioCoins.map { coin -> Double in
        let currentValue = coin.currentHoldingsValue
        // API dan kelgan foiz o'zgarishi butun son (masalan 25 = 25%), uni kasrga aylantiramiz
        let percentChange = (coin.priceChangePercentage24H ?? 0) / 100
        // Formulasi: joriy_qiymat / (1 + foiz_o'zgarishi) = oldingi qiymat
        return currentValue / (1 + percentChange)
    }.reduce(0, +)

    // Foiz o'zgarishi: (yangi - eski) / eski × 100
    let percentageChange = ((portfolioValue - previousValue) / previousValue) * 100

    let portfolio = StatisticModel(
        title: "Portfel qiymati",
        value: portfolioValue.asCurrencyWith2Decimals(),
        percentageChange: percentageChange
    )

    stats.append(contentsOf: [
        StatisticModel(
            title: "Bozor kapitalizatsiyasi",
            value: data.marketCap,
            percentageChange: data.marketCapChangePercentage24HUsd
        ),
        StatisticModel(title: "24 soatlik hajm", value: data.volume),
        StatisticModel(title: "BTC ulushi", value: data.btcDominance),
        portfolio
    ])
    return stats
}

Formulaning mantig'i: Agar tanga narxi 10% oshgan bo'lsa va joriy qiymat 110bolsa,kechagiqiymat:110/(1+0.10)=100 bo'lsa, kechagi qiymat: 110 / (1 + 0.10) = 100. Foiz o'zgarishi: (110 - 100) / 100 × 100 = 10%.


2. Portfel qiymatini sinash

Simulatorda portfelga tanga qo'shing (masalan 0.5 ETH). Portfel qiymati statistikada ko'rinadi:

  • Qiymat: $1,900.00
  • Foiz: qizil yoki yashil uchburchak bilan ko'rsatiladi

Yangi tanga qo'shgach — statistika avtomatik yangilanadi, chunki portfolioCoins publisher-ga ulanib ketgan.


3. Yangilash tugmasi (Reload Button)

HomeView-da ustun sarlavhalari (columnTitles) yaniga yangilash tugmasi qo'shamiz:

private var columnTitles: some View {
    HStack {
        Text("Tanga")
        Spacer()
        if showPortfolio {
            Text("Hisoblar")
        }
        Text("Narx")
            .frame(width: UIScreen.main.bounds.width / 3.5, alignment: .trailing)

        // Yangilash tugmasi — faqat portfel ekranida emas, asosiy ekranda
        Button {
            withAnimation(.linear(duration: 2.0)) {
                vm.reloadData()
            }
        } label: {
            Image(systemName: "goforward")
        }
        // Yuklanayotganda 360° aylanadi
        .rotationEffect(
            Angle(degrees: vm.isLoading ? 360 : 0),
            anchor: .center
        )
    }
    .font(.caption)
    .foregroundColor(Color.theme.secondaryText)
    .padding(.horizontal)
}

4. isLoading holati va reloadData funksiyasi

HomeViewModel-ga yangi holatlar qo'shamiz:

@Published var isLoading: Bool = false

func reloadData() {
    isLoading = true
    coinDataService.getCoins()       // Tangalar narxlarini qayta yuklaymiz
    marketDataService.getData()      // Bozor ma'lumotlarini qayta yuklaymiz
    HapticManager.notification(type: .success)  // Qurilma titraydi
}

isLoading-ni false-ga qaytarish — statistika subscriber-da bo'ladi:

marketDataService.$marketData
    .combineLatest($portfolioCoins)
    .map(mapGlobalMarketData)
    .sink { [weak self] stats in
        self?.statistics = stats
        self?.isLoading = false  // Statistika yangilangach — yuklash tugadi
    }
    .store(in: &cancellables)

CoinDataService-dagi getCoins() va MarketDataService-dagi getData() funksiyalarini private-dan internal (yoki ochiq) qilamiz — boshqa yerdan chaqirish uchun.


5. HapticManager — taktil tebranish

utilities papkasida HapticManager.swift faylini yaratamiz:

import Foundation
import UIKit

class HapticManager {

    // Static generator — har doim tayyor turadi
    private static let generator = UINotificationFeedbackGenerator()

    // Yangilash bosilganda qurilmani titratamiz
    static func notification(type: UINotificationFeedbackGenerator.FeedbackType) {
        generator.notificationOccurred(type)
    }
}

Chaqirish:

HapticManager.notification(type: .success)  // muvaffaqiyatli
HapticManager.notification(type: .error)    // xatolik
HapticManager.notification(type: .warning)  // ogohlantirish

Eslatma: Simulatorda tebranish sezilmaydi. Haqiqiy qurilmada yangilash tugmasini bosganingizda telefon yengil titrashini sezasiz.


Xulosa

Bu darsda juda ko'p mantiq qo'shdik:

  • Portfel qiymati va foizi endi haqiqiy hisoblanadi
  • Yangilash tugmasi bosilganda API ga yangi so'rov yuboriladi
  • Qurilmada taktil fikr-bildirish (haptic) bor
  • Barcha publisher-subscriber zanjiri sinxron ishlaydi

"Butun ilovamiz bitta go'zal publisher-subscriber tizimi bilan yashaydi. Bir joyda narx yangilansa — portfel, statistika, ko'rsatgichlar — hammasi o'zi yangilanadi. Bu iOS dasturlashning kelajagi!"

Keyingi darsda biz ro'yxatni saralash (sorting) va animatsiyalarni qo'shamiz. 🚀

Buy mea coffee