Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 2.4.0 #517

Merged
merged 33 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2c2c9a0
start deactivation screen & logic implementation
Mar 7, 2022
69e5b8f
continue logic implementation
Mar 7, 2022
fc1287b
assures background tasks not scheduled if app deactivated (needs SDK …
Mar 9, 2022
928528d
update SDK, add correct illu, only endTracing if it's currently active
Mar 11, 2022
5f5023a
Switch certificate pinning to Quovadis Root CA 2 G3
UBaggeler Mar 11, 2022
8e1ed3e
fixes loading of certificate
stmitt Mar 14, 2022
dbb1679
Merge pull request #513 from SwissCovid/feature/certificate-pinning-ca
stmitt Mar 14, 2022
cebdf47
fix build, fix authorization issue
Mar 14, 2022
bc41a5c
Bump version to 2.3.2
Mar 16, 2022
0a56fbd
fix possible null values in infobox-config
Mar 16, 2022
897d9bf
bump version to 2.4.0
Mar 17, 2022
8452f64
fix and add tests according to new ConfigResponse
Mar 17, 2022
8fbb735
Explicitly cancel BG Tasks, Load config on every appstart
Mar 17, 2022
9bf4949
update translations, use correct header
Mar 17, 2022
1b81be8
Config request is already started in every `applicationWillEnterForeg…
UBaggeler Mar 17, 2022
f2a5f7c
Ignore local cache when performing config request
UBaggeler Mar 17, 2022
879c3c2
Revert "bump version to 2.4.0"
UBaggeler Mar 17, 2022
ab9e8b7
Fix crash for unsupported devices
Mar 17, 2022
771da3e
Also show deactivated screen if OS is not supported
Mar 17, 2022
7bddbd4
fix reloading of deactivation screen on every didBecomeActive
Mar 17, 2022
ad81570
Remove comment
UBaggeler Mar 17, 2022
c3b433c
Merge pull request #514 from SwissCovid/feature/deactivation
UBaggeler Mar 17, 2022
a205134
use cached config for daktivation screen
stmitt Mar 18, 2022
57ea274
Merge pull request #515 from SwissCovid/bugfix/use-cached-config-for-…
stmitt Mar 18, 2022
de3f832
removes appclip from target
stmitt Mar 19, 2022
99d6f86
bump version to 2.4.0
stmitt Mar 19, 2022
12f706d
only switch to main thread if not already on main thread
stmitt Mar 20, 2022
b24421b
cancel all pending and delivered notifications on deactivation
stmitt Mar 21, 2022
6f75b6f
guard call to resetBackgroundTaskWarningTriggers
stmitt Mar 21, 2022
aa0c6b6
moves config checks of resetBackgroundTaskWarningTriggers into call s…
stmitt Mar 21, 2022
b030707
fixes unit tests
stmitt Mar 21, 2022
b027e97
Merge branch 'master' into feature/remove-appclip
stmitt Mar 21, 2022
6f52f16
Merge pull request #516 from SwissCovid/feature/remove-appclip
stmitt Mar 21, 2022
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
155 changes: 27 additions & 128 deletions DP3TApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"repositoryURL": "https://github.com/DP-3T/dp3t-sdk-ios.git",
"state": {
"branch": null,
"revision": "f0897a3e013e8ad3e693288f82757068b4ce08f2",
"version": "2.4.0"
"revision": "002a66ec4a6564c3803d1410e955eabdf0556a3c",
"version": "2.5.1"
}
},
{
Expand Down
10 changes: 9 additions & 1 deletion DP3TApp/Logic/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// defer window initialization if app was launched in
// background because of location change
if shouldSetupWindow(application: application, launchOptions: launchOptions) {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
// Only schedule warnings if app is not deactivated
if ConfigManager.currentConfig?.deactivate != true {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
}
setupWindow()
willAppearAfterColdstart(application, coldStart: true, backgroundTime: 0)
}
Expand Down Expand Up @@ -114,6 +117,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

window?.makeKeyAndVisible()

guard !(ConfigManager.currentConfig?.deactivate ?? false) else { return }

if UserStorage.shared.appClipCheckinUrl() != nil {
let checkinOnboardingVC = NSCheckinOnboardingViewController()
checkinOnboardingVC.modalPresentationStyle = .fullScreen
Expand All @@ -140,6 +145,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

// Nothing to do here if device is not supported
guard TracingManager.shared.isSupported else {
startForceUpdateCheck()
return
}

Expand All @@ -162,6 +168,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
_ = jumpToMessageIfRequired(onlyFirst: false)
}

ConfigManager.presentDeactivationIfNeeded(config: ConfigManager.currentConfig, window: window)

startForceUpdateCheck()

FakePublishManager.shared.runTask()
Expand Down
10 changes: 8 additions & 2 deletions DP3TApp/Logic/CheckIn/CheckInManager/CheckInManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ class CheckInManager {
switch result {
case let .success(id):
hasCheckedOutOnce = true
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
// Only schedule warnings if app is not deactivated
if ConfigManager.currentConfig?.deactivate != true {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
}
cc.identifier = id
saveAdditionalInfo(checkIn: cc)
case .failure:
Expand Down Expand Up @@ -122,7 +125,10 @@ class CheckInManager {
switch result {
case let .success(id):
hasCheckedOutOnce = true
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
// Only schedule warnings if app is not deactivated
if ConfigManager.currentConfig?.deactivate != true {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
}
var intervalCheckIn = CheckIn(identifier: id, qrCode: checkIn.qrCode, checkInTime: arrivalTime, venue: checkIn.venue)
intervalCheckIn.checkOutTime = departureTime
saveAdditionalInfo(checkIn: intervalCheckIn)
Expand Down
4 changes: 3 additions & 1 deletion DP3TApp/Logic/CheckIn/ProblematicEventsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class ProblematicEventsManager {
syncNeeded = false
}

if syncNeeded {
let deactivated = (ConfigManager.currentConfig?.deactivate ?? false)

if syncNeeded, !deactivated {
sync { _, _ in }
}
}
Expand Down
27 changes: 27 additions & 0 deletions DP3TApp/Logic/Config/ConfigLoadOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,33 @@ class ConfigLoadOperation: Operation {

ConfigLoadOperation.presentedConfigForVersion = ConfigManager.appVersion
}
} else if let c = config, c.deactivate {
if Thread.isMainThread {
let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController())
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.window?.rootViewController? = vc
} else {
DispatchQueue.main.async {
let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController())
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.window?.rootViewController? = vc
}
}

TracingManager.shared.setBackgroundRefreshEnabled(false)

if TracingManager.shared.isActivated {
TracingManager.shared.endTracing()
UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true
}

NSLocalPush.shared.cancelAllPendingAndDeliveredNotifications()

UBPushManager.shared.setActive(false)
CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0)

UserStorage.shared.appDeactivated = true

} else {
self.cancel()
}
Expand Down
68 changes: 64 additions & 4 deletions DP3TApp/Logic/Config/ConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class ConfigManager: NSObject {
static let configBackgroundValidityInterval: TimeInterval = 60 * 60 * 6 // 6h

static var allowTracing: Bool {
return true
return !(ConfigManager.currentConfig?.deactivate ?? false)
}

// MARK: - Version Numbers
Expand Down Expand Up @@ -80,7 +80,10 @@ class ConfigManager: NSObject {
}
}

static func shouldLoadConfig(backgroundTask: Bool, url: String?, lastConfigUrl: String?, lastConfigLoad: Date?) -> Bool {
static func shouldLoadConfig(backgroundTask: Bool, url: String?, lastConfigUrl: String?, lastConfigLoad: Date?, deactivate: Bool?) -> Bool {
if deactivate ?? false {
return true
}
// if the config url was changed (by OS version or app version changing) load config in anycase
if url != lastConfigUrl {
return true
Expand Down Expand Up @@ -110,12 +113,14 @@ class ConfigManager: NSObject {
}

public func loadConfig(backgroundTask: Bool, completion: @escaping (ConfigResponseBody?) -> Void) {
let request = Endpoint.config(appversion: ConfigManager.appVersion, osversion: ConfigManager.osVersion, buildnr: ConfigManager.buildNumber).request()
var request = Endpoint.config(appversion: ConfigManager.appVersion, osversion: ConfigManager.osVersion, buildnr: ConfigManager.buildNumber).request()
request.cachePolicy = .reloadIgnoringLocalCacheData

guard Self.shouldLoadConfig(backgroundTask: backgroundTask,
url: request.url?.absoluteString,
lastConfigUrl: Self.lastConfigUrl,
lastConfigLoad: Self.lastConfigLoad) else {
lastConfigLoad: Self.lastConfigLoad,
deactivate: ConfigManager.currentConfig?.deactivate) else {
Logger.log("Skipping config load request and returning from cache", appState: true)
completion(Self.currentConfig)
return
Expand Down Expand Up @@ -160,6 +165,7 @@ class ConfigManager: NSObject {
loadConfig(backgroundTask: false) { config in
// self must be strong
if let config = config {
Self.presentDeactivationIfNeeded(config: config, window: window)
self.presentAlertIfNeeded(config: config, window: window)
}
}
Expand Down Expand Up @@ -210,6 +216,60 @@ class ConfigManager: NSObject {
}
}
}

public static func presentDeactivationIfNeeded(config: ConfigResponseBody?, window: UIWindow? = nil) {
guard let config = config else { return }

if config.deactivate {
NSLocalPush.shared.cancelAllPendingAndDeliveredNotifications()

if (window?.rootViewController as? UINavigationController)?.visibleViewController is NSDeactivatedInfoViewController {
// The NSDeactivatedInfoViewController is already visible
return
}

let vc = NSNavigationController(rootViewController: NSDeactivatedInfoViewController())

if let window = window {
window.rootViewController = vc
}

TracingManager.shared.setBackgroundRefreshEnabled(false)

if TracingManager.shared.isActivated {
UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true
TracingManager.shared.endTracing()
}

UBPushManager.shared.setActive(false)
CheckInManager.shared.cleanUpOldData(maxDaysToKeep: 0)

UserStorage.shared.appDeactivated = true

} else if !config.deactivate, UserStorage.shared.appDeactivated {
if TracingManager.shared.isAuthorized, UserStorage.shared.tracingWasActivatedBeforeDeaktivation {
TracingManager.shared.startTracing()
}

TracingManager.shared.setBackgroundRefreshEnabled(true)

if TracingManager.shared.isSupported {
window?.rootViewController = (UIApplication.shared.delegate as? AppDelegate)?.navigationController

if !UserStorage.shared.hasCompletedOnboarding {
let onboardingViewController = NSOnboardingViewController()
onboardingViewController.modalPresentationStyle = .fullScreen
window?.rootViewController?.present(onboardingViewController, animated: false)
}

} else {
window?.rootViewController = NSUnsupportedOSViewController()
}

UserStorage.shared.appDeactivated = false
UserStorage.shared.tracingWasActivatedBeforeDeaktivation = true
}
}
}

private extension UIViewController {
Expand Down
5 changes: 4 additions & 1 deletion DP3TApp/Logic/Config/ConfigResponseBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ class ConfigResponseBody: UBCodable {
public var showVaccinationInfo = false
public let vaccinationBookingInfo: LocalizedValue<VaccinationBookingInfo>

public let deactivate: Bool
public let deactivationMessage: LocalizedValue<InfoBox>?

class InfoBox: UBCodable {
let title, msg: String
let title, msg: String?
let url: URL?
let urlTitle: String?
let infoId: String?
Expand Down
6 changes: 6 additions & 0 deletions DP3TApp/Logic/NSLocalPush.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protocol UserNotificationCenter {
func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?)
func removeDeliveredNotifications(withIdentifiers identifiers: [String])
func removeAllDeliveredNotifications()
func removeAllPendingNotificationRequests()
func removePendingNotificationRequests(withIdentifiers identifiers: [String])
func setNotificationCategories(_ categories: Set<UNNotificationCategory>)
}
Expand Down Expand Up @@ -140,6 +141,11 @@ class NSLocalPush: NSObject, LocalPushProtocol {
center.removePendingNotificationRequests(withIdentifiers: allIdentifier)
}

func cancelAllPendingAndDeliveredNotifications() {
center.removeAllDeliveredNotifications()
center.removeAllPendingNotificationRequests()
}

var now: Date {
.init()
}
Expand Down
12 changes: 6 additions & 6 deletions DP3TApp/Logic/Networking/Base/URLSession+pinning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ class CertificateEvaluator: NSObject, URLSessionDelegate {

let bundle = Bundle.main

// all these hosts have a seperate certificate
let hosts = ["www.pt1.bfs.admin.ch",
"www.pt1-d.bfs.admin.ch",
"www.pt1-a.bfs.admin.ch",
Expand All @@ -96,16 +95,17 @@ class CertificateEvaluator: NSObject, URLSessionDelegate {
"www.pt-a.bfs.admin.ch",
"www.pt-t.bfs.admin.ch",
"www.pt.bfs.admin.ch"]
for host in hosts {
if let certificate = bundle.getCertificate(with: host) {
let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [certificate],

if let QuovadisRootCA = bundle.getCertificate(with: "QuoVadis-Root-CA-2-G3", fileExtension: "cer") {
for host in hosts {
let evaluator = UBPinnedCertificatesTrustEvaluator(certificates: [QuovadisRootCA],
acceptSelfSignedCertificates: true,
performDefaultValidation: false,
validateHost: true)
evaluators[host] = evaluator
} else {
assertionFailure("Could not load certificate for pinned host")
}
} else {
assertionFailure("Could not load certificate for pinned host")
}

return UBServerTrustManager(evaluators: evaluators)
Expand Down
5 changes: 4 additions & 1 deletion DP3TApp/Logic/Push/NSPushHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ class NSPushHandler: UBPushHandler {
}

// data are updated -> reschedule background task warning triggers
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
// Only schedule warnings if app is not deactivated
if ConfigManager.currentConfig?.deactivate != true {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
}
}

if self.backgroundTask != .invalid {
Expand Down
9 changes: 7 additions & 2 deletions DP3TApp/Logic/Tracing/DatabaseSyncer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ class DatabaseSyncer {
private var databaseSyncInterval: TimeInterval = 10

func syncDatabaseIfNeeded(completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
let deactivated = (ConfigManager.currentConfig?.deactivate ?? false)
guard !databaseIsSyncing,
UserStorage.shared.hasCompletedOnboarding else {
UserStorage.shared.hasCompletedOnboarding,
!deactivated else {
completionHandler?(.noData)
return
}
Expand Down Expand Up @@ -129,7 +131,10 @@ class DatabaseSyncer {
UIStateManager.shared.syncError = nil
}

NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
// Only schedule warnings if app is not deactivated
if ConfigManager.currentConfig?.deactivate != true {
NSLocalPush.shared.resetBackgroundTaskWarningTriggers()
}

// reload status, user could have been exposed
TracingManager.shared.updateStatus(completion: nil)
Expand Down
Loading
Loading