COVIDSafe code from version 2.6 (#51)

This commit is contained in:
COVIDSafe Support 2021-05-12 17:39:38 -07:00 committed by GitHub
parent 195798ddd5
commit 4d98b6c5e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 910 additions and 144 deletions

49
.circleci/README.md Normal file
View file

@ -0,0 +1,49 @@
## Introduction
CircleCI has a workflow configured to build and upload UAT builds once a merge is done to the dev branch. In the same way, there is a workflow for production and it runs when a merge is done to the master branch.
Fastlane is used to build and distribute the app.
## Fastlane Configuration
The approach taken for the app build and distribution was to provide the `provisioning profile` and the `certificate` manually rather than retrieving it automatically from appstore connect. For this, environment variables are set with the required values for both UAT and PROD.
This aliviates the requirements for authentication and interaction with the appstore leaving upload as the only outstanding task in the lanes. With fastlane's `pilot` we upload the app to TestFlight using application specific password, this action **requires** both `apple_id` and `skip_waiting_for_build_processing`. For more information see https://docs.fastlane.tools/actions/upload_to_testflight/#use-an-application-specific-password-to-upload
Note: Build numbers need to be set correctly on merge otherwise the upload will fail. In order to automate the build number update it is recomended to use an api_key.
### Lanes
There are 2 lanes defined per app build (UAT and PROD):
- import_uat_distribution_certificate: This lane will decode and install the needed provisioning profile and signing certificate for the UAT build.
- import_distribution_certificate: This lane will decode and install the needed provisioning profile and signing certificate for the PROD build.
- beta: This lane will build and upload the UAT build to testflight
- release: This lane will build and upload the PROD build to testflight
## CircleCI Project Configuration
The following environment variables need to be set in the CircleCI web console project configuration.
- APP_STORE_UAT_PROFILE_B64: Base64 encoded provisioning profile to use with UAT builds
- DISTRIBUTION_UAT_P12_B64: Base64 encoded distribution certificate to use with UAT builds
- DISTRIBUTION_UAT_P12_PASSWORD: Password for the UAT certificate above.
- APPLE_ID_UAT: This is the apps apple id. Can be found in appstore connect under App information -> General information.
- APP_STORE_PROFILE_B64: Base64 encoded provisioning profile to use with UAT builds
- DISTRIBUTION_P12_B64: Base64 encoded distribution certificate to use with UAT builds
- DISTRIBUTION_P12_PASSWORD: Password for the UAT certificate above.
- APPLE_ID: This is the apps apple id. Can be found in appstore connect under App information -> General information.
- FASTLANE_USER: App store connect user
- FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: Application specific password generated for Fastlane. This is account specific rather than app specific, share the same for UAT and PROD. For more information on how to generate the password see https://docs.fastlane.tools/best-practices/continuous-integration/#method-3-application-specific-passwords
To get a base64 encoded string of the desired secret run
```
openssl base64 -A -in "filename.extension"
```

69
.circleci/config.yml Normal file
View file

@ -0,0 +1,69 @@
# Circle CI Build config for COVIDSafe
version: 2.1
commands:
prepare-uat:
steps:
- checkout
- run: bundle install
- run: mkdir -pv ~/Library/MobileDevice/Provisioning\ Profiles/
- run: echo ${APP_STORE_UAT_PROFILE_B64} | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/AppStore_UAT.mobileprovision
- run: echo ${DISTRIBUTION_UAT_P12_B64} | base64 --decode > Distribution_UAT.p12
- run: bundle exec fastlane import_uat_distribution_certificate
prepare-release:
steps:
- checkout
- run: bundle install
- run: mkdir -pv ~/Library/MobileDevice/Provisioning\ Profiles/
- run: echo ${APP_STORE_PROFILE_B64} | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/AppStore.mobileprovision
- run: echo ${DISTRIBUTION_P12_B64} | base64 --decode > Distribution.p12
- run: bundle exec fastlane import_distribution_certificate
jobs:
build-uat:
macos:
xcode: 12.4.0 # Specify the Xcode version to use
environment:
FL_OUTPUT_DIR: output
steps:
- prepare-uat
- checkout
- run: bundle exec pod install
- run: bundle exec fastlane beta
- store_artifacts:
path: output
build-release:
macos:
xcode: 12.4.0 # Specify the Xcode version to use
environment:
FL_OUTPUT_DIR: output
steps:
- prepare-release
- checkout
- run: bundle exec pod install
- run: bundle exec fastlane release
- store_artifacts:
path: output
workflows:
build-uat:
jobs:
- build-uat:
filters:
branches:
only: dev
build-release:
jobs:
- build-release:
filters:
branches:
only: master

View file

@ -316,6 +316,8 @@
5BA3624225E463B6002CFF41 /* WebContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BA3624025E463B5002CFF41 /* WebContentView.xib */; };
5BA3624625E4654F002CFF41 /* WebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA3624525E4654E002CFF41 /* WebContentView.swift */; };
5BA3624725E4654F002CFF41 /* WebContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA3624525E4654E002CFF41 /* WebContentView.swift */; };
5BB9B8A0262FE0870056C47E /* KeychainSwift+Singleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB9B89F262FE0870056C47E /* KeychainSwift+Singleton.swift */; };
5BB9B8A1262FE0870056C47E /* KeychainSwift+Singleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB9B89F262FE0870056C47E /* KeychainSwift+Singleton.swift */; };
5BBC571D25526F99005E90AA /* staging-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BBC571C25526F99005E90AA /* staging-Info.plist */; };
5BBE61B125633B6D00B8C983 /* CSGenericContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BBE61B025633B6D00B8C983 /* CSGenericContentView.xib */; };
5BBE61B225633B6D00B8C983 /* CSGenericContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BBE61B025633B6D00B8C983 /* CSGenericContentView.xib */; };
@ -564,6 +566,7 @@
5BA3623325DE133D002CFF41 /* CSGenericErrorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSGenericErrorController.swift; sourceTree = "<group>"; };
5BA3624025E463B5002CFF41 /* WebContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WebContentView.xib; sourceTree = "<group>"; };
5BA3624525E4654E002CFF41 /* WebContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebContentView.swift; sourceTree = "<group>"; };
5BB9B89F262FE0870056C47E /* KeychainSwift+Singleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeychainSwift+Singleton.swift"; sourceTree = "<group>"; };
5BBC571C25526F99005E90AA /* staging-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "staging-Info.plist"; sourceTree = "<group>"; };
5BBE61B025633B6D00B8C983 /* CSGenericContentView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CSGenericContentView.xib; sourceTree = "<group>"; };
5BBE61B625633E8D00B8C983 /* CSGenericViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSGenericViewController.swift; sourceTree = "<group>"; };
@ -800,6 +803,7 @@
isa = PBXGroup;
children = (
B605A7B02427429D008BA819 /* PlistHelper.swift */,
5BB9B89F262FE0870056C47E /* KeychainSwift+Singleton.swift */,
5B900FC02485C4EE00CAA419 /* String+Localization.swift */,
5B69C06525D382AF00DF536D /* String+HtmlAttributed.swift */,
0B1810112431EE610005D11F /* PhoneNumberParser.swift */,
@ -1240,7 +1244,7 @@
5B92D66A243018040049877B /* Sources */,
5B92D6B7243018040049877B /* Frameworks */,
5B92D6B9243018040049877B /* Resources */,
67401E6C50CE0E543E47346A /* [CP] Embed Pods Frameworks */,
34C60B5DD40F4458AB9E6FD0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -1259,7 +1263,7 @@
5DD41D3323DCB03B00FD4AB0 /* Sources */,
5DD41D3423DCB03B00FD4AB0 /* Frameworks */,
5DD41D3523DCB03B00FD4AB0 /* Resources */,
2D8EF319C5D6408D45C0B585 /* [CP] Embed Pods Frameworks */,
DB7F6C492EEEEFCD9ACA09F9 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -1432,24 +1436,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2D8EF319C5D6408D45C0B585 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
67401E6C50CE0E543E47346A /* [CP] Embed Pods Frameworks */ = {
34C60B5DD40F4458AB9E6FD0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1488,6 +1475,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DB7F6C492EEEEFCD9ACA09F9 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CovidSafe/Pods-CovidSafe-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
DC4D19AF69C7C819FCC35F09 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -1576,6 +1580,7 @@
5B92D68A243018040049877B /* Outcome.swift in Sources */,
5B92D68B243018040049877B /* Encounter+EncounterRecord.swift in Sources */,
0B42D0DE2432B39E00E4F44C /* Question2ErrorViewController.swift in Sources */,
5BB9B8A1262FE0870056C47E /* KeychainSwift+Singleton.swift in Sources */,
594E77C024736B77009B8B34 /* EncounterDB.swift in Sources */,
5B728B4724B5667000654ABC /* BLELogViewController.swift in Sources */,
590888B42431B9F5008C9B9F /* UploadDataNavigationController.swift in Sources */,
@ -1796,6 +1801,7 @@
A767D32F242DF1B100DC9E2A /* Errors.swift in Sources */,
5905462A2543E0F6009B82AD /* PayloadDataSupplier.swift in Sources */,
0B22A56B242F286900D1FE60 /* UINavigationBar+Style.swift in Sources */,
5BB9B8A0262FE0870056C47E /* KeychainSwift+Singleton.swift in Sources */,
0B1810122431EE610005D11F /* PhoneNumberParser.swift in Sources */,
1B86119B24303FA200EA4B6B /* Question3ErrorViewController.swift in Sources */,
5BEDEF5425CBC4A900AEEC20 /* RestrictionsViewController.swift in Sources */,
@ -1971,9 +1977,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 121;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 127;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -1981,11 +1987,11 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe;
PRODUCT_NAME = COVIDSafe;
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = COVIDSafe;
SERVICE_UUID = "17E033D3-490E-4BC9-9FE8-2F567643F4D3";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -2055,9 +2061,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 121;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 127;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2065,10 +2071,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe;
PRODUCT_NAME = COVIDSafe;
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = COVIDSafe;
SERVICE_UUID = "B82AB3FC-1595-4F6A-80F0-FE094CC218F9";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_REFLECTION_METADATA_LEVEL = none;
@ -2083,9 +2089,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 120;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 126;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/staging-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2093,13 +2099,13 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
ONLY_ACTIVE_ARCH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe.uat;
PRODUCT_MODULE_NAME = COVIDSafe;
PRODUCT_NAME = "COVIDSafe-staging";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "CovidSafe UAT";
SERVICE_UUID = "CC0AC8B7-03B5-4252-8D84-44D199E16065";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -2113,9 +2119,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 120;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 126;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/staging-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2123,13 +2129,13 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
ONLY_ACTIVE_ARCH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe.uat;
PRODUCT_MODULE_NAME = COVIDSafe;
PRODUCT_NAME = "COVIDSafe-staging";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "CovidSafe UAT";
SERVICE_UUID = "CC0AC8B7-03B5-4252-8D84-44D199E16065";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -2143,9 +2149,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 120;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 126;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/staging-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2153,12 +2159,12 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe.uat;
PRODUCT_MODULE_NAME = COVIDSafe;
PRODUCT_NAME = "COVIDSafe-staging";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "CovidSafe UAT";
SERVICE_UUID = "CC0AC8B7-03B5-4252-8D84-44D199E16065";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_REFLECTION_METADATA_LEVEL = none;
@ -2173,9 +2179,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 120;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 126;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/staging-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2183,12 +2189,12 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe.uat;
PRODUCT_MODULE_NAME = COVIDSafe;
PRODUCT_NAME = "COVIDSafe-staging";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "CovidSafe UAT";
SERVICE_UUID = "CC0AC8B7-03B5-4252-8D84-44D199E16065";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_REFLECTION_METADATA_LEVEL = none;
@ -2321,9 +2327,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 121;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 127;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2331,11 +2337,11 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe;
PRODUCT_NAME = COVIDSafe;
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = COVIDSafe;
SERVICE_UUID = "CC0AC8B7-03B5-4252-8D84-44D199E16065";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -2349,9 +2355,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "CovidSafe/Project Bluetrace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 121;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 127;
DEVELOPMENT_TEAM = 45792XH5L8;
INFOPLIST_FILE = "$(SRCROOT)/CovidSafe/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -2359,10 +2365,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.6;
PRODUCT_BUNDLE_IDENTIFIER = au.gov.health.covidsafe;
PRODUCT_NAME = COVIDSafe;
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = COVIDSafe;
SERVICE_UUID = "B82AB3FC-1595-4F6A-80F0-FE094CC218F9";
SWIFT_OBJC_BRIDGING_HEADER = "CovidSafe/covid-Bridging-Header.h";
SWIFT_REFLECTION_METADATA_LEVEL = none;

View file

@ -31,7 +31,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"

View file

@ -16,10 +16,15 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
return
}
guard let authHeaders = try? authenticatedHeaders() else {
completion(nil, .RequestError)
return
}
CovidNetworking.shared.session.request("\(apiHost)/issueInitialRefreshToken",
method: .post,
encoding: JSONEncoding.default,
headers: authenticatedHeaders
headers: authHeaders
).validate().responseDecodable(of: ChallengeResponse.self) { (response) in
switch response.result {
case .success:
@ -55,7 +60,7 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
return
}
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
guard let token = keychain.get("JWT_TOKEN"),
let refreshToken = keychain.get("REFRESH_TOKEN"),
@ -105,7 +110,7 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
}
static func issueTokensAPI(completion: @escaping (ChallengeResponse?, CovidSafeAPIError?) -> Void) {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
// block api call only if refresh token exists, if it doesn't it means the app should get it for the first time
if UserDefaults.standard.bool(forKey: "ReauthenticationNeededKey") && keychain.get("REFRESH_TOKEN") != nil {
@ -132,13 +137,20 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
completion(response, nil)
}
} else {
let refreshTokenBeforeRefresh = keychain.get("REFRESH_TOKEN")
AuthenticationAPI.issueJWTTokenAPI { (response, error) in
guard let jwt = response?.token,
let refresh = response?.refreshToken,
error == nil else {
// set corrupted
// if the token in the system before the call is different than the one in keychain, this is a concurrency issue, the token was refresh already and that is why this failed. We should not start re-authentication
guard refreshTokenBeforeRefresh == keychain.get("REFRESH_TOKEN") else {
completion(response, .TokenAlreadyRefreshedError)
return
}
// set corrupted only when it has not been changed
UserDefaults.standard.set(true, forKey: "ReauthenticationNeededKey")
completion(response, .TokenExpiredError)
return

View file

@ -20,11 +20,17 @@ class ChangePostcodeAPI: CovidSafeAuthenticatedAPI {
let params = [
"postcode": newPostcode,
]
guard let authHeaders = try? authenticatedHeaders() else {
completion(.RequestError)
return
}
CovidNetworking.shared.session.request("\(apiHost)/device",
method: .post,
parameters: params,
encoding: JSONEncoding.default,
headers: authenticatedHeaders,
headers: authHeaders,
interceptor: CovidRequestRetrier(retries:3)).validate().responseDecodable(of: DeviceResponse.self) { (response) in
switch response.result {
case .success:

View file

@ -56,6 +56,8 @@ enum CovidSafeAPIError: Error {
case ResponseError
case ServerError
case TokenExpiredError
case TokenAlreadyRefreshedError
case MaxRegistrationError
case UnknownError
}
@ -63,18 +65,16 @@ class CovidSafeAuthenticatedAPI {
static var isBusy = false
static var authenticatedHeaders: HTTPHeaders {
get {
let keychain = KeychainSwift()
guard let token = keychain.get("JWT_TOKEN") else {
return []
}
let headers: HTTPHeaders = [
"Authorization": "Bearer \(token)"
]
return headers
static func authenticatedHeaders() throws -> HTTPHeaders {
let keychain = KeychainSwift.shared
guard let token = keychain.get("JWT_TOKEN") else {
throw CovidSafeAPIError.RequestError
}
let headers: HTTPHeaders = [
"Authorization": "Bearer \(token)"
]
return headers
}
static func processUnauthorizedError(_ data: Data) -> CovidSafeAPIError {

View file

@ -20,8 +20,12 @@ final class CovidRequestRetrier: Alamofire.RequestInterceptor {
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
var urlRequest = urlRequest
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
let refreshExists = keychain.get("REFRESH_TOKEN") != nil
// turn off geolock error
UserDefaults.standard.setValue(false, forKey: showGeolockErrorKey)
// prevent authenticated api calls if the re-registration flow has been started
if UserDefaults.standard.bool(forKey: "ReauthenticationNeededKey") &&
refreshExists {
@ -81,13 +85,19 @@ final class CovidRequestRetrier: Alamofire.RequestInterceptor {
return completion(.retryWithDelay(1.0))
}
if let serverHeader = response.headers.first(where: { $0.name == "Server" }),
response.statusCode == 403 && serverHeader.value == "CloudFront" {
UserDefaults.standard.setValue(true, forKey: showGeolockErrorKey)
return completion(.doNotRetryWithError(error))
}
if !triedRefresh &&
(response.statusCode == 403 || response.statusCode == 401) {
(response.statusCode == 401 || response.statusCode == 403) {
triedRefresh = true
retriesExecuted += 1
AuthenticationAPI.issueTokensAPI { (response, authError) in
// this will update the tokens automatically
guard let respError = authError, respError == .TokenExpiredError else {
if let respError = authError, respError == .TokenExpiredError {
completion(.doNotRetryWithError(error))
return
}

View file

@ -26,7 +26,7 @@ class GetTempIdAPI: CovidSafeAuthenticatedAPI {
"version" : apiVersion
]
guard authenticatedHeaders.count > 0 else {
guard let authHeaders = try? authenticatedHeaders(), authHeaders.count > 0 else {
completion(nil, nil, nil, .TokenExpiredError)
return
}
@ -34,7 +34,7 @@ class GetTempIdAPI: CovidSafeAuthenticatedAPI {
CovidNetworking.shared.session.request("\(apiHost)/getTempId",
method: .get,
parameters: params,
headers: authenticatedHeaders,
headers: authHeaders,
interceptor: CovidRequestRetrier(retries: 3)).validate().responseDecodable(of: TempIdResponse.self) { (response) in
switch response.result {
case .success:

View file

@ -65,7 +65,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
var shouldGetMessages = true
let calendar = NSCalendar.current
let currentDate = calendar.startOfDay(for: Date())
let currentDate = Date()
// if the current version is newer than the last version checked, allow messages call
if let currVersionStr = Bundle.main.version, let currVersion = Int(currVersionStr), currVersion > versionChecked {
@ -77,7 +77,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
let components = calendar.dateComponents([.hour], from: lastCheckedDate, to: currentDate)
if let numHours = components.hour {
shouldGetMessages = numHours > 4
shouldGetMessages = numHours >= 4
}
}
@ -109,10 +109,15 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
isBusy = true
guard let authHeaders = try? authenticatedHeaders() else {
completion(nil, .RequestError)
return
}
CovidNetworking.shared.session.request("\(apiHost)/messages",
method: .get,
parameters: params,
headers: authenticatedHeaders,
headers: authHeaders,
interceptor: CovidRequestRetrier(retries: 3)
).validate().responseDecodable(of: MessageResponse.self) { (response) in
switch response.result {
@ -120,7 +125,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
guard let messageResponse = response.value else { return }
// save successful timestamp
let minutesToDefer = Int.random(in: 0..<10)
let minutesToDefer = Int.random(in: 0..<30)
let calendar = NSCalendar.current
let currentDate = Date()
if let deferredDate = calendar.date(byAdding: .minute, value: minutesToDefer, to: currentDate) {

View file

@ -11,7 +11,7 @@ import Alamofire
class PhoneValidationAPI {
static func verifyPhoneNumber(regInfo: RegistrationRequest,
completion: @escaping (String?, Swift.Error?) -> Void) {
completion: @escaping (String?, CovidSafeAPIError?) -> Void) {
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
return
@ -33,8 +33,22 @@ class PhoneValidationAPI {
case .success:
guard let authResponse = response.value else { return }
completion(authResponse.session, nil)
case let .failure(error):
completion(nil, error)
case .failure(_):
var apiError = CovidSafeAPIError.RequestError
if let respData = response.data {
do {
let errorResponse = try JSONDecoder().decode(CovidSafeErrorResponse.self, from: respData)
if errorResponse.message == "MaxRegistrationsReached" {
apiError = .MaxRegistrationError
}
} catch {
// unable to parse response
apiError = .ResponseError
}
}
completion(nil, apiError)
}
}
}

View file

@ -24,10 +24,15 @@ class RestrictionsAPI: CovidSafeAuthenticatedAPI {
let params = ["state": "\(forState.rawValue.lowercased())"]
guard let authHeaders = try? authenticatedHeaders() else {
completion(nil, .RequestError)
return
}
CovidNetworking.shared.session.request("\(apiHost)/restrictions",
method: .get,
parameters: params,
headers: authenticatedHeaders,
headers: authHeaders,
interceptor: CovidRequestRetrier(retries: 3)
).validate().responseDecodable(of: StateRestriction.self) { (response) in
switch response.result {

View file

@ -20,10 +20,15 @@ class StatisticsAPI: CovidSafeAuthenticatedAPI {
let parameters = ["state" : "\(forState.rawValue)"]
guard let authHeaders = try? authenticatedHeaders() else {
completion(nil, .RequestError)
return
}
CovidNetworking.shared.session.request("\(apiHost)/v2/statistics",
method: .get,
parameters: parameters,
headers: authenticatedHeaders,
headers: authHeaders,
interceptor: CovidRequestRetrier(retries: 3)
).validate().responseDecodable(of: StatisticsResponse.self) { (response) in
switch response.result {

View file

@ -21,7 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
setupCoredataDir()
let firstRun = UserDefaults.standard.bool(forKey: "HasBeenLaunched")
if( !firstRun ) {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
keychain.clear()
UserDefaults.standard.set(true, forKey: "HasBeenLaunched")
}

View file

@ -21,6 +21,7 @@
<outlet property="covidStatisticsContainer" destination="eZl-C5-gSv" id="37c-CV-VgS"/>
<outlet property="covidStatisticsSection" destination="Aop-Ae-hRv" id="Qpd-Hv-uA8"/>
<outlet property="inactiveAppSectionView" destination="784-Jf-kOX" id="J3m-Pu-697"/>
<outlet property="inactiveGenericError" destination="5Pd-c8-CYx" id="Fez-q4-Oc5"/>
<outlet property="inactiveSettingsContent" destination="AUW-C2-ven" id="NNP-o9-zkK"/>
<outlet property="inactiveTokenExpiredView" destination="nxM-ji-ttb" id="vVC-KW-yek"/>
<outlet property="locationPermissionsView" destination="u4f-uR-ri3" id="70q-Jr-1cc"/>
@ -127,19 +128,19 @@
</constraints>
</view>
<stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="h36-8c-K2n">
<rect key="frame" x="0.0" y="120" width="414" height="1940.5"/>
<rect key="frame" x="0.0" y="120" width="414" height="2105"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRs-XW-qzv" userLabel="StatusView">
<rect key="frame" x="0.0" y="0.0" width="414" height="1136"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="1300.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rcS-nL-IAO">
<rect key="frame" x="0.0" y="0.0" width="414" height="1124"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="1288.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="784-Jf-kOX" userLabel="InactiveView">
<rect key="frame" x="0.0" y="0.0" width="414" height="696.5"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="861"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="xti-6W-zko" userLabel="Inactive Stack View">
<rect key="frame" x="0.0" y="0.0" width="414" height="696.5"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="861"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4fe-SU-8Q6" userLabel="InActiveHeader">
<rect key="frame" x="0.0" y="0.0" width="414" height="81"/>
@ -246,7 +247,7 @@
</subviews>
<accessibility key="accessibilityConfiguration" label="Bluetooth permissions off">
<accessibilityTraits key="traits" button="YES"/>
<bool key="isElement" value="YES"/>
<bool key="isElement" value="NO"/>
</accessibility>
<constraints>
<constraint firstItem="reL-DQ-aFx" firstAttribute="leading" secondItem="nxM-ji-ttb" secondAttribute="leading" id="DNN-Va-LUA"/>
@ -255,8 +256,69 @@
<constraint firstAttribute="bottom" secondItem="reL-DQ-aFx" secondAttribute="bottom" id="qeE-Hu-OAb"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Pd-c8-CYx" userLabel="Generic Error View">
<rect key="frame" x="0.0" y="369" width="414" height="163.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Nu-a1-6fd" userLabel="Generic Error Bar">
<rect key="frame" x="0.0" y="0.0" width="414" height="163.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Error connectiong to server" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yw4-mY-Pvb">
<rect key="frame" x="16" y="16" width="238.5" height="0.0"/>
<constraints>
<constraint firstAttribute="height" id="K73-MG-bAA"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle3"/>
<color key="textColor" red="0.63921568630000003" green="0.098039215690000001" blue="0.098039215690000001" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Iw2-bN-Bjb">
<rect key="frame" x="16" y="16" width="350" height="131.5"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="NO"/>
</accessibility>
<string key="text">It looks like you may be accessing the internet from outside Australia. This sometimes happens when roaming on an international simcard. Please connect to an Australian network to continue.</string>
<fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
<color key="textColor" red="0.63921568630000003" green="0.098039215690000001" blue="0.098039215690000001" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="geoblock_error_message"/>
</userDefinedRuntimeAttributes>
</label>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="chevron-right-red" highlightedImage="redcross" translatesAutoresizingMaskIntoConstraints="NO" id="JZ8-95-6mR">
<rect key="frame" x="374" y="16" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="5Dg-TP-jeV"/>
<constraint firstAttribute="height" constant="24" id="QDY-8b-rGB"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="yw4-mY-Pvb" firstAttribute="top" secondItem="8Nu-a1-6fd" secondAttribute="top" constant="16" id="8lj-Mp-QPd"/>
<constraint firstItem="Iw2-bN-Bjb" firstAttribute="top" secondItem="yw4-mY-Pvb" secondAttribute="bottom" id="BG1-vf-8aa"/>
<constraint firstItem="yw4-mY-Pvb" firstAttribute="leading" secondItem="8Nu-a1-6fd" secondAttribute="leading" constant="16" id="FB0-Gh-26E"/>
<constraint firstItem="Iw2-bN-Bjb" firstAttribute="leading" secondItem="8Nu-a1-6fd" secondAttribute="leading" constant="16" id="MsQ-XB-0Cp"/>
<constraint firstItem="JZ8-95-6mR" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="yw4-mY-Pvb" secondAttribute="trailing" constant="8" id="N9H-1e-dzZ"/>
<constraint firstAttribute="bottom" secondItem="Iw2-bN-Bjb" secondAttribute="bottom" constant="16" id="SmG-4M-0Ir"/>
<constraint firstAttribute="trailing" secondItem="Iw2-bN-Bjb" secondAttribute="trailing" constant="48" id="YQ6-nh-9k7"/>
<constraint firstAttribute="trailing" secondItem="JZ8-95-6mR" secondAttribute="trailing" constant="16" id="jSl-T3-bbB"/>
<constraint firstItem="JZ8-95-6mR" firstAttribute="top" secondItem="8Nu-a1-6fd" secondAttribute="top" constant="16" id="toG-5o-Kri"/>
</constraints>
</view>
</subviews>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" button="YES"/>
<bool key="isElement" value="NO"/>
</accessibility>
<constraints>
<constraint firstItem="8Nu-a1-6fd" firstAttribute="leading" secondItem="5Pd-c8-CYx" secondAttribute="leading" id="GvM-yl-CrV"/>
<constraint firstAttribute="bottom" secondItem="8Nu-a1-6fd" secondAttribute="bottom" id="gZR-KX-tky"/>
<constraint firstItem="8Nu-a1-6fd" firstAttribute="top" secondItem="5Pd-c8-CYx" secondAttribute="top" id="gl7-zv-NsS"/>
<constraint firstAttribute="trailing" secondItem="8Nu-a1-6fd" secondAttribute="trailing" id="tnp-QS-aoM"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a5D-xk-n0n" userLabel="Bluetooth Permission Off Section">
<rect key="frame" x="0.0" y="369" width="414" height="80.5"/>
<rect key="frame" x="0.0" y="533.5" width="414" height="80.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZoL-pC-0bR" userLabel="Bluetooth Permission Off Bar">
<rect key="frame" x="0.0" y="0.0" width="414" height="80.5"/>
@ -327,7 +389,7 @@
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ers-f9-BFH" userLabel="Bluetooth Status Off Section">
<rect key="frame" x="0.0" y="450.5" width="414" height="101.5"/>
<rect key="frame" x="0.0" y="615" width="414" height="101.5"/>
<subviews>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p5d-dk-foR" userLabel="Bluetooth Status Bar Off">
<rect key="frame" x="0.0" y="0.0" width="414" height="101.5"/>
@ -394,7 +456,7 @@
</connections>
</view>
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="u4f-uR-ri3" userLabel="Location Settings">
<rect key="frame" x="0.0" y="553" width="414" height="143.5"/>
<rect key="frame" x="0.0" y="717.5" width="414" height="143.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aGh-Fm-LNa" userLabel="Location Off bar">
<rect key="frame" x="0.0" y="0.0" width="414" height="142.5"/>
@ -473,7 +535,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="m1D-93-7sF" userLabel="ActiveView">
<rect key="frame" x="0.0" y="696.5" width="414" height="427.5"/>
<rect key="frame" x="0.0" y="861" width="414" height="427.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="981-Cd-Xm3" userLabel="Active Stack View">
<rect key="frame" x="0.0" y="0.0" width="414" height="427.5"/>
@ -592,7 +654,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Aop-Ae-hRv" userLabel="StatisticsViewSection">
<rect key="frame" x="0.0" y="1136" width="414" height="124"/>
<rect key="frame" x="0.0" y="1300.5" width="414" height="124"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="eZl-C5-gSv" userLabel="StatisticsView">
<rect key="frame" x="0.0" y="12" width="414" height="100"/>
@ -610,7 +672,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9cE-NC-A20" userLabel="Help">
<rect key="frame" x="0.0" y="1260" width="414" height="153"/>
<rect key="frame" x="0.0" y="1424.5" width="414" height="153"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="co1-dK-1WU">
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
@ -743,7 +805,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7cN-DY-lc3" userLabel="Change language">
<rect key="frame" x="0.0" y="1413" width="414" height="153"/>
<rect key="frame" x="0.0" y="1577.5" width="414" height="153"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bE9-gT-Hba">
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
@ -876,7 +938,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CGS-rw-uZS" userLabel="PrivacyPolicy">
<rect key="frame" x="0.0" y="1566" width="414" height="153"/>
<rect key="frame" x="0.0" y="1730.5" width="414" height="153"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BBB-zD-Non">
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
@ -885,7 +947,7 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="129"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Privacy Policy" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mFo-iF-cX3">
<rect key="frame" x="72" y="16" width="302" height="24"/>
<rect key="frame" x="72" y="16" width="302" height="77.5"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" none="YES"/>
<bool key="isElement" value="NO"/>
@ -908,7 +970,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Privacy Policy for COVIDSafe Application" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gF9-eh-ER2">
<rect key="frame" x="72" y="40" width="302" height="73"/>
<rect key="frame" x="72" y="93.5" width="302" height="19.5"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" none="YES"/>
<bool key="isElement" value="NO"/>
@ -1009,7 +1071,7 @@
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vs9-rS-UOM" userLabel="Share CovidSafe">
<rect key="frame" x="0.0" y="1719" width="414" height="56"/>
<rect key="frame" x="0.0" y="1883.5" width="414" height="56"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RCa-zU-3Vo">
<rect key="frame" x="0.0" y="12" width="414" height="32"/>
@ -1120,7 +1182,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JSe-D6-hyV" userLabel="Upload Data">
<rect key="frame" x="0.0" y="1719" width="414" height="165.5"/>
<rect key="frame" x="0.0" y="1883.5" width="414" height="165.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8pS-Df-p0U">
<rect key="frame" x="0.0" y="12" width="414" height="141.5"/>
@ -1240,7 +1302,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eZ2-CQ-dtQ" userLabel="Version View">
<rect key="frame" x="0.0" y="1884.5" width="414" height="56"/>
<rect key="frame" x="0.0" y="2049" width="414" height="56"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version number:" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O1w-Sl-OIH" userLabel="Version">
<rect key="frame" x="16" y="12" width="382" height="20"/>

View file

@ -4,15 +4,18 @@ import KeychainSwift
import SafariServices
import Reachability
let reauthenticationNeededKey = "ReauthenticationNeededKey"
let showGeolockErrorKey = "showGeolockErrorKey"
class HomeViewController: UIViewController, HomeDelegate {
private var observer: NSObjectProtocol?
private let reauthenticationNeededKey = "ReauthenticationNeededKey"
@IBOutlet weak var bluetoothStatusOffView: UIView!
@IBOutlet weak var bluetoothPermissionOffView: UIView!
@IBOutlet weak var locationPermissionsView: UIView!
@IBOutlet weak var inactiveSettingsContent: UIView!
@IBOutlet weak var inactiveTokenExpiredView: UIView!
@IBOutlet weak var inactiveGenericError: UIView!
@IBOutlet weak var shareView: UIView!
@IBOutlet weak var inactiveAppSectionView: UIView!
@IBOutlet weak var activeAppSectionView: UIView!
@ -42,8 +45,17 @@ class HomeViewController: UIViewController, HomeDelegate {
var allPermissionOn = true
var registrationNeeded: Bool {
return UserDefaults.standard.bool(forKey: reauthenticationNeededKey)
var showErrorToUser: Bool {
return showRegistrationError ||
showGenericError
}
var showGenericError: Bool {
return UserDefaults.standard.bool(forKey: showGeolockErrorKey)
}
var showRegistrationError: Bool {
return UserDefaults.standard.bool(forKey: reauthenticationNeededKey)
}
var bluetoothStatusOn = true
@ -222,7 +234,7 @@ class HomeViewController: UIViewController, HomeDelegate {
func updateJWTKeychainAccess() {
let hasUpdatedKeychainAccess = UserDefaults.standard.bool(forKey: "HasUpdatedKeychainAccess")
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
if (!hasUpdatedKeychainAccess) {
if let jwt = keychain.get("JWT_TOKEN") {
if (keychain.set(jwt, forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)) {
@ -272,6 +284,7 @@ class HomeViewController: UIViewController, HomeDelegate {
self?.toggleShareView()
self?.toggleStatisticsView()
self?.toggleRegistrationNeededView()
self?.toggleGenericErrorView()
}
})
}
@ -349,37 +362,41 @@ class HomeViewController: UIViewController, HomeDelegate {
}
fileprivate func toggleUploadView() {
toggleViewVisibility(view: self.uploadView, isVisible: !self.didUploadData && !self.registrationNeeded)
toggleViewVisibility(view: self.uploadView, isVisible: !self.didUploadData && !self.showErrorToUser)
}
fileprivate func toggleShareView() {
toggleViewVisibility(view: shareView, isVisible: !registrationNeeded)
toggleViewVisibility(view: shareView, isVisible: !showErrorToUser)
}
fileprivate func toggleStatisticsView() {
toggleViewVisibility(view: covidStatisticsSection, isVisible: !registrationNeeded)
toggleViewVisibility(view: covidStatisticsSection, isVisible: !showErrorToUser)
}
fileprivate func toggleHeaderView() {
toggleViewVisibility(view: inactiveAppSectionView, isVisible: !self.allPermissionOn || registrationNeeded)
toggleViewVisibility(view: inactiveSettingsContent, isVisible: !self.allPermissionOn && !registrationNeeded)
toggleViewVisibility(view: activeAppSectionView, isVisible: self.allPermissionOn)
toggleViewVisibility(view: inactiveAppSectionView, isVisible: !self.allPermissionOn || showErrorToUser)
toggleViewVisibility(view: inactiveSettingsContent, isVisible: !self.allPermissionOn && !showErrorToUser)
toggleViewVisibility(view: activeAppSectionView, isVisible: self.allPermissionOn && !showErrorToUser)
}
fileprivate func toggleBluetoothStatusView() {
toggleViewVisibility(view: bluetoothStatusOffView, isVisible: self.bluetoothPermissionOn && !self.bluetoothStatusOn && !registrationNeeded)
toggleViewVisibility(view: bluetoothStatusOffView, isVisible: self.bluetoothPermissionOn && !self.bluetoothStatusOn && !showErrorToUser)
}
fileprivate func toggleBluetoothPermissionStatusView() {
toggleViewVisibility(view: bluetoothPermissionOffView, isVisible: !self.allPermissionOn && !self.bluetoothPermissionOn && !registrationNeeded)
toggleViewVisibility(view: bluetoothPermissionOffView, isVisible: !self.allPermissionOn && !self.bluetoothPermissionOn && !showErrorToUser)
}
fileprivate func toggleLocationPermissionStatusView() {
toggleViewVisibility(view: locationPermissionsView, isVisible: !allPermissionOn && !locationPermissionOn && (bluetoothPermissionOn && bluetoothStatusOn) && !registrationNeeded)
toggleViewVisibility(view: locationPermissionsView, isVisible: !allPermissionOn && !locationPermissionOn && (bluetoothPermissionOn && bluetoothStatusOn) && !showErrorToUser)
}
fileprivate func toggleRegistrationNeededView() {
toggleViewVisibility(view: inactiveTokenExpiredView, isVisible: registrationNeeded)
toggleViewVisibility(view: inactiveTokenExpiredView, isVisible: showRegistrationError)
}
fileprivate func toggleGenericErrorView() {
toggleViewVisibility(view: inactiveGenericError, isVisible: showGenericError)
}
func attemptTurnOnBluetooth() {

View file

@ -53,7 +53,7 @@ final class InfoViewController: UIViewController {
silentNotificationsCountLabel.text = "\(UserDefaults.standard.integer(forKey: "debugSilentNotificationCount"))"
apnTokenLabel.text = UserDefaults.standard.string(forKey: "deviceTokenForAPN")
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .medium
@ -92,7 +92,7 @@ final class InfoViewController: UIViewController {
}
@IBAction func requestUploadOTP(_ sender: UIButton) {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
guard let jwt = keychain.get("JWT_TOKEN") else {
DLog("Error trying to upload when not logged in")
return
@ -199,7 +199,7 @@ final class InfoViewController: UIViewController {
}
@IBAction func setReauthenticationNeeded(_ sender: Any) {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
keychain.set("corruptedjwt", forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)
}

View file

@ -15,7 +15,7 @@ class InitialScreenViewController: UIViewController, EncounterDBMigrationProgres
var migrationStart: Date?
var isKeychainAvailable = false
var isDisplayTimeElapsed = false
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
var giveupTimer: Timer?
var initialDelayTimer: Timer?
var migrationViewController: UIViewController?

View file

@ -0,0 +1,13 @@
//
// KeychainSwift+Singleton.swift
// CovidSafe
//
// Copyright © 2021 Australian Government. All rights reserved.
//
import Foundation
import KeychainSwift
extension KeychainSwift {
static var shared = KeychainSwift()
}

View file

@ -240,7 +240,7 @@ class OTPViewController: UIViewController, RegistrationHandler {
result(.WrongOTP)
return
}
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
keychain.set(tokenToStore, forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)
keychain.set(refreshToken, forKey: "REFRESH_TOKEN", withAccess: .accessibleAfterFirstUnlock)
UserDefaults.standard.set(true, forKey: "HasUpdatedKeychainAccess")

View file

@ -135,12 +135,34 @@ class PhoneNumberViewController: UIViewController, UITextFieldDelegate, Registra
self?.activityIndicator.stopAnimating()
self?.getOTPButton.isEnabled = true
if let error = error {
let alertMessage = error == .MaxRegistrationError ? "max_registrations".localizedString() : "PhoneVerificationErrorMessage".localizedString()
let errorAlert = UIAlertController(title: "PhoneVerificationErrorTitle".localizedString(),
message: "PhoneVerificationErrorMessage".localizedString(),
message: alertMessage,
preferredStyle: .alert)
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
DLog("Unable to verify phone number")
}))
if error == .MaxRegistrationError {
errorAlert.addAction(UIAlertAction(title: "max_registrations_button1".localizedString(), style: .default, handler: { _ in
DLog("Max registrations error, request deletion tapped")
let deleteUrl = URLHelper.getDataDeletionURL()
guard let url = URL(string: deleteUrl) else {
DLog("Unable to create url")
return
}
let safariVC = SFSafariViewController(url: url)
self?.present(safariVC, animated: true, completion: nil)
}))
errorAlert.addAction(UIAlertAction(title: "max_registration_button2".localizedString(), style: .default, handler: { _ in
DLog("Max registrations error, close alert")
}))
} else {
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
DLog("Unable to verify phone number")
}))
}
self?.present(errorAlert, animated: true)
DLog("Phone number verification error: \(error.localizedDescription)")
return

View file

@ -21,7 +21,7 @@ class QuestionUploadDataViewController: UIViewController {
// MARK: -
private func showUploadDataFlow() {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
setIsLoading(true)
guard let jwt = keychain.get("JWT_TOKEN") else {
DLog("Error trying to upload when not logged in")

View file

@ -11,7 +11,7 @@ import KeychainSwift
class RegistrationIntroViewController: UIViewController {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

View file

@ -64,7 +64,6 @@ class RegistrationSuccessViewController: UIViewController {
if reauthenticating {
dismiss(animated: true, completion: nil)
} else {
let homeVC = HomeViewController(nibName: "HomeView", bundle: nil)
let tabVC = MainTabBarViewController()
self.navigationController?.setViewControllers([tabVC], animated: true)
}

View file

@ -62,4 +62,8 @@ struct URLHelper {
return "\(getHelpURL())#location-permissions"
}
static func getDataDeletionURL() -> String {
return "https://covidsafe-form.service.gov.au"
}
}

View file

@ -12,7 +12,7 @@ import KeychainSwift
final class UploadHelper {
public static func uploadEncounterData(pin: String?, _ result: @escaping (UploadResult, String?) -> Void) {
let keychain = KeychainSwift()
let keychain = KeychainSwift.shared
guard let managedContext = EncounterDB.shared.persistentContainer?.viewContext else {
result(.Failed, "[001]")

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "تابع";
"action_report_an_issue" = "بلِّغ عن مشكلة";
"action_upload_done" = "تمّ";
@ -286,6 +287,8 @@
"factors_intro_2" = "كما يمكن أن تتأثر المصافحات بالعوامل البيئية، بما في ذلك:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "رجوع";
"global_close" = "إغلاق";
"global_double_tap" = "أنقر مرتين للتصحيح";
@ -367,6 +370,9 @@
"location_off" = "الموقع الجغرافي: معطّل";
"location_off_description" = "يتطلب جهاز iphone الخاص بك تشغيل الموقع الجغرافي ليتمكن تطبيق COVIDSafe من العمل. لا يقوم تطبيق COVIDSafe بتتبّع بيانات الموقع الجغرافي أو تخزينها.";
"main_restrictions" = "القيود الرئيسية";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "تحديث تطبيق COVIDSafe قيد الإنجاز. \n\nيُرجى التأكد من عدم إغلاق هاتفك إلى أن يتم اكتمال التحديث.";
"minute" = "دقيقة";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "Συνεχίστε";
"action_report_an_issue" = "Αναφέρετε το πρόβλημα";
"action_upload_done" = "Συνεχίστε";
@ -286,6 +287,8 @@
"factors_intro_2" = "Οι χειραψίες μπορούν επίσης να επηρεαστούν από περιβαλλοντικούς παράγοντες, όπως:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "Πίσω";
"global_close" = "Κλείστε";
"global_double_tap" = "πατήστε δύο φορές για επεξεργασία";
@ -367,6 +370,9 @@
"location_off" = "Τοποθεσία: ΑΠΕΝΕΡΓΟΠΟΙΗΜΕΝΗ";
"location_off_description" = "Το iPhone σας απαιτεί άδεια τοποθεσίας για να λειτουργήσει την COVIDSafe. Η COVIDSafe ΔΕΝ παρακολουθεί ούτε αποθηκεύει τα δεδομένα τοποθεσίας σας.";
"main_restrictions" = "Κύριοι περιορισμοί";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "Η ενημέρωση της COVIDSafe συνεχίζεται. \n\nΒεβαιωθείτε ότι το τηλέφωνό σας δεν είναι απενεργοποιημένο έως ότου ολοκληρωθεί η ενημέρωση.";
"minute" = "Λεπτό";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "Delete this app now";
"action_continue" = "Continue";
"action_report_an_issue" = "Report an issue";
"action_upload_done" = "Done";
@ -286,6 +287,8 @@
"factors_intro_2" = "Handshakes can also be affected by environmental factors, including:";
/* Example: "From 22 to 28 January 2021" */
"from" = "From";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "You may be connected using an internet provider from outside Australia. This could happen when using international data roaming on a non-Australian SIM card.\n\nPlease connect to an Australian network or use a local Wi-Fi internet connection to continue.";
"global_back" = "Back";
"global_close" = "Close";
"global_double_tap" = "double tap to edit";
@ -367,6 +370,9 @@
"location_off" = "Location: OFF";
"location_off_description" = "Your iPhone requires Location permission for COVIDSafe to work. COVIDSafe does NOT track or store your location data.";
"main_restrictions" = "Main restrictions";
"max_registration_button2" = "Cancel";
"max_registrations" = "You've reached the limit to register using the same mobile number.\n\nTo use the same mobile number to register again, you must request to delete your current registration information.";
"max_registrations_button1" = "Request deletion";
/* Splash Screen */
"migration_in_progress" = " COVIDSafe update in progress. \n\n Please make sure you phone is not switched off until the update is complete.";
"minute" = "Minute";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "Continua";
"action_report_an_issue" = "Segnala un problema";
"action_upload_done" = "Eseguito";
@ -286,6 +287,8 @@
"factors_intro_2" = "Anche le strette di mano possono essere influenzate da fattori ambientali:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "Indietro";
"global_close" = "Chiudi";
"global_double_tap" = "Fai un doppio click per modificare";
@ -367,6 +370,9 @@
"location_off" = "Posizione: OFF";
"location_off_description" = "Il tuo iPhone richiede l'autorizzazione alla localizzazione affinché COVIDSafe funzioni. COVIDSafe NON memorizza né utilizza i dati relativi alla tua posizione.";
"main_restrictions" = "Restrizioni principali";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "Aggiornamento COVIDSafe in corso. \n\nAssicurati che il cellulare non sia spento fino al completamento dell'aggiornamento.";
"minute" = "Minuto";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "계속";
"action_report_an_issue" = "문제 신고하기";
"action_upload_done" = "완료";
@ -286,6 +287,8 @@
"factors_intro_2" = "다음과 같은 환경 요인 역시 블루투스 악수에 영향을 미칠 수 있습니다.";
/* Example: "From 22 to 28 January 2021" */
"from" = "부터";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "뒤로";
"global_close" = "닫기";
"global_double_tap" = "두 번 탭해서 수정하세요";
@ -367,6 +370,9 @@
"location_off" = "위치 서비스: 꺼짐";
"location_off_description" = "COVIDSafe가 제대로 실행되기 위해서는 여러분의 아이폰에서 위치 서비스 기능을 허용해야 합니다. COVIDSafe는 여러분의 위치 정보를 추적하거나 저장하지 않습니다.";
"main_restrictions" = "주요 규제 조치";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "COVIDSafe 업데이트 진행 중. \n\n업데이트가 완료될 때까지 휴대폰이 꺼지지 않도록 해주세요.";
"minute" = "분";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "ਜਾਰੀ ਰੱਖੋ";
"action_report_an_issue" = "ਮੁੱਦੇ/ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ|";
"action_upload_done" = "ਕਰ ਦਿੱਤਾ";
@ -286,6 +287,8 @@
"factors_intro_2" = "ਹੱਥ ਮਿਲਾਉਣੇ, ਵਾਤਾਵਰਣਕ ਕਾਰਕਾਂ ਦੁਆਰਾ ਵੀ ਪ੍ਰਭਾਵਤ ਹੋ ਸਕਦੇ ਹਨ, ਜਿੰਨ੍ਹਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹਨ :";
/* Example: "From 22 to 28 January 2021" */
"from" = "ਵੱਲੋਂ";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "ਪਿੱਛੇ";
"global_close" = "ਬੰਦ ਕਰੋ";
"global_double_tap" = "ਸੋਧਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰੋ";
@ -367,6 +370,9 @@
"location_off" = "ਲੋਕੇਸ਼ਨ: ਔਫ";
"location_off_description" = "COVIDSafe ਕੰਮ ਕਰ ਸਕੇ, ਇਸ ਦੇ ਲਈ ਤੁਹਾਡੇ iPhone ਨੂੰ ਲੋਕੇਸ਼ਨ ਆਗਿਆ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। *COVIDSafe ਤੁਹਾਡੇ ਸਥਾਨਕ ਅੰਕੜਿਆਂ ਨੂੰ ਨਹੀਂ ਢੂੰਡਦਾ ਜਾਂ ਸੰਭਾਲਦਾ।*";
"main_restrictions" = "ਮੁੱਖ ਪਾਬੰਦੀਆਂ";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "COVIDSafe ਅੱਪਡੇਟ ਚੱਲ ਰਿਹਾ ਹੈ। \n\nਕਿਰਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡਾ ਫ਼ੋਨ ਤਦ ਤੱਕ ਬੰਦ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਜਦ ਤੱਕ ਅੱਪਡੇਟ ਪੂਰਾ ਨਹੀਂ ਹੋ ਜਾਂਦਾ।";
"minute" = "ਮਿੰਟ";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "Devam";
"action_report_an_issue" = "Sorun bildir";
"action_upload_done" = "Bitti";
@ -286,6 +287,8 @@
"factors_intro_2" = "El sıkışmalar çevresel faktörlerden de etkilenebilir, örneğin:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "Geri";
"global_close" = "Kapat";
"global_double_tap" = "düzeltme yapmak için iki defa tıklayınız";
@ -367,6 +370,9 @@
"location_off" = "Konum: KAPALI";
"location_off_description" = "iPhoneunuz, COVIDSafein çalışması için Konum iznine ihtiyaç duyar. COVIDSafe konum verilerinizi İZLEMEZ veya SAKLAMAZ.";
"main_restrictions" = "Ana kısıtlamalar";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = " COVIDSafe güncellemesi devam ediyor. \n\n Lütfen güncelleme tamamlanana kadar telefonunuzu kapatmayın.";
"minute" = "Dakika";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "Tiếp tục";
"action_report_an_issue" = "Báo cáo sự cố";
"action_upload_done" = "Hoàn tất";
@ -286,6 +287,8 @@
"factors_intro_2" = "Chức năng bắt tay cũng có thể bị ảnh hưởng bởi các yếu tố môi trường, bao gồm:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "Trở lại";
"global_close" = "Đóng";
"global_double_tap" = "nhấn đúp để chỉnh sửa";
@ -367,6 +370,9 @@
"location_off" = "Vị trí: TẮT";
"location_off_description" = "iPhone của bạn yêu cầu quyền truy cập Vị trí để COVIDSafe hoạt động. COVIDSafe KHÔNG theo dõi hoặc lưu trữ dữ liệu vị trí của bạn.";
"main_restrictions" = "Những hạn chế chính";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "COVIDSafe trong tiến trình cập nhật. \n\nVui lòng đảm bảo điện thoại của bạn không bị tắt cho đến khi cập nhật hoàn tất.";
"minute" = "Phút";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "继续";
"action_report_an_issue" = "报告问题";
"action_upload_done" = "完成";
@ -286,6 +287,8 @@
"factors_intro_2" = "蓝牙握手也会受到环境因素的影响,这些因素包括:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "返回";
"global_close" = "关闭";
"global_double_tap" = "双击进行编辑";
@ -367,6 +370,9 @@
"location_off" = "位置:关闭";
"location_off_description" = "您的 iPhone 要求COVIDSafe 获得位置权限才能运行。COVIDSafe 不会跟踪或存储您的位置数据。";
"main_restrictions" = "主要限制措施";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "COVIDSafe正在更新。 \n\n请保持开机状态直至更新完成。";
"minute" = "分钟";

View file

@ -1,3 +1,4 @@
"[test]decommission_message" = "";
"action_continue" = "繼續";
"action_report_an_issue" = "報告問題";
"action_upload_done" = "完成";
@ -286,6 +287,8 @@
"factors_intro_2" = "藍牙握手也可以受到環境因素影響,包括:";
/* Example: "From 22 to 28 January 2021" */
"from" = "";
/* Error message for when a user tries to signup outside of Australia. */
"geoblock_error_message" = "";
"global_back" = "返回";
"global_close" = "關閉";
"global_double_tap" = "點觸兩次來編輯";
@ -367,6 +370,9 @@
"location_off" = "位置:關閉";
"location_off_description" = "你的iPhone要求COVIDSafe獲得位置許可權才能正常運作。COVIDSafe不會跟蹤或者存儲你的位置資料。";
"main_restrictions" = "主要限制措施";
"max_registration_button2" = "";
"max_registrations" = "";
"max_registrations_button1" = "";
/* Splash Screen */
"migration_in_progress" = "正在更新 COVIDSafe。\n\n請保持開機狀態直至更新完成為止。";
"minute" = "分鐘 ";

5
Gemfile Normal file
View file

@ -0,0 +1,5 @@
source "https://rubygems.org"
gem "fastlane"
gem 'cocoapods'
gem 'shellwords'

269
Gemfile.lock Normal file
View file

@ -0,0 +1,269 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.3)
activesupport (5.2.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.1.1)
aws-partitions (1.443.0)
aws-sdk-core (3.113.1)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.43.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.93.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.3)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.0.3)
cocoapods (1.10.1)
addressable (~> 2.6)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.10.1)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.6.6)
nap (~> 1.0)
ruby-macho (~> 1.4)
xcodeproj (>= 1.19.0, < 2.0)
cocoapods-core (1.10.1)
activesupport (> 5.0, < 6)
addressable (~> 2.6)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.4)
cocoapods-downloader (1.4.0)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-trunk (1.5.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
concurrent-ruby (1.1.8)
declarative (0.0.20)
declarative-option (0.1.0)
digest-crc (0.6.3)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.2)
escape (0.0.4)
ethon (0.12.0)
ffi (>= 1.3.0)
excon (0.79.0)
faraday (1.3.0)
faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-net_http (1.0.1)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fastimage (2.2.3)
fastlane (2.180.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.37.0, < 0.39.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
ffi (1.15.0)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.38.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-apis-core (0.3.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.14)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
rexml
signet (~> 0.14)
webrick
google-apis-iamcredentials_v1 (0.3.0)
google-apis-core (~> 0.1)
google-apis-storage_v1 (0.3.0)
google-apis-core (~> 0.1)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.1.0)
google-cloud-storage (1.31.0)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.16.1)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
jmespath (1.4.0)
json (2.5.1)
jwt (2.2.2)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.0)
minitest (5.14.4)
molinillo (0.6.6)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
os (1.1.1)
plist (3.6.0)
public_suffix (4.0.6)
rake (13.0.3)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby-macho (1.4.0)
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
security (0.1.3)
shellwords (0.1.0)
signet (0.15.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.19.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
cocoapods
fastlane
shellwords
BUNDLED WITH
2.1.4

View file

@ -31,3 +31,11 @@ target 'CovidSafe-staging' do
pod 'FlagKit'
pod 'ReachabilitySwift'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
end
end
end

View file

@ -1,8 +1,8 @@
PODS:
- Alamofire (5.0.5)
- Alamofire (5.4.0)
- FlagKit (2.2)
- KeychainSwift (19.0.0)
- lottie-ios (3.1.6)
- lottie-ios (3.1.9)
- ReachabilitySwift (5.0.0)
DEPENDENCIES:
@ -21,12 +21,12 @@ SPEC REPOS:
- ReachabilitySwift
SPEC CHECKSUMS:
Alamofire: df2f8f826963b08b9a870791ad48e07a10090b2e
Alamofire: 3b6a534a3df22db367e4dedeeca73d1ddfcf0e2f
FlagKit: eca7dc89064b0c986302ba9acefad1bf80a435b1
KeychainSwift: a06190cf933ad46b1e0abc3d77d29c06331715c7
lottie-ios: 85ce835dd8c53e02509f20729fc7d6a4e6645a0a
lottie-ios: 3a3758ef5a008e762faec9c9d50a39842f26d124
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
PODFILE CHECKSUM: 5c536190b2facfc3f56594f2313066237ffac6ab
PODFILE CHECKSUM: 3075a84136d3a2d8fde2aeecdc0d0c5147e0306f
COCOAPODS: 1.10.1
COCOAPODS: 1.9.1

6
fastlane/Appfile Normal file
View file

@ -0,0 +1,6 @@
itc_team_id(<idhere>) # App Store Connect Team ID
team_id(<portalTeamId>) # Developer Portal Team ID
# For more information about the Appfile, see:
# https://docs.fastlane.tools/advanced/#appfile

96
fastlane/Fastfile Normal file
View file

@ -0,0 +1,96 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:ios)
platform :ios do
before_all do
setup_circle_ci
end
desc "Install UAT certificate (in CI)"
lane :import_uat_distribution_certificate do
import_certificate(
certificate_path: "Distribution_UAT.p12",
certificate_password: ENV["DISTRIBUTION_UAT_P12_PASSWORD"] || "default",
keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
)
install_provisioning_profile(path: "~/Library/MobileDevice/Provisioning\ Profiles/AppStore_UAT.mobileprovision")
end
desc "Install PROD certificate (in CI)"
lane :import_distribution_certificate do
import_certificate(
certificate_path: "Distribution.p12",
certificate_password: ENV["DISTRIBUTION_P12_PASSWORD"] || "default",
keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
)
install_provisioning_profile(path: "~/Library/MobileDevice/Provisioning\ Profiles/AppStore.mobileprovision")
end
desc "Push a new UAT build to TestFlight"
lane :beta do
# To increment build number is recommended to use api_key
# increment_build_number(
# build_number: app_store_build_number + 1,
# xcodeproj: "CovidSafe.xcodeproj"
# )
gym(workspace: "CovidSafe.xcworkspace",
scheme: "covid-staging",
export_options: {
method: "app-store",
provisioningProfiles: {
"au.gov.health.covidsafe.uat" => "CovidSafe UAT"
}
},
configuration: "Release")
upload_to_testflight(apple_id: ENV["APPLE_ID_UAT"],
skip_submission: true,
skip_waiting_for_build_processing: true)
end
desc "Push a new PROD build to TestFlight"
lane :release do
# To increment build number is recommended to use api_key
# increment_build_number(
# build_number: app_store_build_number + 1,
# xcodeproj: "CovidSafe.xcodeproj"
# )
gym(workspace: "CovidSafe.xcworkspace",
scheme: "covid-production",
export_options: {
method: "app-store",
provisioningProfiles: {
"au.gov.health.covidsafe" => "COVIDSafe"
}
},
configuration: "Release")
upload_to_testflight(apple_id: ENV["APPLE_ID"],
skip_submission: true,
skip_waiting_for_build_processing: true)
end
end

24
fastlane/README.md Normal file
View file

@ -0,0 +1,24 @@
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew install fastlane`
# Available Actions
## iOS
- import_uat_distribution_certificate: This lane will decode and install the needed provisioning profile and signing certificate for the UAT build.
- import_distribution_certificate: This lane will decode and install the needed provisioning profile and signing certificate for the PROD build.
- beta: This lane will build and upload the UAT build to testflight
- release: This lane will build and upload the PROD build to testflight