Skip to content
This repository has been archived by the owner on Feb 13, 2021. It is now read-only.

Commit

Permalink
Add support for additional data sources (#11)
Browse files Browse the repository at this point in the history
* Add initial data source support

* Country name cleaning
  • Loading branch information
julianschiavo committed Mar 18, 2020
1 parent df06d41 commit 36540ca
Show file tree
Hide file tree
Showing 15 changed files with 520 additions and 141 deletions.
36 changes: 36 additions & 0 deletions Covidcheck.xcodeproj/project.pbxproj
Expand Up @@ -21,6 +21,15 @@
B90821712417C9F600ECE461 /* String-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B928435523EFADC0009E87AE /* String-Extensions.swift */; };
B90821722417C9F600ECE461 /* UIFont-Improved.swift in Sources */ = {isa = PBXBuildFile; fileRef = B924DE5423E3BC3600D1B11B /* UIFont-Improved.swift */; };
B90821732417C9F600ECE461 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B92E3A582414BE2A001D3A78 /* Flags.xcassets */; };
B92471D62421024E00CB94BF /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471D52421024E00CB94BF /* DataSource.swift */; };
B92471D8242102B900CB94BF /* SCMPDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471D7242102B900CB94BF /* SCMPDataSource.swift */; };
B92471D92421071C00CB94BF /* SCMPDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471D7242102B900CB94BF /* SCMPDataSource.swift */; };
B92471DA2421075A00CB94BF /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471D52421024E00CB94BF /* DataSource.swift */; };
B92471DC24210A2300CB94BF /* BingDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471DB24210A2300CB94BF /* BingDataSource.swift */; };
B92471DD24210A2300CB94BF /* BingDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471DB24210A2300CB94BF /* BingDataSource.swift */; };
B92471DF24219F8300CB94BF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471DE24219F8300CB94BF /* Preferences.swift */; };
B92471E024219F8300CB94BF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471DE24219F8300CB94BF /* Preferences.swift */; };
B92471E22421A6AB00CB94BF /* PreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92471E12421A6AB00CB94BF /* PreferenceView.swift */; };
B924DE3523E302B500D1B11B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B924DE3423E302B500D1B11B /* AppDelegate.swift */; };
B924DE3723E302B500D1B11B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B924DE3623E302B500D1B11B /* SceneDelegate.swift */; };
B924DE3B23E302BB00D1B11B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B924DE3A23E302BB00D1B11B /* Assets.xcassets */; };
Expand Down Expand Up @@ -98,6 +107,11 @@
B902958D241DEF130028695E /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = "<group>"; };
B902958F241DF8630028695E /* TokenList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenList.swift; sourceTree = "<group>"; };
B90821622417C7EF00ECE461 /* Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = "<group>"; };
B92471D52421024E00CB94BF /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = "<group>"; };
B92471D7242102B900CB94BF /* SCMPDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SCMPDataSource.swift; sourceTree = "<group>"; };
B92471DB24210A2300CB94BF /* BingDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BingDataSource.swift; sourceTree = "<group>"; };
B92471DE24219F8300CB94BF /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
B92471E12421A6AB00CB94BF /* PreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceView.swift; sourceTree = "<group>"; };
B924DE3123E302B500D1B11B /* Covidcheck.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Covidcheck.app; sourceTree = BUILT_PRODUCTS_DIR; };
B924DE3423E302B500D1B11B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B924DE3623E302B500D1B11B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -174,6 +188,16 @@
path = Preset;
sourceTree = "<group>";
};
B92471D42421020700CB94BF /* Sources */ = {
isa = PBXGroup;
children = (
B92471D52421024E00CB94BF /* DataSource.swift */,
B92471D7242102B900CB94BF /* SCMPDataSource.swift */,
B92471DB24210A2300CB94BF /* BingDataSource.swift */,
);
path = Sources;
sourceTree = "<group>";
};
B924DE2823E302B500D1B11B = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -203,6 +227,8 @@
B924DE3623E302B500D1B11B /* SceneDelegate.swift */,
B92E3A542414AF5C001D3A78 /* iOSBaseView.swift */,
B902958D241DEF130028695E /* InfoView.swift */,
B92471DE24219F8300CB94BF /* Preferences.swift */,
B92471E12421A6AB00CB94BF /* PreferenceView.swift */,
B924DE3A23E302BB00D1B11B /* Assets.xcassets */,
B924DE3F23E302BB00D1B11B /* LaunchScreen.storyboard */,
B924DE4223E302BB00D1B11B /* Info.plist */,
Expand Down Expand Up @@ -271,6 +297,7 @@
children = (
B924DE4823E302FA00D1B11B /* CountryInfo.swift */,
B924DE4A23E3038B00D1B11B /* DataManager.swift */,
B92471D42421020700CB94BF /* Sources */,
);
path = Data;
sourceTree = "<group>";
Expand Down Expand Up @@ -414,23 +441,28 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B92471D8242102B900CB94BF /* SCMPDataSource.swift in Sources */,
B98BC92E24186B8A00E6937D /* WidthCalculation.swift in Sources */,
B924DE4923E302FA00D1B11B /* CountryInfo.swift in Sources */,
B924DE5323E30CC500D1B11B /* RegionCountryData.swift in Sources */,
B928435623EFADC0009E87AE /* String-Extensions.swift in Sources */,
B902958E241DEF130028695E /* InfoView.swift in Sources */,
B986990A24112BFE00947D03 /* Token.swift in Sources */,
B90821632417C7EF00ECE461 /* Images.swift in Sources */,
B92471DF24219F8300CB94BF /* Preferences.swift in Sources */,
B924DE4B23E3038B00D1B11B /* DataManager.swift in Sources */,
B98BC93424186CAE00E6937D /* RegionInfoSection.swift in Sources */,
B92471DC24210A2300CB94BF /* BingDataSource.swift in Sources */,
B924DE3523E302B500D1B11B /* AppDelegate.swift in Sources */,
B92E3A552414AF5C001D3A78 /* iOSBaseView.swift in Sources */,
B924DE5523E3BC3600D1B11B /* UIFont-Improved.swift in Sources */,
B924DE3723E302B500D1B11B /* SceneDelegate.swift in Sources */,
B9029590241DF8630028695E /* TokenList.swift in Sources */,
B92471D62421024E00CB94BF /* DataSource.swift in Sources */,
B924DE4F23E3076900D1B11B /* RegionList.swift in Sources */,
B98BC93124186BA400E6937D /* ListButton.swift in Sources */,
B924DE5123E307BF00D1B11B /* CountryRow.swift in Sources */,
B92471E22421A6AB00CB94BF /* PreferenceView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -444,11 +476,15 @@
B98BC93524186CAE00E6937D /* RegionInfoSection.swift in Sources */,
B98BC93224186BA400E6937D /* ListButton.swift in Sources */,
B98BC92F24186B8A00E6937D /* WidthCalculation.swift in Sources */,
B92471DA2421075A00CB94BF /* DataSource.swift in Sources */,
B908216D2417C9F600ECE461 /* CountryInfo.swift in Sources */,
B92471E024219F8300CB94BF /* Preferences.swift in Sources */,
B908216B2417C9F600ECE461 /* Token.swift in Sources */,
B90821692417C9F600ECE461 /* RegionList.swift in Sources */,
B92471DD24210A2300CB94BF /* BingDataSource.swift in Sources */,
B90821712417C9F600ECE461 /* String-Extensions.swift in Sources */,
B92E3A2C2414AEED001D3A78 /* ComplicationController.swift in Sources */,
B92471D92421071C00CB94BF /* SCMPDataSource.swift in Sources */,
B908216A2417C9F600ECE461 /* CountryRow.swift in Sources */,
B92E3A4A2414AF3F001D3A78 /* WatchBaseView.swift in Sources */,
B9029591241DF8630028695E /* TokenList.swift in Sources */,
Expand Down
6 changes: 5 additions & 1 deletion Covidcheck/AppDelegate.swift
Expand Up @@ -12,7 +12,11 @@ import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
true
if UserDefaults.standard.string(forKey: "CurrentDataSource") == nil {
UserDefaults.standard.set("Bing", forKey: "CurrentDataSource")
}

