Compare commits
6 Commits
main
...
rebrand-bi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59cba0a988 | ||
|
|
c70486ab59 | ||
|
|
7fb175c621 | ||
|
|
91cb735ca6 | ||
|
|
30d563f7e2 | ||
|
|
894f871fd5 |
10
.github/workflows/Xcode-build-analyze.yml
vendored
10
.github/workflows/Xcode-build-analyze.yml
vendored
@ -22,20 +22,20 @@ jobs:
|
||||
xcodebuild -version
|
||||
|
||||
- name: Resolve packages
|
||||
run: xcodebuild -resolvePackageDependencies -project hellbender.xcodeproj -scheme hellbender
|
||||
run: xcodebuild -resolvePackageDependencies -project birch.xcodeproj -scheme birch
|
||||
|
||||
- name: Validate Package.resolved
|
||||
run: |
|
||||
if ! git diff --quiet hellbender.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved; then
|
||||
if ! git diff --quiet birch.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved; then
|
||||
echo "::error::Package.resolved has uncommitted changes after resolution. Commit the updated Package.resolved."
|
||||
git diff hellbender.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
|
||||
git diff birch.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
scheme: hellbender
|
||||
file_to_build: hellbender.xcodeproj
|
||||
scheme: birch
|
||||
file_to_build: birch.xcodeproj
|
||||
filetype_parameter: project
|
||||
run: |
|
||||
xcodebuild clean build analyze -scheme "$scheme" -"$filetype_parameter" "$file_to_build" CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
|
||||
|
||||
2
.github/workflows/Xcode-unit-tests.yml
vendored
2
.github/workflows/Xcode-unit-tests.yml
vendored
@ -17,4 +17,4 @@ jobs:
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
xcodebuild test -project hellbender.xcodeproj -scheme hellbender -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -only-testing:hellbenderTests -parallel-testing-enabled NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
|
||||
xcodebuild test -project birch.xcodeproj -scheme birch -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -only-testing:birchTests -parallel-testing-enabled NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
|
||||
|
||||
24
.github/workflows/reproducible-build-check.yml
vendored
24
.github/workflows/reproducible-build-check.yml
vendored
@ -23,12 +23,12 @@ jobs:
|
||||
|
||||
- name: Build 1
|
||||
run: |
|
||||
DERIVED_DATA="/tmp/hellbender-build-1"
|
||||
DERIVED_DATA="/tmp/birch-build-1"
|
||||
rm -rf "$DERIVED_DATA"
|
||||
xcodebuild archive \
|
||||
-scheme hellbender \
|
||||
-project hellbender.xcodeproj \
|
||||
-archivePath "$DERIVED_DATA/hellbender.xcarchive" \
|
||||
-scheme birch \
|
||||
-project birch.xcodeproj \
|
||||
-archivePath "$DERIVED_DATA/birch.xcarchive" \
|
||||
-derivedDataPath "$DERIVED_DATA" \
|
||||
-configuration Release \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
@ -37,12 +37,12 @@ jobs:
|
||||
|
||||
- name: Build 2
|
||||
run: |
|
||||
DERIVED_DATA="/tmp/hellbender-build-2"
|
||||
DERIVED_DATA="/tmp/birch-build-2"
|
||||
rm -rf "$DERIVED_DATA"
|
||||
xcodebuild archive \
|
||||
-scheme hellbender \
|
||||
-project hellbender.xcodeproj \
|
||||
-archivePath "$DERIVED_DATA/hellbender.xcarchive" \
|
||||
-scheme birch \
|
||||
-project birch.xcodeproj \
|
||||
-archivePath "$DERIVED_DATA/birch.xcarchive" \
|
||||
-derivedDataPath "$DERIVED_DATA" \
|
||||
-configuration Release \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
@ -51,13 +51,13 @@ jobs:
|
||||
|
||||
- name: Normalize both builds
|
||||
run: |
|
||||
APP1="/tmp/hellbender-build-1/hellbender.xcarchive/Products/Applications/hellbender.app"
|
||||
APP2="/tmp/hellbender-build-2/hellbender.xcarchive/Products/Applications/hellbender.app"
|
||||
APP1="/tmp/birch-build-1/birch.xcarchive/Products/Applications/birch.app"
|
||||
APP2="/tmp/birch-build-2/birch.xcarchive/Products/Applications/birch.app"
|
||||
./scripts/normalize-app.sh "$APP1"
|
||||
./scripts/normalize-app.sh "$APP2"
|
||||
|
||||
- name: Compare builds
|
||||
run: |
|
||||
APP1="/tmp/hellbender-build-1/hellbender.xcarchive/Products/Applications/hellbender.app"
|
||||
APP2="/tmp/hellbender-build-2/hellbender.xcarchive/Products/Applications/hellbender.app"
|
||||
APP1="/tmp/birch-build-1/birch.xcarchive/Products/Applications/birch.app"
|
||||
APP2="/tmp/birch-build-2/birch.xcarchive/Products/Applications/birch.app"
|
||||
./scripts/compare-builds.sh "$APP1" "$APP2"
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
// Hellbender.xcconfig — Target-level settings for the hellbender app target
|
||||
// Birch.xcconfig — Target-level settings for the birch app target
|
||||
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = AppIcon-Dark
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor
|
||||
CODE_SIGN_STYLE = Automatic
|
||||
CURRENT_PROJECT_VERSION = 23
|
||||
DEVELOPMENT_ASSET_PATHS = "hellbender/Preview Content"
|
||||
DEVELOPMENT_ASSET_PATHS = "birch/Preview Content"
|
||||
GENERATE_INFOPLIST_FILE = NO
|
||||
INFOPLIST_FILE = hellbender/Info.plist
|
||||
INFOPLIST_FILE = birch/Info.plist
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.6
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks
|
||||
MARKETING_VERSION = 0.1.2
|
||||
42
README.md
42
README.md
@ -1,29 +1,29 @@
|
||||
<p align="center">
|
||||
<img src="https://hellbenderwallet.com/assets/AppIcon-og.png" alt="Hellbender" width="128" height="128" style="border-radius: 24px;" />
|
||||
<img src="https://birchwallet.app/assets/AppIcon-og.png" alt="Birch" width="128" height="128" style="border-radius: 24px;" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Hellbender</h1>
|
||||
<h1 align="center">Birch</h1>
|
||||
|
||||
<p align="center">
|
||||
<em>Travel to your private keys and leave your laptop at home.</em>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/welcome.png" alt="Welcome" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/wallet-setup.png" alt="Setup Choice" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/multisig-config.png" alt="New Wallet Multisig Config" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/cosigner-import.png" alt="Cosigner Import" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/verify-wallet-top.png" alt="Verify Wallet" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/verify-wallet-backup.png" alt="Backup PDF/QR" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/transactions.png" alt="Transactions" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/send.png" alt="Send" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/receive.png" alt="Receive" width="150" />
|
||||
<img src="https://hellbenderwallet.com/assets/screenshots/utxos.png" alt="UTXO" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/welcome.png" alt="Welcome" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/wallet-setup.png" alt="Setup Choice" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/multisig-config.png" alt="New Wallet Multisig Config" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/cosigner-import.png" alt="Cosigner Import" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/verify-wallet-top.png" alt="Verify Wallet" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/verify-wallet-backup.png" alt="Backup PDF/QR" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/transactions.png" alt="Transactions" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/send.png" alt="Send" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/receive.png" alt="Receive" width="150" />
|
||||
<img src="https://birchwallet.app/assets/screenshots/utxos.png" alt="UTXO" width="150" />
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Hellbender is an iOS Bitcoin multisig coordinator written in Swift. It operates as a **watch-only wallet** — private keys never touch your phone. Coordinate signing across air-gapped hardware wallets using animated QR codes, bringing cold storage security with mobile convenience.
|
||||
Birch is an iOS Bitcoin multisig coordinator written in Swift. It operates as a **watch-only wallet** — private keys never touch your phone. Coordinate signing across air-gapped hardware wallets using animated QR codes, bringing cold storage security with mobile convenience.
|
||||
|
||||
## Features
|
||||
|
||||
@ -65,7 +65,7 @@ All dependencies are managed via Swift Package Manager and resolve automatically
|
||||
git clone https://github.com/newtonick/hellbender-wallet.git
|
||||
cd hellbender-wallet
|
||||
```
|
||||
2. Open `hellbender.xcodeproj` in Xcode
|
||||
2. Open `birch.xcodeproj` in Xcode
|
||||
3. SPM dependencies resolve automatically on first open
|
||||
4. Build and run on a simulator or device
|
||||
|
||||
@ -77,7 +77,7 @@ GitHub Actions runs `xcodebuild clean build analyze` on every push and pull requ
|
||||
|
||||
### Reproducible Builds
|
||||
|
||||
Hellbender supports **functionally equivalent** reproducible builds. Given the same source code and Xcode version, two independent builds will produce the same compiled logic after normalization. Certain metadata bytes (Mach-O UUIDs, timestamps, build-machine identifiers) are expected to differ and are zeroed by the normalization step.
|
||||
Birch supports **functionally equivalent** reproducible builds. Given the same source code and Xcode version, two independent builds will produce the same compiled logic after normalization. Certain metadata bytes (Mach-O UUIDs, timestamps, build-machine identifiers) are expected to differ and are zeroed by the normalization step.
|
||||
|
||||
**What IS reproducible** (after normalization): all code-bearing sections, resources, and application logic.
|
||||
|
||||
@ -94,7 +94,7 @@ Hellbender supports **functionally equivalent** reproducible builds. Given the s
|
||||
./scripts/build-release.sh
|
||||
```
|
||||
|
||||
This creates an unsigned archive at `/tmp/hellbender-build/hellbender.xcarchive`.
|
||||
This creates an unsigned archive at `/tmp/birch-build/birch.xcarchive`.
|
||||
|
||||
#### Verifying two builds
|
||||
|
||||
@ -111,7 +111,7 @@ The comparison exits 0 if the builds are functionally equivalent, 1 if code diff
|
||||
|
||||
## Generating Screenshots
|
||||
|
||||
Hellbender uses [`fastlane snapshot`](https://docs.fastlane.tools/actions/snapshot/) to generate marketing and App Store screenshots. A single UI test walks the app from Welcome through the main tabs, capturing every major screen on each configured device in both dark and light mode.
|
||||
Birch uses [`fastlane snapshot`](https://docs.fastlane.tools/actions/snapshot/) to generate marketing and App Store screenshots. A single UI test walks the app from Welcome through the main tabs, capturing every major screen on each configured device in both dark and light mode.
|
||||
|
||||
### One-time setup
|
||||
|
||||
@ -188,7 +188,7 @@ fastlane/screenshots/
|
||||
|
||||
### How it works
|
||||
|
||||
- [`hellbenderUITests/ScreenshotTests.swift`](hellbenderUITests/ScreenshotTests.swift) is a dedicated XCUITest that walks the app. It reuses the existing `-UITesting` launch argument (defined in `hellbender/hellbenderApp.swift`), which wipes `UserDefaults`/keychain and uses an in-memory SwiftData store so every run starts from a deterministic Welcome screen.
|
||||
- [`birchUITests/ScreenshotTests.swift`](birchUITests/ScreenshotTests.swift) is a dedicated XCUITest that walks the app. It reuses the existing `-UITesting` launch argument (defined in `birch/birchApp.swift`), which wipes `UserDefaults`/keychain and uses an in-memory SwiftData store so every run starts from a deterministic Welcome screen.
|
||||
- The test imports a real testnet4 1-of-2 `wsh(sortedmulti(...))` descriptor with live history, waits for Electrum sync, then visits each screen.
|
||||
- Dark/light mode is driven by the simulator's OS appearance (`xcrun simctl ui ... appearance`). The app's `RootView` follows the OS when the theme is set to `.system`, which it is by default after the `-UITesting` wipe, so no app-side toggle is required.
|
||||
- The device matrix, scheme, status bar override, and other `snapshot` options live in [`fastlane/Snapfile`](fastlane/Snapfile). Device destinations (simulator OS version), the frameit pass, and the custom 13 mini ImageMagick composite all live in [`fastlane/Fastfile`](fastlane/Fastfile).
|
||||
@ -196,14 +196,14 @@ fastlane/screenshots/
|
||||
### Customizing
|
||||
|
||||
- **Add/remove devices:** edit both the `devices([...])` array in `fastlane/Snapfile` and the `DEVICES` hash in `fastlane/Fastfile`.
|
||||
- **Change which screens are captured:** edit `testScreenshotTour` in `hellbenderUITests/ScreenshotTests.swift` and add or remove `snapshot("NN-Name")` calls.
|
||||
- **Change which screens are captured:** edit `testScreenshotTour` in `birchUITests/ScreenshotTests.swift` and add or remove `snapshot("NN-Name")` calls.
|
||||
- **Skip framing:** remove the `frameit(...)` lines and the ImageMagick composite block (steps 4–7) from `fastlane/Fastfile` if you only need the bare PNGs.
|
||||
|
||||
> **Known workaround** (contained in `fastlane/Fastfile`): `frameit` gem 2.232.2's bundled iPhone 13 Mini frame PNG has a ~3-pixel placement-offset bug that leaves a visible edge gap, so 13 mini is composited directly with ImageMagick instead. iPhone 16/17 device support is patched in via `scripts/patch-frameit.rb` (see setup step 4 above).
|
||||
|
||||
## Links
|
||||
|
||||
- **Website**: [hellbenderwallet.com](https://hellbenderwallet.com)
|
||||
- **Website**: [birchwallet.app](https://birchwallet.app)
|
||||
- **TestFlight Beta**: [Join the beta](https://testflight.apple.com/join/PuHVwJDJ)
|
||||
- **Author**: [newtonick](https://github.com/newtonick/hellbender-wallet/)
|
||||
|
||||
@ -211,5 +211,5 @@ fastlane/screenshots/
|
||||
|
||||
MIT License — see [LICENSE](LICENSE) for details.
|
||||
|
||||
Hellbender's dependencies use permissive licenses compatible with MIT:
|
||||
Birch's dependencies use permissive licenses compatible with MIT:
|
||||
bdk-swift (MIT/Apache-2.0), URKit (BSD-2-Clause-Patent), URUI (BSD-2-Clause-Patent), Bbqr (Apache-2.0).
|
||||
|
||||
@ -20,54 +20,54 @@
|
||||
containerPortal = 3C9ACE1C2F5DED94009B00D0 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3C9ACE232F5DED94009B00D0;
|
||||
remoteInfo = hellbender;
|
||||
remoteInfo = birch;
|
||||
};
|
||||
3C9ACE3F2F5DED95009B00D0 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 3C9ACE1C2F5DED94009B00D0 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3C9ACE232F5DED94009B00D0;
|
||||
remoteInfo = hellbender;
|
||||
remoteInfo = birch;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
3C9ACE242F5DED94009B00D0 /* hellbender.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = hellbender.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3C9ACE342F5DED95009B00D0 /* hellbenderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = hellbenderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3C9ACE3E2F5DED95009B00D0 /* hellbenderUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = hellbenderUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3C9ACE242F5DED94009B00D0 /* birch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = birch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3C9ACE342F5DED95009B00D0 /* birchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = birchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3C9ACE3E2F5DED95009B00D0 /* birchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = birchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CC0000010000000000000001 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
|
||||
CC0000010000000000000002 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
CC0000010000000000000003 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
CC0000010000000000000004 /* Hellbender.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Hellbender.xcconfig; sourceTree = "<group>"; };
|
||||
CC0000010000000000000004 /* Birch.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Birch.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
CC0000010000000000000006 /* Exceptions for "hellbender" folder in "hellbender" target */ = {
|
||||
CC0000010000000000000006 /* Exceptions for "birch" folder in "birch" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 3C9ACE232F5DED94009B00D0 /* hellbender */;
|
||||
target = 3C9ACE232F5DED94009B00D0 /* birch */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
3C9ACE262F5DED94009B00D0 /* hellbender */ = {
|
||||
3C9ACE262F5DED94009B00D0 /* birch */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
CC0000010000000000000006 /* Exceptions for "hellbender" folder in "hellbender" target */,
|
||||
CC0000010000000000000006 /* Exceptions for "birch" folder in "birch" target */,
|
||||
);
|
||||
path = hellbender;
|
||||
path = birch;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3C9ACE372F5DED95009B00D0 /* hellbenderTests */ = {
|
||||
3C9ACE372F5DED95009B00D0 /* birchTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = hellbenderTests;
|
||||
path = birchTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3C9ACE412F5DED95009B00D0 /* hellbenderUITests */ = {
|
||||
3C9ACE412F5DED95009B00D0 /* birchUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = hellbenderUITests;
|
||||
path = birchUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
@ -106,9 +106,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC0000010000000000000005 /* Config */,
|
||||
3C9ACE262F5DED94009B00D0 /* hellbender */,
|
||||
3C9ACE372F5DED95009B00D0 /* hellbenderTests */,
|
||||
3C9ACE412F5DED95009B00D0 /* hellbenderUITests */,
|
||||
3C9ACE262F5DED94009B00D0 /* birch */,
|
||||
3C9ACE372F5DED95009B00D0 /* birchTests */,
|
||||
3C9ACE412F5DED95009B00D0 /* birchUITests */,
|
||||
3C9ACE252F5DED94009B00D0 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@ -116,9 +116,9 @@
|
||||
3C9ACE252F5DED94009B00D0 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3C9ACE242F5DED94009B00D0 /* hellbender.app */,
|
||||
3C9ACE342F5DED95009B00D0 /* hellbenderTests.xctest */,
|
||||
3C9ACE3E2F5DED95009B00D0 /* hellbenderUITests.xctest */,
|
||||
3C9ACE242F5DED94009B00D0 /* birch.app */,
|
||||
3C9ACE342F5DED95009B00D0 /* birchTests.xctest */,
|
||||
3C9ACE3E2F5DED95009B00D0 /* birchUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -128,7 +128,7 @@
|
||||
children = (
|
||||
CC0000010000000000000001 /* Base.xcconfig */,
|
||||
CC0000010000000000000002 /* Debug.xcconfig */,
|
||||
CC0000010000000000000004 /* Hellbender.xcconfig */,
|
||||
CC0000010000000000000004 /* Birch.xcconfig */,
|
||||
CC0000010000000000000003 /* Release.xcconfig */,
|
||||
);
|
||||
path = Config;
|
||||
@ -137,9 +137,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
3C9ACE232F5DED94009B00D0 /* hellbender */ = {
|
||||
3C9ACE232F5DED94009B00D0 /* birch */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3C9ACE482F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbender" */;
|
||||
buildConfigurationList = 3C9ACE482F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birch" */;
|
||||
buildPhases = (
|
||||
3C9ACE202F5DED94009B00D0 /* Sources */,
|
||||
3C9ACE212F5DED94009B00D0 /* Frameworks */,
|
||||
@ -150,9 +150,9 @@
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3C9ACE262F5DED94009B00D0 /* hellbender */,
|
||||
3C9ACE262F5DED94009B00D0 /* birch */,
|
||||
);
|
||||
name = hellbender;
|
||||
name = birch;
|
||||
packageProductDependencies = (
|
||||
AA00000500000000000000D0 /* URKit */,
|
||||
AA00000800000000000000D0 /* URUI */,
|
||||
@ -160,13 +160,13 @@
|
||||
3C1E1C452F7B0D99002FDAE2 /* BitcoinDevKit */,
|
||||
3C1E1FA42F7B5F63002FDAE2 /* BitcoinDevKit */,
|
||||
);
|
||||
productName = hellbender;
|
||||
productReference = 3C9ACE242F5DED94009B00D0 /* hellbender.app */;
|
||||
productName = birch;
|
||||
productReference = 3C9ACE242F5DED94009B00D0 /* birch.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
3C9ACE332F5DED95009B00D0 /* hellbenderTests */ = {
|
||||
3C9ACE332F5DED95009B00D0 /* birchTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3C9ACE4B2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbenderTests" */;
|
||||
buildConfigurationList = 3C9ACE4B2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birchTests" */;
|
||||
buildPhases = (
|
||||
3C9ACE302F5DED95009B00D0 /* Sources */,
|
||||
3C9ACE312F5DED95009B00D0 /* Frameworks */,
|
||||
@ -178,18 +178,18 @@
|
||||
3C9ACE362F5DED95009B00D0 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3C9ACE372F5DED95009B00D0 /* hellbenderTests */,
|
||||
3C9ACE372F5DED95009B00D0 /* birchTests */,
|
||||
);
|
||||
name = hellbenderTests;
|
||||
name = birchTests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = hellbenderTests;
|
||||
productReference = 3C9ACE342F5DED95009B00D0 /* hellbenderTests.xctest */;
|
||||
productName = birchTests;
|
||||
productReference = 3C9ACE342F5DED95009B00D0 /* birchTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
3C9ACE3D2F5DED95009B00D0 /* hellbenderUITests */ = {
|
||||
3C9ACE3D2F5DED95009B00D0 /* birchUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 3C9ACE4E2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbenderUITests" */;
|
||||
buildConfigurationList = 3C9ACE4E2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birchUITests" */;
|
||||
buildPhases = (
|
||||
3C9ACE3A2F5DED95009B00D0 /* Sources */,
|
||||
3C9ACE3B2F5DED95009B00D0 /* Frameworks */,
|
||||
@ -201,13 +201,13 @@
|
||||
3C9ACE402F5DED95009B00D0 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3C9ACE412F5DED95009B00D0 /* hellbenderUITests */,
|
||||
3C9ACE412F5DED95009B00D0 /* birchUITests */,
|
||||
);
|
||||
name = hellbenderUITests;
|
||||
name = birchUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = hellbenderUITests;
|
||||
productReference = 3C9ACE3E2F5DED95009B00D0 /* hellbenderUITests.xctest */;
|
||||
productName = birchUITests;
|
||||
productReference = 3C9ACE3E2F5DED95009B00D0 /* birchUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@ -233,7 +233,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 3C9ACE1F2F5DED94009B00D0 /* Build configuration list for PBXProject "hellbender" */;
|
||||
buildConfigurationList = 3C9ACE1F2F5DED94009B00D0 /* Build configuration list for PBXProject "birch" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@ -253,9 +253,9 @@
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
3C9ACE232F5DED94009B00D0 /* hellbender */,
|
||||
3C9ACE332F5DED95009B00D0 /* hellbenderTests */,
|
||||
3C9ACE3D2F5DED95009B00D0 /* hellbenderUITests */,
|
||||
3C9ACE232F5DED94009B00D0 /* birch */,
|
||||
3C9ACE332F5DED95009B00D0 /* birchTests */,
|
||||
3C9ACE3D2F5DED95009B00D0 /* birchUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@ -311,12 +311,12 @@
|
||||
/* Begin PBXTargetDependency section */
|
||||
3C9ACE362F5DED95009B00D0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3C9ACE232F5DED94009B00D0 /* hellbender */;
|
||||
target = 3C9ACE232F5DED94009B00D0 /* birch */;
|
||||
targetProxy = 3C9ACE352F5DED95009B00D0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
3C9ACE402F5DED95009B00D0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3C9ACE232F5DED94009B00D0 /* hellbender */;
|
||||
target = 3C9ACE232F5DED94009B00D0 /* birch */;
|
||||
targetProxy = 3C9ACE3F2F5DED95009B00D0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
@ -326,6 +326,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = CC0000010000000000000002 /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
DEVELOPMENT_TEAM = ZW85AH743B;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -333,22 +334,25 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = CC0000010000000000000003 /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
DEVELOPMENT_TEAM = ZW85AH743B;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
3C9ACE492F5DED95009B00D0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = CC0000010000000000000004 /* Hellbender.xcconfig */;
|
||||
baseConfigurationReference = CC0000010000000000000004 /* Birch.xcconfig */;
|
||||
buildSettings = {
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 25;
|
||||
MARKETING_VERSION = 0.2.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3C9ACE4A2F5DED95009B00D0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = CC0000010000000000000004 /* Hellbender.xcconfig */;
|
||||
baseConfigurationReference = CC0000010000000000000004 /* Birch.xcconfig */;
|
||||
buildSettings = {
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 25;
|
||||
MARKETING_VERSION = 0.2.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -366,7 +370,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hellbender.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hellbender";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/birch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/birch";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -384,7 +388,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hellbender.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hellbender";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/birch.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/birch";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -400,7 +404,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = hellbender;
|
||||
TEST_TARGET_NAME = birch;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -416,14 +420,14 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = hellbender;
|
||||
TEST_TARGET_NAME = birch;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
3C9ACE1F2F5DED94009B00D0 /* Build configuration list for PBXProject "hellbender" */ = {
|
||||
3C9ACE1F2F5DED94009B00D0 /* Build configuration list for PBXProject "birch" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3C9ACE462F5DED95009B00D0 /* Debug */,
|
||||
@ -432,7 +436,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3C9ACE482F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbender" */ = {
|
||||
3C9ACE482F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birch" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3C9ACE492F5DED95009B00D0 /* Debug */,
|
||||
@ -441,7 +445,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3C9ACE4B2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbenderTests" */ = {
|
||||
3C9ACE4B2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birchTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3C9ACE4C2F5DED95009B00D0 /* Debug */,
|
||||
@ -450,7 +454,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3C9ACE4E2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "hellbenderUITests" */ = {
|
||||
3C9ACE4E2F5DED95009B00D0 /* Build configuration list for PBXNativeTarget "birchUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3C9ACE4F2F5DED95009B00D0 /* Debug */,
|
||||
@ -16,9 +16,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3C9ACE232F5DED94009B00D0"
|
||||
BuildableName = "hellbender.app"
|
||||
BlueprintName = "hellbender"
|
||||
ReferencedContainer = "container:hellbender.xcodeproj">
|
||||
BuildableName = "birch.app"
|
||||
BlueprintName = "birch"
|
||||
ReferencedContainer = "container:birch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@ -36,9 +36,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3C9ACE332F5DED95009B00D0"
|
||||
BuildableName = "hellbenderTests.xctest"
|
||||
BlueprintName = "hellbenderTests"
|
||||
ReferencedContainer = "container:hellbender.xcodeproj">
|
||||
BuildableName = "birchTests.xctest"
|
||||
BlueprintName = "birchTests"
|
||||
ReferencedContainer = "container:birch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
@ -47,9 +47,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3C9ACE3D2F5DED95009B00D0"
|
||||
BuildableName = "hellbenderUITests.xctest"
|
||||
BlueprintName = "hellbenderUITests"
|
||||
ReferencedContainer = "container:hellbender.xcodeproj">
|
||||
BuildableName = "birchUITests.xctest"
|
||||
BlueprintName = "birchUITests"
|
||||
ReferencedContainer = "container:birch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
@ -69,9 +69,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3C9ACE232F5DED94009B00D0"
|
||||
BuildableName = "hellbender.app"
|
||||
BlueprintName = "hellbender"
|
||||
ReferencedContainer = "container:hellbender.xcodeproj">
|
||||
BuildableName = "birch.app"
|
||||
BlueprintName = "birch"
|
||||
ReferencedContainer = "container:birch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
@ -86,9 +86,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3C9ACE232F5DED94009B00D0"
|
||||
BuildableName = "hellbender.app"
|
||||
BlueprintName = "hellbender"
|
||||
ReferencedContainer = "container:hellbender.xcodeproj">
|
||||
BuildableName = "birch.app"
|
||||
BlueprintName = "birch"
|
||||
ReferencedContainer = "container:birch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
BIN
birch/Assets.xcassets/AppIcon-Dark.appiconset/AppIcon.png
Normal file
BIN
birch/Assets.xcassets/AppIcon-Dark.appiconset/AppIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
14
birch/Assets.xcassets/AppIcon-Dark.appiconset/Contents.json
Normal file
14
birch/Assets.xcassets/AppIcon-Dark.appiconset/Contents.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
birch/Assets.xcassets/AppIcon.appiconset/AppIcon.png
Normal file
BIN
birch/Assets.xcassets/AppIcon.appiconset/AppIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
14
birch/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
14
birch/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
birch/Assets.xcassets/AppIconPreviewDark.imageset/AppIcon.png
vendored
Normal file
BIN
birch/Assets.xcassets/AppIconPreviewDark.imageset/AppIcon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@ -6,12 +6,10 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
@ -20,4 +18,4 @@
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
birch/Assets.xcassets/AppIconPreviewLight.imageset/AppIcon.png
vendored
Normal file
BIN
birch/Assets.xcassets/AppIconPreviewLight.imageset/AppIcon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
21
birch/Assets.xcassets/AppIconPreviewLight.imageset/Contents.json
vendored
Normal file
21
birch/Assets.xcassets/AppIconPreviewLight.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ import OSLog
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "AppLifecycle")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "AppLifecycle")
|
||||
|
||||
struct ContentView: View {
|
||||
@Query private var wallets: [WalletProfile]
|
||||
@ -100,21 +100,26 @@ private struct PrivacyOverlayView: View {
|
||||
Color.hbBackground
|
||||
.ignoresSafeArea()
|
||||
|
||||
Image("WelcomeIcon")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 120, height: 120)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.stroke(Color.hbBackground, lineWidth: 24)
|
||||
.blur(radius: 12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.strokeBorder(Color.hbBorder.opacity(0.5), lineWidth: 1)
|
||||
)
|
||||
VStack(spacing: 32) {
|
||||
Spacer()
|
||||
|
||||
ThemedAppIcon()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 120, height: 120)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.stroke(Color.hbBackground, lineWidth: 24)
|
||||
.blur(radius: 12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
)
|
||||
|
||||
Text("Birch Wallet")
|
||||
.font(.hbDisplay(34))
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,49 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Hellbender</string>
|
||||
<string>Birch Wallet</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
</dict>
|
||||
<key>CFBundleAlternateIcons</key>
|
||||
<dict>
|
||||
<key>AppIcon-Dark</key>
|
||||
<dict>
|
||||
<key>CFBundleIconFiles</key>
|
||||
<array>
|
||||
<string>AppIcon-Dark</string>
|
||||
</array>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleIcons~ipad</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
</dict>
|
||||
<key>CFBundleAlternateIcons</key>
|
||||
<dict>
|
||||
<key>AppIcon-Dark</key>
|
||||
<dict>
|
||||
<key>CFBundleIconFiles</key>
|
||||
<array>
|
||||
<string>AppIcon-Dark</string>
|
||||
</array>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
@ -23,9 +63,9 @@
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.finance</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Hellbender needs camera access to scan QR codes for importing cosigner keys and signed PSBTs from hardware wallets.</string>
|
||||
<string>Birch needs camera access to scan QR codes for importing cosigner keys and signed PSBTs from hardware wallets.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Hellbender uses Face ID to securely unlock your wallet.</string>
|
||||
<string>Birch uses Face ID to securely unlock your wallet.</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
@ -21,7 +21,7 @@ enum FeeSource: String, CaseIterable {
|
||||
}
|
||||
}
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "BitcoinService")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "BitcoinService")
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
@ -2,7 +2,7 @@ import Foundation
|
||||
import Observation
|
||||
import OSLog
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "FiatPriceService")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "FiatPriceService")
|
||||
|
||||
enum FiatSource: String, CaseIterable {
|
||||
case zeus
|
||||
@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
import SwiftData
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "LabelService")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "LabelService")
|
||||
|
||||
/// Handles label propagation between transactions, UTXOs, and addresses.
|
||||
enum LabelService {
|
||||
@ -3,7 +3,7 @@ import Foundation
|
||||
enum Constants {
|
||||
// MARK: - App
|
||||
|
||||
static let appName = "Hellbender"
|
||||
static let appName = "Birch"
|
||||
static let defaultNetwork: BitcoinNetwork = .testnet4
|
||||
|
||||
// MARK: - BIP48 P2WSH
|
||||
@ -8,7 +8,7 @@ enum LogExporter {
|
||||
static func collectLogs(hours: Double = 1) throws -> String {
|
||||
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
let cutoff = store.position(date: Date().addingTimeInterval(-hours * 3600))
|
||||
let subsystem = Bundle.main.bundleIdentifier ?? "hellbender"
|
||||
let subsystem = Bundle.main.bundleIdentifier ?? "birch"
|
||||
|
||||
let entries = try store.getEntries(at: cutoff, matching: NSPredicate(format: "subsystem == %@", subsystem))
|
||||
|
||||
@ -27,7 +27,7 @@ enum LogExporter {
|
||||
return "No log entries found in the last \(Int(hours)) hour(s)."
|
||||
}
|
||||
|
||||
let header = "Hellbender Logs — Exported \(formatter.string(from: Date()))\n"
|
||||
let header = "Birch Logs — Exported \(formatter.string(from: Date()))\n"
|
||||
+ "Entries: \(lines.count) (last \(Int(hours))h)\n"
|
||||
+ String(repeating: "─", count: 60) + "\n"
|
||||
|
||||
@ -4,7 +4,7 @@ import LocalAuthentication
|
||||
import OSLog
|
||||
import SwiftData
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "AppLock")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "AppLock")
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
@ -3,7 +3,7 @@ import Observation
|
||||
import OSLog
|
||||
import SwiftData
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "BumpFeeViewModel")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "BumpFeeViewModel")
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
@ -3,7 +3,7 @@ import Observation
|
||||
import OSLog
|
||||
import SwiftData
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "SendViewModel")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "SendViewModel")
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
@ -3,7 +3,7 @@ import Observation
|
||||
import OSLog
|
||||
import SwiftData
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "WalletManager")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "WalletManager")
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
@ -3,7 +3,7 @@ import CoreImage.CIFilterBuiltins
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "BBQRDisplayView")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "BBQRDisplayView")
|
||||
|
||||
struct BBQRDisplayView: View {
|
||||
let data: Data
|
||||
@ -78,17 +78,17 @@ struct HBTheme {
|
||||
)
|
||||
|
||||
static let birchLight = HBTheme(
|
||||
background: Color(red: 0.929, green: 0.910, blue: 0.875),
|
||||
surface: Color(red: 0.851, green: 0.824, blue: 0.773),
|
||||
surfaceElevated: Color(red: 0.929, green: 0.910, blue: 0.875),
|
||||
border: Color(red: 0.769, green: 0.741, blue: 0.690),
|
||||
textPrimary: Color(red: 0.165, green: 0.145, blue: 0.125),
|
||||
textSecondary: Color(red: 0.420, green: 0.380, blue: 0.345),
|
||||
accent: Color(red: 0.769, green: 0.584, blue: 0.165),
|
||||
heroBackground: Color(red: 0.851, green: 0.824, blue: 0.773),
|
||||
success: Color(red: 0.353, green: 0.400, blue: 0.259),
|
||||
error: Color(red: 0.549, green: 0.188, blue: 0.125),
|
||||
secondaryAccent: Color(red: 0.353, green: 0.400, blue: 0.259),
|
||||
background: Color(red: 0.949, green: 0.933, blue: 0.902),
|
||||
surface: Color(red: 0.890, green: 0.867, blue: 0.827),
|
||||
surfaceElevated: Color(red: 0.949, green: 0.933, blue: 0.902),
|
||||
border: Color(red: 0.690, green: 0.663, blue: 0.616),
|
||||
textPrimary: Color(red: 0.137, green: 0.122, blue: 0.106),
|
||||
textSecondary: Color(red: 0.310, green: 0.282, blue: 0.251),
|
||||
accent: Color(red: 0.698, green: 0.525, blue: 0.133),
|
||||
heroBackground: Color(red: 0.890, green: 0.867, blue: 0.827),
|
||||
success: Color(red: 0.278, green: 0.373, blue: 0.224),
|
||||
error: Color(red: 0.600, green: 0.180, blue: 0.120),
|
||||
secondaryAccent: Color(red: 0.278, green: 0.373, blue: 0.224),
|
||||
colorScheme: .light
|
||||
)
|
||||
}
|
||||
@ -105,8 +105,8 @@ enum AppTheme: String, CaseIterable {
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .system: "System"
|
||||
case .dark: "Dark"
|
||||
case .light: "Light"
|
||||
case .dark: "Hellbender Dark"
|
||||
case .light: "Hellbender Light"
|
||||
case .birchDark: "Birch Dark"
|
||||
case .birchLight: "Birch Light"
|
||||
}
|
||||
@ -128,7 +128,7 @@ enum AppTheme: String, CaseIterable {
|
||||
@Observable
|
||||
final class ThemeManager {
|
||||
static let shared = ThemeManager()
|
||||
private(set) var theme: HBTheme = .dark
|
||||
private(set) var theme: HBTheme = .birchDark
|
||||
|
||||
private init() {
|
||||
let saved = UserDefaults.standard.string(forKey: Constants.themeKey) ?? AppTheme.system.rawValue
|
||||
@ -143,7 +143,7 @@ final class ThemeManager {
|
||||
/// Sets the displayed theme to the appropriate custom palette for the given OS color scheme.
|
||||
/// Only used when the System theme is selected — does not save to UserDefaults.
|
||||
func applySystemColorScheme(_ colorScheme: ColorScheme) {
|
||||
theme = colorScheme == .dark ? .dark : .light
|
||||
theme = colorScheme == .dark ? .birchDark : .birchLight
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,6 +288,21 @@ extension View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Themed App Icon
|
||||
|
||||
/// Renders the app-icon artwork that matches the current theme's light/dark appearance.
|
||||
/// Uses `AppIconPreviewLight` on light color schemes, `AppIconPreviewDark` on dark.
|
||||
/// The theme is applied via `.preferredColorScheme` at the root, so this works for
|
||||
/// all AppTheme cases (system, birch light/dark).
|
||||
struct ThemedAppIcon: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
var body: some View {
|
||||
Image(colorScheme == .dark ? "AppIconPreviewDark" : "AppIconPreviewLight")
|
||||
.resizable()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Network Badge
|
||||
|
||||
struct NetworkBadge: View {
|
||||
@ -6,7 +6,7 @@ import SwiftUI
|
||||
import URKit
|
||||
import URUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "URScannerSheet")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "URScannerSheet")
|
||||
|
||||
struct URScannerSheet: View {
|
||||
let onResult: (AppURResult) -> Void
|
||||
@ -265,7 +265,7 @@ struct ConnectionStatusView: View {
|
||||
}
|
||||
|
||||
private func copyDebugInfo() {
|
||||
var lines = ["=== Hellbender Debug Info ==="]
|
||||
var lines = ["=== Birch Debug Info ==="]
|
||||
lines.append("Timestamp: \(ISO8601DateFormatter().string(from: Date()))")
|
||||
|
||||
// SwiftData wallet info
|
||||
@ -2,7 +2,7 @@ import OSLog
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "Navigation")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "Navigation")
|
||||
|
||||
struct MainTabView: View {
|
||||
@State private var selectedTab = 0
|
||||
@ -243,7 +243,7 @@ struct BroadcastResultView: View {
|
||||
walletID: walletID
|
||||
)
|
||||
} catch {
|
||||
Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "LabelService")
|
||||
Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "LabelService")
|
||||
.error("Failed to propagate change label: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ import OSLog
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "Settings")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "Settings")
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@ -24,11 +24,6 @@ struct SettingsView: View {
|
||||
// Security
|
||||
AppLockSettingsSection()
|
||||
|
||||
// Appearance
|
||||
Section("Appearance") {
|
||||
AppearanceSettingsRow()
|
||||
}
|
||||
|
||||
// Fee Estimation
|
||||
Section("Fee Estimation") {
|
||||
FeeSettingsRow()
|
||||
@ -39,6 +34,16 @@ struct SettingsView: View {
|
||||
FiatSettingsRow()
|
||||
}
|
||||
|
||||
// Appearance
|
||||
Section("Appearance") {
|
||||
AppearanceSettingsRow()
|
||||
}
|
||||
|
||||
// App Icon
|
||||
Section("App Icon") {
|
||||
AppIconSettingsRow()
|
||||
}
|
||||
|
||||
// About
|
||||
Section("About") {
|
||||
HStack {
|
||||
@ -92,6 +97,108 @@ private struct AppearanceSettingsRow: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - App Icon Settings
|
||||
|
||||
private enum AppIconOption: String, CaseIterable, Identifiable {
|
||||
case light
|
||||
case dark
|
||||
|
||||
var id: String {
|
||||
rawValue
|
||||
}
|
||||
|
||||
/// Name passed to `UIApplication.setAlternateIconName`; `nil` selects the primary icon.
|
||||
var alternateIconName: String? {
|
||||
switch self {
|
||||
case .light: nil
|
||||
case .dark: "AppIcon-Dark"
|
||||
}
|
||||
}
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .light: "Light"
|
||||
case .dark: "Dark"
|
||||
}
|
||||
}
|
||||
|
||||
var previewAssetName: String {
|
||||
switch self {
|
||||
case .light: "AppIconPreviewLight"
|
||||
case .dark: "AppIconPreviewDark"
|
||||
}
|
||||
}
|
||||
|
||||
static var current: AppIconOption {
|
||||
UIApplication.shared.alternateIconName == "AppIcon-Dark" ? .dark : .light
|
||||
}
|
||||
}
|
||||
|
||||
private struct AppIconSettingsRow: View {
|
||||
@State private var selected: AppIconOption = .current
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
ForEach(AppIconOption.allCases) { option in
|
||||
AppIconTile(option: option, isSelected: selected == option) {
|
||||
select(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.hbSurface)
|
||||
}
|
||||
|
||||
private func select(_ option: AppIconOption) {
|
||||
guard selected != option else { return }
|
||||
UIApplication.shared.setAlternateIconName(option.alternateIconName) { error in
|
||||
Task { @MainActor in
|
||||
if let error {
|
||||
logger.error("Failed to set app icon: \(error.localizedDescription, privacy: .public)")
|
||||
} else {
|
||||
logger.info("App icon changed to \(option.displayName, privacy: .public)")
|
||||
selected = option
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AppIconTile: View {
|
||||
let option: AppIconOption
|
||||
let isSelected: Bool
|
||||
let onTap: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: onTap) {
|
||||
VStack(spacing: 8) {
|
||||
Image(option.previewAssetName)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 72, height: 72)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
.stroke(isSelected ? Color.hbBitcoinOrange : Color.hbBorder, lineWidth: isSelected ? 3 : 1)
|
||||
)
|
||||
|
||||
HStack(spacing: 4) {
|
||||
if isSelected {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.system(size: 12))
|
||||
.foregroundStyle(Color.hbBitcoinOrange)
|
||||
}
|
||||
Text(option.displayName)
|
||||
.font(.hbBody(13))
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Denomination Settings
|
||||
|
||||
private struct DenominationSettingsRow: View {
|
||||
@ -2,7 +2,7 @@ import OSLog
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "UTXODetail")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "UTXODetail")
|
||||
|
||||
struct UTXODetailView: View {
|
||||
let utxo: UTXOItem
|
||||
@ -3,7 +3,7 @@ import SwiftData
|
||||
import SwiftUI
|
||||
import URKit
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "WalletInfo")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "WalletInfo")
|
||||
|
||||
struct WalletInfoView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@ -2,7 +2,7 @@ import OSLog
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "TransactionDetailView")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "TransactionDetailView")
|
||||
|
||||
struct TransactionDetailView: View {
|
||||
let transaction: TransactionItem
|
||||
@ -3,7 +3,7 @@ import SwiftData
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbender", category: "TransactionListView")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "birch", category: "TransactionListView")
|
||||
|
||||
struct TransactionListView: View {
|
||||
@Query private var wallets: [WalletProfile]
|
||||
@ -75,7 +75,7 @@ struct WalletVerifyView: View {
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
}
|
||||
|
||||
Text("The output descriptor is your **only** recovery path. If you lose Hellbender (phone dies, app deleted, data corrupted), the descriptor is the only thing needed to rebuild the wallet in any compatible coordinator (Sparrow, Nunchuk, etc.). Without it, you'd need to re-gather all cosigner xpubs and reconstruct the exact same configuration — which may not be possible.")
|
||||
Text("The output descriptor is your **only** recovery path. If you lose Birch (phone dies, app deleted, data corrupted), the descriptor is the only thing needed to rebuild the wallet in any compatible coordinator (Sparrow, Nunchuk, etc.). Without it, you'd need to re-gather all cosigner xpubs and reconstruct the exact same configuration — which may not be possible.")
|
||||
.font(.hbBody(13))
|
||||
.foregroundStyle(Color.hbTextSecondary)
|
||||
|
||||
@ -140,7 +140,7 @@ struct WalletVerifyView: View {
|
||||
.font(.hbHeadline)
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
|
||||
Text("Verifying your first receive address confirms that Hellbender built the correct output descriptor and will generate the same addresses as your cosigner devices. If the addresses don't match, funds sent to this wallet could be unspendable.")
|
||||
Text("Verifying your first receive address confirms that Birch built the correct output descriptor and will generate the same addresses as your cosigner devices. If the addresses don't match, funds sent to this wallet could be unspendable.")
|
||||
.font(.hbBody(13))
|
||||
.foregroundStyle(Color.hbTextSecondary)
|
||||
|
||||
@ -8,8 +8,7 @@ struct WelcomeStepView: View {
|
||||
Spacer()
|
||||
|
||||
// Icon
|
||||
Image("WelcomeIcon")
|
||||
.resizable()
|
||||
ThemedAppIcon()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 120, height: 120)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
@ -19,13 +18,9 @@ struct WelcomeStepView: View {
|
||||
.blur(radius: 12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 28, style: .continuous))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 28, style: .continuous)
|
||||
.strokeBorder(Color.hbBorder.opacity(0.5), lineWidth: 1)
|
||||
)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
Text("Hellbender Wallet")
|
||||
Text("Birch Wallet")
|
||||
.font(.hbDisplay(34))
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user