From 16f4844969745227d95b28201180233df5a09a71 Mon Sep 17 00:00:00 2001 From: williams Date: Mon, 19 Oct 2026 08:16:43 +0100 Subject: [PATCH 1/4] docs: plan the Swift concurrency adoption (#500) Closes #500 Co-Authored-By: Claude Haiku 5.5 --- docs/migration/swift-concurrency-plan.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/migration/swift-concurrency-plan.md diff --git a/docs/migration/swift-concurrency-plan.md b/docs/migration/swift-concurrency-plan.md new file mode 100644 index 00000000..e2987138 --- /dev/null +++ b/docs/migration/swift-concurrency-plan.md @@ -0,0 +1,24 @@ +# Swift concurrency adoption plan + +Tracks issue #500. + +## Current state + +- 38 Swift files use `async`. +- 25 files still use completion handlers or `DispatchQueue`. The largest are + `Sources/App/AppDelegate.swift`, `Sources/Services/AppAttestService.swift`, + `Sources/Services/CertificatePinning.swift`, `Sources/Services/NotificationService.swift`, + `Sources/Services/SilentNotificationService.swift`, `Sources/Services/SpotlightService.swift` + and `Sources/Services/VaultEventSocket.swift`. +- 64 places already use `@MainActor` or `actor`. + +## Plan, in order + +1. Services called only from `async` code: move `NotificationService` and `SpotlightService` to + `async` functions first. Their callers already await. +2. `VaultEventSocket`: wrap the socket callbacks in an `AsyncStream`, keeping the current API for + the callers that have not moved yet. +3. `AppDelegate` and `CertificatePinning` last. Both run at launch and in delegate callbacks, so + they need the most review. +4. `Sendable`: add it to types that cross actor boundaries as each service moves. +5. Actor isolation: keep UI-facing types on `@MainActor`, as they already are. From d64a604db4e73af31bb4492a5affb9cf3af7f119 Mon Sep 17 00:00:00 2001 From: williams Date: Mon, 19 Oct 2026 08:51:37 +0100 Subject: [PATCH 2/4] docs: audit Android SavedStateHandle use (#497) Deep link view model and rate limiter already keep state in SavedStateHandle. The notes list the remaining view models to check. Closes #497 Co-Authored-By: Claude Haiku 5.5 --- docs/migration/android-saved-state.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docs/migration/android-saved-state.md diff --git a/docs/migration/android-saved-state.md b/docs/migration/android-saved-state.md new file mode 100644 index 00000000..cfcda734 --- /dev/null +++ b/docs/migration/android-saved-state.md @@ -0,0 +1,18 @@ +# Android SavedStateHandle audit + +Tracks issue #497. + +## Finding + +Process-death state is already kept with `SavedStateHandle` in the places that need it: + +- `android/app/src/main/java/com/ethosprotocol/ui/DeepLinkViewModel.kt` +- `android/app/src/main/java/com/ethosprotocol/services/DeepLinkRateLimiter.kt`, which takes + the handle as a parameter for its cooldowns, as the TwoFactorViewModel OTP limit does. + +## Remaining work + +1. Check the other view models in `ui/ViewModels.kt` (`AuthViewModel`, `SessionsViewModel`, + `TwoFactorViewModel`) for state that is lost on process death, and move only that state to + `SavedStateHandle`. +2. Add a restore test for each view model that changes. From fdecde468cac94b30d377798d31f0b16e9955787 Mon Sep 17 00:00:00 2001 From: williams Date: Mon, 19 Oct 2026 08:51:37 +0100 Subject: [PATCH 3/4] docs: record the decision on @dynamicMemberLookup for view models (#498) Closes #498 Co-Authored-By: Claude Haiku 5.5 --- docs/migration/dynamic-member-lookup-decision.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/migration/dynamic-member-lookup-decision.md diff --git a/docs/migration/dynamic-member-lookup-decision.md b/docs/migration/dynamic-member-lookup-decision.md new file mode 100644 index 00000000..443efdae --- /dev/null +++ b/docs/migration/dynamic-member-lookup-decision.md @@ -0,0 +1,9 @@ +# @dynamicMemberLookup on ViewModels: decision + +Tracks issue #498. + +## Decision + +Not adopted. `@dynamicMemberLookup` forwards property names through a subscript, so the compiler +no longer checks that a member exists. A typo compiles and fails at runtime. The view models +use plain properties, which are checked at compile time, and the verbosity it removes is small. From 79c97ea5947be37e34e78e76e7e12026742fc99a Mon Sep 17 00:00:00 2001 From: williams Date: Mon, 19 Oct 2026 08:51:37 +0100 Subject: [PATCH 4/4] docs: record the current Hilt and Compose integration (#499) Closes #499 Co-Authored-By: Claude Haiku 5.5 --- docs/migration/hilt-compose-state.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docs/migration/hilt-compose-state.md diff --git a/docs/migration/hilt-compose-state.md b/docs/migration/hilt-compose-state.md new file mode 100644 index 00000000..064eecfd --- /dev/null +++ b/docs/migration/hilt-compose-state.md @@ -0,0 +1,18 @@ +# Hilt with Compose: current state + +Tracks issue #499. + +## Finding + +The integration is already in place: + +- Nine view models carry `@HiltViewModel` (`android/app/src/main/java/com/ethosprotocol/ui/`), + including `AuthViewModel`, `SessionsViewModel`, `TwoFactorViewModel`, `DeepLinkViewModel` and + `NotificationPreferencesViewModel`. +- Compose screens obtain them through Hilt rather than by building them by hand. + +## Remaining work + +1. Add a short pattern note to `docs/` that shows `hiltViewModel()` in a composable and the + `@HiltViewModel` class beside it. +2. Check that every screen in `ui/screens/` gets its view model through `hiltViewModel()`.