Published on

SwiftUI crypto app — jonli bozor ma'lumotlarini API dan yuklab ko'rsatish

Authors

SwiftUI crypto app — jonli bozor ma'lumotlarini API dan yuklab ko'rsatish

Oldingi darsda statistika ko'rish uchun view va model tayyorladik, lekin ularda hali haqiqiy ma'lumot yo'q edi. Bu darsda biz CoinGecko API dan global bozor ma'lumotlarini yuklab olamiz — bozor kapitalizatsiyasi, 24 soatlik savdo hajmi, Bitcoin ulushi — va ularni statistika ko'rinishida ekranda chiqaramiz.


1. API javobini ko'rish

CoinGecko saytida GET /global endpointi mavjud. Parametrlar talab etilmaydi, shunchaki bajarasiz. Javob quyidagicha keladi:

{
  "data": {
    "total_market_cap": { "usd": 2580000000000 },
    "total_volume": { "usd": 342000000000 },
    "market_cap_percentage": { "btc": 42.5, "eth": 18.3 },
    "market_cap_change_percentage_24h_usd": 1.2
  }
}

Diqqat: bozor ma'lumotlari "data" kalit so'zi ichida keladi, shuning uchun ikki model kerak bo'ladi.


2. MarketDataModel yaratish

models papkasida MarketDataModel.swift faylini yarataymiz. app.quicktype.io yordamida JSON dan Swift struct hosil qilingan, keyin keraksiz maydonlar olib tashlanadi:

import Foundation

// API dan keladigan tashqi qobiq: { "data": { ... } }
struct GlobalData: Codable {
    let data: MarketDataModel?
}

// Haqiqiy bozor ma'lumotlari
struct MarketDataModel: Codable {

    let totalMarketCap: [String: Double]
    let totalVolume: [String: Double]
    let marketCapPercentage: [String: Double]
    let marketCapChangePercentage24HUsd: Double

    // JSON kalitlarida katta-kichik va underscore bor — CodingKeys kerak
    enum CodingKeys: String, CodingKey {
        case totalMarketCap = "total_market_cap"
        case totalVolume = "total_volume"
        case marketCapPercentage = "market_cap_percentage"
        case marketCapChangePercentage24HUsd = "market_cap_change_percentage_24h_usd"
    }

    // USD qiymatida bozor kapitalizatsiyasi
    var marketCap: String {
        if let item = totalMarketCap.first(where: { $0.key == "usd" }) {
            return "$" + item.value.formattedWithAbbreviations()
        }
        return ""
    }

    // USD qiymatida 24 soatlik savdo hajmi
    var volume: String {
        if let item = totalVolume.first(where: { $0.key == "usd" }) {
            return "$" + item.value.formattedWithAbbreviations()
        }
        return ""
    }

    // Bitcoin bozor ulushi (foiz ko'rinishida)
    var btcDominance: String {
        if let item = marketCapPercentage.first(where: { $0.key == "btc" }) {
            return item.value.asPercentString()
        }
        return ""
    }
}

Nima uchun CodingKeys? API JSON-da total_market_cap deb keladi, Swift esa totalMarketCap ko'rinishini talab qiladi (camelCase). CodingKeys ularni bog'laydi.


3. Sonlarni odam o'qishi uchun formatlash

Double+Extensions.swift ga yangi funksiya qo'shamiz. Bu funksiya katta sonlarni qisqartiradi:

