• This repository has been archived on 14/Nov/2022
  • Stars
    star
    120
  • Rank 295,983 (Top 6 %)
  • Language
    Swift
  • License
    European Union Pu...
  • Created over 4 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

CoronaMelder - COVID-19 Notification App for iOS

CI

This repository contains the native iOS implementation of the Dutch COVID-19 Notification App CoronaMelder.

Table of Contents

  1. About the app 1.1 App Requirements 1.2 Feature overview 1.3 Dependencies 1.4 Deactivation
  2. Development & Contribution process 2.1 Build Requirements 2.2 Getting started 2.3 Continuous Integration & reproducible builds 2.4 Validate GAEN signature 2.5 SSL Hash Generation 2.6 GAEN API Disclaimer 2.7 Developer Menu
  3. Where to begin development
  4. Background Task
  5. Theming, Strings, Fonts and Images 5.1 Translations 5.1.1 Uploading iOS translations to OneSky 5.1.2 Downloading and importing iOS translations from OneSky
  6. Release Procedure

1. About the app

CoronaMelder is a COVID-19 exposure notification app. Its main use is to notify users of the app when they previously were in (close) contact with a person who later tested positive for the coronavirus. The app does this by using Apple and Google's GAEN framework that uses Bluetooth Low Energy to monitor proximity to other people who have CoronaMelder or any of the other GAEN-based apps installed on their iOS or Android phone.

1.1 App Requirements

The app can run on devices that meet the following requirements.

  • Operating System: iOS 12.5+
  • Internet connection (either Wifi or Mobile Data)
  • Bluetooth

Note: Although the app can be installed on iOS 12.5 and higher, iOS 13 to iOS 13.6 don't support the GAEN features that the app uses. On these iOS versions, CoronaMelder will permanently show a splash screen asking users to update their device to iOS 14.

1.2 Feature Overview

This is a general overview of the features that are available in the app:

  • Onboarding. When the app starts or the first time, the user is informed of the functionality of the app and is asked to give permission for local notifications and the use of the GAEN framework.
  • Share GGD Key. If you have tested positive for coronavirus, you can share you unique identifiers publicly to make sure people who came into contact with you during the infectious period are alerted. (This action can only be performed when the user is called by the GGD).
  • Pausing. The user has the option to pause the contact tracing framework for a set number of hours. This is useful when you are in an environment where other measures have been taken to protect people from the virus or in situations where you are aware that you are interacting with a large number of potentially infected individuals (like in Coronavirus testlocations) and you don't want to receive notifications about this in the future.
  • Q&A Section. In-app information about the functionality of the app, how your privacy is protected and how the app works in detail.
  • Requesting a Coronatest. Information on how to request a Coronatest with links to related websites or phonenumbers. Note that you can not directly request a test within the app itself.
  • Invite people to use the app. Allows the user to share a link to coronamelder.nl to their friends & family using the native iOS share sheet.

Other functions of the app that are automatically performed and are not available through the UI of the app:

  • Contact Tracing using the GAEN framework. The app regularly downloads published keys from a public API and checks if the user ever came into contact with the device from which these infectious keys originated. This work is performed in regularly scheduled background tasks.
  • Decoy Traffic Generation. Since the "Share GGD Key" functionality of the app sends data to the API, any network traffic coming from the app could be construed as a sign that the user was infected. Because this presents a potential breach of privacy, the app regularly schedules similar (decoy) calls to the API to mask this traffic.

1.3 Dependencies

The app uses a number of external dependencies. To make sure we always use the correct version of these dependencies and to mitigate the risk of contaminating the codebase with unwanted code, the source code of these dependencies can be found within the repository in the vendor/ folder.

Here is an overview of what dependencies are used and why.

1.4 Deactivation