return true
}

// MARK: UISceneSession Lifecycle
Expand Down
19 changes: 18 additions & 1 deletion Covidcheck/InfoView.swift
Expand Up @@ -15,14 +15,31 @@ struct InfoView: View {
/// Whether the view is currently presented
@Binding var isPresented: Bool

let allPreferences: [Preference] = [DataSourcePreference()]

var body: some View {
NavigationView {
List {
Section(header: Text("ABOUT")) {
Text("Covidcheck is made by Julian Schiavo and open sourced on Github under the Unlicense license.")
}
Section(header: Text("WHAT THE SYMBOLS MEAN")) {
Section(header: Text("PREFERENCES")) {
ForEach(allPreferences, id: \.id) { preference in
NavigationLink(destination: PreferenceView(preference: preference, isPresented: self.$isPresented)) {
HStack {
Text(preference.name)
.foregroundColor(.primary)
Spacer()
Text(preference.value)
.foregroundColor(.secondary)
}
}
}
}
Section(header: Text("SYMBOLS AND ORDERING")) {
VStack(alignment: .leading) {
Text("Regions are ordered by number of current cases (infected but not dead or recovered). Countries are ordered by number of infections.")
.lineLimit(nil)
Token(color: .red, image: Images.casesInfected, text: "Number of people infected")
Token(color: .black, image: Images.casesDead, text: "Number of deaths from COVID-19")
Token(color: .green, image: Images.casesRecovered, text: "Number of recovered cases")
Expand Down
48 changes: 48 additions & 0 deletions Covidcheck/PreferenceView.swift
@@ -0,0 +1,48 @@
//
// PreferenceView.swift
// Covidcheck
//
// Created by Julian Schiavo on 18/3/2020.
// Copyright © 2020 Julian Schiavo. All rights reserved.
//

import SwiftUI

struct PreferenceView: View {

let preference: Preference

@Binding var isPresented: Bool

var body: some View {
if preference.type == .picker {
return PickerPreferenceView(preference: preference, isPresented: $isPresented)
} else {
fatalError("")
}
}
}

struct PickerPreferenceView: View {

let preference: Preference

@Binding var isPresented: Bool

var body: some View {
guard preference.type == .picker else { fatalError("Wrong preference type") }
let pickerOptions = preference.pickerOptions
let pickerOptionNames = Array(pickerOptions.keys).sorted()

return List(pickerOptionNames, id: \.self) { option in
Button(action: {
self.preference.switch(to: pickerOptions[option])
self.isPresented.toggle()
}, label: {
Text(option)
})
}
.listStyle(GroupedListStyle())
.navigationBarTitle(preference.name)
}
}
56 changes: 56 additions & 0 deletions Covidcheck/Preferences.swift
@@ -0,0 +1,56 @@
//
// Preferences.swift
// Covidcheck
//
// Created by Julian Schiavo on 18/3/2020.
// Copyright © 2020 Julian Schiavo. All rights reserved.
//

import Foundation

enum PreferenceType {
case boolean
case picker
}

protocol Preference {
var type: PreferenceType { get }
var id: String { get }
var name: String { get }
var value: String { get }
var pickerOptions: [String: String] { get }

func `switch`(to: String?)
}

struct DataSourcePreference: Preference {
let dataSources: [String: DataSource.Type] = ["SCMP": SCMPDataSource.self, "Bing": BingDataSource.self]
let defaultID = "Bing"

let type = PreferenceType.picker

let id = "DataSource"
let name = "Data Source"

var value: String {
current.name
}

var current: DataSource {
let id = UserDefaults.standard.string(forKey: "CurrentDataSource") ?? defaultID
guard let dataSource = dataSources[id] ?? dataSources[defaultID] else {
fatalError("Unknown data source ID")
}
return dataSource.init()
}

var pickerOptions: [String: String] {
let array = dataSources.mapValues { $0.init().name }.map { ($0.value, $0.key) }
return Dictionary(uniqueKeysWithValues: array)
}

func `switch`(to id: String?) {
UserDefaults.standard.set(id ?? defaultID, forKey: "CurrentDataSource")
DataManager.shared.updateData()
}
}
2 changes: 1 addition & 1 deletion Shared/CountryRow.swift
Expand Up @@ -96,7 +96,7 @@ struct CountryRow: View {

struct CountryRow_Previews: PreviewProvider {
static var previews: some View {
let countryInfo = CountryInfo(name: "Country", infectionCount: 50, deathCount: 10, recoveredCount: 20, difference: 100, lastUpdated: "", comments: "")
let countryInfo = CountryInfo(name: "Country", data: .antarctica, infectionCount: 50, deathCount: 10, recoveredCount: 20, difference: 100, lastUpdated: "", comments: "")
return CountryRow(country: countryInfo, width: .constant(200))
}
}
Expand Down
44 changes: 1 addition & 43 deletions Shared/Data/CountryInfo.swift
Expand Up @@ -8,43 +8,6 @@

import Foundation

/// A model describing the raw country info received from the data source
struct RawCountryInfo: Codable {
var name: String?
var infections: String?
var deaths: String?
var recovered: String?
var comments: String?
var lastUpdated: String?

enum CodingKeys: String, CodingKey {
case name = "country"
case infections = "cases"
case deaths
case recovered
case comments
case lastUpdated = "lastupdated"
}

/// Create a `CountryInfo` object from the raw data, removing illegal characters
var country: CountryInfo {
let filteredInfections = infections?.removingNonNumericCharacters() ?? ""
let infectionCount = Int(filteredInfections) ?? 0

let filteredDeaths = deaths?.removingNonNumericCharacters() ?? ""
let deathCount = Int(filteredDeaths) ?? 0

let filteredRecovered = recovered?.removingNonNumericCharacters() ?? ""
let recoveredCount = Int(filteredRecovered) ?? 0

let nameFixed = name ?? ""
let commentsFixed = comments ?? ""
let lastUpdatedFixed = lastUpdated ?? ""

return CountryInfo(name: nameFixed, infectionCount: infectionCount, deathCount: deathCount, recoveredCount: recoveredCount, lastUpdated: lastUpdatedFixed, comments: commentsFixed)
}
}

/// A model for a region which contains multiple countries
struct RegionInfo: Identifiable {
var id: String { region.name }
Expand All @@ -68,6 +31,7 @@ struct RegionInfo: Identifiable {
struct CountryInfo: Identifiable {
var id: String { name }
let name: String
let data: CountryData?

let infectionCount: Int
let deathCount: Int
Expand All @@ -77,10 +41,4 @@ struct CountryInfo: Identifiable {

let lastUpdated: String
let comments: String

var data: CountryData? {
CountryData(rawValue: name) ??
CountryData(rawValue: name.replacingOccurrences(of: "*", with: "")) ??
CountryData(rawValue: name.replacingOccurrences(of: " ", with: ""))
}
}

0 comments on commit 36540ca

Please sign in to comment.