Skip to content

Conversation

@alltheseas
Copy link
Collaborator

@alltheseas alltheseas commented Oct 21, 2025

problem

fixes "nil" crash from TestFlight error review #3279

solution overview

  • Replaces the force-unwrapped damus_state! usages in ContentView with a guarded withDamusState helper so sheets, overlays, and URL handlers only run when the state is ready—resolving
    the TestFlight crash (“Optional unwrap in ContentView... Unexpectedly found nil”).
  • Boots DamusState once via .task, defers post-connect setup until initialization is complete, and logs/returns early when lifecycle notifications arrive before the state is
    available.
  • Confirms ShareExtension builds cleanly via xcodebuild -scheme ShareExtension -configuration Debug -destination 'generic/platform=iOS Simulator' build, while the simulator now exits
    the loading spinner and shows the timeline.

testing

ran in xcode iOS Simulator to ensure damus compiles, runs, and does not crash. could not recreate a crash

technical description for @danieldaquino

Technical Overview

  - damus/damus/ContentView.swift now boots DamusState exactly once via .task { await startInitialConnectIfNeeded() }, so the initial spinner transitions only after connect() finishes.
      - Added @State flags (hasStartedInitialConnect, hasCompletedPostConnectSetup) to fence the initialization path; connect() is marked @MainActor and hands off to
        runPostConnectionSetupIfNeeded when the state exists.
  - Every sheet/overlay/notification entry point calls the guarded withDamusState helper at damus/damus/ContentView.swift:253, returning AnyView and early-exiting when damus_state is
    still nil. This replaces the prior damus_state! force-unwraps that triggered the TestFlight “Unexpectedly found nil” crash.
  - URL / wallet / follow handlers (e.g., attachOpenURLAndTimerHandlers and attachNotificationHandlers) now bail with structured logging when invoked before initialization and resume
    work with a concrete state value; lifecycle callbacks reuse the post-connect helper instead of duplicating setup.
  - The main navigation container was factored into contentView(for:), mainNavigation(for:), and tabContainer(for:) to keep the view tree simpler and reduce SwiftUI’s type-checking load
    that had been causing ShareExtension compile failures on the local relay branch.

  Verification

  - CLI build: xcodebuild -scheme ShareExtension -configuration Debug -destination 'generic/platform=iOS Simulator' build
  - Simulator: confirmed the app leaves the loading spinner and loads the timeline.

  Please look closely at flows that previously relied on early damus_state (sheet presentation, notification handlers, background reactivation) to ensure no regressions with the guarded
  logic.


@alltheseas alltheseas changed the title Refine ContentView damus_state bootstrap flow Fix TestFlight erro: Unexpectedly found nil” (damus/ContentView.swift:305) Refine ContentView damus_state bootstrap flow Oct 21, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses a TestFlight crash caused by force-unwrapping damus_state before it was fully initialized. The solution refactors ContentView to use optional DamusState? with proper guards and a dedicated withDamusState helper to safely handle state-dependent UI elements and URL handlers.

Key changes:

  • Replaced force-unwrapped damus_state! with optional damus_state? and guard clauses throughout
  • Introduced bootstrap flow control via hasStartedInitialConnect and hasCompletedPostConnectSetup flags
  • Refactored view construction into separate methods (mainNavigation, attachOpenURLAndTimerHandlers, etc.) with proper nil checks

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

let settings = UserSettingsStore.globally_load_for(pubkey: pubkey)

let new_relay_filters = await load_relay_filters(pubkey) == nil
_ = load_relay_filters(pubkey)
Copy link

Copilot AI Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value of load_relay_filters(pubkey) is being discarded with _, but the previous code checked if the result was nil (new_relay_filters). If the return value is no longer needed, this is acceptable, but if it was being used elsewhere (e.g., to determine if filters were newly loaded), discarding it may remove important logic. Consider verifying whether the new_relay_filters variable was used later in the function or elsewhere in the codebase.

Suggested change
_ = load_relay_filters(pubkey)
let new_relay_filters = load_relay_filters(pubkey)
if new_relay_filters != nil {
// If relay filters were newly loaded, perform any necessary logic here.
// For example, you might want to log, initialize, or trigger updates.
print("Relay filters were newly loaded for pubkey: \(pubkey)")
}

Copilot uses AI. Check for mistakes.
@alltheseas alltheseas changed the title Fix TestFlight erro: Unexpectedly found nil” (damus/ContentView.swift:305) Refine ContentView damus_state bootstrap flow Fix TestFlight error: Unexpectedly found nil” (damus/ContentView.swift:305) Refine ContentView damus_state bootstrap flow Oct 21, 2025
@danieldaquino
Copy link
Collaborator

Thank you @alltheseas for this!

Do you have rough stats on how often you were seeing the crash before the fix?

@danieldaquino danieldaquino added the pr-in-queue The author has multiple active PRs. So this has been placed in a queue behind their other PRs. label Oct 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-in-queue The author has multiple active PRs. So this has been placed in a queue behind their other PRs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants