Skip to content

refactor: extract RelayUtils.swift from AppState (Stage 1)#30

Merged
DocNR merged 1 commit into
mainfrom
refactor/relay-utils
May 7, 2026
Merged

refactor: extract RelayUtils.swift from AppState (Stage 1)#30
DocNR merged 1 commit into
mainfrom
refactor/relay-utils

Conversation

@DocNR
Copy link
Copy Markdown
Owner

@DocNR DocNR commented May 7, 2026

Summary

Stage 1 of the AppState god-object split — extracts three pure parallel-fanout helpers (connectToRelays, publishEventToRelays, fetchEventsFromRelays) plus their _testOnly* DEBUG shims out of Clave/AppState.swift into a new Shared/RelayUtils.swift namespace. Zero behavior change.

Why this stage first

  • Lowest-risk extraction in the 4-stage plan: methods are already pure (no self. references), already side-effect-free, already covered by unit tests.
  • Establishes the extraction pattern (caseless enum namespace, matching Shared/LogExporter.swift precedent) for the higher-risk stages that follow.
  • AppState.swift drops from 2,338 → 2,260 LOC — small absolute win, but the ratio of surface area to risk is the cleanest of the four stages.

Design decisions (locked during brainstorm)

Decision Choice Reasoning
Type shape Caseless enum RelayUtils with static methods Matches LogExporter.swift precedent; uninstantiable namespace
_testOnly* wrappers Dropped Existed only because originals were private; with extraction the methods are internal-by-default
Actor isolation None (plain static func ... async) Matches today's behavior; LightRelay is already @unchecked Sendable
Logger category None Stage 1 is structural-only; richer per-relay error reporting belongs in the existing BACKLOG follow-up
Test file naming Renamed AppStateMultiRelayHelpersTestsRelayUtilsTests Matches Clave's "what's tested" convention

What changed

  • NEW Shared/RelayUtils.swift (71 LOC) — caseless enum namespace with the three static async methods. Function bodies and doc comments preserved byte-for-byte.
  • EDIT Clave/AppState.swift — deleted lines 2261–2337 (the // MARK: - Multi-relay helpers section + #if DEBUG test shims); updated 3 call sites in handleNostrConnect from connectToRelays(...)RelayUtils.connectToRelays(...), etc.
  • RENAME + EDIT ClaveTests/AppStateMultiRelayHelpersTests.swiftClaveTests/RelayUtilsTests.swift — 4 tests, same assertions, dropped the now-unnecessary let appState = AppState() setup line.
  • EDIT Clave.xcodeproj/project.pbxproj — 4 entries added for RelayUtils.swift (Clave target only, NSE excluded since the methods are nostrconnect-handshake helpers, not signing-path code).

What this PR does NOT change

Test plan

  • xcodebuild test -scheme Clave -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.4' -skip-testing:ClaveUITests on main: 240 passed / 0 failed
  • Same command on refactor/relay-utils: 240 passed / 0 failed ✅ (identical count; new RelayUtilsTests class replaces AppStateMultiRelayHelpersTests 1:1 with same 4 tests)
  • Visual diff: only deletions in the multi-relay helpers section + 3 1-line call-site edits. No surrounding code touched.
  • Build 64 archive + on-device noStrudel control trace (gated on this PR + the follow-up pbxproj-bump PR landing)

Follow-up PR

chore: bump pbxproj to build 64 — separate PR after this one merges, matching the #27/#28 pattern. Build 64 internal-TF only; on-device verification with the new [Pair] log category from #27 will confirm no nostrconnect handshake regression.

🤖 Generated with Claude Code

Stage 1 of the AppState god-object split (BACKLOG High Priority).
Moves connectToRelays / publishEventToRelays / fetchEventsFromRelays
plus the _testOnly* DEBUG shims out of AppState.swift into a new
caseless `enum RelayUtils` namespace under Shared/.

Zero behavior change:
- Function bodies and doc comments preserved byte-for-byte.
- Silent error swallow unchanged (deferred to BACKLOG follow-up
  "Better error-message detail in nostrconnect Activity log").
- Actor isolation unchanged (plain async, non-isolated).

Design decisions:
- enum (not struct) namespace, matching Shared/LogExporter.swift.
  Caseless enum is uninstantiable; signals "namespace, not value type".
- Drop _testOnly* wrappers entirely. They existed only because the
  originals were `private`; with extraction the methods are
  internal-by-default and tests call RelayUtils.* directly.
- No Logger added. Stage 1 is structural-only; richer per-relay
  error reporting belongs in the existing BACKLOG follow-up.

Tests:
- AppStateMultiRelayHelpersTests.swift renamed to RelayUtilsTests.swift
  (matches Clave's "what's tested" naming convention).
- 4 tests, same assertions, same inputs. Drop `let appState = AppState()`
  setup line — no longer needed.

Verification:
- xcodebuild test -skip-testing:ClaveUITests on iPhone 17 / iOS 26.4:
  240 passed / 0 failed in both pre-baseline and post-refactor runs.
- AppState.swift -78 lines net (2338 -> 2260).
- New file 71 LOC; pbxproj +4 entries (Clave target only, NSE excluded).

Stages 2-4 of the AppState split (LegacyMigrations, ProfileFetcher,
NostrConnectCoordinator, ProxyClient, AccountManager,
PendingApprovalCoordinator) are out of scope for this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@DocNR DocNR merged commit dcfa725 into main May 7, 2026
@DocNR DocNR deleted the refactor/relay-utils branch May 7, 2026 19:23
DocNR added a commit that referenced this pull request May 7, 2026
…31)

Internal-only build for on-device verification of the AppState
RelayUtils.swift extraction (#30). Bumps CURRENT_PROJECT_VERSION
63 -> 64 across all 4 targets (Clave, ClaveTests, ClaveUITests,
ClaveNSE) in both Debug and Release configs.

Stage 1 verification gates:
- xcodebuild test passes identically pre and post (240/240) ✓
- Build 64 archives + uploads to TestFlight (this PR enables)
- noStrudel (or Jumble) nostrconnect control trace shows no
  divergence from a build-63 baseline (gates Stage 2 start)

Stages 2-4 of the AppState god-object split remain in BACKLOG;
each stage gets its own refactor + bump + verify cycle.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant