Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ See the `Package.swift` files in the
| `SkipFirebaseCrashlytics` | ~75% | `log`, `setCustomValue`/`setCustomKeysAndValues`, `setUserID`, `didCrashDuringPreviousExecution`, `checkForUnsentReports`/`sendUnsentReports`/`deleteUnsentReports`, `record(error:userInfo:)`, `recordExceptionModel`, `FIRExceptionModel`/`FIRStackFrame` | `logWithFormat` and `checkAndUpdateUnsentReports` are marked `@available(*, unavailable)`; no per-`FirebaseApp` instance accessor |
| `SkipFirebaseFunctions` | ~45% | Default/regional/emulator instance, `httpsCallable(_:)`, completion-based `call`, automatic Kotlin→Swift result conversion via `deepSwift` | `async`/`await` `call` signatures, streaming RPCs, per-call options (`HTTPSCallableOptions`), explicit call timeouts |
| `SkipFirebaseDatabase` | ~5% | `Database.database()` / `Database.database(app:)` singleton accessors | Effectively the entire API: `DatabaseReference`, `child`/`push`/`setValue`/`updateChildValues`/`removeValue`, `observe`/`observeSingleEvent`, queries, `DataSnapshot`, `ServerValue`, online/offline toggle |
| `SkipFirebaseInstallations` | ~20% | Singleton accessor, `installationID()` | `authTokenForcingRefresh`, `delete()`, `installationIDDidChangeNotification` |
| `SkipFirebaseInstallations` | ~75% | Singleton accessor (`installations()`/`installations(app:)`), `installationID()`, `authToken()`, `authTokenForcingRefresh(_:)`, `delete()`, `InstallationsAuthTokenResult` (`authToken`, `expirationDate`) | `installationIDDidChangeNotification`, per-`FirebaseApp` notification keys |
| `SkipFirebasePerformance` | ~65% | `Performance.sharedInstance()`, `isDataCollectionEnabled`, `trace(name:)`, `HTTPMetric(url:httpMethod:)`, full `Trace` API (start/stop, `incrementMetric`, `valueForMetric`, attributes), full `HTTPMetric` API (start/stop, `responseCode`, payload sizes, content type, attributes), `HTTPMethod` enum | `isInstrumentationEnabled` (no Android equivalent, marked unavailable), `Performance.startTrace(name:)` static convenience, per-`FirebaseApp` instance accessor |

**Overall coverage across the thirteen modules: roughly 60% of the iOS API surface.** The most production-used modules — Core, Firestore, Auth, Storage, Messaging, Analytics, Crashlytics, and RemoteConfig — sit in the 60–80% range and cover the standard read/write/sign-in/log/notify paths. `SkipFirebaseDatabase` (Realtime Database) and `SkipFirebaseInstallations` are mostly stubs.
**Overall coverage across the fourteen modules: roughly 60% of the iOS API surface.** The most production-used modules — Core, Firestore, Auth, Storage, Messaging, Analytics, Crashlytics, and RemoteConfig — sit in the 60–80% range and cover the standard read/write/sign-in/log/notify paths. `SkipFirebaseDatabase` (Realtime Database) is mostly a stub.

### What is working well

Expand All @@ -55,7 +55,7 @@ See the `Package.swift` files in the
- **`SkipFirebaseFunctions`** lacks `async`/`await` call signatures, streaming RPCs, and per-call options. Only completion-based callback calls are supported.
- **`SkipFirebaseRemoteConfig`** does not yet expose `addOnConfigUpdateListener` (real-time config updates) or custom signals.
- **`SkipFirebaseAppCheck`** ships only the `Debug` provider factory; custom provider implementations are not yet bridgeable.
- **`SkipFirebaseInstallations`** is essentially a no-op beyond fetching the installation ID.
- **`SkipFirebaseInstallations`** cannot bridge the `InstallationIDDidChange` notification or the `InstallationIDDidChangeAppNameKey` userInfo key. The Firebase Android SDK has no equivalent push-notification mechanism for installation ID changes — it exposes no broadcast, callback, or listener that fires when the ID rotates. As a result, any code that observes `NotificationCenter.default.publisher(for: .InstallationIDDidChange)` on iOS will compile on Android but never receive an event. If your app relies on this to, for example, re-upload the installation ID to your backend after it changes, you will need an alternative strategy on Android (such as fetching the ID on every app foreground, or polling after Firebase SDK upgrades).

### Firebase modules not yet wrapped

Expand Down
30 changes: 30 additions & 0 deletions Sources/SkipFirebaseInstallations/SkipFirebaseInstallations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,27 @@
#if canImport(FirebaseInstallations)
@_exported import FirebaseInstallations
#elseif SKIP
import Foundation
import SkipFirebaseCore
import kotlinx.coroutines.tasks.await

public final class InstallationsAuthTokenResult {
public let platformValue: com.google.firebase.installations.InstallationTokenResult

public init(platformValue: com.google.firebase.installations.InstallationTokenResult) {
self.platformValue = platformValue
}

public var authToken: String {
platformValue.token
}

public var expirationDate: Date {
// tokenExpirationTimestamp is Unix time in seconds
Date(timeIntervalSince1970: Double(platformValue.tokenExpirationTimestamp))
}
}

public final class Installations {
public let installations: com.google.firebase.installations.FirebaseInstallations

Expand All @@ -25,6 +43,18 @@ public final class Installations {
public func installationID() async throws -> String {
try await self.installations.getId().await()
}

public func authToken() async throws -> InstallationsAuthTokenResult {
InstallationsAuthTokenResult(platformValue: try await self.installations.getToken(false).await())
}

public func authTokenForcingRefresh(_ forceRefresh: Bool) async throws -> InstallationsAuthTokenResult {
InstallationsAuthTokenResult(platformValue: try await self.installations.getToken(forceRefresh).await())
}

public func delete() async throws {
try await self.installations.delete().await()
}
}
#endif
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ let logger: Logger = Logger(subsystem: "SkipFirebaseInstallationsTests", categor
if false {
let installations: Installations = Installations.installations()
let _: String = try await installations.installationID()
let tokenResult: InstallationsAuthTokenResult = try await installations.authToken()
let _: String = tokenResult.authToken
let _: Date = tokenResult.expirationDate
let _: InstallationsAuthTokenResult = try await installations.authTokenForcingRefresh(true)
try await installations.delete()
}
}
}