// 1234567 -> "1.23M", 1234567890 -> "1.23Bn", 1234567890000 -> "1.23Tr"
func formattedWithAbbreviations() -> String {
    let num = abs(Double(self))
    let sign = (self < 0) ? "-" : ""

    switch num {
    case 1_000_000_000_000...:
        let formatted = num / 1_000_000_000_000
        let stringFormatted = formatted.asNumberString()
        return "\(sign)\(stringFormatted)Tr"
    case 1_000_000_000...:
        let formatted = num / 1_000_000_000
        let stringFormatted = formatted.asNumberString()
        return "\(sign)\(stringFormatted)Bn"
    case 1_000_000...:
        let formatted = num / 1_000_000
        let stringFormatted = formatted.asNumberString()
        return "\(sign)\(stringFormatted)M"
    case 1_000...:
        let formatted = num / 1_000
        let stringFormatted = formatted.asNumberString()
        return "\(sign)\(stringFormatted)K"
    case 0...:
        return "\(sign)\(self.asNumberString())"
    default:
        return "\(sign)\(self.asNumberString())"
    }
}

4. MarketDataService — API ga so'rov yuborish

CoinDataService-ni asosiy shabloniga tayanib, MarketDataService.swift faylini yaratamiz:

import Foundation
import Combine

class MarketDataService {

    @Published var marketData: MarketDataModel? = nil

    var marketDataSubscription: AnyCancellable?

    init() {
        getData()
    }

    func getData() {
        guard let url = URL(string: "https://api.coingecko.com/api/v3/global") else { return }

        marketDataSubscription = NetworkingManager.download(url: url)
            .decode(type: GlobalData.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(
                receiveCompletion: NetworkingManager.handleCompletion,
                receiveValue: { [weak self] returnedGlobalData in
                    self?.marketData = returnedGlobalData.data
                    self?.marketDataSubscription?.cancel()
                }
            )
    }
}

Javob GlobalData sifatida dekodlanadi, keyin .data maydonidan MarketDataModel olinadi.


5. HomeViewModel-ga qo'shish

HomeViewModel.swift-da yangi servisni yaratamiz va unga subscriber qo'shamiz:

// Eski coinDataService nomini refactor qilish:
private let coinDataService = CoinDataService()

// Yangi market data service:
private let marketDataService = MarketDataService()

addSubscribers() ichida yangi subscriber:

// Bozor ma'lumotlarini subscriberga qo'shish
marketDataService.$marketData
    .map(mapGlobalMarketData)  // MarketDataModel -> [StatisticModel]
    .sink { [weak self] returnedStats in
        self?.statistics = returnedStats
    }
    .store(in: &cancellables)

6. mapGlobalMarketData funksiyasi

HomeViewModel-da statistika ma'lumotlarini hosil qiluvchi private funksiya:

private func mapGlobalMarketData(data: MarketDataModel?) -> [StatisticModel] {
    var stats: [StatisticModel] = []

    guard let data = data else { return stats }

    let marketCap = StatisticModel(
        title: "Bozor kapitalizatsiyasi",
        value: data.marketCap,
        percentageChange: data.marketCapChangePercentage24HUsd
    )

    let volume = StatisticModel(
        title: "24 soatlik hajm",
        value: data.volume
    )

    let btcDominance = StatisticModel(
        title: "BTC ulushi",
        value: data.btcDominance
    )

    let portfolio = StatisticModel(
        title: "Portfel qiymati",
        value: "$0.00",  // Keyingi darslarda to'ldiriladi
        percentageChange: 0
    )

    stats.append(contentsOf: [marketCap, volume, btcDominance, portfolio])
    return stats
}

7. Natija

Simulatorda ilovani ishga tushirsangiz, endi statistika bo'limida haqiqiy API ma'lumotlari ko'rinadi:

  • Bozor kapitalizatsiyasi: $2.58Tr
  • 24 soatlik hajm: $342Bn
  • BTC ulushi: 42.5%
  • Portfel: $0.00 (hozircha)

"Ko'rdingizmi, yangi API qo'shish qanchalik tez bo'ldi? Tarmoq menejeri va subscriber tizimini oldindan qurib qo'yganimiz sababli, bu bir necha daqiqada amalga oshdi. Mana arxitekturaning kuchi!"

Keyingi darsda biz portfelni boshqarish uchun yangi view yaratamiz. 🚀

Buy mea coffee