Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ To better foster an open, innovative and inclusive community please refer to our

### Report a bug

If you think you've found a bug, please log a new issue in the [GitHub issue
tracker. When filing issues, please use our [issue
If you think you've found a bug, please log a new issue in the
[GitHub issue tracker](https://github.com/nventive/FlutterApplicationTemplate/issues).
When filing issues, please use our [issue
template](.github/ISSUE_TEMPLATE.md). The best way to get your bug fixed is to
be as detailed as you can be about the problem. Providing a minimal project with
steps to reproduce the problem is ideal. Here are questions you can answer
Expand Down
6 changes: 3 additions & 3 deletions build/templates/replace-firebase-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ parameters:
steps:
- task: DownloadSecureFile@1
name: firebaseJson
displayName: "Download Keystore from Secure Files"
displayName: "Download firebaseJson from Secure Files"
inputs:
secureFile: ${{ parameters.firebaseJsonFile }}

- task: DownloadSecureFile@1
name: firebaseOptionsDart
displayName: "Download Keystore from Secure Files"
displayName: "Download firebaseOptionsDart from Secure Files"
inputs:
secureFile: ${{ parameters.firebaseOptionsDartFile }}

- task: DownloadSecureFile@1
name: googleServicesJson
displayName: "Download Keystore from Secure Files"
displayName: "Download googleServicesJson from Secure Files"
inputs:
secureFile: ${{ parameters.googleServicesJsonFile }}

Expand Down
2 changes: 1 addition & 1 deletion build/templates/validate-commits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ steps:
}

if($invalidCommits.count -gt 0) {
Write-Error "The following commit messages do no follow the Conventional Commits standard: `n$($invalidCommits -join "`n")"
Write-Error "The following commit messages do not follow the Conventional Commits standard: `n$($invalidCommits -join "`n")"
exit 1
} else {
Write-Host "All commit messages are valid."
Expand Down
6 changes: 3 additions & 3 deletions build/variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
DockerVersion: '25.0.5'

# MobSF Auth key.
# Corrresponds to the key used for authenticating requests to the MobSF API. In this pipeline setup, MobSF runs in a docker container and by
# Corresponds to the key used for authenticating requests to the MobSF API. In this pipeline setup, MobSF runs in a docker container and by
# default the API key is randomly generated by MobSF itself when running it in a dedicated server, but because we're automating the process
# we need to manually set it beforehand so we can authenticate further requests. Can be set to any string. More info:
# - https://mobsf.github.io/docs/#/extras?id=extra-features
Expand All @@ -80,8 +80,8 @@
macOSHostedAgentImage: 'macOS-15'
ubuntuHostedAgentImage: 'ubuntu-22.04'

# Name of the folder where the artefacts will be placed. Variable used in build and release phases.
# We make seperate folders so that releases can each download only the folder they need.
# Name of the folder where the artifacts will be placed. Variable used in build and release phases.
# We make separate folders so that releases can each download only the folder they need.
AndroidArtifactName: Android
iOSArtifactName: iOS
WindowsArtifactName: Windows
Expand Down
4 changes: 2 additions & 2 deletions doc/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ See [ForcedUpdate.md](ForcedUpdate.md) for more details.

### Kill Switch

This application contains Kill switch feature.
This application contains a kill switch feature.

See [KillSwitch.md](KillSwitch.md) for more details.

Expand All @@ -96,7 +96,7 @@ See [HTTP.md](HTTP.md) for more details.

### Local Storage

This applications uses [Shared Preferences](https://pub.dev/packages/shared_preferences) to store data locally.
This application uses [Shared Preferences](https://pub.dev/packages/shared_preferences) to store data locally.

### JSON Serialization

Expand Down
8 changes: 7 additions & 1 deletion doc/AzurePipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ This is where the exact build steps are defined. These vary depending on the pla
1. Push the built artifacts (.ipa, .apk/.aab, release notes, etc.).
1. Cleanup.

The release stages are even more straigtforward than the build ones. One thing to note is that, for the same reason as it is done at the end of the build steps, a clean-up step is included in every stage.
The release stages are even more straightforward than the build ones. One thing to note is that, for the same reason as it is done at the end of the build steps, a clean-up step is included in every stage.

### Firebase App Distribution Release Stage ([stage-release-firebase-app-distribution.yml](../build/stage-release-firebase-app-distribution.yml))

Expand All @@ -89,5 +89,11 @@ This should only be run for configurations that properly sign the application.
Similar to the App Store stage, this stage pushes the **AAB** produced by the build to the Google Play Store.
This is also meant for a properly signed AAB.

### Security Scan Stage ([stage-security-scan.yml](../build/stage-security-scan.yml))

This stage runs a static application security testing (SAST) scan on the built application binaries using MobSF.

See [SecurityScan.md](SecurityScan.md) for more details.

This pipeline should be setup as a **scheduled pipeline** that runs every night and **should NOT be part of build validation**.
PRs should not be blocked when APIs are down.
4 changes: 2 additions & 2 deletions doc/Diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ This is useful when you want to see something happening live.
The default buttons include commands such as the following.
- **Expand/Minimize** opens or closes the expanded view of the overlay.
- **Move** moves the overlay left or right.
- **X** hides the overlay for the remaining of the app cycle.
- **X** hides the overlay for the remainder of the app cycle.
- If you want to permanently hide the diagnostic, go to the environment section within the expanded overlay.

## Expanded overlay widgets

The expanded diagnostics overlay has by default two widgets.

The navigation widget has some buttons with commands on them that allow you to do some navigation in the app.
The DeviceInfo widgets contains some information on the device and the app.
The DeviceInfo widget contains some information on the device and the app.
The environment widget which lets the user change the environment. See [Environment.md](./Environment.md) for more details.
The loggers widget which lets the user test logging and modify logging configuration. See [Logging.md](./Logging.md) for more details.
4 changes: 2 additions & 2 deletions doc/Environment.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Environments

We use `.env` files to store environment configuration with the help of [dotenv](https://pub.dev/packages/dotenv).
We use `.env` files to store environment configuration with the help of [flutter_dotenv](https://pub.dev/packages/flutter_dotenv).

## Runtime environments

Expand Down Expand Up @@ -42,6 +42,6 @@ Multiple environment features can be tested from the diagnostics overlay.
This is configured in [EnvironmentPickerWidget](../src/app/lib/presentation/diagnostic/environment_picker_widget.dart).

- You can see the current runtime environment.
- You can see what the environment will be overriden to.
- You can see what the environment will be overridden to.
- You can switch to another runtime environment.
- You can reset the environment to its default value.
2 changes: 1 addition & 1 deletion doc/ForcedUpdate.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The forced update feature is for when you want to force the user to update the app.
You could use this, for example, when the backend changes and you do not want the users to still use the old API.

To force an update, we wait for a `Future` `checkUpdateRequired()` to be resolved from the `UpdateRequiredservice` in the [main file of the app.](../src/app/lib/main.dart).
To force an update, we wait for a `Future` `waitForUpdateRequired()` to be resolved from the `UpdateRequiredService` in the [main file of the app.](../src/app/lib/main.dart).

This will redirect the user to [a page](../src/app/lib/presentation/forced_update/forced_update_page.dart) from which they cannot navigate back.
The minimum update required is defined in a [Firebase Remote Config](/doc/FirebaseRemoteConfig.md).
4 changes: 2 additions & 2 deletions doc/KillSwitch.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This could be used for example when the server is down for some time, to avoid t

To trigger the kill switch, we subscribe to the `isKillSwitchActivatedStream` `stream` from the `KillSwitchService` in the [Main](../src/app/lib/main.dart).

If the kill switch is activated, the user is brought to the `KillSwitchPage` where he can see a message that tells him the app is currently unavailable.
If the kill switch is deactivated afterwards, the user is brought back to the initial navigation flow, which means he will be in the login page if he is not connected and to the home page if he is connected.
If the kill switch is activated, the user is brought to the `KillSwitchPage` where they can see a message that tells them the app is currently unavailable.
If the kill switch is deactivated afterwards, the user is brought back to the initial navigation flow, which means they will be on the login page if they are not connected and on the home page if they are connected.

Whether the kill switch is activated or not on mobile and web platforms is defined in a [remote config](FirebaseRemoteConfig.md).
6 changes: 3 additions & 3 deletions doc/Logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ We use the following convention for log levels:

## Log providers

- We've implemented [CustomFileOutput](../src/app/lib/business/logger/custom_file_output.dart) for file logging.
- We've implemented [CustomConsoleOutput](../src/app/lib/business/logger/custom_file_output.dart) to workaround this issue https://github.com/flutter/flutter/issues/64491.
- We've implemented [CustomFileOutput](../src/app/lib/access/logger/custom_file_output.dart) for file logging.
- We've implemented [CustomConsoleOutput](../src/app/lib/access/logger/custom_console_output.dart) to workaround this issue https://github.com/flutter/flutter/issues/64491.
- We've implemented [LevelLogFilter](../src/app/lib/business/logger/level_log_filter.dart) to ensure that logs are filtered for a given log level.

The loggers are configured inside the [LoggerManager.cs](../src/app/lib/business/logger/logger_manager.dart) file.
The loggers are configured inside the [LoggerManager.dart](../src/app/lib/business/logger/logger_manager.dart) file.

## Logging

Expand Down
2 changes: 1 addition & 1 deletion doc/SecurityScan.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ This job mirrors the iOS security scan job but targets Android applications. It
- `artifactName`: The name of the Android artifact, appended with the specified applicationEnvironment.

## Usage
### How to Add the the new Stage for Security Scan
### How to Add the new Stage for Security Scan

> 1. Create a new `stage`. (*stage: Security_Scan_Build_[Environment]*)
> 2. Configure the `dependsOn` property to the stage is creating the build, like `Build_Staging` and `Build_Production`.
Expand Down
14 changes: 7 additions & 7 deletions doc/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ For more documentation on testing, read the references listed at the bottom.
dynamic matcher, {
// added in case of failure
String? reason,
// true or a String with the reasion to skip
// true or a String with the reason to skip
dynamic skip,
}
)
Expand Down Expand Up @@ -127,11 +127,11 @@ For more documentation on testing, read the references listed at the bottom.
}
```

## Functionnal/Integration Testing
## Functional/Integration Testing

- We use [integration_test](https://docs.flutter.dev/testing/integration-tests) to create functionnal/integration tests. You can create a test like this :
- We use [integration_test](https://docs.flutter.dev/testing/integration-tests) to create functional/integration tests. You can create a test like this :

```cs
```dart
// Import the test package
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
Expand Down Expand Up @@ -163,17 +163,17 @@ For more documentation on testing, read the references listed at the bottom.
They are all run in the same `main` because of [this issue](https://github.com/flutter/flutter/issues/135673).

To test different behaviors and interactions between the components of the app you need to simulate user interactions with the tester.tap(target) method like this:
```cs
```dart
await tester.tap(dadJokes.first);
```

- To assert the result of a test, use `expect()` exactly like with unit tests.

- tester.pumpAndSettle() is used both to trigger a frame change and to wait for the last pump to have settled before moving on. For example, we use it after pumping the app widget and we also use it when we naviguated and we want to update the UI.
- tester.pumpAndSettle() is used both to trigger a frame change and to wait for the last pump to have settled before moving on. For example, we use it after pumping the app widget and we also use it when we navigated and we want to update the UI.

### Mocking

For functionnal testing we use the decorator pattern because we are testing the actual behavior of the app so we don't wanna use mockito to mock the logic of the classes of the app.
For functional testing we use the decorator pattern because we are testing the actual behavior of the app so we don't want to use mockito to mock the logic of the classes of the app.
To use the decorator pattern you simply call the mocking repository and the method called setMocking(true).
It's important to set the mocking in the main function before the tests otherwise the mocking doesn't take effect.
In most cases, except for api tests, the data sources (repositories) should be mocked for integration testing.
Expand Down
21 changes: 21 additions & 0 deletions src/app/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,26 @@ post_install do |installer|
config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
end

# Workaround: move .xcprivacy files from Sources to Resources build phase.
# Some pods (e.g. bugsee_flutter) incorrectly place PrivacyInfo.xcprivacy in
# the Sources phase, causing "no rule to process file ... of type 'text.xml'"
# during xcodebuild archive.
sources_phase = target.build_phases.find { |p| p.is_a?(Xcodeproj::Project::Object::PBXSourcesBuildPhase) }
next unless sources_phase

xcprivacy_files = sources_phase.files.select { |f| f.file_ref&.path&.end_with?('.xcprivacy') }
next if xcprivacy_files.empty?

resources_phase = target.build_phases.find { |p| p.is_a?(Xcodeproj::Project::Object::PBXResourcesBuildPhase) }
resources_phase ||= target.new_resources_build_phase

xcprivacy_files.each do |build_file|
file_ref = build_file.file_ref
sources_phase.remove_file_reference(file_ref)
unless resources_phase.files.map(&:file_ref).include?(file_ref)
resources_phase.add_file_reference(file_ref)
end
end
end
end
2 changes: 1 addition & 1 deletion src/app/lib/access/diagnostics/diagnostics_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ abstract interface class DiagnosticsRepository {
Future disableDiagnostics();
}

/// Implementation of [DiagnosticRepository].
/// Implementation of [DiagnosticsRepository].
final class _DiagnosticsRepository implements DiagnosticsRepository {
/// The key used to store the diagnostic dismissed state in shared preferences.
final String _diagnosticDisabledKey = 'diagnosticDisabled';
Expand Down
4 changes: 2 additions & 2 deletions src/app/lib/access/firebase/firebase_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FirebaseRemoteConfigRepository
"is_kill_switch_active": false,
});

_logger.i("Firebase has been Initialized and registered in the container.");
_logger.i("Firebase has been initialized and registered in the container.");

// Listen to updates to the remote config. This is only available on mobile platforms.
if (Platform.isAndroid || Platform.isIOS) {
Expand All @@ -54,7 +54,7 @@ class FirebaseRemoteConfigRepository
});
}

// Fetch and activate gets the data from the server and makes it available trough the singleton.
// Fetch and activate gets the data from the server and makes it available through the singleton.
remoteConfig.fetchAndActivate().then((_) {
_updateRemoteConfig();
});
Expand Down
2 changes: 1 addition & 1 deletion src/app/lib/access/logger/custom_file_output.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ final class CustomFileOutput extends LogOutput {
final bool _overrideExisting;
final Encoding _encoding;

/// Regex used to remove ansi color codes from from log files.
/// Regex used to remove ansi color codes from log files.
final _ansiPattern = RegExp(r'\x1B\[\d+(;\d+)*m');

/// It's a reference to [_file] used to write logs inside.
Expand Down
8 changes: 4 additions & 4 deletions src/app/lib/access/logger/logger_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ abstract interface class LoggerRepository {

/// Implementation of [LoggerRepository].
final class _LoggerRepository implements LoggerRepository {
/// The key used to store the selected environment in shared preferences.
/// The key used to store the logging configuration in shared preferences.
final String _consoleLoggingKey = 'consoleLogging';
final String _fileloggingKey = 'fileLogging';
final String _fileLoggingKey = 'fileLogging';

@override
Future<LoggingConfigurationData> getLoggingConfiguration() async {
final sharedPreferences = await SharedPreferences.getInstance();
return LoggingConfigurationData(
isConsoleLoggingEnabled: sharedPreferences.getBool(_consoleLoggingKey),
isFileLoggingEnabled: sharedPreferences.getBool(_fileloggingKey),
isFileLoggingEnabled: sharedPreferences.getBool(_fileLoggingKey),
);
}

Expand All @@ -48,7 +48,7 @@ final class _LoggerRepository implements LoggerRepository {
Future setIsFileLoggingEnabled(bool isFileLoggingEnabled) async {
final sharedPreferences = await SharedPreferences.getInstance();
var isSaved = await sharedPreferences.setBool(
_fileloggingKey,
_fileLoggingKey,
isFileLoggingEnabled,
);

Expand Down
10 changes: 5 additions & 5 deletions src/app/lib/business/logger/logger_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ abstract interface class LoggerManager {
/// Gets whether file logging is enabled.
bool get isFileLoggingEnabled;

/// Gets whether logging configuration been changed via either [setIsConsoleLoggingEnabled] or [setIsFileLoggingEnabled].
/// Gets whether logging configuration has been changed via either [setIsConsoleLoggingEnabled] or [setIsFileLoggingEnabled].
bool get hasConfigurationBeenChanged;

/// Create an instancee of [Logger].
/// Create an instance of [Logger].
Future<Logger> createLogInstance();

/// Deletes log file
Expand Down Expand Up @@ -178,18 +178,18 @@ final class _LoggerManager implements LoggerManager {
await _loggerRepository.setIsConsoleLoggingEnabled(isConsoleLoggingEnabled);

this.isConsoleLoggingEnabled = isConsoleLoggingEnabled;
hasConfigurationBeenChanged = _checkIfChangesHasBeenMade();
hasConfigurationBeenChanged = _checkIfChangesHaveBeenMade();
}

@override
Future setIsFileLoggingEnabled(bool isFileLoggingEnabled) async {
await _loggerRepository.setIsFileLoggingEnabled(isFileLoggingEnabled);

this.isFileLoggingEnabled = isFileLoggingEnabled;
hasConfigurationBeenChanged = _checkIfChangesHasBeenMade();
hasConfigurationBeenChanged = _checkIfChangesHaveBeenMade();
}

bool _checkIfChangesHasBeenMade() =>
bool _checkIfChangesHaveBeenMade() =>
isConsoleLoggingEnabled != _initialIsConsoleLoggingEnabled ||
isFileLoggingEnabled != _initialIsFileLoggingEnabled;
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/lib/business/mocking/mocking_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import 'package:app/access/mocking/mocking_repository.dart';
abstract interface class MockingManager {
factory MockingManager(MockingRepository mockingRepository) = _MockingManager;

/// Gets whether file logging is enabled.
/// Gets whether mocking is enabled.
bool get isMockingEnabled;

/// Gets whether mocking configuration been changed via either [setIsConsoleLoggingEnabled].
/// Gets whether mocking configuration has been changed via [setIsMockingEnabled].
bool get hasConfigurationBeenChanged;

/// Sets whether mocking should be enabled on next app launch.
Expand Down
6 changes: 3 additions & 3 deletions src/app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ Future initializeComponents({bool? isMocked}) async {
_logger.d("Initialized environment and logger.");

_registerHttpClient();
_logger.i("HttpClient has been Initialized and registered in the container.");
_logger.i("HttpClient has been initialized and registered in the container.");

await _registerRepositories(isMocked);
_logger
.i("Repositories has been Initialized and registered in the container.");
.i("Repositories have been initialized and registered in the container.");

_registerServices();
_logger.i("Services has been Initialized and registered in the container.");
_logger.i("Services have been initialized and registered in the container.");

GetIt.I.get<UpdateRequiredService>().waitForUpdateRequired().then((value) {
_logger.d("Force update is now required.");
Expand Down
Loading