Skip to content

[FSSDK-12346] fix: defer NWPathMonitor creation to avoid crash during multi-SDK startup#650

Merged
muzahidul-opti merged 5 commits into
masterfrom
fix/lazy-nwpathmonitor-init
Jul 8, 2026
Merged

[FSSDK-12346] fix: defer NWPathMonitor creation to avoid crash during multi-SDK startup#650
muzahidul-opti merged 5 commits into
masterfrom
fix/lazy-nwpathmonitor-init

Conversation

@muzahidul-opti

Copy link
Copy Markdown
Contributor

Summary

  • Deferred NWPathMonitor creation from NetworkReachability.init to first shouldBlockNetworkAccess() call
  • Removed force casts (as! NWPathMonitor) in favor of local variable binding
  • Added monitorStarted flag in stop() to prevent late monitor creation after teardown
  • Added tests verifying monitor is not created during init and not created after stop()

In Flutter apps with multiple SDKs (Firebase, Datadog, MParticle) on iOS 16, NWPathMonitor was eagerly created during DefaultEventDispatcher stored-property initialization. This caused a null function pointer crash (EXC_BAD_ACCESS at 0x0) due to dyld contention over the weak-linked Network framework during concurrent framework loading. The crash did not reproduce on iOS 17+ (improved dyld concurrency) or in sample apps (no competing SDKs).

By deferring NWPathMonitor creation to first use during event dispatch, the Network framework symbols are resolved well after app startup contention has settled.

Test plan

  • Added testMonitorNotCreatedDuringInit — confirms monitor is nil after init, non-nil after shouldBlockNetworkAccess()
  • Added testMonitorNotCreatedAfterStop — confirms monitor stays nil if stop() was called before first use
  • Updated testReachabilityMonitoring to explicitly trigger monitor start
  • All existing NetworkReachabilityTests and EventDispatcherRetryTests pass

Issues

  • FSSDK-12346

…rtup

NWPathMonitor was eagerly created in NetworkReachability.init, which runs
during DefaultEventDispatcher stored-property initialization. In Flutter
apps with multiple SDKs (Firebase, Datadog, MParticle) on iOS 16, this
caused a null function pointer crash (EXC_BAD_ACCESS at 0x0) due to dyld
contention over the weak-linked Network framework during concurrent
framework loading.

- Defer NWPathMonitor creation from init to first shouldBlockNetworkAccess() call
- Remove force casts (as! NWPathMonitor) in favor of local variable binding
- Prevent monitor start after stop() to maintain test isolation
- Add tests verifying monitor is not created during init

Fixes: FSSDK-12346

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Defers NWPathMonitor initialization in NetworkReachability until first use to avoid early Network framework symbol resolution during multi-SDK startup, and adds tests to verify the new lifecycle behavior.

Changes:

  • Moved NWPathMonitor creation out of init into a new startMonitorIfNeeded() method, invoked on first shouldBlockNetworkAccess() call.
  • Added monitorStarted gating and updated stop() behavior to prevent monitor creation after teardown.
  • Added/updated unit tests to assert monitor creation timing and stop() behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
Sources/Utils/NetworkReachability.swift Defers monitor creation to first use and adds lifecycle gating around starting/stopping the monitor.
Tests/OptimizelyTests-Common/NetworkReachabilityTests.swift Adds tests ensuring the monitor is not created during init and not created after stop(), and updates monitoring test to explicitly start the monitor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/Utils/NetworkReachability.swift Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coveralls

coveralls commented Jul 8, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 93.889% (+0.03%) from 93.859% — fix/lazy-nwpathmonitor-init into master

muzahidul-opti and others added 3 commits July 8, 2026 19:37
numContiguousFails was read in shouldBlockNetworkAccess() and written in
updateNumContiguousFails() from different threads without synchronization.
Both now use queue.sync for consistent thread-safe access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hammers updateNumContiguousFails and shouldBlockNetworkAccess from
1000 concurrent threads to verify no data race or crash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make all mutable state private (monitor, connected, numContiguousFails,
  maxContiguousFails, monitorStarted) — accessed only within queue
- Provide thread-safe computed accessors (isConnected, numContiguousFails,
  maxContiguousFails, isMonitorActive) for external use
- Read numContiguousFails and connected in a single queue.sync snapshot
  in shouldBlockNetworkAccess() to eliminate TOCTOU race
- Move monitor creation and cancellation fully inside queue.sync,
  with only NWPathMonitor.start(queue:) outside to avoid deadlock
- Set monitor = nil in stop() for clean resource release
- Update tests to use isMonitorActive instead of accessing monitor directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread Sources/Utils/NetworkReachability.swift
Comment thread Sources/Utils/NetworkReachability.swift
@muzahidul-opti muzahidul-opti merged commit 605f65e into master Jul 8, 2026
10 of 13 checks passed
@muzahidul-opti muzahidul-opti deleted the fix/lazy-nwpathmonitor-init branch July 8, 2026 16:07
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.

4 participants