Skip to content

Commit b6c0f04

Browse files
authored
Merge pull request #223 from DP-3T/develop
Release 1.3.0
2 parents 2b376c9 + e0fe01c commit b6c0f04

21 files changed

+254
-70
lines changed

.github/workflows/build.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ jobs:
1414
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
1515
- uses: actions/checkout@v2
1616

17-
- name: Switch to Xcode 11.6
18-
run: sudo xcode-select --switch /Applications/Xcode_11.6.app
17+
- name: Switch to Xcode 11.7
18+
run: sudo xcode-select --switch /Applications/Xcode_11.7.app
1919

2020
# Generate xcode project
2121
- name: Generate xcodeproj
2222
run: swift package generate-xcodeproj --xcconfig-overrides Sources/Config.xcconfig
2323

2424
# Compile project and run tests
2525
- name: Compile and run tests
26-
run: fastlane scan
26+
run: fastlane scan --disable_concurrent_testing true
2727

2828
sampleapp:
2929
runs-on: macOS-latest
@@ -32,8 +32,8 @@ jobs:
3232
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
3333
- uses: actions/checkout@v2
3434

35-
- name: Switch to Xcode 11.6
36-
run: sudo xcode-select --switch /Applications/Xcode_11.6.app
35+
- name: Switch to Xcode 11.7
36+
run: sudo xcode-select --switch /Applications/Xcode_11.7.app
3737

3838
# Compile sample app for iOS Simulator (no signing)
3939
- name: Compile and run tests

.github/workflows/deploy_to_cocoapods.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
steps:
1414
- uses: actions/checkout@v1
1515

16-
- name: Switch to Xcode 11.6
17-
run: sudo xcode-select --switch /Applications/Xcode_11.6.app
16+
- name: Switch to Xcode 11.7
17+
run: sudo xcode-select --switch /Applications/Xcode_11.7.app
1818

1919
- name: Install Cocoapods
2020
run: gem install cocoapods

.github/workflows/distribute.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jobs:
1111
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
1212
- uses: actions/checkout@v2
1313

14-
- name: Switch to Xcode 11.6
15-
run: sudo xcode-select --switch /Applications/Xcode_11.6.app
14+
- name: Switch to Xcode 11.7
15+
run: sudo xcode-select --switch /Applications/Xcode_11.7.app
1616

1717
- name: Run fastlane build
1818
env:

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog for DP3T-SDK iOS
22

3+
## Version 1.3.0 (29.09.2020)
4+
- Improve last day TEK export handling for iOS > 13.7 (must not disable EN until the following day)
5+
- Introduce new tracing error (.authorizationUnknown) to be able to handle users that did not grant (or revoked authorization by disabling exposure notifications in the iOS settings).
6+
- all apps using this SDK must specify ENDeveloperRegion and ENAPIVersion in their info.plist
7+
38
## Version 1.2.1 (31.08.2020)
49
- ensures that backgroundtask keeps running until outstandingPublishOperation is finished
510

DP3TSDK.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Pod::Spec.new do |spec|
33

44
spec.name = "DP3TSDK"
5-
spec.version = ENV['LIB_VERSION'] || '1.2.1'
5+
spec.version = ENV['LIB_VERSION'] || '1.3'
66
spec.summary = "Open protocol for COVID-19 proximity tracing using Bluetooth Low Energy on mobile devices"
77

88
spec.description = <<-DESC

