iOS开发:如何加快Firebase Firestore数据获取速度?耗时优化咨询
Hey there! Let's break down your questions one by one and look at how we can optimize that Firestore call.
1. Is 1.8 seconds of latency normal?
For a first-time fetch of Firestore data, this isn't completely unheard of—Firestore has to establish a connection, handle authentication (if applicable), and pull down the data. But if your cities collection only has a small number of documents (say, under 100), this is definitely on the slower side. Subsequent calls should be faster thanks to Firestore's local caching, but we can do better for the initial load too.
2. Are there issues in your code causing extra latency?
Looking at your code, there's one critical bug and a few minor inefficiencies:
Critical Bug
In your loop over documents, you have:
guard let cityName = document.data()["name"] as? String, let cityCoordinate = document.data()["coordinate"] as? GeoPoint else {return}
If any single document is missing the name or coordinate fields (or has the wrong type), your entire loop will terminate early, and you'll return nil to the completion handler. This is a silent failure that could break your data loading—you should skip invalid documents instead of aborting the whole operation.
Minor Inefficiencies
- You're manually extracting fields from each document instead of reusing your existing
init(dictionary:)initializer. This adds redundant code but doesn't cause major latency, but it's worth cleaning up. - Your timing measurement includes the entire block from the start of the function to when the completion is called, which includes local processing time—but the bulk of the 1.8s is almost certainly the network request itself.
3. Can we optimize this to under 1 second?
Absolutely! Here are the key optimizations to make:
a. Use a Geographically Close Firestore Instance
Firestore's latency is heavily dependent on the region of your database. If your user base is in, say, Southeast Asia but your Firestore is hosted in the US, you'll see significant delays. Go to your Firebase Console and switch your Firestore database to a region closer to your users (e.g., asia-southeast1 for Singapore, asia-east2 for Hong Kong). This alone can cut latency in half.
b. Fetch Only the Fields You Need
Right now, you're pulling down the entire document, but you only need name and coordinate. Use selectFields to reduce the data transferred over the network:
FirestoreCollectionReference.cities.reference() .selectFields(["name", "coordinate"]) .getDocuments { /* ... */ }
c. Fix the Document Loop and Reuse Initializer
Clean up the loop to skip bad documents and use your existing initializer. First, update your init(dictionary:) to be failable (safer than force-unwrapping):
init?(dictionary: [String:Any]) { guard let name = dictionary["name"] as? String, let coordinate = dictionary["coordinate"] as? GeoPoint else { print("Skipping invalid city document") return nil } self.name = name self.coordinate = coordinate }
Then simplify the loop with compactMap:
let cityArray = documentsSnapshot.documents.compactMap { document in CityKM(dictionary: document.data()) }
d. Enable Firestore Local Caching (Optional)
Firestore enables local caching by default, but you can ensure it's configured correctly. For subsequent app launches, your app will load data from the cache first (if available) while fetching fresh data in the background, making loads nearly instant.
e. Check for Unnecessary Overhead
Make sure you're not running heavy processing on the main thread after fetching data (though getDocuments is asynchronous, so it won't block). Also, if your city data is public, confirm your Firestore rules allow unauthenticated access—this removes extra authentication handshake overhead.
Optimized Code Example
Here's the updated getAllCitiesDataFromFirestore method with all fixes:
static func getAllCitiesDataFromFirestore(completion: @escaping ([CityKM]?) -> Void) { let startTime = CFAbsoluteTimeGetCurrent() FirestoreCollectionReference.cities.reference() .selectFields(["name", "coordinate"]) .getDocuments { (snapshot, error) in if let error = error { print("Failed to retrieve all cities data: \(error.localizedDescription)") completion(nil) return } guard let documentsSnapshot = snapshot, !documentsSnapshot.isEmpty else { completion(nil) return } let cityArray = documentsSnapshot.documents.compactMap { document in CityKM(dictionary: document.data()) } completion(cityArray) let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime print("Time needed to get all cities data from Firestore : \(timeElapsed) s.") } }
With these changes—especially switching to a closer Firestore region—you should easily get latency down to under 1 second for initial loads, and even faster for subsequent ones.
内容的提问来源于stack exchange,提问作者Agung

