Skip to content

Commit 511d84f

Browse files
authored
Merge branch 'master' into feature/auth-on-tvOs
2 parents 7f7b158 + aab3a5f commit 511d84f

1 file changed

Lines changed: 214 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# GitHub Copilot Instructions — firebase-kotlin-sdk
2+
3+
## Project Overview
4+
5+
This is a **Kotlin-first, multiplatform SDK for Firebase**. It wraps the official Firebase platform SDKs (Android, iOS, JS, JVM) behind a unified Kotlin common API, enabling Firebase to be used directly from shared Kotlin Multiplatform (KMP) source sets targeting **Android**, **iOS**, **Desktop (JVM)**, and **Web (JS)**.
6+
7+
All modules are published under the `dev.gitlive` group ID (e.g. `dev.gitlive:firebase-firestore`).
8+
9+
---
10+
11+
## Architecture
12+
13+
### Module Structure
14+
15+
Each Firebase product is a separate Gradle module (e.g. `firebase-auth`, `firebase-firestore`, `firebase-database`). Inside each module the source is split by KMP targets:
16+
17+
```
18+
src/
19+
commonMain/ ← shared public API (Kotlin)
20+
androidMain/ ← wraps Firebase Android SDK (and is also used as the physical dir for `jvmMain` in some modules, e.g. firebase-firestore)
21+
appleMain/ ← shared Apple targets (iOS/tvOS/macOS) wrapping Firebase iOS SDK via Kotlin/Native
22+
jsMain/ ← wraps Firebase JS SDK
23+
jvmMain/ ← JVM desktop/server target (may map to src/androidMain/kotlin in some modules)
24+
commonTest/ ← shared tests
25+
androidTest/
26+
appleTest/
27+
jsTest/
28+
jvmTest/
29+
```
30+
31+
The `commonMain` source set defines the **public API**. Platform-specific source sets contain the `actual` implementations that delegate to the respective native SDK.
32+
33+
### Shared modules
34+
35+
- `firebase-app``FirebaseApp` and `Firebase` object
36+
- `firebase-common` — shared types, serialization helpers, `FirebaseEncoder`/`FirebaseDecoder`
37+
- `firebase-common-internal` — internal utilities not part of the public API
38+
39+
---
40+
41+
## Kotlin-First Design Principles
42+
43+
These principles **must** be followed in all new and modified code.
44+
45+
### 1. Suspend functions instead of callbacks or Tasks
46+
47+
Async operations that return a single value use `suspend fun`. Never use callbacks, `Task`, `Promise`, or listener patterns in `commonMain`.
48+
49+
```kotlin
50+
// ✅ Correct
51+
suspend fun signInWithEmailAndPassword(email: String, password: String): AuthResult
52+
53+
// ❌ Wrong
54+
fun signInWithEmailAndPassword(email: String, password: String, callback: (AuthResult) -> Unit)
55+
```
56+
57+
### 2. `Flow` instead of listeners
58+
59+
Streams of values use `kotlinx.coroutines.flow.Flow`. The flow should be cold — a new listener is registered on collection and removed on cancellation/completion.
60+
61+
```kotlin
62+
// ✅ Correct
63+
val snapshots: Flow<DocumentSnapshot>
64+
65+
// ❌ Wrong
66+
fun addSnapshotListener(listener: (DocumentSnapshot) -> Unit): ListenerRegistration
67+
```
68+
69+
### 3. Default arguments instead of the Builder pattern
70+
71+
Prefer Kotlin default arguments over builder classes. When the upstream Android SDK uses a Builder, provide a Kotlin-idiomatic overload with default arguments **in addition to** accepting the built object (for API compatibility).
72+
73+
```kotlin
74+
// ✅ Correct
75+
suspend fun updateProfile(displayName: String? = null, photoURL: String? = null)
76+
77+
// ❌ Avoid as the sole API
78+
suspend fun updateProfile(request: UserProfileChangeRequest)
79+
```
80+
81+
### 4. Infix notation for query operators
82+
83+
Firestore and Database query operators use infix functions inside a `where { }` builder lambda.
84+
85+
```kotlin
86+
citiesRef.where { "state" equalTo "CA" }
87+
citiesRef.where { "regions" contains "west_coast" }
88+
citiesRef.where {
89+
all(
90+
"state" equalTo "CA",
91+
any("capital" equalTo true, "population" greaterThanOrEqualTo 1_000_000)
92+
)
93+
}
94+
```
95+
96+
### 5. Operator overloading where natural
97+
98+
Use operator overloading where semantics are obvious (e.g. callable HTTP Functions via `invoke`).
99+
100+
---
101+
102+
## Serialization
103+
104+
The SDK uses **`kotlinx.serialization`** throughout. Never use platform-specific serialization mechanisms in `commonMain`.
105+
106+
- Custom classes passed to/from Firebase must be annotated with `@Serializable`.
107+
- Always accept an explicit `SerializationStrategy`/`DeserializationStrategy` parameter alongside a reified/inferred overload.
108+
- `encodeDefaults` defaults to `true`; allow it to be overridden via a `buildSettings` lambda.
109+
- Support `serializersModule` for contextual and polymorphic serialization.
110+
- Use `@FirebaseClassDiscriminator` (defined in `firebase-common`) on sealed classes to control the type discriminator field name.
111+
- Special sentinel values (`ServerValue.TIMESTAMP`, `Timestamp.ServerTimestamp`, `FieldValue.serverTimestamp`) must remain serializable.
112+
- For Firestore update operations, provide an `updateFields` builder that allows per-field serializer overrides.
113+
114+
---
115+
116+
## API Compatibility Goal
117+
118+
The target is **near binary compatibility** with the [Firebase Android SDK Kotlin API](https://firebase.google.com/docs/reference/kotlin/packages):
119+
120+
- Match class names, function names, and parameter names from the Android SDK.
121+
- Package imports should be the **only change** needed when porting Android code: `com.google.firebase``dev.gitlive.firebase`.
122+
- When an Android SDK API is Java-first (uses builders, callbacks, etc.), provide **both** the Android-compatible form *and* a Kotlin-idiomatic overload.
123+
- When the Android SDK API is already Kotlin-first, simply match it.
124+
125+
---
126+
127+
## Accessing the Underlying Platform SDK
128+
129+
Each wrapper class exposes the underlying native SDK object via extension properties:
130+
131+
- `.android` — Firebase Android SDK object (also used for JVM via `firebase-java-sdk`)
132+
- `.ios` — Firebase iOS SDK object (Kotlin/Native)
133+
- `.js` — Firebase JS SDK object
134+
135+
These are only accessible from the respective platform source sets. Do **not** use them in `commonMain`.
136+
137+
---
138+
139+
## Platform-Specific Notes
140+
141+
### Android
142+
- Some modules (e.g. `firebase-config`) require **Core library desugaring** for `minSdk < 26`.
143+
- Access the underlying Android object via the `.android` extension property in `androidMain`.
144+
145+
### iOS
146+
- The Firebase iOS SDK is **not** a transitive dependency — consuming projects must link it via CocoaPods or SPM.
147+
- Tests need the relevant Firebase pods in the `cocoapods` block of `build.gradle.kts`.
148+
149+
### JVM / Desktop
150+
- Uses [`firebase-java-sdk`](https://github.com/GitLiveApp/firebase-java-sdk), which mirrors the Android SDK API.
151+
- Requires additional initialization compared to mobile targets (see `firebase-java-sdk` docs).
152+
- Accessed via the `.android` extension property (same as Android).
153+
154+
---
155+
156+
## Documentation
157+
158+
- Every **public** class, function, and property must have **KDoc**.
159+
- Use [`@param`](https://kotlinlang.org/docs/kotlin-doc.html#param-name), [`@return`](https://kotlinlang.org/docs/kotlin-doc.html#return), and [`@throws`](https://kotlinlang.org/docs/kotlin-doc.html#throws-exception) tags where applicable.
160+
- Published docs live at [gitliveapp.github.io/firebase-kotlin-sdk](https://gitliveapp.github.io/firebase-kotlin-sdk/).
161+
162+
---
163+
164+
## Code Style
165+
166+
- Follow the **IntelliJ Kotlin code style**.
167+
- Run `./gradlew formatKotlin` to auto-format before committing.
168+
- Run `./gradlew lintKotlin` to validate style.
169+
- No `GlobalScope` usage (except in comments/examples showing what *not* to do).
170+
171+
---
172+
173+
## Binary API Validation
174+
175+
This library tracks its public binary API. After any public API change:
176+
177+
```bash
178+
./gradlew apiDump
179+
```
180+
181+
Commit the updated `.api` files alongside your code changes.
182+
183+
---
184+
185+
## Testing
186+
187+
Tests live in `commonTest` (shared) and platform-specific test source sets. A Firebase emulator must be running for integration tests:
188+
189+
```bash
190+
# Inside the /test directory
191+
firebase emulators:start
192+
```
193+
194+
Run tests per platform:
195+
196+
```bash
197+
./gradlew connectedAndroidTest # requires running emulator
198+
./gradlew iosSimulatorArm64Test # Apple Silicon
199+
./gradlew iosX64Test # Intel Mac
200+
./gradlew jsNodeTest
201+
./gradlew jvmTest
202+
```
203+
204+
---
205+
206+
## What Copilot Should Avoid
207+
208+
- Do **not** generate Java-style callback APIs in `commonMain`.
209+
- Do **not** use `runBlocking` in production SDK code.
210+
- Do **not** add platform-specific imports to `commonMain` source files.
211+
- Do **not** use `GlobalScope` — callers manage coroutine scope.
212+
- Do **not** skip KDoc on public API members.
213+
- Do **not** use `lateinit var` for public API properties; prefer `val` or nullable types with proper initialization.
214+
- Do **not** hardcode serializers when a reified/inferred overload is possible.

0 commit comments

Comments
 (0)