Skip to content
This repository has been archived by the owner on Oct 28, 2022. It is now read-only.

Request more coins from CoinGecko if any of received coins are not in… #60

Merged
merged 1 commit into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 40 additions & 33 deletions XRatesKit/Classes/Providers/CoinGeckoProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,23 @@ class CoinGeckoProvider {
private let providerCoinsManager: ProviderCoinsManager
private let networkManager: NetworkManager
private let expirationInterval: TimeInterval
private let coinsPerPage = 200
private let coinsPerPage = 250

init(providerCoinsManager: ProviderCoinsManager, networkManager: NetworkManager, expirationInterval: TimeInterval) {
self.providerCoinsManager = providerCoinsManager
self.networkManager = networkManager
self.expirationInterval = expirationInterval
}

private func allCoinMarketsNextDelayedSingle(currencyCode: String, fetchDiffPeriod: TimePeriod, page: Int, itemCount: Int?) -> Single<[CoinMarket]> {
guard let itemCount = itemCount, itemCount > coinsPerPage else {
return Single.just([])
}

let single = allCoinMarketsSingle(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, page: page + 1, itemCount: itemCount - coinsPerPage)

return Single<Int>
.timer(.seconds(1), scheduler: SerialDispatchQueueScheduler(qos: .background))
.flatMap { _ in single }
}

private func allCoinMarketsSingle(currencyCode: String, fetchDiffPeriod: TimePeriod, page: Int = 1, itemCount: Int? = nil, coinIds: String? = nil) -> Single<[CoinMarket]> {
var pageParams = ""
if let itemCount = itemCount {
let perPage = min(coinsPerPage, itemCount)
pageParams += "&per_page=\(perPage)&page=\(page)"
}

private func marketsRequest(currencyCode: String, fetchDiffPeriod: TimePeriod, pageParams: String = "", coinIdsParams: String = "") -> DataRequest {
let priceChangePercentage: String
switch fetchDiffPeriod {
case .hour24, .dayStart: priceChangePercentage = ""
default: priceChangePercentage = "&price_change_percentage=\(fetchDiffPeriod.title)"
}

let url = "\(provider.baseUrl)/coins/markets?\(coinIds ?? "")&vs_currency=\(currencyCode)\(priceChangePercentage)&order=market_cap_desc\(pageParams)"
let request = networkManager.session.request(url, method: .get, encoding: JSONEncoding())

let mapper = CoinGeckoTopMarketMapper(providerCoinManager: providerCoinsManager, currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, expirationInterval: expirationInterval)
return networkManager.single(request: request, mapper: mapper)
.flatMap { [weak self] coinMarkets -> Single<[CoinMarket]> in
let single = self?.allCoinMarketsNextDelayedSingle(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, page: page, itemCount: itemCount) ?? Single.just([])
return single.map { nextCoinMarkets -> [CoinMarket] in coinMarkets + nextCoinMarkets }
}
let url = "\(provider.baseUrl)/coins/markets?\(coinIdsParams)&vs_currency=\(currencyCode)\(priceChangePercentage)&order=market_cap_desc\(pageParams)"
return networkManager.session.request(url, method: .get, encoding: JSONEncoding())
}

}
Expand All @@ -79,14 +54,46 @@ extension CoinGeckoProvider {
return networkManager.single(request: request, mapper: mapper)
}

func topCoinMarketsSingle(currencyCode: String, fetchDiffPeriod: TimePeriod, itemCount: Int) -> Single<[CoinMarket]> {
allCoinMarketsSingle(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, itemCount: itemCount)
func topCoinMarketsSingle(currencyCode: String, fetchDiffPeriod: TimePeriod, itemCount: Int, page: Int = 1) -> Single<[CoinMarket]> {
let expectedItemsCount = min(coinsPerPage, itemCount)
let perPage = page > 1 ? coinsPerPage : min(coinsPerPage, itemCount)
let pageParams = "&per_page=\(perPage)&page=\(page)"

let request = marketsRequest(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, pageParams: pageParams)
let mapper = CoinGeckoTopMarketMapper(providerCoinManager: providerCoinsManager, currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, expirationInterval: expirationInterval)

return networkManager.single(request: request, mapper: mapper)
.flatMap { [weak self] coinMarkets -> Single<[CoinMarket]> in
guard let provider = self else {
return Single.just(coinMarkets)
}

if itemCount <= provider.coinsPerPage, itemCount <= coinMarkets.count {
return Single.just(Array(coinMarkets[0..<itemCount]))
}

var nextItemCount = itemCount
if coinMarkets.count < expectedItemsCount {
nextItemCount += expectedItemsCount - coinMarkets.count
}
nextItemCount = nextItemCount - provider.coinsPerPage

let single = provider.topCoinMarketsSingle(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, itemCount: nextItemCount, page: page + 1)

return Single<Int>
.timer(.seconds(1), scheduler: SerialDispatchQueueScheduler(qos: .background))
.flatMap { _ in single }
.map { nextCoinMarkets -> [CoinMarket] in coinMarkets + nextCoinMarkets }
}
}

func coinMarketsSingle(currencyCode: String, fetchDiffPeriod: TimePeriod, coinTypes: [CoinType]) -> Single<[CoinMarket]> {
let externalIds = coinTypes.compactMap { providerCoinsManager.providerId(coinType: $0, provider: .CoinGecko) }

return allCoinMarketsSingle(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, coinIds: "&ids=\(externalIds.joined(separator: ","))")
let request = marketsRequest(currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, coinIdsParams: externalIds.joined(separator: ","))
let mapper = CoinGeckoTopMarketMapper(providerCoinManager: providerCoinsManager, currencyCode: currencyCode, fetchDiffPeriod: fetchDiffPeriod, expirationInterval: expirationInterval)

return networkManager.single(request: request, mapper: mapper)
}

}
Expand All @@ -99,7 +106,7 @@ extension CoinGeckoProvider: IChartPointProvider {
}


let url = "\(provider.baseUrl)/coins/\(externalId)/market_chart?vs_currency=\(key.currencyCode)&interval=\(key.chartType.days)"
let url = "\(provider.baseUrl)/coins/\(externalId)/market_chart?vs_currency=\(key.currencyCode)&days=\(key.chartType.days)"
let request = networkManager.session.request(url, method: .get, encoding: JSONEncoding())

return networkManager.single(request: request, mapper: CoinGeckoMarketChartsMapper())
Expand Down
8 changes: 5 additions & 3 deletions XRatesKit/Classes/Storage/GrdbStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ class GrdbStorage {

migrator.registerMigration("recreateCoinInfoRecords") { db in
if try db.tableExists("coin_info") {
try db.drop(index: "coin_info")
try db.drop(table: "coin_info")
}
if try db.tableExists("provider_coin_info") {
try db.drop(index: "provider_coin_info")
try db.drop(table: "provider_coin_info")
}

try db.create(table: CoinInfoRecord.databaseTableName) { t in
Expand Down Expand Up @@ -236,7 +236,9 @@ class GrdbStorage {
}

migrator.registerMigration("changePrimaryKeyForMarketInfoRecord") { db in
try db.drop(table: MarketInfoRecord.databaseTableName)
if try db.tableExists(MarketInfoRecord.databaseTableName) {
try db.drop(table: MarketInfoRecord.databaseTableName)
}

try db.create(table: MarketInfoRecord.databaseTableName) { t in
t.column(MarketInfoRecord.Columns.coinId.name, .text).notNull()
Expand Down