diff --git a/CryptoWidgetKitApp/ContentView.swift b/CryptoWidgetKitApp/ContentView.swift index fb33291..4457347 100644 --- a/CryptoWidgetKitApp/ContentView.swift +++ b/CryptoWidgetKitApp/ContentView.swift @@ -12,6 +12,7 @@ struct ContentView: View { @Environment(CryptoViewModel.self) var cryptoViewModel var body: some View { + @Bindable var cryptoViewModel = cryptoViewModel NavigationStack { List(cryptoViewModel.cryptos, id: \.coinInfo.id) { crypto in HStack { @@ -20,7 +21,27 @@ struct ContentView: View { Text(crypto.display?.usd.price ?? "0") } } + .redacted(reason: cryptoViewModel.isLoading ? .placeholder : []) .task { await cryptoViewModel.fetch() } + .refreshable { await cryptoViewModel.fetch() } + .alert(isPresented: $cryptoViewModel.showError, content: { + Alert(title: Text("An error occurred, try again later"), + dismissButton: .default( + Text("Retry"), + action: { + Task { + await cryptoViewModel.fetch() + } + })) + }) + .toolbar { + ToolbarItemGroup(placement: .bottomBar) { + Text("API Cache 120 seconds") + .font(.footnote) + .foregroundColor(.secondary) + .frame(maxWidth: .infinity) + } + } } } } diff --git a/CryptoWidgetKitApp/ViewModels/CryptoViewModel.swift b/CryptoWidgetKitApp/ViewModels/CryptoViewModel.swift index 55b73e1..faf940a 100644 --- a/CryptoWidgetKitApp/ViewModels/CryptoViewModel.swift +++ b/CryptoWidgetKitApp/ViewModels/CryptoViewModel.swift @@ -13,13 +13,17 @@ final class CryptoViewModel { // MARK: - Properties private(set) var cryptos: [Crypto] = [] + private(set) var isLoading: Bool = true + var showError: Bool = false // MARK: - Dependencies private let apiService: CryptoAPIService // MARK: - Init - init(apiService: CryptoAPIService = CryptoAPIService()) { + init(apiService: CryptoAPIService = CryptoAPIService(), + initialState: [Crypto] = .mock) { self.apiService = apiService + self.cryptos = initialState } } @@ -28,10 +32,25 @@ extension CryptoViewModel { /// Fetch the cryptos from API and update the cryptos array func fetch() async { + isLoading = true do { + sleep(3) cryptos = try await apiService.fetchRetrieveFullList().data + isLoading = false } catch { print(error) + showError = true + } + } +} + +// MARK: - Crypto Extension +extension [Crypto] { + + /// Mock Crypto + static var mock: [Crypto] { + (0..<5).map { + Crypto(coinInfo: CoinInfo(id: $0.description, name: "name", fullName: "fullName", imageURL: "imageURL"), display: nil) } } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..302515a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,16 @@ +# Refs: +# - https://docs.codecov.com/docs/common-recipe-list +# - https://docs.codecov.com/docs/codecovyml-reference +# +# After making changes, run below command to validate +# curl --data-binary @codecov.yml https://codecov.io/validate +coverage: + status: + project: + default: + target: 80% + threshold: 1% + patch: + default: + target: 60% + threshold: 1% \ No newline at end of file