mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 10:29:06 +02:00
Removed ID and secure storage services to improve compatibility with different platforms. Removed app tracking handling from the logging service.
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -7,8 +7,6 @@ The Arcane Framework is a powerful Dart package designed to provide a robust arc
|
|||||||
- **Logging**: Easily log messages with metadata, stack traces, and different log levels via `ArcaneLogger`.
|
- **Logging**: Easily log messages with metadata, stack traces, and different log levels via `ArcaneLogger`.
|
||||||
- **Authentication**: Built-in support for handling user authentication workflows.
|
- **Authentication**: Built-in support for handling user authentication workflows.
|
||||||
- **Theming**: Switch between light and dark themes with `ArcaneReactiveTheme`.
|
- **Theming**: Switch between light and dark themes with `ArcaneReactiveTheme`.
|
||||||
- **Secure Storage**: Store sensitive data like tokens and user information securely using `ArcaneSecureStorage`.
|
|
||||||
- **Session and Install ID Management**: Manage unique identifiers for user sessions and app installs.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -252,32 +250,8 @@ await Arcane.logger.registerInterfaces([
|
|||||||
DebugConsole.I,
|
DebugConsole.I,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Initialize app tracking (recommended for iOS)
|
|
||||||
await Arcane.logger.initalizeAppTracking(
|
|
||||||
trackingDialog: () async {
|
|
||||||
if (Arcane.logger.trackingStatus != TrackingStatus.notDetermined) return;
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: "Incoming tracking consent dialog",
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await Arcane.logger.initializeInterfaces();
|
|
||||||
},
|
|
||||||
child: Text("Continue"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initialize registered logging interfaces
|
// Initialize registered logging interfaces
|
||||||
// NOTE: This step may be deferred until a user has consented to app tracking. It is perofrmed automatically when called via `initializeAppTracking`.
|
// NOTE: This step may be deferred until a user has consented to app tracking.
|
||||||
await Arcane.logger.initializeInterfaces();
|
await Arcane.logger.initializeInterfaces();
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -303,7 +277,7 @@ Arcane.log(
|
|||||||
|
|
||||||
Multiple logging interfaces can be registered simultaneously.
|
Multiple logging interfaces can be registered simultaneously.
|
||||||
|
|
||||||
**Important**: Logging interfaces should generally be initialized after being registered with the logger service. This ensures that all logging interfaces are properly initialized before any messages are logged. This should typically be done manually in order to properly present the user with a message stating that they're about to be prompted for tracking permissions (on iOS). See the Arcane logger documentation for further details.
|
**Important**: Logging interfaces should generally be initialized after being registered with the logger service. This ensures that all logging interfaces are properly initialized before any messages are logged. This should typically be done manually in order to properly present the user with a message stating that they're about to be prompted for tracking permissions (on iOS).
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
@@ -567,36 +541,6 @@ Arcane.theme.setDarkTheme(customDarkTheme);
|
|||||||
Arcane.theme.setLightTheme(customLightTheme);
|
Arcane.theme.setLightTheme(customLightTheme);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Secure Storage
|
|
||||||
|
|
||||||
You can store sensitive data like tokens securely with `ArcaneSecureStorage`:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Store a value in secure storage
|
|
||||||
await Arcane.storage.setValue(ArcaneSecureStorage.emailKey, "user@example.com");
|
|
||||||
|
|
||||||
// Retrieve a value from secure storage
|
|
||||||
final email = await Arcane.storage.getValue(ArcaneSecureStorage.emailKey);
|
|
||||||
|
|
||||||
// Delete all stored data
|
|
||||||
await Arcane.storage.deleteAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install and Session IDs
|
|
||||||
|
|
||||||
Use `ArcaneIdService` to manage unique IDs for app installations and user sessions:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Get the install ID
|
|
||||||
final installId = await Arcane.id.installId;
|
|
||||||
|
|
||||||
// Get the session ID
|
|
||||||
final sessionId = await Arcane.id.sessionId;
|
|
||||||
|
|
||||||
// Generate a new ID
|
|
||||||
final newId = Arcane.id.newId;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We welcome contributions to the Arcane Framework. If you’d like to contribute, please:
|
We welcome contributions to the Arcane Framework. If you’d like to contribute, please:
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
library arcane_framework;
|
library arcane_framework;
|
||||||
|
|
||||||
export "package:app_tracking_transparency/app_tracking_transparency.dart";
|
|
||||||
export "package:arcane_framework/src/arcane.dart";
|
export "package:arcane_framework/src/arcane.dart";
|
||||||
export "package:arcane_framework/src/arcane_app.dart";
|
export "package:arcane_framework/src/arcane_app.dart";
|
||||||
export "package:arcane_framework/src/providers/environment_provider.dart";
|
export "package:arcane_framework/src/providers/environment_provider.dart";
|
||||||
export "package:arcane_framework/src/providers/service_provider.dart";
|
export "package:arcane_framework/src/providers/service_provider.dart";
|
||||||
export "package:arcane_framework/src/services/authentication/authentication_service.dart";
|
export "package:arcane_framework/src/services/authentication/authentication_service.dart";
|
||||||
export "package:arcane_framework/src/services/feature_flags/feature_flags_service.dart";
|
export "package:arcane_framework/src/services/feature_flags/feature_flags_service.dart";
|
||||||
export "package:arcane_framework/src/services/id/id_service.dart";
|
|
||||||
export "package:arcane_framework/src/services/logging/logging_service.dart";
|
export "package:arcane_framework/src/services/logging/logging_service.dart";
|
||||||
export "package:arcane_framework/src/services/reactive_theme/reactive_theme_service.dart";
|
export "package:arcane_framework/src/services/reactive_theme/reactive_theme_service.dart";
|
||||||
export "package:arcane_framework/src/services/secure_storage/secure_storage_service.dart";
|
|
||||||
export "package:result_monad/result_monad.dart";
|
export "package:result_monad/result_monad.dart";
|
||||||
|
|||||||
@@ -37,17 +37,6 @@ class Arcane {
|
|||||||
/// customizing them.
|
/// customizing them.
|
||||||
static ArcaneReactiveTheme get theme => ArcaneReactiveTheme.I;
|
static ArcaneReactiveTheme get theme => ArcaneReactiveTheme.I;
|
||||||
|
|
||||||
/// Provides access to the singleton instance of the secure storage service.
|
|
||||||
///
|
|
||||||
/// `ArcaneSecureStorage` is responsible for securely storing key-value pairs,
|
|
||||||
/// such as tokens or user data.
|
|
||||||
static ArcaneSecureStorage get storage => ArcaneSecureStorage.I;
|
|
||||||
|
|
||||||
/// Provides access to the singleton instance of the ID service.
|
|
||||||
///
|
|
||||||
/// `ArcaneIdService` manages unique identifiers for installations and sessions.
|
|
||||||
static ArcaneIdService get id => ArcaneIdService.I;
|
|
||||||
|
|
||||||
/// Returns a list of all services available in the Arcane framework.
|
/// Returns a list of all services available in the Arcane framework.
|
||||||
///
|
///
|
||||||
/// This list includes the feature flags, authentication, theme, and ID services.
|
/// This list includes the feature flags, authentication, theme, and ID services.
|
||||||
@@ -55,7 +44,6 @@ class Arcane {
|
|||||||
features,
|
features,
|
||||||
auth,
|
auth,
|
||||||
theme,
|
theme,
|
||||||
id,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Logs a message using the integrated logger.
|
/// Logs a message using the integrated logger.
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// provides one.
|
/// provides one.
|
||||||
Future<String?> get refreshToken => authInterface.refreshToken;
|
Future<String?> get refreshToken => authInterface.refreshToken;
|
||||||
|
|
||||||
static ArcaneSecureStorage get _storage => Arcane.storage;
|
|
||||||
|
|
||||||
/// Registers an `ArcaneAuthInterface` within the `ArcaneAuthenticationService`.
|
/// Registers an `ArcaneAuthInterface` within the `ArcaneAuthenticationService`.
|
||||||
Future<void> registerInterface(ArcaneAuthInterface authInterface) async {
|
Future<void> registerInterface(ArcaneAuthInterface authInterface) async {
|
||||||
_authInterface = authInterface;
|
_authInterface = authInterface;
|
||||||
@@ -113,7 +111,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
|
|
||||||
await loggedOut.fold(
|
await loggedOut.fold(
|
||||||
onSuccess: (_) async {
|
onSuccess: (_) async {
|
||||||
await _storage.deleteAll();
|
|
||||||
setUnauthenticated();
|
setUnauthenticated();
|
||||||
onLoggedOut();
|
onLoggedOut();
|
||||||
},
|
},
|
||||||
@@ -147,7 +144,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
await _storage.setValue(ArcaneSecureStorage.emailKey, email);
|
|
||||||
setAuthenticated();
|
setAuthenticated();
|
||||||
if (onLoggedIn != null) await onLoggedIn();
|
if (onLoggedIn != null) await onLoggedIn();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
part of "id_service.dart";
|
|
||||||
|
|
||||||
/// Enum representing different types of IDs managed by the `ArcaneIdService`.
|
|
||||||
///
|
|
||||||
/// The `ID` enum has two possible values:
|
|
||||||
/// - `session`: Represents the session ID.
|
|
||||||
/// - `install`: Represents the install ID.
|
|
||||||
enum ID {
|
|
||||||
/// Represents the session ID.
|
|
||||||
session,
|
|
||||||
|
|
||||||
/// Represents the install ID.
|
|
||||||
install,
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import "package:arcane_framework/arcane_framework.dart";
|
|
||||||
import "package:flutter/widgets.dart";
|
|
||||||
import "package:uuid/uuid.dart";
|
|
||||||
|
|
||||||
part "id_enums.dart";
|
|
||||||
|
|
||||||
/// A singleton service that manages unique IDs, including install and session IDs.
|
|
||||||
///
|
|
||||||
/// The `ArcaneIdService` provides a way to generate and retrieve unique identifiers
|
|
||||||
/// for application installs and sessions. It interacts with secure storage to persist
|
|
||||||
/// the install ID across app launches and generates new session IDs for each session.
|
|
||||||
class ArcaneIdService extends ArcaneService {
|
|
||||||
/// Whether the service is mocked for testing purposes.
|
|
||||||
static bool _mocked = false;
|
|
||||||
|
|
||||||
/// The singleton instance of `ArcaneIdService`.
|
|
||||||
static final ArcaneIdService _instance = ArcaneIdService._internal();
|
|
||||||
|
|
||||||
/// Provides access to the singleton instance of `ArcaneIdService`.
|
|
||||||
static ArcaneIdService get I => _instance;
|
|
||||||
|
|
||||||
ArcaneIdService._internal();
|
|
||||||
|
|
||||||
/// Whether the service has been initialized.
|
|
||||||
bool _initialized = false;
|
|
||||||
|
|
||||||
/// Returns `true` if the service has been initialized.
|
|
||||||
bool get initialized => I._initialized;
|
|
||||||
|
|
||||||
/// The unique install ID.
|
|
||||||
///
|
|
||||||
/// This ID is persisted across app launches and is used to uniquely identify
|
|
||||||
/// the installation of the app.
|
|
||||||
String? _installId;
|
|
||||||
|
|
||||||
/// Retrieves the install ID.
|
|
||||||
///
|
|
||||||
/// If the install ID is not yet initialized, this method initializes the service
|
|
||||||
/// and generates a new ID if necessary.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// String? id = await ArcaneIdService.I.installId;
|
|
||||||
/// ```
|
|
||||||
Future<String?> get installId async {
|
|
||||||
if (!initialized) await _init();
|
|
||||||
return I._installId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The unique session ID.
|
|
||||||
///
|
|
||||||
/// This ID is generated for each app session and is used to uniquely identify
|
|
||||||
/// the current session.
|
|
||||||
String? _sessionId;
|
|
||||||
|
|
||||||
/// Retrieves the session ID.
|
|
||||||
///
|
|
||||||
/// If the session ID is not yet initialized, this method initializes the service
|
|
||||||
/// and generates a new session ID.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// String? sessionId = await ArcaneIdService.I.sessionId;
|
|
||||||
/// ```
|
|
||||||
Future<String?> get sessionId async {
|
|
||||||
if (!initialized) await _init();
|
|
||||||
return I._sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a new unique ID.
|
|
||||||
///
|
|
||||||
/// This method uses UUID version 7 to generate a new unique ID.
|
|
||||||
String get newId => uuid.v7();
|
|
||||||
|
|
||||||
/// The `Uuid` instance used for generating unique IDs.
|
|
||||||
static const Uuid uuid = Uuid();
|
|
||||||
|
|
||||||
/// Initializes the `ArcaneIdService`.
|
|
||||||
///
|
|
||||||
/// This method retrieves the install ID from secure storage, generating and storing a new
|
|
||||||
/// one if it does not exist. It also generates a new session ID.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// await ArcaneIdService.I._init();
|
|
||||||
/// ```
|
|
||||||
Future<ArcaneIdService> _init() async {
|
|
||||||
if (_mocked) return I;
|
|
||||||
if (!Arcane.storage.initialized) Arcane.storage.init();
|
|
||||||
|
|
||||||
I._installId = await Arcane.storage.getValue(
|
|
||||||
ArcaneSecureStorage.installIdKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (I._installId == null) {
|
|
||||||
// Generate a new ID and store it
|
|
||||||
I._installId = uuid.v7();
|
|
||||||
await Arcane.storage.setValue(
|
|
||||||
ArcaneSecureStorage.installIdKey,
|
|
||||||
I._installId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
I._sessionId = uuid.v7();
|
|
||||||
I._initialized = true;
|
|
||||||
return I;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the service as mocked for testing purposes.
|
|
||||||
///
|
|
||||||
/// When the service is mocked, it bypasses certain initializations and uses
|
|
||||||
/// mocked data for testing.
|
|
||||||
@visibleForTesting
|
|
||||||
static void setMocked() => _mocked = true;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import "dart:async";
|
import "dart:async";
|
||||||
import "dart:io" show Platform;
|
|
||||||
|
|
||||||
import "package:arcane_framework/arcane_framework.dart";
|
import "package:arcane_framework/arcane_framework.dart";
|
||||||
import "package:arcane_helper_utils/arcane_helper_utils.dart";
|
import "package:arcane_helper_utils/arcane_helper_utils.dart";
|
||||||
@@ -14,9 +13,7 @@ part "logging_interface.dart";
|
|||||||
///
|
///
|
||||||
/// The `ArcaneLogger` provides a centralized way to log messages across
|
/// The `ArcaneLogger` provides a centralized way to log messages across
|
||||||
/// different parts of an application. It supports multiple logging interfaces,
|
/// different parts of an application. It supports multiple logging interfaces,
|
||||||
/// metadata, and platform-specific error handling. It integrates with
|
/// metadata, and platform-specific error handling.
|
||||||
/// [AppTrackingTransparency] for tracking authorization status on fruit-shaped
|
|
||||||
/// operating systems.
|
|
||||||
class ArcaneLogger {
|
class ArcaneLogger {
|
||||||
ArcaneLogger._internal();
|
ArcaneLogger._internal();
|
||||||
|
|
||||||
@@ -35,11 +32,6 @@ class ArcaneLogger {
|
|||||||
/// Additional metadata that is included in all logs.
|
/// Additional metadata that is included in all logs.
|
||||||
Map<String, String> get additionalMetadata => I._additionalMetadata;
|
Map<String, String> get additionalMetadata => I._additionalMetadata;
|
||||||
|
|
||||||
TrackingStatus _trackingStatus = TrackingStatus.notDetermined;
|
|
||||||
|
|
||||||
/// The tracking authorization status for the current platform.
|
|
||||||
TrackingStatus get trackingStatus => I._trackingStatus;
|
|
||||||
|
|
||||||
bool _initialized = false;
|
bool _initialized = false;
|
||||||
|
|
||||||
/// Whether the logger has been initialized.
|
/// Whether the logger has been initialized.
|
||||||
@@ -66,36 +58,23 @@ class ArcaneLogger {
|
|||||||
// Handles unhandled Flutter errors by logging them.
|
// Handles unhandled Flutter errors by logging them.
|
||||||
FlutterError.onError = (errorDetails) {
|
FlutterError.onError = (errorDetails) {
|
||||||
log(
|
log(
|
||||||
"UNHANDLED FLUTTER ERROR",
|
errorDetails.exceptionAsString(),
|
||||||
level: Level.error,
|
level: Level.error,
|
||||||
module: errorDetails.library,
|
module: errorDetails.library,
|
||||||
stackTrace: errorDetails.stack,
|
stackTrace: errorDetails.stack,
|
||||||
metadata: {
|
|
||||||
"details": errorDetails.exceptionAsString(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles unhandled platform-specific errors by logging them.
|
// Handles unhandled platform-specific errors by logging them.
|
||||||
PlatformDispatcher.instance.onError = (error, stack) {
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
log(
|
log(
|
||||||
"UNHANDLED PLATFORM ERROR",
|
"$error",
|
||||||
level: Level.error,
|
level: Level.error,
|
||||||
stackTrace: stack,
|
stackTrace: stack,
|
||||||
metadata: {
|
|
||||||
"details": error.toString(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
return true;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
I._trackingStatus =
|
|
||||||
await AppTrackingTransparency.trackingAuthorizationStatus;
|
|
||||||
|
|
||||||
if (!(Platform.isIOS || Platform.isMacOS)) {
|
|
||||||
I._trackingStatus = TrackingStatus.authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
I._initialized = true;
|
I._initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,9 +192,8 @@ class ArcaneLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a [LoggingInterface] with the [ArcaneLogger]. If the current
|
/// Registers a [LoggingInterface] with the [ArcaneLogger]. Due to iOS app
|
||||||
/// operating system is not a fruit-shaped OS, it will automatically be
|
/// tracking permissions, permission to track must first be checked for
|
||||||
/// initalized. Otherwise, app tracking permissions must first be checked for
|
|
||||||
/// and (optionally) granted before the interface is automatically initialized.
|
/// and (optionally) granted before the interface is automatically initialized.
|
||||||
///
|
///
|
||||||
/// Once your [LoggingInterface] has been registered and initialized, logs
|
/// Once your [LoggingInterface] has been registered and initialized, logs
|
||||||
@@ -248,42 +226,6 @@ class ArcaneLogger {
|
|||||||
return I;
|
return I;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will ask the user to approve app tracking permissions on
|
|
||||||
/// fruit-shaped operating systems. An optional `trackingDialog` method can be
|
|
||||||
/// passed in, which could be used to display a message to users that they're
|
|
||||||
/// about to be asked for tracking permissions. The `trackingDialog` method
|
|
||||||
/// will only be run if the tracking status is `notDetermined`.
|
|
||||||
///
|
|
||||||
/// If app tracking has been allowed, all registered [LoggingInterface]s will
|
|
||||||
/// be initialized.
|
|
||||||
Future<void> initalizeAppTracking({
|
|
||||||
Future<void>? trackingDialog,
|
|
||||||
}) async {
|
|
||||||
if (I._mocked) return;
|
|
||||||
if (!I._initialized) await _init();
|
|
||||||
if (I._trackingStatus == TrackingStatus.authorized) {
|
|
||||||
await initializeInterfaces();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the system can show an authorization request dialog
|
|
||||||
if (I._trackingStatus == TrackingStatus.notDetermined) {
|
|
||||||
// Show a custom explainer dialog before the system dialog
|
|
||||||
if (trackingDialog != null) await trackingDialog;
|
|
||||||
// Wait for dialog popping animation
|
|
||||||
await Future.delayed(const Duration(milliseconds: 200));
|
|
||||||
// Request system's tracking authorization dialog
|
|
||||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
|
||||||
}
|
|
||||||
|
|
||||||
I._trackingStatus =
|
|
||||||
await AppTrackingTransparency.trackingAuthorizationStatus;
|
|
||||||
|
|
||||||
if (I._trackingStatus == TrackingStatus.authorized) {
|
|
||||||
await initializeInterfaces();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a specific key from the persistent metadata.
|
/// Removes a specific key from the persistent metadata.
|
||||||
ArcaneLogger removePersistentMetadata(String key) {
|
ArcaneLogger removePersistentMetadata(String key) {
|
||||||
final bool keyPresent = additionalMetadata.containsKey(key);
|
final bool keyPresent = additionalMetadata.containsKey(key);
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
import "package:arcane_helper_utils/arcane_helper_utils.dart";
|
|
||||||
import "package:flutter_secure_storage/flutter_secure_storage.dart";
|
|
||||||
import "package:get_it/get_it.dart";
|
|
||||||
|
|
||||||
/// A singleton class that provides secure storage functionality using
|
|
||||||
/// `FlutterSecureStorage`.
|
|
||||||
///
|
|
||||||
/// The `ArcaneSecureStorage` class is responsible for securely storing and
|
|
||||||
/// retrieving key-value pairs, such as user email and install IDs. It supports
|
|
||||||
/// caching for certain keys and provides initialization, deletion, and
|
|
||||||
/// read/write methods for interacting with the secure storage.
|
|
||||||
class ArcaneSecureStorage {
|
|
||||||
/// The singleton instance of `ArcaneSecureStorage`.
|
|
||||||
static final ArcaneSecureStorage _instance = ArcaneSecureStorage._internal();
|
|
||||||
|
|
||||||
/// Provides access to the singleton instance of `ArcaneSecureStorage`.
|
|
||||||
static ArcaneSecureStorage get I => _instance;
|
|
||||||
|
|
||||||
ArcaneSecureStorage._internal();
|
|
||||||
|
|
||||||
/// The underlying secure storage instance.
|
|
||||||
///
|
|
||||||
/// This is initialized with `FlutterSecureStorage`, using encrypted shared
|
|
||||||
/// preferences for Android.
|
|
||||||
late final FlutterSecureStorage _storage;
|
|
||||||
|
|
||||||
/// Caches the user's email in memory.
|
|
||||||
///
|
|
||||||
/// This is used to reduce the number of reads to secure storage for the email
|
|
||||||
/// key.
|
|
||||||
String? _emailCache;
|
|
||||||
|
|
||||||
/// Provides access to the cached email if it exists.
|
|
||||||
String? get cachedEmail => _emailCache;
|
|
||||||
|
|
||||||
/// The key used to store and retrieve the email from secure storage.
|
|
||||||
static const String emailKey = "email";
|
|
||||||
|
|
||||||
/// The key used to store and retrieve the install ID from secure storage.
|
|
||||||
static const String installIdKey = "installId";
|
|
||||||
|
|
||||||
/// Indicates whether the secure storage has been initialized.
|
|
||||||
bool _initialized = false;
|
|
||||||
|
|
||||||
/// Returns `true` if the secure storage has been initialized.
|
|
||||||
bool get initialized => I._initialized;
|
|
||||||
|
|
||||||
/// Initializes the secure storage and registers it with `GetIt` for
|
|
||||||
/// dependency injection.
|
|
||||||
///
|
|
||||||
/// This method sets up the `FlutterSecureStorage` with encrypted shared
|
|
||||||
/// preferences for Android, and registers it under the instance name
|
|
||||||
/// `ArcaneSecureStorage`. It also sets the initialized flag to `true`.
|
|
||||||
ArcaneSecureStorage init() {
|
|
||||||
GetIt.I.registerSingleton<FlutterSecureStorage>(
|
|
||||||
instanceName: "ArcaneSecureStorage",
|
|
||||||
const FlutterSecureStorage(
|
|
||||||
aOptions: AndroidOptions(
|
|
||||||
encryptedSharedPreferences: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
I._storage = GetIt.I<FlutterSecureStorage>(
|
|
||||||
instanceName: "ArcaneSecureStorage",
|
|
||||||
);
|
|
||||||
I._initialized = true;
|
|
||||||
return I;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes all key-value pairs stored in secure storage.
|
|
||||||
///
|
|
||||||
/// This method clears the cache and deletes all data stored in the secure\
|
|
||||||
/// storage. It returns `true` on success and `false` on failure.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// bool success = await ArcaneSecureStorage.I.deleteAll();
|
|
||||||
/// ```
|
|
||||||
Future<bool> deleteAll() async {
|
|
||||||
if (!initialized) init();
|
|
||||||
try {
|
|
||||||
_emailCache = null;
|
|
||||||
await _storage.deleteAll();
|
|
||||||
return true;
|
|
||||||
} catch (exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves a value associated with the given [key] from secure storage.
|
|
||||||
///
|
|
||||||
/// If the [key] is `emailKey`, the value will be cached in memory for future
|
|
||||||
/// use.
|
|
||||||
/// This method returns `null` if the key is not found or if an error occurs.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// String? email = await ArcaneSecureStorage.I.getValue(ArcaneSecureStorage.emailKey);
|
|
||||||
/// ```
|
|
||||||
Future<String?> getValue(String key) async {
|
|
||||||
if (!initialized) init();
|
|
||||||
String? value;
|
|
||||||
|
|
||||||
try {
|
|
||||||
value = await _storage.read(key: key);
|
|
||||||
if (value.isNullOrEmpty) return null;
|
|
||||||
|
|
||||||
// Cache the email for future use
|
|
||||||
if (key == emailKey) _emailCache = value;
|
|
||||||
} catch (e) {
|
|
||||||
throw Exception(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the given [value] associated with the [key] to secure storage.
|
|
||||||
///
|
|
||||||
/// This method returns `true` on success and `false` on failure.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// bool success = await ArcaneSecureStorage.I.setValue(ArcaneSecureStorage.emailKey, "user@example.com");
|
|
||||||
/// ```
|
|
||||||
Future<bool> setValue(String key, String? value) async {
|
|
||||||
if (!initialized) init();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await _storage.write(key: key, value: value);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-3
@@ -1,5 +1,5 @@
|
|||||||
name: arcane_framework
|
name: arcane_framework
|
||||||
description: "Arcane Framework is a comprehensive Dart package designed to streamline the management of essential services like logging, authentication, theming, feature flags, and secure storage in Flutter applications. With a robust architecture and easy-to-use APIs, it empowers developers to build scalable, dynamic apps with minimal overhead."
|
description: "The Arcane Framework provides a modular, feature-rich solution for bootstrapping new applications."
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
repository: https://github.com/hanskokx/arcane_framework
|
repository: https://github.com/hanskokx/arcane_framework
|
||||||
issue_tracker: https://github.com/hanskokx/arcane_framework/issues
|
issue_tracker: https://github.com/hanskokx/arcane_framework/issues
|
||||||
@@ -12,7 +12,6 @@ environment:
|
|||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
app_tracking_transparency: ^2.0.6
|
|
||||||
arcane_helper_utils: ^1.0.1
|
arcane_helper_utils: ^1.0.1
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
flutter:
|
flutter:
|
||||||
@@ -22,7 +21,6 @@ dependencies:
|
|||||||
get_it: ^7.7.0
|
get_it: ^7.7.0
|
||||||
logger: ^2.4.0
|
logger: ^2.4.0
|
||||||
result_monad: ^2.3.2
|
result_monad: ^2.3.2
|
||||||
uuid: ^4.5.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^4.0.0
|
flutter_lints: ^4.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user