model数据
// 一定要一一对应上
struct CoinModel:Identifiable,Codable{
let id, symbol, name: String
let image: String
let currentPrice :Double
let marketCap, marketCapRank, fullyDilutedValuation: Double?
let totalVolume, high24H, low24H: Double?
let priceChange24H, priceChangePercentage24H: Double?
let marketCapChange24H: Double?
let marketCapChangePercentage24H: Double?
let circulatingSupply, totalSupply, maxSupply, ath: Double?
let athChangePercentage: Double?
let athDate: String?
let atl, atlChangePercentage: Double?
let atlDate: String?
let lastUpdated: String?
let sparklineIn7D: SparklineIn7D?
let priceChangePercentage24HInCurrency: Double?
let currentHoldinds:Double?
// 查看是否有下划线
enum CodingKeys:String,CodingKey{
case id,symbol,name,image
case currentPrice = "current_price"
case marketCap = "market_cap"
case marketCapRank = "market_cap_rank"
case fullyDilutedValuation = "fully_diluted_valuation"
case totalVolume = "total_volume"
case high24H = "high_24h"
case low24H = "low_24h"
case priceChange24H = "price_change_24h"
case priceChangePercentage24H = "price_change_percentage_24h"
case marketCapChange24H = "market_cap_change_24h"
case marketCapChangePercentage24H = "market_cap_change_percentage_24h"
case circulatingSupply = "circulating_supply"
case totalSupply = "total_supply"
case maxSupply = "max_supply"
case ath
case athChangePercentage = "ath_change_percentage"
case athDate = "ath_date"
case atl
case atlChangePercentage = "atl_change_percentage"
case atlDate = "atl_date"
case lastUpdated = "last_updated"
case sparklineIn7D = "sparkline_in_7d"
case priceChangePercentage24HInCurrency = "price_change_percentage_24h_in_currency"
case currentHoldinds
}
// 更新模型
func updateHolding(amount:Double) -> CoinModel{
return CoinModel(id: id, symbol: symbol, name: name, image: image, currentPrice: currentPrice, marketCap: marketCap, marketCapRank: marketCapRank, fullyDilutedValuation: fullyDilutedValuation, totalVolume: totalVolume, high24H: high24H, low24H: low24H, priceChange24H: priceChange24H, priceChangePercentage24H: priceChangePercentage24H, marketCapChange24H: marketCapChange24H, marketCapChangePercentage24H: marketCapChangePercentage24H, circulatingSupply: circulatingSupply, totalSupply: totalSupply, maxSupply: maxSupply, ath: ath, athChangePercentage: athChangePercentage, athDate: athDate, atl: atl, atlChangePercentage: atlChangePercentage, atlDate: atlDate, lastUpdated: lastUpdated, sparklineIn7D: sparklineIn7D, priceChangePercentage24HInCurrency: priceChangePercentage24HInCurrency, currentHoldinds: amount)
}
var*currentHoldingsValue:Double{
return (currentHoldinds ?? 0) * currentPrice
}
var rank:Int{
return Int(marketCapRank ?? 0)
}
}
// MARK: - SparklineIn7D
struct SparklineIn7D:Codable { // 迷你图
let price: [Double]?
}
网络请求封装
import Foundation
import Combine
// 网络请求封装
class NetworkingManager{
enum NetworkingError:LocalizedError{
case badURLResponse(url:URL)
case unknown
var errorDescription: String?{
switch self{
case .badURLResponse(url:**let** url):return "[hot] bad response from URL \(url)"
case .unknown:return "Unknown error occured"
}
}
}
static func download(url:URL) -> AnyPublisher<Data,Error>{
return URLSession.shared.dataTaskPublisher(for: url)
.subscribe(on: DispatchQueue.global(qos: .default))
.tryMap({try handleURLResponse(output: $0,url: url)})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
static func handleURLResponse(output:URLSession.DataTaskPublisher.Output,url:URL) throws -> Data{
// throw NetworkingError.badURLResponse(url: url)
guard let response = output.response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else{
throw NetworkingError.badURLResponse(url: url)
}
return output.data
}
static func handleCompletion(completion:Subscribers.Completion<Error>){
switch completion{
case .finished:
break
case .failure(**let** error):
print(error.localizedDescription)
}
}
}
请求
import Foundation
import Combine
class CoinDataSerice{
// 订阅 和发布者
@Published var allCoins:[CoinModel] = []
var cancellables = Set<AnyCancellable>()
var coinSubscription:AnyCancellable?
init(){
getCoins()
}
private func getCoins(){
guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24h&locale=en") else{**return**}
coinSubscription = NetworkingManager.download(url: url)
.decode(type: [CoinModel].self, decoder: JSONDecoder())
.sink(receiveCompletion: NetworkingManager.handleCompletion, receiveValue: {[weak self] (returnedCoins) in
self?.allCoins = returnedCoins
self?.coinSubscription?.cancel()
})
}
}
数据与模型数据交互
import Foundation
import Combine
class HomeViewModel:ObservableObject{
@Published var allCoins:[CoinModel] = []
@Published var portfoliCoins:[CoinModel] = []
private let dataService = CoinDataSerice()
private var cancellables = Set<AnyCancellable>()
init(){
// 这里不懂
// DispatchQueue.main.asyncAfter(deadline: .now() + 2.0){
// self.allCoins.append(DevelooperPreview.instance.coin)
// self.portfoliCoins.append(DevelooperPreview.instance.coin)
//
// }
addSubscribers()
}
func addSubscribers(){
dataService.$allCoins
.sink { [**weak** **self**] (returnedCoins) **in**
self?.allCoins = returnedCoins
self?.portfoliCoins = returnedCoins
}.store(in: &cancellables)
}
}
页面显示