README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ DP3T-SDK is available through [Cocoapods](https://cocoapods.org/)
7474

7575
```ruby
7676

77-
pod 'DP3TSDK', => '1.2.1'
77+
pod 'DP3TSDK', => '1.3'
7878

7979
```
8080

@@ -164,6 +164,17 @@ The SDK supports iOS 13 background tasks. It uses the provided `exposure-notific
164164

165165
If a `DP3TBackgroundHandler` was passed to the SDK on initialisation it will be called on each background task execution by the SDK.
166166

167+
## Apps using the DP3T-SDK for iOS
168+
Name | Country | Source code | Store | Release-Date
169+
---- | ----------- | ------------- | ------------- | -------------
170+
SwissCovid | Switzerland | [Github](https://github.com/DP-3T/dp3t-app-ios-ch) | [AppStore](https://apps.apple.com/ch/app/swisscovid/id1509275381) | 25. Mai 2020
171+
ASI | Ecuador | [minka.gob.ec](https://minka.gob.ec/asi-ecuador/ios) | [AppStore](https://apps.apple.com/app/id1523594087) | 2. August 2020
172+
Hoia | Estonia | [koodivaramu.eesti.ee](https://koodivaramu.eesti.ee/tehik/hoia/dp3t-app-ios) | [AppStore](https://apps.apple.com/app/id1515441601) | 19. August 2020
173+
STAYAWAY COVID | Portugal | [Github](https://github.com/stayawayinesctec/stayaway-app) | [AppStore](https://apps.apple.com/pt/app/id1519479652) | 28. August 2020
174+
Radar COVID | Spain | [Github](https://github.com/RadarCOVID/radar-covid-ios) | [AppStore](https://apps.apple.com/es/app/radar-covid/id1520443509) |
175+
176+
If your project/country is not yet listed but uses the DP3T-SDK feel free to send a pull-request to add it to the [README](README).
177+
167178
## License
168179

169180
This project is licensed under the terms of the MPL 2 license. See the [LICENSE](LICENSE) file.

SampleApp/DP3TSampleApp/ControlViewController.swift

+14-2
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,15 @@ class ControlViewController: UIViewController {
407407
}
408408

409409
@objc func resetInfectionState() {
410-
try? DP3TTracing.resetInfectionStatus()
410+
do {
411+
try DP3TTracing.resetInfectionStatus()
412+
} catch let error as DP3TTracingError {
413+
let ac = UIAlertController(title: "Error",
414+
message: error.description,
415+
preferredStyle: .alert)
416+
ac.addAction(.init(title: "OK", style: .destructive))
417+
self.present(ac, animated: true)
418+
} catch {}
411419
}
412420

413421
@objc func resetExposureDays() {
@@ -537,13 +545,17 @@ extension DP3TTracingError {
537545
case let .networkingError(error: error):
538546
return "networkingError \(error.localizedDescription)"
539547
case .permissonError:
540-
return "networkingError"
548+
return "permissionError"
549+
case .authorizationUnknown:
550+
return "authorizationUnknown"
541551
case .userAlreadyMarkedAsInfected:
542552
return "userAlreadyMarkedAsInfected"
543553
case let .exposureNotificationError(error: error):
544554
return "exposureNotificationError \(error.localizedDescription)"
545555
case .cancelled:
546556
return "cancelled"
557+
case .infectionStatusNotResettable:
558+
return "infectionStatusNotResettable"
547559
}
548560
}
549561
}

SampleApp/fastlane/Fastfile

+3-9
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,12 @@ platform :ios do
44
desc "Push a new build to AppCenter"
55
lane :distribute do
66

7-
create_keychain(
8-
name: "githubactionci",
9-
password: "githubkeychain",
10-
default_keychain: true,
11-
unlock: true
12-
)
7+
# Create temporary keychain,...
8+
setup_ci
139

1410
match(
1511
type: "development",
16-
readonly: is_ci,
17-
keychain_name: "githubactionci",
18-
keychain_password: "githubkeychain"
12+
readonly: is_ci
1913
)
2014

2115
update_code_signing_settings(

Sources/DP3TSDK/Background/DP3TBackgroundTaskManager.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ class DP3TBackgroundTaskManager {
130130

131131
let outstandingPublishOperation = OutstandingPublishOperation(keyProvider: keyProvider,
132132
serviceClient: serviceClient,
133-
runningInBackground: true)
133+
runningInBackground: true,
134+
tracer: tracer)
134135
completionGroup.enter()
135136
outstandingPublishOperation.completionBlock = {
136137
completionGroup.leave()

Sources/DP3TSDK/Background/OutstandingPublishOperation.swift

+38-2
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,28 @@ class OutstandingPublishOperation: Operation {
2020

2121
private let runningInBackground: Bool
2222

23+
private var defaults: DefaultStorage
24+
25+
private var tracer: Tracer
26+
2327
var now: Date {
2428
.init()
2529
}
2630

2731
static let serialQueue = DispatchQueue(label: "org.dpppt.outstandingPublishQueue")
2832

29-
init(keyProvider: DiagnosisKeysProvider, serviceClient: ExposeeServiceClientProtocol, storage: OutstandingPublishStorage = OutstandingPublishStorage(), runningInBackground: Bool) {
33+
init(keyProvider: DiagnosisKeysProvider,
34+
serviceClient: ExposeeServiceClientProtocol,
35+
storage: OutstandingPublishStorage = OutstandingPublishStorage(),
36+
runningInBackground: Bool,
37+
defaults: DefaultStorage = Default.shared,
38+
tracer: Tracer) {
3039
self.keyProvider = keyProvider
3140
self.serviceClient = serviceClient
3241
self.storage = storage
3342
self.runningInBackground = runningInBackground
43+
self.defaults = defaults
44+
self.tracer = tracer
3445
}
3546

3647
override func main() {
@@ -53,6 +64,12 @@ class OutstandingPublishOperation: Operation {
5364
// ignore outstanding keys older than one day, upload token will be invalid
5465
logger.log("skipping outstanding key %{public}@ because of age and removing publish from storage", op.debugDescription)
5566
storage.remove(publish: op)
67+
68+
// if we are running on iOS > 13.7 we need to disable the tracing
69+
if #available(iOS 13.7, *), !op.fake {
70+
tracer.setEnabled(false, completionHandler: nil)
71+
}
72+
enableResettingOfInfectionStatus(fake: op.fake)
5673
continue
5774
}
5875

@@ -88,7 +105,13 @@ class OutstandingPublishOperation: Operation {
88105
} else {
89106
// get all keys up until today
90107
group.enter()
91-
keyProvider.getDiagnosisKeys(onsetDate: nil, appDesc: serviceClient.descriptor) { result in
108+
109+
// if we are running on iOS > 13.7 we need to disable the tracing after retreiving the key
110+
var disableAfterCompletion = false
111+
if #available(iOS 13.7, *) {
112+
disableAfterCompletion = true
113+
}
114+
keyProvider.getDiagnosisKeys(onsetDate: nil, appDesc: serviceClient.descriptor, disableExposureNotificationAfterCompletion: disableAfterCompletion) { result in
92115
switch result {
93116
case let .success(keys):
94117
let rollingStartNumber = DayDate(date: op.dayToPublish).period
@@ -114,6 +137,8 @@ class OutstandingPublishOperation: Operation {
114137
logger.error("could not retrieve key")
115138
}
116139

140+
enableResettingOfInfectionStatus(fake: op.fake)
141+
117142
logger.log("removing publish operation %{public}@ from storage", op.debugDescription)
118143
storage.remove(publish: op)
119144

@@ -153,12 +178,23 @@ class OutstandingPublishOperation: Operation {
153178
logger.log("removing publish operation %{public}@ from storage", op.debugDescription)
154179
storage.remove(publish: op)
155180

181+
enableResettingOfInfectionStatus(fake: op.fake)
182+
156183
self.cancel()
157184
return
158185
}
159186
logger.log("successfully published %{public}@ removing publish from storage", op.debugDescription)
160187
storage.remove(publish: op)
188+
189+
enableResettingOfInfectionStatus(fake: op.fake)
161190
}
162191
}
163192
}
193+
194+
fileprivate func enableResettingOfInfectionStatus(fake: Bool){
195+
if !fake {
196+
self.logger.log("enabling resetting of infection status")
197+
self.defaults.infectionStatusIsResettable = true
198+
}
199+
}
164200
}

Sources/DP3TSDK/Cryptography/DiagnosisKeysProvider.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protocol DiagnosisKeysProvider: class {
1616

1717
func getFakeKeys(count: Int, startingFrom: Date) -> [CodableDiagnosisKey]
1818

19-
func getDiagnosisKeys(onsetDate: Date?, appDesc: ApplicationDescriptor, completionHandler: @escaping (Result<[CodableDiagnosisKey], DP3TTracingError>) -> Void)
19+
func getDiagnosisKeys(onsetDate: Date?, appDesc: ApplicationDescriptor, disableExposureNotificationAfterCompletion: Bool, completionHandler: @escaping (Result<[CodableDiagnosisKey], DP3TTracingError>) -> Void)
2020
}
2121

2222
extension DiagnosisKeysProvider {
@@ -41,7 +41,7 @@ extension DiagnosisKeysProvider {
4141
fileprivate var logger = Logger(.main, category: "DiagnosisKeysProvider")
4242

4343
extension ENManager: DiagnosisKeysProvider {
44-
func getDiagnosisKeys(onsetDate: Date?, appDesc: ApplicationDescriptor, completionHandler: @escaping (Result<[CodableDiagnosisKey], DP3TTracingError>) -> Void) {
44+
func getDiagnosisKeys(onsetDate: Date?, appDesc: ApplicationDescriptor, disableExposureNotificationAfterCompletion: Bool, completionHandler: @escaping (Result<[CodableDiagnosisKey], DP3TTracingError>) -> Void) {
4545
logger.trace()
4646
if !exposureNotificationEnabled {
4747
// Enable exposure notifications first, if currently not enabled (e.g. last day key)
@@ -55,8 +55,7 @@ extension ENManager: DiagnosisKeysProvider {
5555
}
5656
}
5757
} else {
58-
// Do not disable if it's currently already enabled
59-
self.getDiagnosisKeysInternal(onsetDate: onsetDate, appDesc: appDesc, disableExposureNotificationAfterCompletion: false, completionHandler: completionHandler)
58+
self.getDiagnosisKeysInternal(onsetDate: onsetDate, appDesc: appDesc, disableExposureNotificationAfterCompletion: disableExposureNotificationAfterCompletion, completionHandler: completionHandler)
6059
}
6160
}
6261

Sources/DP3TSDK/DP3TError.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,17 @@ public enum DP3TTracingError: Error {
3131
/// Bluetooth device turned off
3232
case bluetoothTurnedOff
3333

34-
/// Bluetooth permission error
34+
/// User has not been prompted for authorization yet (using startTracing() will prompt the user).
35+
case authorizationUnknown
36+
37+
/// The user either denied authorization or region is not active
3538
case permissonError
3639

3740
/// The user was marked as infected
3841
case userAlreadyMarkedAsInfected
42+
43+
/// the infection status is not resettable currently
44+
case infectionStatusNotResettable
3945
}
4046

4147
/// A set of networking errors returned from the SDK

Sources/DP3TSDK/DP3TSDK.swift

+23-5
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ class DP3TSDK {
178178

179179
let group = DispatchGroup()
180180

181-
let outstandingPublishOperation = OutstandingPublishOperation(keyProvider: diagnosisKeysProvider, serviceClient: service, runningInBackground: runningInBackground)
181+
let outstandingPublishOperation = OutstandingPublishOperation(keyProvider: diagnosisKeysProvider,
182+
serviceClient: service,
183+
runningInBackground: runningInBackground,
184+
tracer: tracer)
182185
group.enter()
183186
outstandingPublishOperation.completionBlock = {
184187
group.leave()
@@ -266,7 +269,7 @@ class DP3TSDK {
266269
}
267270
} else {
268271
group.enter()
269-
diagnosisKeysProvider.getDiagnosisKeys(onsetDate: onset, appDesc: applicationDescriptor) { result in
272+
diagnosisKeysProvider.getDiagnosisKeys(onsetDate: onset, appDesc: applicationDescriptor, disableExposureNotificationAfterCompletion: false) { result in
270273
diagnosisKeysResult = result
271274
group.leave()
272275
}
@@ -294,15 +297,23 @@ class DP3TSDK {
294297
delayedKeyDate: DayDate())
295298

296299
self.service.addExposeeList(model, authentication: authentication) { [weak self] result in
300+
guard let self = self else { return }
297301
DispatchQueue.main.async {
298302
switch result {
299303
case let .success(outstandingPublish):
300304
if !isFakeRequest {
301-
self?.state.infectionStatus = .infected
302-
self?.tracer.setEnabled(false, completionHandler: nil)
305+
self.state.infectionStatus = .infected
306+
if #available(iOS 13.7, *) {
307+
// if we are running on iOS > 13.7 we have to keep EN framework running in order to export the key of the last day
308+
// EN framework will later get disabled
309+
self.log.log("disable resetting of infection status")
310+
self.defaults.infectionStatusIsResettable = false
311+
} else {
312+
self.tracer.setEnabled(false, completionHandler: nil)
313+
}
303314
}
304315

305-
self?.outstandingPublishesStorage.add(outstandingPublish)
316+
self.outstandingPublishesStorage.add(outstandingPublish)
306317

307318
callback(.success(()))
308319
case let .failure(error):
@@ -314,6 +325,10 @@ class DP3TSDK {
314325
}
315326
}
316327

328+
var isInfectionStatusResettable: Bool {
329+
defaults.infectionStatusIsResettable
330+
}
331+
317332
/// reset exposure days
318333
func resetExposureDays() throws {
319334
exposureDayStorage.markExposuresAsDeleted()
@@ -322,6 +337,9 @@ class DP3TSDK {
322337

323338
/// reset the infection status
324339
func resetInfectionStatus() throws {
340+
guard defaults.infectionStatusIsResettable else {
341+
throw DP3TTracingError.infectionStatusNotResettable
342+
}
325343
state.infectionStatus = .healthy
326344
}
327345

Sources/DP3TSDK/DP3TTracing.swift

+10-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private var instance: DP3TSDK!
2828
/// DP3TTracing
2929
public enum DP3TTracing {
3030
/// The current version of the SDK
31-
public static let frameworkVersion: String = "1.2.1"
31+
public static let frameworkVersion: String = "1.3"
3232

3333
/// sets global parameter values which are used throughout the sdk
3434
public static var parameters: DP3TParameters {
@@ -150,6 +150,15 @@ public enum DP3TTracing {
150150
try instance.resetExposureDays()
151151
}
152152

153+
/// checks if infection status is resettable
154+
155+
public static var isInfectionStatusResettable: Bool {
156+
guard instance != nil else {
157+
fatalError("DP3TSDK not initialized call `initialize(with:delegate:)`")
158+
}
159+
return instance.isInfectionStatusResettable
160+
}
161+
153162
/// reset the infection status
154163

155164
public static func resetInfectionStatus() throws {

0 commit comments

Comments
 (0)