firebase-ios-sdk: Firestore networking fails intermittently

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 12.5.1
  • Firebase SDK version: 8.5.0
  • Installation method: Swift Package Manager
  • Firebase Component: Firestore

[REQUIRED] Step 2: Describe the problem

Steps to reproduce:

Occasionally (feels like a quarter of the time) when I launch my app it fails to retrieve data from the Firestore server. The snapshot listener is called with cached local data, but nothing from the server. Then, a few minutes later, data arrives successfully.

My app waits for non-cached server data before proceeding, so this is really noticeable. It has been happening all year, but I assumed it was a hardware issue (an old second-hand iPhone 7). But it happens just as often in Xcode simulators.

Note that most of the time Firestore works fine, which makes this bug difficult to reproduce. And yes, I have a very reliable internet connection.

To test the hypothesis that the main thread was blocked, I tried adding a function that calls itself asynchronously every few seconds and prints a message. But it ran fine.

Relevant Code:

  private func onUserChanged(uid: String) {
    if let listener = userSnapshotListener {
      listener.remove()
    }
    requireServerData = true
    userSnapshotListener = Firestore.firestore()
      .collection("user")
      .document(uid)
      .addSnapshotListener(
        includeMetadataChanges: true
      ) { documentSnapshot, err in
        if let err = err {
          os_log("Query user: %s",
                 type: .error, err.localizedDescription)
        } else if let snapshot = documentSnapshot {
          self.processUserSnapshot(uid: uid, document: snapshot)
        }
      }
  }
  
  // Processes a recently-fetched update to the current user's
  // Firestore data.
  private func processUserSnapshot(
    uid: String,
    document: DocumentSnapshot
  ) {
    // Ensure the first result is from the server, not the cache.
    if requireServerData {
      if document.metadata.isFromCache {
        print("rejecting cached result")
        return
      }
      requireServerData = false
    }
    print("got result from the server")
  }

Here’s a typical log for the app (called Metropolis). It uses Firebase Messaging as well, although that’s probably not relevant. And I’ve seen Firestore take a lot longer than this to reach the server.

2021-08-06 17:34:18.502040+1000 Metropolis[82323:3195908] 8.5.0 - [Firebase/Messaging][I-FCM001000] FIRMessaging Remote Notifications proxy enabled, will swizzle remote notification receiver handlers. If you'd prefer to manually integrate Firebase Messaging, add "FirebaseAppDelegateProxyEnabled" to your Info.plist, and set it to NO. Follow the instructions at:
https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in_firebase_messaging
to ensure proper integration.
rejecting cached result
2021-08-06 17:34:18.743205+1000 Metropolis[82323:3195909] 8.5.0 - [Firebase/Messaging][I-FCM012002] Error in application:didFailToRegisterForRemoteNotificationsWithError: remote notifications are not supported in the simulator
2021-08-06 17:34:18.894094+1000 Metropolis[82323:3195909] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
2021-08-06 17:34:19.506400+1000 Metropolis[82323:3195909] Product request failed: UNKNOWN_ERROR
2021-08-06 17:34:29.728918+1000 Metropolis[82323:3196063] 8.5.0 - [Firebase/Firestore][I-FST000001] Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
 This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
2021-08-06 17:34:48.921787+1000 Metropolis[82323:3196267] 8.5.0 - [Firebase/Firestore][I-FST000001] WatchStream (600002acaa18) Stream error: 'Unavailable: failed to connect to all addresses'
2021-08-06 17:34:48.938634+1000 Metropolis[82323:3196267] 8.5.0 - [Firebase/Firestore][I-FST000001] WatchStream (600002acaa18) Stream error: 'Unavailable: failed to connect to all addresses'
2021-08-06 17:34:49.869870+1000 Metropolis[82323:3196265] 8.5.0 - [Firebase/Firestore][I-FST000001] WatchStream (600002acaa18) Stream error: 'Unavailable: failed to connect to all addresses'
got result from the server

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19 (2 by maintainers)

Most upvoted comments

The underlying issues seems to be grpc’s use of getaddrinfo. This is not recommended by Apple.

The Dart project ran into a similar issue here. It seems that getaddrinfo doesn’t work properly on iOS when the LAN supports ipv6 but there’s no ipv6 route to a DNS server.

In resolve_address_posix.cc I changed hints.ai_family = AF_UNSPEC; to AF_INET and the problem went away. Unfortunately that’s not a long-term solution.

To reproduce, make sure dig -6 firestore.googleapis.com times out on your Mac, then run the quickstart app in a simulator. Or make sure your WiFi router doesn’t support ipv6 DNS and try it on an iPhone with only WiFi enabled.

It sounds like you’ll need to punt this to the grpc team.

I’ve encountered the problem both at home (fibre + wifi) and in a co-working space (wifi). In both locations the wifi is excellent, suitable for video-conferencing. It occurs on both a physical device (iPhone 7) and various simulators.

I don’t have any proxies in place. If it was a proxy issue, the app would never reach the server, ever. But most of the time it connects immediately, and when it does have a problem it connects eventually (but it can take minutes).

Note the Firebase authentication and messaging always work fine. This problem only affects Firestore, and seems to be caused by grpc.

I’m not sure if the Quickstart app will reproduce the problem, because it looks like it will run happily in offline mode, so the grpc problems won’t affect it.