React Native Exposure Notification Service is a react native module, which provides a common interface to Apple/Google's Exposure Notification APIs
For more on contact tracing see:
To integrate with your react-native app, edit your apps package.json and add the following dependency:
{
"dependencies": {
"react-native-exposure-notification-service": "git+https://github.com/nearform/react-native-exposure-notification.git"
}
}
or from the terminal type:
yarn add git+https://github.com/nearform/react-native-exposure-notification.git
Note: you must have access to this git repository, and supply a generated personal access token with adequate rights.
React Native Exposure Notifications uses autolinking to allow your project discover and use this code.
On Android there are no further steps.
CocoaPods on iOS needs this extra step:
cd ios && pod install && cd ..
import ExposureNotificationModule from 'react-native-exposure-notification-service'
ExposureNotificationModule.start()
const canSupport = await ExposureNotificationModule.canSupport()
Used to check if the device can support the relevant exposure notification API. This returns a promise that resolves a boolean determining if the device can support tracing or not.
exposure api is available on the device.
const supported = await ExposureNotificationModule.isSupported()
Used to check if the device has the contact tracing APIs installed. This returns a promise that resolves with true if contact tracing is supported.
const enabled = await ExposureNotificationModule.exposureEnabled()
Use to check if the contact tracing is enabled. This returns a promise that resolves with true if contact tracing is enabled.
Note: On android, if enabled is true, tracing has started.
const authorised = await ExposureNotificationModule.isAuthorised()
Use to check if the user has authorised contact tracing. Calling this method will NOT trigger an authorisation request. This returns a promise that resolves with a string representing the current authorisation state and can be one of: granted
, denied
, blocked
, unavailable
or unknown
const authorised = await ExposureNotificationModule.authoriseExposure()
Use to trigger an authorisation dialogue. This returns a promise that resolves with true if the user has authorised contact tracing, if they denied the request then false is returned.
ExposureNotificationModule.configure(options)
Use to configure the module. This method is synchronous, and should be called before start etc. It takes an options object as a parameter with the following properties:
exposureCheckFrequency
: a number representing the period between exposure downloads in minutesserverURL
: a string representing the the server api url (should not have trailing /)authToken
: a string representing the current authorization tokenrefreshToken
: a string representing a token used to refresh the authorization tokenstoreExposuresFor
: a number representing the number of days to store data forfileLimit
: a number representing the file limitversion
: a string representing the app version numbernotificationTitle
: a string representing the title for positive exposure notifications popup,notificationDesc
: a string representing the description for positive exposure notifications popup,callbackNumber
: a string representing the phone number of a user if opted into automatic callback on positive exposure notification,analyticsOptin
: a boolean representing whether the user opted in or not
const started = await ExposureNotificationModule.start()
Use to start exposure notifications. This method should only be called when canSupport(), isSupported() and isAuthorised() all return/resolve positive values and after configure() has been called.
A promise is returned and will resolve to true after a successful start, otherwise it will resolve to false.
const status = await ExposureNotificationModule.status()
Used to get the current start status. This method returns a promise that resolves to a map containing 2 keys state
and type
both with string values.
The state can return as active
, disabled
, unavailable
or unknown
, and if set to disabled
will contain the type presenting the reason why.
State changes also trigger the event onStatusChanged
.
ExposureNotificationModule.stop()
Used to stop contact tracing and all scheduled tasks. Exposure notifications must be authorised again after this method is called.
const result = await ExposureNotificationModule.deleteAllData()
Used to delete all app related data including config & exposure data. This returns a promise that resolves true if all data is successfully removed.
const result = await ExposureNotificationModule.deleteExposureData()
Used to deletes exposure data but leaves configuration data intact. This returns a promise that resolves true if exposure data is successfully removed.
const keys = await ExposureNotificationModule.getDiagnosisKeys()
Used to retrieve a devices own diagnosis keys (typically all keys before today within a 14 day window). This returns a promise that resolves to an array of maps containing the key keyData
, encoded as a base64 string. This key should be used in export files for exposure matching. If the user denies the request, the returned promise will reject.
Note: this will trigger a dialog from the underlying exposure notifications API, requesting permission from the device user.
ExposureNotificationModule.checkExposure()
Used to manually check exposure during testing. Typically checkExposure is performed in background on schedule specified in configure. This facilitates an immediate check.
On successful matches, this will raise a notification to the user, and also raise an exposure
event to the app
const contacts = await ExposureNotificationModule.getCloseContacts()
Used to retrieve the summary array of matches recorded. This async function returns a promise that resolves to a array of maps with the following keys:
exposureAlertDate
attenuationDurations
daysSinceLastExposure
matchedKeyCount
maxRiskScore
summationRiskScore
const result = await ExposureNotificationModule.triggerUpdate()
Used to trigger play services update should the user be using a version older than 201817017
Create an emitter
object using the ExposureNotificationModule
import { NativeEventEmitter } from 'react-native'
import ExposureNotificationModule from 'react-native-exposure-notifications'
const emitter = new NativeEventEmitter(ExposureNotificationModule)
In a component or custom hook, you could use an effect to subscribe to the native module's events as follows:
useEffect(() => {
function handleEvent(ev) {
if(ev.exposure) {
console.log('You have come in contact with someone diagnosed with COVID-19')
}
// handle other events...
}
const subscription = emitter.addListener('exposureEvent', handleEvent)
return () => {
subscription.remove()
emitter.removeListener('exposureEvent', handleEvent)
}
}, [])
There are two major events:
exposure
: fires when an exposure match event happens, which could be used to update the UI (contains a string)onStatusChange
: fires when the exposure tracing status changes. This event is a map map containing 2 keysstate
andtype
both with string values.
When exposure
fires, getCloseContacts()
would typically be called to retrieve the summary information.
When onStatusChange
fires, it will contain the same data returned from a call to status()
Note: Other events may fire, depending on the platform and whether debug/release. These are mostly for debug and error logging only and should not be used for triggering application logic.
A test application to run the methods above, can be found under the test-app
folder.
To run this, from the terminal:
cd test-app
yarn
yarn start
- and in a separate terminal window
yarn android
oryarn ios
Typically, it is better to run the test application on a device rather than a simulator.
Note: The check exposure function will not succeed unless connected to a valid server, if you have access to a valid server, modify ./test-app/config.js
with your settings.
To debug in android you will need to generate a debug keystore file, from the terminal execute:
cd android/app
then
keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000
cd ../..
When building/running an app using the native module, several issues can arise.
The Exposure Notification API provided by Google uses the Nearby API installed with Google Play Services.
This API is being rolled out by Google so that as many devices as possible support the API.
The minimum android version required is 23 (marshmallow).
The application performs a test to determine if the required API is available on the device using the isSupported()
method. If it is not installed google play services must be updated with the Nearby API.
If you have trouble with this, try triggerUpdate()
to see if Play Services can be updated or update manually.
Applications using the Exposure API should be government agencies, and so not all applications can access the API directly.
If using minify when building an .apk on android, classes are often obfuscated, in which case some errors can arise when using reflection.
To keep required classes from being obfuscated, edit you proguard-rules.pro
and add the following keep rules
-keep public class com.horcrux.svg.** {*;}
-keep class com.google.crypto.tink.** { *; }
-keep class net.sqlcipher.** { *; }
-keep class net.sqlcipher.database.* { *; }
-keep class * extends androidx.room.RoomDatabase
-keep class * extends com.google.auto
-keep class org.checkerframework.checker.nullness.qual.** { *; }
If you make changes to the native module, and are referencing the package in your project, you may need to re-install it occasionally. You can do this by pointing at a different commit, branch or version in your package.json.
You can link to commit using #<commit>
at the end of the git reference, or add #<feature>\/<branch>
or if versioning use, #semver:<semver>
(if the repo has any tags or refs matching that range). If none of these are specified, then the master branch is used.
eg.
{
"dependencies": {
"react-native-exposure-notification": "git+https://github.com/nearform/react-native-exposure-notification.git#semver:^1.0"
}
}
If you are developing and make changes to a branch, and are not seeing changes being reflected in your react-native app, you can try reinstall the module as follows:
yarn remove react-native-exposure-notification && yarn add git+https://github.com/nearform/react-native-exposure-notification.git`
You can also link to the module directly on your file system if developing locally:
yarn add file:<path-to-module>`
In order to upload/download diagnosis keys for exposure notifications, an applications using this module needs to connect to a server that accepts upload of tokens, and packages them into valid export zip files.
React Native Exposure Notification is MIT licensed, as found in the LICENSE file.