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

Commit

Permalink
Merge pull request #60 from horizontalsystems/bugfix
Browse files Browse the repository at this point in the history
Request more coins from CoinGecko if any of received coins are not in…
  • Loading branch information
esen committed Mar 9, 2021
2 parents 3e33aaa + 4deaa7c commit 57bc540
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 36 deletions.
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

0 comments on commit 57bc540

Please sign in to comment.