Skip to content

Migrate Connectivity Monitoring to NWPathMonitor on Apple Platforms#16112

Open
cherylEnkidu wants to merge 33 commits into
mainfrom
cheryl/SCNetworkReachability
Open

Migrate Connectivity Monitoring to NWPathMonitor on Apple Platforms#16112
cherylEnkidu wants to merge 33 commits into
mainfrom
cheryl/SCNetworkReachability

Conversation

@cherylEnkidu
Copy link
Copy Markdown
Contributor

@cherylEnkidu cherylEnkidu commented Apr 23, 2026

Design detail: go/firestore-nwpathmonitor

Summary

This PR migrates the network connectivity monitoring implementation for Apple platforms from the legacy SCNetworkReachability API to the modern NWPathMonitor API. This modernizes the codebase and resolves potential issues with older network reachability APIs, while adding robust thread-safety guards during initialization and destruction.

Key Changes

Firestore Core / Remote

  • Migrated to NWPathMonitor: Replaced SCNetworkReachability with NWPathMonitor in ConnectivityMonitorApple (connectivity_monitor_apple.mm).
  • Extracted Header File: Created connectivity_monitor_apple.h to define the class and document critical lifecycle requirements.
  • Strict Queue Invariants: Documented and enforced that ConnectivityMonitorApple must be both constructed and destructed on the AsyncQueue to prevent race conditions and use-after-free bugs with the monitor's handlers.
  • Shutdown Sequence Fix: Updated FirestoreClient::TerminateInternal() in firestore_client.cc to reset the connectivity_monitor_ last, ensuring it is destroyed on the AsyncQueue after all callback registrants are gone.

Utilities

  • AsyncQueue Extension: Added IsCurrentQueue() to AsyncQueue to allow components to verify they are running on the correct queue (used in ConnectivityMonitorApple destruction).

Tests

  • New Integration Test: Added FSTConnectivityMonitorTests.mm to verify that UIApplicationWillEnterForegroundNotification correctly triggers callbacks when the network is available.
  • Minor Cleanup: Removed an unused self.db = [self firestore]; line in FIRIndexingTests.mm setup.

Build & Dependencies

  • Framework Dependencies: Added the Network framework to FirebaseFirestoreInternal.podspec and Package.swift for supported Apple platforms.
  • Project Files: Updated the Xcode project to include the new files.

Testing Done

  • A new integration test was added in FSTConnectivityMonitorTests.mm to verify foreground notification behavior.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

@cherylEnkidu cherylEnkidu marked this pull request as ready for review April 23, 2026 18:30
@cherylEnkidu cherylEnkidu requested a review from a team as a code owner April 23, 2026 19:31
@ncooke3 ncooke3 self-assigned this Apr 23, 2026
@cherylEnkidu
Copy link
Copy Markdown
Contributor Author

@ncooke3 Hi Nick, I’m still working on this PR. The replacement is less intuitive than I expected. I’ll ping you once the code is ready. :)

@cherylEnkidu cherylEnkidu changed the title Refactor ConnectivityMonitorApple to use NW_PATH_MONITOR Migrate Connectivity Monitoring to NWPathMonitor on Apple Platforms May 5, 2026
if (!snapshot.empty && !snapshot.metadata.fromCache) {
if (!snapshot.empty && !snapshot.metadata.fromCache &&
!snapshot.metadata.hasPendingWrites) {
[testExpectiation fulfill];
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fix flaky testWatchSurvivesNetworkDisconnect under new connectivity timing.

The test used addSnapshotListenerWithIncludeMetadataChanges:YES and a filter of !empty && !fromCache, expecting the listener to fulfill the expectation exactly once. With metadata changes enabled, the listener fires twice in the recovery sequence: once when the snapshot is raised from the server with hasPendingWrites=true (write enqueued, awaiting ack), and again when hasPendingWrites flips to false after server ack. Both pass the existing filter, causing over-fulfill.

This is a latent bug in the test, not a regression from the connectivity monitor refactor. The connectivity changes in this PR shifted the timing of network state delivery in a way that exposed it.

Tighten the filter to also require !hasPendingWrites, which is the condition the test actually intends to wait for: the write has fully round-tripped to the server.


nw_path_monitor_start(monitor_);

#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unlike the previous SCNetworkReachability implementation, NWPathMonitor delivers its initial status asynchronously, creating a brief, rare window upon resuming from the background where a nullopt status might cause the foreground handler to skip forced callbacks, resulting in at worst a single missed eager connection reset before the subsequent update arrives.

In theory this window is tiny and the notification does not fire on cold launch, so the race is unlikely
to be observable. I would prefer to keep this logic simple instead of adding extra code block to handle this edge case.

ConnectivityMonitorApple::~ConnectivityMonitorApple() {
HARD_ASSERT(this->queue()->IsCurrentQueue(),
"ConnectivityMonitorApple must be destroyed on its AsyncQueue. "
"See class comment for why.");
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This assertion is mainly used for ensuring the test is been implemented probably. In production, both ~FirestoreClient() and FirestoreClient::TerminateInternal() would make sure ConnectivityMonitorApple is destroyed on the AsyncQueue.

@cherylEnkidu
Copy link
Copy Markdown
Contributor Author

./gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request migrates the network connectivity monitoring implementation for Apple platforms from the legacy SCNetworkReachability API to the modern NWPathMonitor API. The changes include updating build configurations, adding a new connectivity monitor implementation, and updating the Firestore client termination logic. I have identified a few minor improvements: some redundant includes should be removed, and the indentation of the empty block in the dispatch_sync call should be corrected for consistency.

Comment thread Firestore/core/src/remote/connectivity_monitor_apple.mm Outdated
Comment thread Firestore/core/src/remote/connectivity_monitor_apple.mm Outdated
Comment thread Firestore/core/src/remote/connectivity_monitor_apple.mm Outdated
Copy link
Copy Markdown
Member

@ncooke3 ncooke3 left a comment

Choose a reason for hiding this comment

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

LGTM for the non-C++ changes. Could this be a breaking change in any way?

Comment thread Firestore/CHANGELOG.md
Comment on lines 1 to +5
# 12.13.0
- [feature] Added search stage support for `languageCode`, `offset`, `limit`, and `retrievalDepth`.
- [feature] Added support for Pipeline expressions `arraySlice`, `arrayFilter`, `arrayTransform` and `arrayTransformWithIndex`. (#16001)
- [fixed] Add missing `noexcept` specifiers to move, hash, swap operations [#16117].
- [changed] Migrates the network connectivity monitoring implementation for Apple platforms from the legacy SCNetworkReachability API to the modern NWPathMonitor API.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: 80 chars wrapping and put new entries under new "Unreleased" header

Suggested change
- [changed] Migrates the network connectivity monitoring implementation for Apple platforms from the legacy SCNetworkReachability API to the modern NWPathMonitor API.
# Unreleased
- [changed] Migrates the network connectivity monitoring implementation for
Apple platforms from the legacy SCNetworkReachability API to the modern
NWPathMonitor API.
# 12.13.0
- [feature] Added search stage support for `languageCode`, `offset`, `limit`, and `retrievalDepth`.
- [feature] Added support for Pipeline expressions `arraySlice`, `arrayFilter`, `arrayTransform` and `arrayTransformWithIndex`. (#16001)
- [fixed] Add missing `noexcept` specifiers to move, hash, swap operations [#16117].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants