iOS 14 WidgetKit Pt 2 | Building COVID-19 API Stats | SiriKit Intent Configurable Parameter
intentTimelineProvider
프로토콜 구현intentConfiguration
구현extension CountryParam {
convenience init(country: Country) {
self.init(identifier: country.id, display: country.name)
self.iso = country.iso
}
static var global: CountryParam {
CountryParam(country: .init(id: "global", name: "Global", iso: ""))
}
}
struct TotalStatsIntentTimelineProvider: IntentTimelineProvider {
typealias Entry = TotalCaseEntry
typealias Intent = SelectCountryIntent
let service = Covid19APIService.shared
func placeholder(in context: Context) -> TotalCaseEntry {
.placeholder
}
func getSnapshot(for configuration: SelectCountryIntent, in context: Context, completion: @escaping (TotalCaseEntry) -> Void) {
if context.isPreview {
completion(.placeholder)
} else {
fetchTotalCaseStats(for: configuration.country ?? CountryParam.global) { result in
switch result {
case .success(let entry):
completion(entry)
case .failure(_):
completion(.placeholder)
}
}
}
}
func getTimeline(for configuration: SelectCountryIntent, in context: Context, completion: @escaping (Timeline<TotalCaseEntry>) -> Void) {
fetchTotalCaseStats(for: configuration.country ?? CountryParam.global) { result in
switch result {
case .success(let entry):
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60 * 10)))
completion(timeline)
case .failure(_):
let timeline = Timeline(entries: [TotalCaseEntry.placeholder], policy: .after(Date().addingTimeInterval(60 * 2)))
completion(timeline)
}
}
}
private func fetchTotalCaseStats(for param: CountryParam, completion: @escaping (Result<TotalCaseEntry, Error>) -> Void) {
guard let id = param.identifier else {
completion(.failure(Covid19APIError.noData))
return
}
switch id {
case CountryParam.global.identifier:
service.getGlobalTotalCount { result in
switch result {
case .success(let stats):
let totalCaseEntry = TotalCaseEntry(date: Date(), totalCount: .init(title: "🌎", confirmed: stats.totalConfirmed, death: stats.totalDeaths, recovered: stats.totalRecovered))
completion(.success(totalCaseEntry))
case .failure(let error):
completion(.failure(error))
}
}
default:
service.getTotalCount(countryId: id) { result in
switch result {
case .success(let totalCase):
let totalCaseEntry = TotalCaseEntry(date: Date(), totalCount: .init(title: param.iso?.flag ?? param.displayString, confirmed: totalCase.confirmed, death: totalCase.deaths, recovered: totalCase.recovered))
completion(.success(totalCaseEntry))
case .failure(let error):
completion(.failure(error))
}
}
}
}
}
fetchTotalCaseStats
는 이전의 글로벌 데이터만을 패치해온 것과 달리 현 시점에서는 국가 데이터 또한 패치import Intents
class IntentHandler: INExtension, SelectCountryIntentHandling {
func provideCountryOptionsCollection(for intent: SelectCountryIntent, with completion: @escaping (INObjectCollection<CountryParam>?, Error?) -> Void) {
Covid19APIService.shared.getAllCountries { result in
switch result {
case .success(let countries):
let countryParams = countries.map({ CountryParam(country: $0)})
completion(INObjectCollection(sections: [
INObjectSection(title: "Global", items: [CountryParam.global]),
INObjectSection(title: "Countries", items: countryParams)
]), nil)
case .failure(_):
completion(INObjectCollection(items: [CountryParam.global]), nil)
}
}
}
override func handler(for intent: INIntent) -> Any {
return self
}
}
INObjectCollection
을 통해 글로벌과 각 국가 목록을 제공import WidgetKit
import SwiftUI
@main
struct Covid19StatsWidget: Widget {
let kind: String = "Covid19StatsWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: SelectCountryIntent.self, provider: TotalStatsIntentTimelineProvider(), content: { entry in
StatsWidgetEntryView(entry: entry)
})
.configurationDisplayName("Covid19-stats")
.description("Show latest lifetime stats")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}
IntentConfiguration
을 통해 SelectCountryIntent
를 파라미터로 넘겨준 뒤 TotalStatsIntentTimelineProvider()
를 통해 제공