DashSync migration part 1#1
Closed
podkovyrin wants to merge 8 commits intodevelopfrom
Closed
Conversation
bfoss765
added a commit
that referenced
this pull request
Nov 13, 2025
…rized commits Updated both CLAUDE.md and AI-DEVELOPMENT-GUIDE.md with much stronger and clearer language about the critical requirement to NEVER commit or push without explicit user permission. Changes include: - Added prominent visual markers (emojis, bold text) to catch attention - Declared this the #1 rule and #1 user complaint - Provided concrete examples of violations vs correct behavior - Listed specific phrases that grant permission - Made it absolutely clear there are NO EXCEPTIONS to this rule This addresses the recurring issue where AI assistants commit changes without waiting for user permission. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7 tasks
llbartekll
added a commit
that referenced
this pull request
Apr 8, 2026
The SDK's `ModelContainerHelper.createContainer()` is unusable in dashwallet-ios for two compounding reasons that the SDK author never hit because `SwiftExampleApp/` has neither App Group nor iCloud entitlements: 1. **CloudKit auto-validation rejects the SDK's schema.** dashwallet-ios has both an App Group entitlement (Watch app + Today extension data sharing) and an iCloud entitlement. SwiftData's default `cloudKitDatabase: .automatic` and `groupContainer: .automatic` auto-detect both and try to register the container as a CloudKit store under the App Group container. The validator then enumerates ~120 violations across the SDK's 10 `@Model` types — non-optional attributes without defaults, non-optional relationships, missing relationship inverses (PersistentIdentity↔PersistentTokenBalance), and ubiquitous `@Attribute(.unique)` constraints which CloudKit doesn't support at all. The whole container init throws `SwiftDataError #1` (loadIssueModelContainer). 2. **Threading.** Even with the CloudKit issue worked around, the SDK helper's runtime schema registration only succeeds reliably when called from main on iOS 17. The SDK example app uses a `@MainActor`-isolated `UnifiedAppState.init()` to enforce this (`SwiftExampleApp/UnifiedAppState.swift:64`). Our migrator, coordinator, wallet creator, and wallet wiper all dispatched to `DispatchQueue.global(qos: .userInitiated)` and called the helper off-main, which would race even if CloudKit weren't an issue. Fix: introduce `SwiftDashSDKContainer.swift`, a shared `@objc(DWSwiftDashSDKContainer)` singleton that owns a `ModelContainer` constructed once on the main thread. The container is built directly via `ModelConfiguration(...)` with explicit `cloudKitDatabase: .none`, an explicit store URL under `Library/Application Support/SwiftDashSDKLocal/HDWallet.store`, and a minimal `Schema([HDWallet.self])` — bypassing the SDK helper entirely. The 9 platform `Persistent*` models are unused by dashwallet-ios so omitting them from the schema is harmless. `AppDelegate.application:didFinishLaunchingWithOptions:` calls `[DWSwiftDashSDKContainer warmUp]` BEFORE the seed migrator, the SPV coordinator, the wallet creator, and the wallet wiper. The warmUp is synchronous, runs on main (precondition-enforced), and creates the container exactly once per app launch. All four downstream callers now read `SwiftDashSDKContainer.modelContainer` from their background queues and construct their own background `ModelContext` against it, which Apple documents as the supported pattern. Reads from background queues are safe because the `dispatch_async` calls that schedule the background work establish a happens-before edge with the warmUp write. Each caller handles the nil case (warmUp failed on main) by logging a clear error and either rolling back any partial state (migrator/creator) or refusing to proceed (coordinator/wiper). Also bumps the SPV coordinator's seed-migrator polling timeout from 30s to 120s — covers slow simulator boot + the migrator's heavy work + a generous buffer for first-launch SwiftData store creation. Verified end-to-end: imported a wallet via the recover flow (which calls `WalletCreator.importWallet`), the wallet creator wrote an HDWallet record to the new sandbox store, and the SPV coordinator read it back and started syncing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
llbartekll
added a commit
that referenced
this pull request
Apr 8, 2026
…ause The fix in 0b70276 is structurally correct, but the explanatory comments around it were written from an early hypothesis that turned out to be wrong. An exploration agent's research suggested the SDK's ModelContainerHelper.createContainer() was racing schema registration when invoked from a background queue on iOS 17, and the SDK's example app uses a @MainActor-isolated init for the same reason. I implemented the shared-container fix on that basis. Two rounds of testing later we discovered the actual root cause from a CoreData log dump: dashwallet-ios has both an iCloud entitlement and an App Group entitlement. SwiftData's defaults `cloudKitDatabase: .automatic` and `groupContainer: .automatic` auto-detect both, register the SDK's 10-model schema as a CloudKit-backed store under the App Group container, and then fail validation because the SDK's `Persistent*` models violate ~120 CloudKit rules (non-optional attributes without defaults, unique constraints, missing relationship inverses, non-optional relationships). The bypass — minimal `Schema([HDWallet.self])`, explicit `cloudKitDatabase: .none`, explicit store URL under Library/Application Support/SwiftDashSDKLocal — works end-to-end and is verified by an actual SPV sync running against a wallet imported via the recover flow. But the comments still tell the iOS-17 threading story. Cleanup, six edits across five files plus one numeric constant: - AppDelegate.m: reframe the warmUp block comment from "races schema registration on iOS 17" → "fails CloudKit validation on iCloud + App Group entitled apps". - SwiftDashSDKContainer.swift: fix the file-header bullet that claimed we set `groupContainer: .none` (we don't — the explicit `url:` overrides any group-container default, which is correct behavior, but the comment lied about the mechanism). Soften the `warmUp()` doc to say the main-thread requirement is defensive practice matching the SDK example app, not load-bearing for SwiftData. The Thread.isMainThread precondition stays — it's free belt-and-suspenders against any future SwiftData regression. - SwiftDashSDKKeyMigrator.swift, SwiftDashSDKWalletCreator.swift, SwiftDashSDKWalletWiper.swift: drop the "throws SwiftDataError #1 on iOS 17" attribution from each background-queue context comment. Replace with "the SDK helper fails CloudKit validation on entitled apps; see SwiftDashSDKContainer.swift for the full rationale". - SwiftDashSDKKeyMigrator.swift, SwiftDashSDKSPVCoordinator.swift: drop "main-thread warmUp must have failed" from error logs and the user-visible error string. warmUp() can fail for filesystem or schema reasons too, not just threading. New phrasing is "warmUp() failed; check Console.app for `📦 SDKBOX` logs". - SwiftDashSDKSPVCoordinator.swift: revert `seedMigratorWaitTimeout` from 120.0 → 30.0. The bump to 120s came in during the diagnostic round with a rationale ("buffer for first-launch SwiftData store creation") that's now obsolete because the store is created upfront in `warmUp()` BEFORE the coordinator's polling loop even starts. The migrator's heavy work is ~300-500 ms; 30 s was always plenty. Pure documentation hygiene plus one numeric constant. Zero behavior changes. Both dashwallet and dashpay targets build clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
llbartekll
added a commit
that referenced
this pull request
Apr 9, 2026
M6 stopped DashSync's SPV but left BalanceModel reading from DSWallet.balance, which is now frozen at whatever DashSync had cached the moment M6 ran. This commit unfreezes the home screen balance by sourcing it from SwiftDashSDK via a new SwiftDashSDKWalletState singleton. A new SwiftDashSDKWalletState class is the right home for wallet-side @published state: SPV chain sync (handled by SwiftDashSDKSPVCoordinator) and wallet state (balance, transactions, addresses) are different concerns even though the FFI couples their event delivery. Future follow-ups for transactions (#6), addresses (#1), and identities (#16) will live alongside `balance` in this class instead of bloating the SPV coordinator. - Add SwiftDashSDKWalletState singleton with @published var balance, applyBalance(_:), seedInitialBalance(walletManager:walletId:), and clearBalance() methods. Holds the WalletBalance struct (4 UInt64 fields, with `total` and `spendable` computed). 💰 WALLET :: log tag. - SwiftDashSDKSPVCoordinator: WalletEventsHandler.onBalanceUpdated becomes a thin forwarder to SwiftDashSDKWalletState.shared.applyBalance. performStart calls SwiftDashSDKWalletState.shared.seedInitialBalance after walletManager.importWallet succeeds. Coordinator no longer owns any wallet-side @published state. - BalanceModel subscribes to SwiftDashSDKWalletState.shared.\$balance via Combine and reads from .balance?.total instead of DWEnvironment.sharedInstance().currentWallet.balance. - SwiftDashSDKWalletWiper calls SwiftDashSDKWalletState.shared.clearBalance() after the SwiftData wipe so post-wipe state doesn't show the previous wallet's balance. - Register the new file in both dashwallet and dashpay targets in project.pbxproj (UUID family A5D5DD000000000000010C/D/E). Other DashSync balance consumers (BalanceNotifier, SendAmountModel, DWPhoneWCSessionManager, DashPay/CrowdNode/CoinJoin/DashSpendPay) still read from DSWallet and remain stale. Each gets its own follow-up commit. Aim of this commit is the smallest change that fixes the most visible part of the M6 regression — the home screen number — with the right architectural shape so #6 and beyond can build on it cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
llbartekll
added a commit
that referenced
this pull request
Apr 17, 2026
Migrates the receive-address read path from DSAccount.receiveAddress to WalletManager.getReceiveAddress(walletId:) via the new thin shim SwiftDashSDKReceiveAddressReader, mirroring the AddressValidator pattern. Eleven production call sites switched in one cut, with DWReceiveModel re-fetching on transactionsDidChange so the displayed address advances as SPV catches up after first launch post-migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.