Currently the app has been deactivated, ceasing all server communications, revert 1cef3786 to enable communication with the CDN again.

  • CocoaLumberjack Logging framework. When the app is build with debugging enabled, the app logs information to a local logfile that can be exported via the developer menu (sidebar menu that can be accessed by swiping to the left with 2 fingers on the screen). When the app is built for release, this logging is disabled. This means that builds that are published to the App Store do not log any information.

  • Lottie Lottie is used to display JSON-based animations within the app. The animation files can be found in ENCore/Resources/Animations

  • Mockolo Mockolo is an application that can generate mock classes based on protocols. These mock classes are used in our test suites to inject as dependencies in classes that are tested.

  • OpenSSL-for-iPhone This project relies on OpenSSL to validate the KeySet signatures. OpenSSL binaries (v1.1.1d) are included and can be built using make build_openssl. By default the compiled binaries are part of the repo to reduce CI build times. Feel free to compile the binaries yourself. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)

  • Reachability (https://github.com/ashleymills/Reachability.swift) Helper library to determine network connection status and reachability of the API.

  • RxSwift (https://github.com/ReactiveX/RxSwift) Reactive Programming framework. RxSwift is used throughout the app to monitor the state of the App, the GAEN framework, the network etc. and update the UI accordingly.

  • Snapkit (https://github.com/SnapKit/SnapKit) SnapKit simplifies autolayout code within the app by providing a Domain Specific Language to describe the layout of the interface.

  • Swift-snapshot-testing (https://github.com/pointfreeco/swift-snapshot-testing) Used to snapshot test UI components of the app. Snapshot tests are unit tests that generate an image or textual description of the UI and then compare the UI against those references in later test runs to catch (unexpected) visual changes to the interface.

  • SwiftFormat (https://github.com/nicklockwood/SwiftFormat) Command Line tool to standardize code formatting. This can run as a pre-commit githook that can be installed using make install_dev_deps.

  • XcodeGen (https://github.com/yonaskolb/XcodeGen) Command Line tool to generate an Xcode projectfile based on a project.yml description file. The .xcodeproj file that is generated by this tool is not checked into the git repository but has to be created when checking out the code by running make project.

  • ZipFoundation (https://github.com/weichsel/ZIPFoundation) Zip / unzip library. Used to unpack all compressed content we receive from the API.

2. Development & Contribution process

The development team works on the repository in a private fork (for reasons of compliance with existing processes) and shares its work as often as possible. If you plan to make non-trivial changes, we recommend to open an issue beforehand where we can discuss your planned changes. This increases the chance that we might be able to use your contribution (or it avoids doing work if there are reasons why we wouldn't be able to use it).

2.1 Build Requirements

To build and develop the app you need:

  • Xcode 13
  • Xcode Command Line tools (Specifically "Make").
  • Homebrew

2.2 Getting Started

The project uses a Makefile in the root folder of the repository to perform basic actions needed for the development of the app.

  • make dev to install all required libraries and components to develop the app
  • make project to generate the .xcodeproj file for the app. This uses xcodegen and the project.yml file in the root folder of the repository.

Other commands that are available and might be useful during the development process:

  • make clean_snapshots to delete all recorded snapshot test reference file in the repository. This is handy if you want to re-record all snapshots after a big change or after changing the Simulator / OS version you want the snapshots to be recorded on.
  • make push_notification send a mock push notification to the app running in the simulator. The payload for this notification can be found in tools/push/payload.apns

2.3 Continuous Integration & reproducible builds

In order to facilitate CI and reproducible builds (minvws/nl-covid19-notification-app-coordination#6) this codebase can be built using Github Actions.

2.4 Validate GAEN signature

Both Apple and Google validate signatures when processing exposure keysets. These so-called GAEN signatures are generated using a private key on our backend. The public key is sent to Google and Apple and for every bundle-identifier, region-identifier, key-version combination they store a public key.

Examples:

  • nl.rijksoverheid.en.test, region 204, key version v1 -> public key 1
  • nl.rijksoverheid.en, region 204, key version v1 -> public key 2

To validate the generated GAEN signatures on mobile please execute the following steps:

  • Remove all test entitlements from Debug.entitlements, this leaves two entitlements left: com.apple.developer.exposure-notification and the data protection one
  • Compile the app with the USE_DEVELOPER_MENU environment variable set
  • Once ran, open the development menu, select "Erase Local Storage"
  • Validate the correct network environment in the development menu
  • Select "Download and Process New KeySets" from the development menu and look at the debugger
  • Make sure the number of valid keysets in > 0 (it's logged)
  • Make sure you don't see Error: AddFile, ENErrorDomain:5 'Unable to find Application's Exposure Notification Configuration'
  • In case of success you'll see an ENExposureSummary object in the logs

This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)

2.5 SSL Hash Generation

let certificate = """
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
"""
let cert = certificate
    .replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "")
    .replacingOccurrences(of: "-----END CERTIFICATE-----", with: "")
    .replacingOccurrences(of: "\n", with: "")
let certData = Data(base64Encoded: cert)!

guard let secCert = SecCertificateCreateWithData(nil, certData as CFData) else {
    fatalError()
}

print(Certificate(certificate: secCert).signature!)

2.6 GAEN API Disclaimer

Keep in mind that the Apple Exposure Notification API is only accessible by verified health authorities. Other devices trying to access the API using the code in this repository will fail to do so.

2.7 Developer menu

To aid in development of the app, a special hidden side menu was built that can be used to check or change the state of the app. This developer menu is only included in debug or test builds of the app and can be accessed on any screen bij swiping to the left with 2 fingers. Some of the features that can be found in this menu are:

  • Showing screens of the app that can normally only be accessed in specific situations. Like the onboarding screens, the OS update screen or the App Update screen.
  • Changing the state of the GAEN framework. Enabling / disabling it, mimicking a disabled bluetooth connection.
  • Simulating a possible exposure on a specific date.
  • Removing / fetching exposure key sets.
  • Erasing all locally stored data.
  • Sharing the log files of the app.

3. Where to begin development

Even though the architecture of the app is quite straight forward, it can still be hard to grasp the complexity as a new developer. Here are some helpful points in the codebase to start from.

  • MainViewController is a container that contains 2 other viewcontrollers (StatusViewController and MoreInformationViewController) that together represent the main screen of the app. It shows the status of the user / framework (exposed or not exposed) and a menu that gives access to all other features of the app.
  • RootRouter is the location of the startup sequence of the app. It determines which screen is shown when the app starts (onboarding screens, the main screen or something else)
  • BackgroundController handles the scheduling and execution of the background task that is the "heartbeat" of the app. It keeps the app up-to-date and performs exposure checks.

4. Background Task

One of the most important aspects of the app is a regularly scheduled background task that wakes up the app. Since the scheduling of background tasks is normally not guaranteed by iOS, CoronaMelder (like any other GAEN app) is given a special entitlement by Apple (based on the Bundle ID) that allows this scheduling to be more reliable. The task is currently scheduled to run every hour. However, not all work in this task will be performed during each run since they might depend on content from the backend that is updated in other intervals.

This is an overview of the work that is performed by the background task. The scheduling and execution of this work can all be found in the BackgroundController class:

  • removePreviousExposureDateIfNeeded. This cleans up any stored exposure date after 14 days. The exposure date is stored during this period to make it possible for the app to determine if an exposure was detected before.
  • activateExposureController. This prepares the GAEN framework for some of the work following work.
  • updateStatusStream. This makes sure the app is aware of the status of the GAEN framework, including things like the state of Bluetooth or Internet connection on the device.
  • fetchAndProcessKeysets. This downloads the latest keyset batches from the API, submits them to the GAEN framework and checks if the keysets lead to a possible exposure.
  • processPendingUploads. This uploads the users GGD key if a previous attempt by the user failed to do so (for instance due to network issues)
  • sendInactiveFrameworkNotificationIfNeeded. If the app is unable to download or process keysets for 24 hours, the app sends a local notification to the user that indicates that CoronaMelder is not functioning correctly.
  • sendNotificationIfAppShouldUpdate. In some situations, the development team might want to force users to update to the latest version of the app. For instance if there is a bug that prevents the app from functioning correctly. In that case, the appconfig file coming from the API will contain a "iOSMinimumVersion" field. This task checks that minimum version and sends a local notification to the user indicating that they should update the app.
  • updateTreatmentPerspective. This updates the content we show to users if a possible exposure was detected. The content is retrieved from the API and should always be in line with the latest government guidelines.
  • sendExposureReminderNotificationIfNeeded. If we previously detected an exposure, this task periodically reminds the user of this exposure using a local notification.
  • processDecoyRegisterAndStopKeys. This triggers decoy calls to the API that are intended to obfuscate the network traffic. This obfuscation makes sure that it is not possible to determine if somebody has shared a GGD-key (and was infected) based on the network traffic of the app.

5. Theming, Strings, Fonts and Images

To ensure a clean look & feel of the app, we have standardised the use of UI components such as text, fonts and images.

  • Strings are all stored in (translated) .strings files and are only accessed through static properties and functions in Localization.swift. This makes sure we don't make errors in the string's name and allows us to also fallback to a specific base language if the user's system language is not available. See more about the translation of these strings in Managing Translations.
  • Colors and Fonts can be accessed through a Theme object that is propagated throughout the app.
  • Fonts are standardised in ENFoundation/Fonts.swift. The name of the fonts we use follows iOS's semantic font naming conventions.
  • Colors are stored in ENFoundation/Resources/Colors.xcassets and can be accessed through the Theme object. The color assets file contains both Light and Dark appearances because we support Dark mode within the application.
  • Images are stored in ENCore/Resources/Assets.xcassets. These can be accessed by static properties on UIImage itself (like UIImage.chevron). Most images have Light and Dark appearances set to support Dark mode within the app.

5.1 Managing Translations

The content of the app is translated into 10 different languages. These translations are done via OneSky.

5.1.1 Uploading iOS translations to OneSky

  • The App contains 2 Resource folders:
    • Sources/EN/Resources
    • Sources/ENCore/Resources
  • Open OneSky and go to the iOS Project
  • Click the + icon next to Files on the left side of the screen
  • Drag Sources/ENCore/Resources/nl.lproj/Localizable.strings to OneSky
  • Rename Sources/EN/Resources/nl.lproj/Localizable.strings to Main.strings
  • Drag Sources/EN/Resources/nl.lproj/Main.strings to OneSky
  • Always select the deprecate option in OneSky

When updating existing translations by uploading files, make sure the Dutch language is not finalised, otherwise updates won't "overwrite" existing translations. Translations are set to crowdsource mode which means that everyone with access to the url can sign up and add translations for non-finalised strings, so for that reason finalising the translations is advisable too.

5.1.2 Downloading and importing iOS translations from OneSky

  • Check out the master branch in git
  • Go to OneSky -> Translation Overview and click Download Translation on the top right
  • Select all languages and all files
  • Click Export
  • Unzip the downloaded file
  • Open a terminal and go to /tools/scripts
  • Type "sh import-onesky.sh /absolute/path/of/extracted/onesky/folder". This will import the files and copy them to the correct location in the project

6. Release Procedure

The following steps need to be taken to create a version of CoronaMelder and release it to the iOS App Store:

  • Update the version number of the app in the project.yml. The version number is set by the CFBundleShortVersionString property in that file.
  • When the master branch is built, various build versions are automatically sent to Firebase App Distribution to make it easier to test the release.
  • Once the team is satisfied with the quality of the builds on Firebase App Distribution, the build can be sent to TestFlight using the internal release system (Azure DevOps)
  • Once the app hits TestFlight, we perform a manual regression test on the build to make sure the app performs as normal in this production-like environment.
  • If the team determines the app is ready for release, we manually submit the app for App Store Review via App Store Connect. We typically choose a manual release action instead of automatically releasing it on App Store approval to make sure we release the app on a day when we have enough personel to monitor the release.

After release After the app is released to the App Store we perform some actions to tag the release, make it public and verify the integrity of the released code:

  • We tag the release in our internal Git repository with a name matching the release version. for example 2.4.2.
  • We push the code from our internal Git repository to the GitHub repository to make it public.
  • We publish the created tag to GitHub too.
  • We communicate the tag that was pushed to GitHub to an Escrow party (via the internal Product Owner) that can confirm that the code of the released app matches the code that was tagged. This ensures that no malicious changes were made to the code during the release process.

More Repositories

1

nl-covid19-notification-app-design

Design documentatie rondom Covid19 Notificatie App
196
star
2

nl-covid19-data-dashboard

The dashboard provides information on the outbreak and prevalence of COVID-19 in The Netherlands
TypeScript
174
star
3

nl-covid19-notification-app-website

Project website
HTML
173
star
4

nl-covid19-notification-app-android

Android sources for the Dutch Covid19 Notification App
Kotlin
162
star
5

nl-kat-coordination

Repo nl-kat-coordination for minvws
Python
122
star
6

nl-covid19-notification-app-coordination

CoronaMelder Documentation and Repo Overview
HTML
78
star
7

nl-covid19-notification-app-backend

Server-side code for CoronaMelder.
C#
77
star
8

nl-covid19-coronacheck-app-coordination

CoronaCheck Documentation and Repo Overview
TeX
45
star
9

nl-covid19-coronacheck-app-android

Kotlin
44
star
10

nl-covid19-testvac-qr-core

This is a proof of concept (PoC) for creating a QR code system for proving that one has had a valid vaccination record
Go
40
star
11

nl-covid19-coronacheck-app-ios

Swift
25
star
12

nl-covid19-data-backend-processing

Calculations behind the corona dashboard, which provides information on the outbreak and prevalence of COVID-19 in The Netherlands.
TSQL
17
star
13

nl-covid19-notification-app-community-website

Community website COVID-19 notificatieapp
Nix
16
star
14

nl-covid19-notification-lab-android

Android app for experiments with GAEN and Bluetooth protocols
Kotlin
15
star
15

nl-covid19-coronacheck-provider-docs

C#
13
star
16

nl-covid19-coronacheck-website

B2C website CoronaCheck
Vue
10
star
17

nl-kat-boefjes

Repo nl-kat-boefjes for openkat
Python
9
star
18

nl-covid19-coronacheck-backend-bizrules-signing-service

Signing service for DCC and Domestic Certs
Python
8
star
19

nl-covid19-notification-app-statistics

Collection of statistics related to CoronaMelder
8
star
20

nl-rdo-manon

Manon is een design framework
Svelte
8
star
21

pUZI-php

Proficient UZI pass reader in php.
Shell
8
star
22

nl-covid19-coronacheck-app-dgcg-integration

DGCG Integration code for CoronaCheck.
C#
8
star
23

horsebattery

A password generator inspired by https://xkcd.com/936/
PHP
8
star
24

nl-rdo-woo-web

Source code for the (new) WOO platform used by the Ministry of Health, Welfare and Sport
PHP
8
star
25

nl-eHealth-experimental

Collaboration within the eHealth network of the European Communities; experimental concepts for FHIR records for cross border use (vacinnations in different countries, use record of country A in country B, etc).
8
star
26

nl-covid19-coronacheck-mobile-core

Go
7
star
27

nl-kat-octopoes

Repo nl-kat-octopoes for openkat
Python
7
star
28

nl-covid19-coronacheck-idemix

Go
6
star
29

nl-rdo-zammad-api-export

This can be used to export tickets from Zammad to PDF. Each ticket will have its own PDF file.
PHP
6
star
30

nl-covid19-notification-app-provenance

Dutch Corona App - Provenance and Escrow/verification details of (production) builds
Shell
6
star
31

.github

6
star
32

nl-covid19-coronacheck-app-design

5
star
33

nl-kat-rocky

Repo nl-kat-rocky for openkat
HTML
5
star
34

nl-kat-mula

Repo nl-kat-mula for openkat
Python
4
star
35

nl-covid19-notification-app-iccportal

TypeScript
4
star
36

nl-covid19-notification-lab-ios

iOS app for experiments with GAEN and Bluetooth protocols
Swift
4
star
37

nl-covid19-dbco-app-ios

Swift
4
star
38

nl-kat-bytes

Repo nl-kat-bytes for openkat
Python
4
star
39

nl-covid19-coronacheck-app-coronatestprovider-portal-deprecated

3
star
40

go-uzi-middleware

UZI pass middleware for go
Go
3
star
41

nl-covid19-coronacheck-app-bff

Coronatester - backend for frontend
PHP
3
star
42

nl-covid19-dbco-app-coordination

DBCO Documentation and Repo Overview
Shell
2
star
43

nl-covid19-coronacheck-hcert

Go
2
star
44

laravel-crypto

PHP
2
star
45

nl-covid19-dbco-app-design

2
star
46

nl-rdo-max

Multiple Authentication eXchange (MAX) - OIDC to SAML
Python
2
star
47

nl-covid19-coronacheck-web-pdf-tools

JavaScript
2
star
48

nl-covid19-coronacheck-app-provenance

2
star
49

nl-rdo-app-ios-modules

Repo nl-rdo-app-ios-modules for minvws
Swift
2
star
50

generiekefuncties-toestemming

Samenwerken aan de Generieke Functie Toestemming
2
star
51

generiekefuncties-adressering

Samenwerken aan de Generieke Functie Adressering
2
star
52

nl-covid19-coronacheck-app-coronatestprovider-example

PHP
2
star
53

nl-covid19-coronacheck-signature-demo-deprecated

C#
2
star
54

nl-covid19-dbco-portal-design

2
star
55

pUzi-python

Proficient UZI pass reader in python.
Python
2
star
56

nl-rdo-woo-coordination

WOO Platform Coordination
1
star
57

nl-kat-keiko

Repo nl-kat-keiko for minvws
Python
1
star
58

puzi-auth-bundle

Symfony bundle for UZI authentication
PHP
1
star
59

pUZI-laravel

Laravel bundle for UZI authentication
PHP
1
star
60

libsodium-windows-cli

Libsodium Windows CLI
C#
1
star
61

nl-ggd-patientid-auth-provider

Web portal to allow patient id authentication via OIDC
PHP
1
star
62

nl-rdo-openid-connect-php-laravel

Repo nl-rdo-openid-connect-php-laravel for minvws
PHP
1
star
63

generiekefuncties-lokalisatie

Repo generiekefuncties-lokalisatie for minvws
1
star
64

nl-dodo-prikkenzonderafspraak

Repo nl-dodo-prikkenzonderafspraak for minvws
TypeScript
1
star
65

horsebattery-laravel

horsebattery-laravel
PHP
1
star
66

nl-rdo-git-repository-tools

Repo nl-rdo-git-repository-tools for minvws
Swift
1
star
67

nl-covid19-dienst-testen-api

Repo nl-covid19-dienst-testen-api for minvws
PHP
1
star
68

nl-contact-tracing-odds-and-ends

Various tools and utilities
Shell
1
star
69

nl-covid19-dbco-app-backend

PHP
1
star
70

nl-covid19-coronacheck-authentication-service

OpenID / SAML glue logic for DICTU Toegangs Verlenings Service
HTML
1
star
71

nl-ggd-meldportaal-web-public

Repo nl-ggd-meldportaal-web-public for minvws
PHP
1
star
72

rdo-aptly-manage

Repo rdo-aptly-manage-public for minvws
Python
1
star
73

base45-go

Go
1
star
74

nl-dodo-mijnvraagovercorona-web

Repo nl-dodo-mijnvraagovercorona-web-cleanup for minvws
TypeScript
1
star