fix(swift-example-app): route orphan recovery to per-network managers#3612
Conversation
When restoring orphan-mnemonic wallets after a reinstall, every recovered wallet was landing under the active network — so a regtest mnemonic recovered from a Testnet active context ended up persisted as testnet, with the user's Local tab stuck empty until they manually switched and re-recovered. Three issues stacked together: 1. The Rust `PlatformWalletManager` is network-locked and stamps `wallet.network = self.sdk.network` at registration time. The recovery flow always called `createWallet` on the env-injected active manager, so the wallet's persisted network reflected the wrong manager rather than the wallet's original network from keychain metadata. Fixed by adding `WalletManagerStore.backgroundManager(for:)` that lazy-builds a per-network manager (and SDK) without flipping the user-visible active network, and routing each orphan to the manager for its `metadata.resolvedNetworks.first`. 2. `SDK(network: .regtest)` was failing with `DAPI addresses not available for network: Regtest` whenever `useDockerSetup` had been auto-disabled by leaving regtest. Regtest has no remote DAPI on the Rust side, so the SDK constructor now always uses local DAPI for regtest regardless of the toggle. 3. After `createWallet`, the new row lived in the target manager's background `ModelContext`, but the launch-time main context didn't pick up the cross-context save in time for the post-recovery `isImported` flush, so `@Query` consumers never re-evaluated and the wallet only appeared after the next launch. Pre-warming per-network managers for orphan networks at bootstrap, plus a short retry on the `mainContext.fetch` after `createWallet`, makes the wallet appear in its correct tab on the same run.
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThe PR extends the wallet manager store with background per-network manager materialization while hardening regtest DAPI setup requirements. It modifies the app to recover orphan-network wallets using background managers with cross-context retry-fetch logic and pre-warms wallet managers for orphan networks during bootstrap. ChangesMulti-Network Wallet Recovery with Background Manager Materialization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review GateCommit:
|
|
✅ DashSDKFFI.xcframework built for this PR.
SwiftPM (host the zip at a stable URL, then use): .binaryTarget(
name: "DashSDKFFI",
url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
checksum: "21bc88957ac7d4e5ccdcd8676381e879ec43d44b4c0db87ccb9e7466822d1a60"
)Xcode manual integration:
|
Issue being fixed or feature implemented
When restoring orphan-mnemonic wallets after a reinstall, every recovered wallet was landing under the active network — so a regtest mnemonic recovered while the user was on Testnet ended up persisted with
networkRaw = testnet, and the Local tab stayed stuck empty until the user manually switched networks and re-ran recovery.Three issues stacked on top of each other to produce the observed UX:
PlatformWalletManageris network-locked andregister_walletrecordswallet.network = self.sdk.network.ContentView.recoverWalletalways calledcreateWalleton the env-injected activewalletManager, so the persisted row's network reflected the active manager — not the original network read from keychain metadata.OptionsViewauto-disablesuseDockerSetupwhen leaving regtest. With the toggle off,SDK(network: .regtest)returnedDAPI addresses not available for network: Regtestbecause regtest has no remote DAPI defaults on the Rust side — so even when recovery tried to route to the regtest manager, it couldn't build one.ModelContextand the launch-time main context's@Queryconsumers didn't observe it in time for the post-recoveryisImportedflush. The wallet only appeared in the correct tab after the next app launch when the main context was rebuilt fresh from disk.What was done?
WalletManagerStore.backgroundManager(for:)and amakeActive: Booloverload onactivate(...). The recovery flow now resolves each orphan's original network from keychain metadata and callscreateWalleton the manager for that network, lazy-building one (with its own SDK) without changing the user's currently visible network.SDK(network:)now forces local DAPI for regtest unconditionally — theuseDockerSetuptoggle still controls non-regtest networks, but for regtest local DAPI is the only valid option, so building it on demand from any context now succeeds.createWallet,recoverWalletretries themainContext.fetchup to 10× with a 50 ms yield between attempts so the cross-context merge has time to land before we move on; once the fetch finds the row, the existingrow.isImported = true; modelContext.save()block fires and@Queryre-evaluates immediately.How Has This Been Tested?
Manual end-to-end on iOS Simulator (iPhone 17, iOS 26.3):
useDockerSetupis off at the time of recovery (the new SDK constructor branch covers this).xcodebuild -project SwiftExampleApp/SwiftExampleApp.xcodeproj -scheme SwiftExampleApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17,arch=arm64' build→BUILD SUCCEEDED.Breaking Changes
None. New APIs are additive (
WalletManagerStore.backgroundManager(for:),activate(...makeActive:)overload with default-true preserves the existing call sites). TheSDK(network:)change for regtest only affects a previously-failing path.Checklist:
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
Bug Fixes
Improvements