Compare commits
10 Commits
f2219e9df8
...
3c04ad18b7
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c04ad18b7 | |||
| c71b9f5fb0 | |||
| 40e086c591 | |||
| 8bd5a47c42 | |||
| cb30e67bbe | |||
| be65f4610c | |||
| e88a1fca56 | |||
| 35dd86e81c | |||
| 53b3f7ed3a | |||
| 1a71670f9d |
31
README.md
31
README.md
@ -1,4 +1,4 @@
|
||||
# SecureStorgageSample
|
||||
# SecureStorageSample
|
||||
|
||||
A sample iOS app demonstrating the LocalData package capabilities for secure, typed storage across multiple domains.
|
||||
|
||||
@ -24,10 +24,10 @@ The project also includes a watchOS companion app target for watch-specific demo
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Open `SecureStorgageSample.xcodeproj`
|
||||
1. Open `SecureStorageSample.xcodeproj`
|
||||
2. Select an iOS simulator or device
|
||||
3. Build and run (⌘R)
|
||||
4. To use App Group demos, set your App Group identifier in `SecureStorgageSample/SecureStorgageSample/Models/AppGroupConfiguration.swift` and enable the entitlement for each target that should share data.
|
||||
4. To use App Group demos, enable the App Group entitlement for each target that should share data. The identifier is derived from the bundle ID via SharedKit constants.
|
||||
|
||||
## Project Structure
|
||||
|
||||
@ -37,10 +37,11 @@ SharedPackage/
|
||||
└── Sources/
|
||||
└── SharedKit/
|
||||
├── Constants/
|
||||
│ └── StorageKeyNames.swift
|
||||
│ ├── StorageKeyNames.swift
|
||||
│ └── StorageServiceIdentifiers.swift
|
||||
└── Models/
|
||||
└── UserProfile.swift
|
||||
SecureStorgageSample/
|
||||
SecureStorageSample/
|
||||
├── ContentView.swift # Tabbed navigation
|
||||
├── Models/
|
||||
│ ├── Credential.swift
|
||||
@ -89,9 +90,9 @@ The app demonstrates various storage configurations:
|
||||
- 6 access control options (biometry, passcode, etc.)
|
||||
|
||||
### File System
|
||||
- Documents directory (persisted, backed up)
|
||||
- Caches directory (can be purged)
|
||||
- JSON and PropertyList serializers
|
||||
- **Documents**: Permanent storage, backed up to iCloud. Use for critical user data.
|
||||
- **Caches**: Purgeable storage (iOS may delete when low on space), not backed up. Use for temporary data.
|
||||
- JSON and PropertyList serializers supported.
|
||||
|
||||
### App Group Storage
|
||||
- Shared UserDefaults via App Group identifier
|
||||
@ -103,10 +104,23 @@ The app demonstrates various storage configurations:
|
||||
- PBKDF2 or HKDF key derivation
|
||||
- Complete file protection
|
||||
- External key material example via `KeyMaterialProviding`
|
||||
- Global encryption configuration (Keychain service/account) in app `init`
|
||||
|
||||
### Platform & Sync
|
||||
- Platform availability (phoneOnly, watchOnly, all)
|
||||
- Sync policies (never, manual, automaticSmall)
|
||||
- Global sync configuration (max file size) in app `init`
|
||||
|
||||
## Global Configuration
|
||||
|
||||
The app demonstrates how to configure the `LocalData` library globally during startup in `SecureStorageSampleApp.swift`:
|
||||
|
||||
- **Encryption**: Customized Keychain service (`SecureStorageSample`) and account (`AppMasterKey`) names to isolate the library's master encryption key from other apps.
|
||||
- **Sync**: Set a custom `maxAutoSyncSize` of 50KB to control which data is automatically synchronized to the Apple Watch, overriding the library's 100KB default.
|
||||
- **File Storage**: Scoping all library files into a `SecureStorage` sub-directory. This ensures that the library's data (whether in the main sandbox or a shared App Group container) is kept neat and isolated within its own folder, rather than cluttering the root directories.
|
||||
- **Storage Defaults**: Pre-configuring the default Keychain service and App Group identifier. This allows common keys in the app to omit these identifiers, reducing boilerplate and making the code more maintainable.
|
||||
|
||||
This approach centralizes infrastructure settings and avoids hardcoding environment-specific values within individual storage keys.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@ -117,6 +131,7 @@ The app demonstrates various storage configurations:
|
||||
|
||||
- Storage keys are now split into one file per key and grouped by domain; platform-focused keys live in `StorageKeys/Platform` with comments calling out availability/sync focus.
|
||||
- The shared model/constants live in `SharedPackage` (`SharedKit`) to keep the watch/iOS data contract centralized.
|
||||
- Keychain service IDs and App Group identifiers are centralized in `SharedKit/Constants/StorageServiceIdentifiers.swift` to avoid hardcoded strings in keys.
|
||||
- The watch app uses a handler-based WatchConnectivity layer so new payload types can be added in `Services/Handlers` without bloating the main service.
|
||||
- A `StorageKeyCatalog` sample is included to generate a security audit report of all storage keys.
|
||||
- Each `StorageKey` includes a `description` used in audit reports.
|
||||
|
||||
58
SecureStorageSample Watch App/README.md
Normal file
58
SecureStorageSample Watch App/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# SecureStorageSample Watch App
|
||||
|
||||
A watchOS companion app demonstrating data synchronization with the iOS app using WatchConnectivity.
|
||||
|
||||
## Overview
|
||||
|
||||
This watch app receives `UserProfile` data synced from the paired iPhone via `WCSession.updateApplicationContext`. It does **not** use LocalData directly for storage—instead, it displays synced data in memory.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
SecureStorageSample Watch App/
|
||||
├── ContentView.swift # Displays synced profile data
|
||||
├── SecureStorageSampleApp.swift
|
||||
├── Protocols/
|
||||
│ └── WatchDataHandling.swift # Protocol for payload handlers
|
||||
├── State/
|
||||
│ └── WatchProfileStore.swift # Holds synced profile in memory
|
||||
└── Services/
|
||||
├── WatchConnectivityService.swift
|
||||
└── Handlers/
|
||||
└── UserProfileWatchHandler.swift
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. **iOS app** calls `SyncHelper` when storing data with `syncPolicy: .automaticSmall` or `.manual`
|
||||
2. `SyncHelper` sends data via `WCSession.updateApplicationContext`
|
||||
3. **Watch app** receives context in `WatchConnectivityService`
|
||||
4. The service dispatches each payload key to its registered `WatchDataHandling` handler
|
||||
5. `UserProfileWatchHandler` decodes the profile and updates `WatchProfileStore`
|
||||
|
||||
## Adding New Sync Payloads
|
||||
|
||||
1. Create a new handler conforming to `WatchDataHandling`:
|
||||
|
||||
```swift
|
||||
struct MyDataWatchHandler: WatchDataHandling {
|
||||
let key = "myData"
|
||||
|
||||
func handle(data: Data) {
|
||||
// Decode and update state
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Register it in `WatchConnectivityService.registerDefaultHandlers()`
|
||||
|
||||
## Limitations
|
||||
|
||||
- **No persistent storage on watch**: Data is held in memory only
|
||||
- **One-way sync**: Watch receives data from iPhone; it does not send data back
|
||||
- **Simulator limitations**: WatchConnectivity may not work reliably in the simulator
|
||||
|
||||
## Requirements
|
||||
|
||||
- watchOS 10.0+
|
||||
- Paired with iOS app via WatchConnectivity
|
||||
@ -8,10 +8,14 @@ final class UserProfileWatchHandler: WatchDataHandling {
|
||||
private let store: WatchProfileStore
|
||||
private let decoder = JSONDecoder()
|
||||
|
||||
init(store: WatchProfileStore = .shared) {
|
||||
init(store: WatchProfileStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(store: .shared)
|
||||
}
|
||||
|
||||
func handle(data: Data) {
|
||||
do {
|
||||
let profile = try decoder.decode(UserProfile.self, from: data)
|
||||
|
||||
@ -20,14 +20,14 @@
|
||||
containerPortal = EA179CF92F1722BB00B1D54A /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = EA179D002F1722BB00B1D54A;
|
||||
remoteInfo = SecureStorgageSample;
|
||||
remoteInfo = SecureStorageSample;
|
||||
};
|
||||
EA179D192F1722BC00B1D54A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = EA179CF92F1722BB00B1D54A /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = EA179D002F1722BB00B1D54A;
|
||||
remoteInfo = SecureStorgageSample;
|
||||
remoteInfo = SecureStorageSample;
|
||||
};
|
||||
EA65D6F22F17DD6800C48466 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
@ -67,28 +67,28 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
EA179D012F1722BB00B1D54A /* SecureStorgageSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SecureStorgageSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA179D0E2F1722BC00B1D54A /* SecureStorgageSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecureStorgageSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA179D182F1722BC00B1D54A /* SecureStorgageSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecureStorgageSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA179D012F1722BB00B1D54A /* SecureStorageSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SecureStorageSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA179D0E2F1722BC00B1D54A /* SecureStorageSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecureStorageSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA179D182F1722BC00B1D54A /* SecureStorageSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecureStorageSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA65D6E52F17DD6700C48466 /* SecureStorageSample Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SecureStorageSample Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA65D6F12F17DD6800C48466 /* SecureStorageSample Watch AppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SecureStorageSample Watch AppTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA65D6FB2F17DD6800C48466 /* SecureStorageSample Watch AppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SecureStorageSample Watch AppUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
EA179D032F1722BB00B1D54A /* SecureStorgageSample */ = {
|
||||
EA179D032F1722BB00B1D54A /* SecureStorageSample */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = SecureStorgageSample;
|
||||
path = SecureStorageSample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EA179D112F1722BC00B1D54A /* SecureStorgageSampleTests */ = {
|
||||
EA179D112F1722BC00B1D54A /* SecureStorageSampleTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = SecureStorgageSampleTests;
|
||||
path = SecureStorageSampleTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorgageSampleUITests */ = {
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorageSampleUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = SecureStorgageSampleUITests;
|
||||
path = SecureStorageSampleUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EA65D6E62F17DD6700C48466 /* SecureStorageSample Watch App */ = {
|
||||
@ -161,9 +161,9 @@
|
||||
EA179CF82F1722BB00B1D54A = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA179D032F1722BB00B1D54A /* SecureStorgageSample */,
|
||||
EA179D112F1722BC00B1D54A /* SecureStorgageSampleTests */,
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorgageSampleUITests */,
|
||||
EA179D032F1722BB00B1D54A /* SecureStorageSample */,
|
||||
EA179D112F1722BC00B1D54A /* SecureStorageSampleTests */,
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorageSampleUITests */,
|
||||
EA65D6E62F17DD6700C48466 /* SecureStorageSample Watch App */,
|
||||
EA65D6F42F17DD6800C48466 /* SecureStorageSample Watch AppTests */,
|
||||
EA65D6FE2F17DD6800C48466 /* SecureStorageSample Watch AppUITests */,
|
||||
@ -175,9 +175,9 @@
|
||||
EA179D022F1722BB00B1D54A /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA179D012F1722BB00B1D54A /* SecureStorgageSample.app */,
|
||||
EA179D0E2F1722BC00B1D54A /* SecureStorgageSampleTests.xctest */,
|
||||
EA179D182F1722BC00B1D54A /* SecureStorgageSampleUITests.xctest */,
|
||||
EA179D012F1722BB00B1D54A /* SecureStorageSample.app */,
|
||||
EA179D0E2F1722BC00B1D54A /* SecureStorageSampleTests.xctest */,
|
||||
EA179D182F1722BC00B1D54A /* SecureStorageSampleUITests.xctest */,
|
||||
EA65D6E52F17DD6700C48466 /* SecureStorageSample Watch App.app */,
|
||||
EA65D6F12F17DD6800C48466 /* SecureStorageSample Watch AppTests.xctest */,
|
||||
EA65D6FB2F17DD6800C48466 /* SecureStorageSample Watch AppUITests.xctest */,
|
||||
@ -195,9 +195,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
EA179D002F1722BB00B1D54A /* SecureStorgageSample */ = {
|
||||
EA179D002F1722BB00B1D54A /* SecureStorageSample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = EA179D222F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSample" */;
|
||||
buildConfigurationList = EA179D222F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSample" */;
|
||||
buildPhases = (
|
||||
EA179CFD2F1722BB00B1D54A /* Sources */,
|
||||
EA179CFE2F1722BB00B1D54A /* Frameworks */,
|
||||
@ -210,20 +210,20 @@
|
||||
EA65D70F2F17DDEB00C48466 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
EA179D032F1722BB00B1D54A /* SecureStorgageSample */,
|
||||
EA179D032F1722BB00B1D54A /* SecureStorageSample */,
|
||||
);
|
||||
name = SecureStorgageSample;
|
||||
name = SecureStorageSample;
|
||||
packageProductDependencies = (
|
||||
EA179D552F17379800B1D54A /* LocalData */,
|
||||
EA65D7312F17DDEB00C48466 /* SharedKit */,
|
||||
);
|
||||
productName = SecureStorgageSample;
|
||||
productReference = EA179D012F1722BB00B1D54A /* SecureStorgageSample.app */;
|
||||
productName = SecureStorageSample;
|
||||
productReference = EA179D012F1722BB00B1D54A /* SecureStorageSample.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
EA179D0D2F1722BC00B1D54A /* SecureStorgageSampleTests */ = {
|
||||
EA179D0D2F1722BC00B1D54A /* SecureStorageSampleTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = EA179D252F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSampleTests" */;
|
||||
buildConfigurationList = EA179D252F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSampleTests" */;
|
||||
buildPhases = (
|
||||
EA179D0A2F1722BC00B1D54A /* Sources */,
|
||||
EA179D0B2F1722BC00B1D54A /* Frameworks */,
|
||||
@ -235,19 +235,19 @@
|
||||
EA179D102F1722BC00B1D54A /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
EA179D112F1722BC00B1D54A /* SecureStorgageSampleTests */,
|
||||
EA179D112F1722BC00B1D54A /* SecureStorageSampleTests */,
|
||||
);
|
||||
name = SecureStorgageSampleTests;
|
||||
name = SecureStorageSampleTests;
|
||||
packageProductDependencies = (
|
||||
EA65D7312F17DDEB00C48466 /* SharedKit */,
|
||||
);
|
||||
productName = SecureStorgageSampleTests;
|
||||
productReference = EA179D0E2F1722BC00B1D54A /* SecureStorgageSampleTests.xctest */;
|
||||
productName = SecureStorageSampleTests;
|
||||
productReference = EA179D0E2F1722BC00B1D54A /* SecureStorageSampleTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
EA179D172F1722BC00B1D54A /* SecureStorgageSampleUITests */ = {
|
||||
EA179D172F1722BC00B1D54A /* SecureStorageSampleUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = EA179D282F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSampleUITests" */;
|
||||
buildConfigurationList = EA179D282F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSampleUITests" */;
|
||||
buildPhases = (
|
||||
EA179D142F1722BC00B1D54A /* Sources */,
|
||||
EA179D152F1722BC00B1D54A /* Frameworks */,
|
||||
@ -259,13 +259,13 @@
|
||||
EA179D1A2F1722BC00B1D54A /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorgageSampleUITests */,
|
||||
EA179D1B2F1722BC00B1D54A /* SecureStorageSampleUITests */,
|
||||
);
|
||||
name = SecureStorgageSampleUITests;
|
||||
name = SecureStorageSampleUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = SecureStorgageSampleUITests;
|
||||
productReference = EA179D182F1722BC00B1D54A /* SecureStorgageSampleUITests.xctest */;
|
||||
productName = SecureStorageSampleUITests;
|
||||
productReference = EA179D182F1722BC00B1D54A /* SecureStorageSampleUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
EA65D6E42F17DD6700C48466 /* SecureStorageSample Watch App */ = {
|
||||
@ -371,7 +371,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = EA179CFC2F1722BB00B1D54A /* Build configuration list for PBXProject "SecureStorgageSample" */;
|
||||
buildConfigurationList = EA179CFC2F1722BB00B1D54A /* Build configuration list for PBXProject "SecureStorageSample" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@ -389,9 +389,9 @@
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
EA179D002F1722BB00B1D54A /* SecureStorgageSample */,
|
||||
EA179D0D2F1722BC00B1D54A /* SecureStorgageSampleTests */,
|
||||
EA179D172F1722BC00B1D54A /* SecureStorgageSampleUITests */,
|
||||
EA179D002F1722BB00B1D54A /* SecureStorageSample */,
|
||||
EA179D0D2F1722BC00B1D54A /* SecureStorageSampleTests */,
|
||||
EA179D172F1722BC00B1D54A /* SecureStorageSampleUITests */,
|
||||
EA65D6E42F17DD6700C48466 /* SecureStorageSample Watch App */,
|
||||
EA65D6F02F17DD6800C48466 /* SecureStorageSample Watch AppTests */,
|
||||
EA65D6FA2F17DD6800C48466 /* SecureStorageSample Watch AppUITests */,
|
||||
@ -492,12 +492,12 @@
|
||||
/* Begin PBXTargetDependency section */
|
||||
EA179D102F1722BC00B1D54A /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = EA179D002F1722BB00B1D54A /* SecureStorgageSample */;
|
||||
target = EA179D002F1722BB00B1D54A /* SecureStorageSample */;
|
||||
targetProxy = EA179D0F2F1722BC00B1D54A /* PBXContainerItemProxy */;
|
||||
};
|
||||
EA179D1A2F1722BC00B1D54A /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = EA179D002F1722BB00B1D54A /* SecureStorgageSample */;
|
||||
target = EA179D002F1722BB00B1D54A /* SecureStorageSample */;
|
||||
targetProxy = EA179D192F1722BC00B1D54A /* PBXContainerItemProxy */;
|
||||
};
|
||||
EA65D6F32F17DD6800C48466 /* PBXTargetDependency */ = {
|
||||
@ -642,7 +642,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = SecureStorgageSample/SecureStorgageSample.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = SecureStorageSample/SecureStorageSample.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||
@ -659,7 +659,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSample;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -676,7 +676,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = SecureStorgageSample/SecureStorgageSample.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = SecureStorageSample/SecureStorageSample.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||
@ -693,7 +693,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSample;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -714,7 +714,7 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSampleTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -722,7 +722,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SecureStorgageSample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SecureStorgageSample";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SecureStorageSample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SecureStorageSample";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -735,7 +735,7 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSampleTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -743,7 +743,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SecureStorgageSample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SecureStorgageSample";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SecureStorageSample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SecureStorageSample";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -754,7 +754,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSampleUITests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSampleUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -762,7 +762,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = SecureStorgageSample;
|
||||
TEST_TARGET_NAME = SecureStorageSample;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -773,7 +773,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSampleUITests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSampleUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
@ -781,7 +781,7 @@
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = SecureStorgageSample;
|
||||
TEST_TARGET_NAME = SecureStorageSample;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -797,13 +797,13 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecureStorageSample;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.mbrucedogs.SecureStorgageSample;
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.mbrucedogs.SecureStorageSample;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSample.watchkitapp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSample.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
@ -830,13 +830,13 @@
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecureStorageSample;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.mbrucedogs.SecureStorgageSample;
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.mbrucedogs.SecureStorageSample;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorgageSample.watchkitapp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SecureStorageSample.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
@ -944,7 +944,7 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
EA179CFC2F1722BB00B1D54A /* Build configuration list for PBXProject "SecureStorgageSample" */ = {
|
||||
EA179CFC2F1722BB00B1D54A /* Build configuration list for PBXProject "SecureStorageSample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
EA179D202F1722BC00B1D54A /* Debug */,
|
||||
@ -953,7 +953,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
EA179D222F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSample" */ = {
|
||||
EA179D222F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
EA179D232F1722BC00B1D54A /* Debug */,
|
||||
@ -962,7 +962,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
EA179D252F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSampleTests" */ = {
|
||||
EA179D252F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSampleTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
EA179D262F1722BC00B1D54A /* Debug */,
|
||||
@ -971,7 +971,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
EA179D282F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorgageSampleUITests" */ = {
|
||||
EA179D282F1722BC00B1D54A /* Build configuration list for PBXNativeTarget "SecureStorageSampleUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
EA179D292F1722BC00B1D54A /* Debug */,
|
||||
@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA65D6E42F17DD6700C48466"
|
||||
BuildableName = "SecureStorageSample Watch App.app"
|
||||
BlueprintName = "SecureStorageSample Watch App"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA179D002F1722BB00B1D54A"
|
||||
BuildableName = "SecureStorageSample.app"
|
||||
BlueprintName = "SecureStorageSample"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA65D6F02F17DD6800C48466"
|
||||
BuildableName = "SecureStorageSample Watch AppTests.xctest"
|
||||
BlueprintName = "SecureStorageSample Watch AppTests"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA65D6FA2F17DD6800C48466"
|
||||
BuildableName = "SecureStorageSample Watch AppUITests.xctest"
|
||||
BlueprintName = "SecureStorageSample Watch AppUITests"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA65D6E42F17DD6700C48466"
|
||||
BuildableName = "SecureStorageSample Watch App.app"
|
||||
BlueprintName = "SecureStorageSample Watch App"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA65D6E42F17DD6700C48466"
|
||||
BuildableName = "SecureStorageSample Watch App.app"
|
||||
BlueprintName = "SecureStorageSample Watch App"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA179D002F1722BB00B1D54A"
|
||||
BuildableName = "SecureStorageSample.app"
|
||||
BlueprintName = "SecureStorageSample"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA179D002F1722BB00B1D54A"
|
||||
BuildableName = "SecureStorageSample.app"
|
||||
BlueprintName = "SecureStorageSample"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "EA179D002F1722BB00B1D54A"
|
||||
BuildableName = "SecureStorageSample.app"
|
||||
BlueprintName = "SecureStorageSample"
|
||||
ReferencedContainer = "container:SecureStorageSample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "container:SecureStorgageSample.xcodeproj">
|
||||
location = "container:SecureStorageSample.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Main navigation view with tabbed interface for all LocalData demos.
|
||||
//
|
||||
69
SecureStorageSample/SecureStorageSampleApp.swift
Normal file
69
SecureStorageSample/SecureStorageSampleApp.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// SecureStorageSampleApp.swift
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Created by Matt Bruce on 1/13/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import LocalData
|
||||
|
||||
@main
|
||||
struct SecureStorageSampleApp: App {
|
||||
init() {
|
||||
_ = WatchConnectivityService.shared
|
||||
Task {
|
||||
// 1. Global Encryption Configuration
|
||||
// We isolate our library's master key in the Keychain by providing a specific service
|
||||
// and account name. This prevents conflicts with other apps using the same library.
|
||||
// We also set PBKDF2 iterations for consistent security across the app.
|
||||
let config = EncryptionConfiguration(
|
||||
masterKeyService: "SecureStorageSample",
|
||||
masterKeyAccount: "AppMasterKey",
|
||||
pbkdf2Iterations: 20_000
|
||||
)
|
||||
await StorageRouter.shared.updateEncryptionConfiguration(config)
|
||||
|
||||
// 2. Global Sync Configuration
|
||||
// Overriding the default 100KB limit for automatic Watch sync to 50KB.
|
||||
// This demonstrates how to tune performance for low-bandwidth scenarios.
|
||||
let syncConfig = SyncConfiguration(maxAutoSyncSize: 50_000)
|
||||
await StorageRouter.shared.updateSyncConfiguration(syncConfig)
|
||||
|
||||
// 3. Global File Storage Configuration
|
||||
// We scope all our library files into a "SecureStorage" sub-directory
|
||||
// underneath the standard Documents/Caches folders.
|
||||
let fileConfig = FileStorageConfiguration(subDirectory: "SecureStorage")
|
||||
await StorageRouter.shared.updateFileStorageConfiguration(fileConfig)
|
||||
|
||||
// 4. Global Storage Defaults
|
||||
// Setting default identifiers for Keychain and App Groups.
|
||||
// This allows keys to be defined more concisely without repeating these IDs.
|
||||
let storageConfig = StorageConfiguration(
|
||||
defaultKeychainService: StorageServiceIdentifiers.bundleIdentifier,
|
||||
defaultAppGroupIdentifier: StorageServiceIdentifiers.appGroupIdentifier
|
||||
)
|
||||
await StorageRouter.shared.updateStorageConfiguration(storageConfig)
|
||||
|
||||
do {
|
||||
try await StorageRouter.shared.registerCatalog(AppStorageCatalog.self)
|
||||
} catch {
|
||||
assertionFailure("Storage catalog registration failed: \(error)")
|
||||
}
|
||||
await StorageRouter.shared.registerKeyMaterialProvider(
|
||||
ExternalKeyMaterialProvider(),
|
||||
for: SampleKeyMaterialSources.external
|
||||
)
|
||||
}
|
||||
#if DEBUG
|
||||
let report = StorageAuditReport.renderText(for: AppStorageCatalog.self)
|
||||
print(report)
|
||||
#endif
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import LocalData
|
||||
|
||||
import SharedKit
|
||||
extension StorageKeys {
|
||||
/// Stores a shared setting in App Group UserDefaults.
|
||||
/// - Domain: App Group UserDefaults
|
||||
@ -10,7 +10,7 @@ extension StorageKeys {
|
||||
typealias Value = String
|
||||
|
||||
let name = "app_group_setting"
|
||||
let domain: StorageDomain = .appGroupUserDefaults(identifier: AppGroupConfiguration.identifier)
|
||||
let domain: StorageDomain = .appGroupUserDefaults(identifier: StorageServiceIdentifiers.appGroupIdentifier)
|
||||
let security: SecurityPolicy = .none
|
||||
let serializer: Serializer<String> = .json
|
||||
let owner = "SampleApp"
|
||||
@ -24,7 +24,7 @@ extension StorageKeys {
|
||||
}
|
||||
|
||||
var domain: StorageDomain {
|
||||
.appGroupFileSystem(identifier: AppGroupConfiguration.identifier, directory: directory)
|
||||
.appGroupFileSystem(identifier: StorageServiceIdentifiers.appGroupIdentifier, directory: directory)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import LocalData
|
||||
import SharedKit
|
||||
|
||||
extension StorageKeys {
|
||||
/// Stores user preferences in App Group UserDefaults.
|
||||
@ -10,7 +11,7 @@ extension StorageKeys {
|
||||
typealias Value = [String: AnyCodable]
|
||||
|
||||
let name = "user_preferences"
|
||||
let domain: StorageDomain = .appGroupUserDefaults(identifier: AppGroupConfiguration.identifier)
|
||||
let domain: StorageDomain = .appGroupUserDefaults(identifier: StorageServiceIdentifiers.appGroupIdentifier)
|
||||
let security: SecurityPolicy = .none
|
||||
let serializer: Serializer<[String: AnyCodable]> = .json
|
||||
let owner = "SampleApp"
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// EncryptedStorageDemo.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Demonstrates encrypted file storage with LocalData package.
|
||||
//
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// FileSystemDemo.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Demonstrates file system storage with LocalData package.
|
||||
//
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// KeychainDemo.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Demonstrates Keychain storage with LocalData package.
|
||||
//
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// PlatformSyncDemo.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Demonstrates platform availability and sync policies with LocalData package.
|
||||
//
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// UserDefaultsDemo.swift
|
||||
// SecureStorgageSample
|
||||
// SecureStorageSample
|
||||
//
|
||||
// Demonstrates UserDefaults storage with LocalData package.
|
||||
//
|
||||
@ -1,14 +1,14 @@
|
||||
//
|
||||
// SecureStorgageSampleTests.swift
|
||||
// SecureStorgageSampleTests
|
||||
// SecureStorageSampleTests.swift
|
||||
// SecureStorageSampleTests
|
||||
//
|
||||
// Created by Matt Bruce on 1/13/26.
|
||||
//
|
||||
|
||||
import Testing
|
||||
@testable import SecureStorgageSample
|
||||
@testable import SecureStorageSample
|
||||
|
||||
struct SecureStorgageSampleTests {
|
||||
struct SecureStorageSampleTests {
|
||||
|
||||
@Test func example() async throws {
|
||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||
@ -1,13 +1,13 @@
|
||||
//
|
||||
// SecureStorgageSampleUITests.swift
|
||||
// SecureStorgageSampleUITests
|
||||
// SecureStorageSampleUITests.swift
|
||||
// SecureStorageSampleUITests
|
||||
//
|
||||
// Created by Matt Bruce on 1/13/26.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class SecureStorgageSampleUITests: XCTestCase {
|
||||
final class SecureStorageSampleUITests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
@ -1,13 +1,13 @@
|
||||
//
|
||||
// SecureStorgageSampleUITestsLaunchTests.swift
|
||||
// SecureStorgageSampleUITests
|
||||
// SecureStorageSampleUITestsLaunchTests.swift
|
||||
// SecureStorageSampleUITests
|
||||
//
|
||||
// Created by Matt Bruce on 1/13/26.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class SecureStorgageSampleUITestsLaunchTests: XCTestCase {
|
||||
final class SecureStorageSampleUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>SecureStorageSample Watch App.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>SecureStorageWatch Watch App.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>SecureStorgageSample.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>SecureStorgageSampleWatch.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,8 +0,0 @@
|
||||
import Foundation
|
||||
import SharedKit
|
||||
|
||||
enum AppGroupConfiguration {
|
||||
static var identifier: String {
|
||||
StorageServiceIdentifiers.appGroupIdentifier
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
//
|
||||
// SecureStorgageSampleApp.swift
|
||||
// SecureStorgageSample
|
||||
//
|
||||
// Created by Matt Bruce on 1/13/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import LocalData
|
||||
|
||||
@main
|
||||
struct SecureStorgageSampleApp: App {
|
||||
init() {
|
||||
_ = WatchConnectivityService.shared
|
||||
do {
|
||||
try StorageRouter.shared.registerCatalog(AppStorageCatalog.self)
|
||||
} catch {
|
||||
assertionFailure("Storage catalog registration failed: \(error)")
|
||||
}
|
||||
Task {
|
||||
await StorageRouter.shared.registerKeyMaterialProvider(
|
||||
ExternalKeyMaterialProvider(),
|
||||
for: SampleKeyMaterialSources.external
|
||||
)
|
||||
}
|
||||
#if DEBUG
|
||||
let report = StorageAuditReport.renderText(for: AppStorageCatalog.self)
|
||||
print(report)
|
||||
#endif
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user