The short answer: Firestore's real-time listener API is structurally identical on iOS, Android, and Web. When the same semantics appear in Swift, Kotlin, and TypeScript, there's no sync logic to duplicate across platforms. Security rules live in one file and apply everywhere. Offline persistence comes included on mobile.
That's the pitch. But "Firestore solves multiplatform" isn't universally true, so the counter-evidence comes first.
Three reasons Firestore is the wrong choice
Counter-argument 1: Vendor lock-in is real and migration is painful
Firestore's query model is incompatible with SQL. Exporting data to PostgreSQL requires manual ETL work, and document-to-relational mapping is lossy (encore.dev, Firebase Alternatives 2026). Supabase — the most direct Firebase alternative — is open-source, self-hostable, and cheaper at scale. Choosing Firestore today means accepting that migration later will be non-trivial. That's not a scare story; it's the tradeoff.
Counter-argument 2: Read/write billing surprises teams at scale
Firestore charges per document operation. A screen that loads 20 items in a list costs 20 reads per page view. At 100K daily active users viewing 10 pages each, that's 20 million reads per day — and a data modeling decision that fetches 40 per page instead doubles that. Standard edition pricing ($0.03/100k reads) makes that difference worth $4,380/year (Firemap.dev, Firestore Pricing Explained 2026). There is no hard usage cap; without a budget alert, the surprise lands on the invoice.
Counter-argument 3: One document, one write per second
"Firestore limits a single document to ~1 write/second. Use distributed counters or sharded aggregation instead."
— Firebase Documentation (firebase.google.com/docs/firestore/quotas)
Any scenario where multiple clients race to update the same document — a live attendance counter, a voting tally, a shared draft — hits this ceiling. Last-write-wins (LWW) conflict resolution depends on client clocks, making concurrent edits non-deterministic. Working around it requires Distributed Counters or a custom conflict strategy (moldstud.com, Firebase Data Sync Challenges).
Reason 1: The listener API is symmetric across all three platforms
Firestore's snapshot listener pushes changes to your app whenever underlying data changes — on iOS, Android, and Web — with the same semantics.
// iOS (Swift)
db.collection("class_rooms").document(classRoomId)
.collection("schedules")
.addSnapshotListener { snapshot, error in
guard let docs = snapshot?.documents else { return }
schedules = docs.compactMap { try? $0.data(as: Schedule.self) }
}
// Android (Kotlin)
db.collection("class_rooms").document(classRoomId)
.collection("schedules")
.addSnapshotListener { snapshot, error ->
if (snapshot == null) return@addSnapshotListener
schedules = snapshot.documents.mapNotNull { it.toObject(Schedule::class.java) }
}
// Web (TypeScript)
onSnapshot(
collection(db, "class_rooms", classRoomId, "schedules"),
(snapshot) => {
schedules = snapshot.docs.map((d) => d.data() as Schedule)
}
)
These three implementations are structurally identical. Register a listener, and Firestore pushes changes. A schedule created on the web console appears on the iOS app and the Android app without any additional plumbing. Cross-platform frameworks are reported to cut development time by up to 40% (neontri.com 2026); Firestore's symmetric API is what makes that saving real in practice rather than theoretical.
Reason 2: Security rules are one file, applied everywhere
Firestore Security Rules are evaluated server-side. One firestore.rules file covers every SDK — iOS, Android, and Web — simultaneously.
// firestore.rules (simplified for illustration)
match /class_rooms/{classRoomId} {
allow read, write: if request.auth != null &&
request.auth.uid in resource.data.adminUserIds;
match /schedules/{scheduleId} {
allow read: if request.auth != null &&
request.auth.uid in
get(/databases/$(database)/documents/class_rooms/$(classRoomId))
.data.joinedUserIds;
allow write: if request.auth != null &&
request.auth.uid in
get(/databases/$(database)/documents/class_rooms/$(classRoomId))
.data.adminUserIds;
}
}
"Only classroom members can read; only admins can write" — change one line, and it applies on iOS, Android, and Web in the next deploy. There's no per-platform access control to keep in sync.
The same logic applies to the data model. Schedule is a Swift struct on iOS, a Kotlin data class on Android, and a TypeScript interface on Web — but all three map from the same Firestore document shape. Adding a field means updating three type definitions; the database schema itself doesn't change.
Reason 3: Offline persistence is on by default on mobile
In a tutoring center, the classroom iPad or Android tablet might lose Wi-Fi briefly. Firestore enables offline persistence by default on iOS and Android: reads serve from a local cache, writes queue and sync automatically when connectivity returns (firebase.google.com/docs/firestore). Web requires an opt-in but uses the same API.
Building offline sync from scratch is weeks of engineering — cache invalidation, retry logic, conflict resolution. Firestore absorbs that cost. Firebase's active-domain count grew from 3,092 in January 2020 to 42,377 by early 2025, a 13.7× increase (technologychecker.io 2026); this built-in infrastructure cost-saving is a significant part of why.
Why the counter-arguments don't apply to E-Space (right now)
All three counter-arguments assume high scale or high update frequency. E-Space is neither — yet.
On vendor lock-in: E-Space's current dataset covers a few hundred tutoring classrooms. The cost of migrating to PostgreSQL exists in theory but doesn't justify engineering time today. When the scale changes, the decision can change too.
On billing surprises: Scheduling data for a tutoring center isn't read-heavy by construction. There's no live feed, no social graph, no high-frequency event stream. A budget alert is set; invoices are predictable.
On the 1 write/sec limit: Lesson schedule updates are infrequent and non-concurrent. Two admins editing the same time slot simultaneously is an edge case that doesn't happen in practice. There are no counters in the data model that would require Distributed Counters.
These aren't permanent exemptions — they're current facts. If E-Space's data model or usage patterns change materially, the database choice gets re-evaluated.
Conclusion
Symmetric listener APIs, one security-rules file, and built-in offline sync: these are the practical reasons a single Firestore instance works for three platforms at E-Space's current scale. Vendor lock-in, unpredictable billing, and the 1 write/sec ceiling are real risks — they just don't apply to a small-to-mid-scale tutoring SaaS today. The choice is specific to the product's size and access patterns. Copy it at your own risk.