Skip to content

feat(firebase): tvOS support for Core, Auth, Firestore, Storage, Messaging - #3

Merged
DenisovAV merged 2 commits into
mainfrom
feat/firebase-tvos-plugins
Jul 3, 2026
Merged

feat(firebase): tvOS support for Core, Auth, Firestore, Storage, Messaging#3
DenisovAV merged 2 commits into
mainfrom
feat/firebase-tvos-plugins

Conversation

@MAUstaoglu

Copy link
Copy Markdown
Member

Summary

Adds federated *_tvos implementations bringing Firebase to Apple TV via the
flutter-tvos toolchain, built on the Firebase Apple SDK (which has supported
tvOS since 8.9.0). This closes the gap where the FlutterFire packages declare
ios/macos but not tvos, even though the underlying native SDK runs on tvOS.

PackagetvOS support
firebase_core_tvosFull
cloud_firestore_tvosFull
firebase_storage_tvosFull
firebase_auth_tvosAnonymous, email/password, email-link, Sign in with Apple, custom-token. MFA, phone, browser-OAuth providers and reCAPTCHA are absent from the Firebase tvOS SDK and return unsupported-platform.
firebase_messaging_tvosFCM token, topic subscription, permission request. Rich-notification payload (onMessage) and tapped-notification/interaction APIs are unavailable on tvOS.

Each package re-exports the upstream Dart API and ships the native tvOS
pluginClass; the four leaf packages depend on firebase_core_tvos.

Runtime verification

Verified against a live Firebase project on both the tvOS simulator (JIT) and
a physical Apple TV (tvOS 26.5, AOT/profile):

  • firebase_coreinitializeApp succeeds
  • firebase_auth — anonymous sign-in returns a real user
  • cloud_firestore — document write + read-back round-trip
  • firebase_storage — executes; returns a normal Storage error when no bucket is provisioned
  • firebase_messaging — executes; returns apns-token-not-set (APNs needs a push-enabled profile)

Platform.operatingSystem == "tvos" and Platform.isIOS == true confirmed under AOT.

Two issues that only surfaced at runtime were fixed:

  • firebase_auth_tvos aborted at plugin registration because the generated Pigeon
    setup asserts every multi-factor selector exists; the tvOS-disabled methods are
    now kept as unsupported-platform stubs rather than removed.
  • firebase_auth_tvos + firebase_messaging_tvos in the same app failed to compile
    (-[FIRAuth canHandleNotification:], unavailable on tvOS); now guarded.

Notes

Each package includes a PORTING_REPORT.md documenting exactly which native APIs
were adapted or disabled and why. These are 0.0.1 initial releases; the intended
publish order is firebase_core_tvos first, then the four leaf packages.

…torage, Messaging
Federated `*_tvos` packages bringing Firebase to Apple TV via the flutter-tvos
toolchain, built on the Firebase Apple SDK (tvOS supported since 8.9.0):
- firebase_core_tvos — full support
- cloud_firestore_tvos — full support
- firebase_storage_tvos — full support
- firebase_auth_tvos — anonymous, email/password, email-link, Sign in with
Apple, custom-token. MFA / phone / browser-OAuth /
reCAPTCHA return `unsupported-platform` (absent from
the Firebase tvOS SDK).
- firebase_messaging_tvos — token / topic / permission. Rich-notification payload
and interaction APIs are unavailable on tvOS.
Each package re-exports the upstream Dart API and supplies the native tvOS
pluginClass; the four leaf packages depend on firebase_core_tvos. Runtime-verified
on the tvOS simulator and a physical Apple TV in AOT mode: core/auth/firestore
perform live round-trips, storage/messaging execute and surface the expected
backend/APNs errors. See each package's PORTING_REPORT.md for the native changes
and tvOS support matrix.
@MAUstaoglu
MAUstaogluforce-pushed the feat/firebase-tvos-plugins branch from 8acb66e to 222313aCompareJuly 2, 2026 06:27

@DenisovAVDenisovAV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — firebase tvOS plugins

Verdict: APPROVE WITH CHANGES. The engineering is sound and unusually well-verified — live round-trips on the simulator and a physical Apple TV 4K under AOT. Native Firebase SDK version alignment (all podspecs → 12.15.0, matching upstream FlutterFire's firebase_sdk_version.rb), the Pigeon wire ABI (v26.3.4), the auth launch-crash fix (all 49 HostApi selectors have a tvOS-reachable impl, MFA/TOTP kept as unsupported-platform stubs), import repointing, and the pluginClass-only Dart shape are all verified clean. Nice work.

There is one silent-failure class worth fixing before publish, plus a couple of packaging/docs items.

Should fix before publish

  1. Phone-auth returns completion(nil, nil) on tvOS → deterministic null-error on the Dart side.verifyPhoneNumberApp and updatePhoneNumberApp call completion(nil, nil) on tvOS, unlike every other disabled path (MFA/TOTP/OAuth/reCAPTCHA), which returns an unsupported-platformFlutterError. The Pigeon return types here are non-nullable (Future<String> / InternalUserDetails), so a nil reply throws PlatformException(code: 'null-error', ...) — the app gets an opaque error instead of "phone auth is unsupported on tvOS," and no verification callback fires. This also contradicts the PORTING_REPORT, which describes phone verification as disabled like the MFA stubs (which return unsupported-platform). Fix is ~4 lines each — mirror the sibling MFA stub 30 lines away. Inline comments below.

  2. Missing pubspec_overrides.yaml. The four leaf packages declare firebase_core_tvos: ^0.0.1 as a hosted constraint with a comment saying local dev resolves it via pubspec_overrides.yaml — but no such file exists anywhere in the PR. Example apps are fine (they use path: deps), but a bare flutter pub get / pub publish --dry-run inside a leaf package can't resolve unpublished firebase_core_tvos 0.0.1. Either commit the overrides or document local creation + the intended publish order (core first).

Nice to have

  1. Auth README marks methods ✅ that were only compile-verified; the PORTING_REPORT only runtime-verified signInAnonymously end-to-end. Consider annotating runtime-verified vs. compile-only rows.
  2. onMessage / onMessageOpenedApp / getInitialMessage are silent no-ops on tvOS for real SDK reasons (no hang — completions fire, getInitialMessage → nil). Documented in the PORTING_REPORT; worth an explicit consumer-facing note that these streams never emit on tvOS.
  3. Storage/messaging examples pin deps with no tvOS impl (image_picker, flutter_local_notifications); worth a flutter pub get to confirm they still resolve against the pinned SDK (the auth example already hit this with font_awesome_flutter).

Minor polish (dead capturedCompletion locals, ~> vs = podspec pins, placeholder tests, inaccurate false_secrets: comments) noted but not blocking.

Full report with the passed-checks list is in the maintainer's docs/pr-reviews/.

#if TARGET_OS_OSX || TARGET_OS_TV
NSLog(@"The Firebase Phone Authentication provider is not supported on this "
@"platform.");
completion(nil, nil);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent failure on tvOS.completion(nil, nil) reports success with a nil result. The Pigeon Dart return type for verifyPhoneNumber is non-nullable Future<String> (the event-channel name the codeSent/verificationCompleted/verificationFailed callbacks subscribe to), so a nil reply deterministically throws PlatformException(code: 'null-error', message: 'Host platform returned null value for non-null return value.'). The app never learns phone auth is unsupported — it gets an opaque error and no callback fires.

Every other disabled path here (MFA/TOTP/OAuth/reCAPTCHA) already returns an unsupported-platformFlutterError. Mirror them:

completion(nil, [FlutterError errorWithCode:@"unsupported-platform"message:@"Phone number verification is not supported by the Firebase SDK on tvOS."details:nil]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Resolved in 26b67fc — now returns the unsupported-platformFlutterError. Verified against the diff.

NSLog(@"Updating a users phone number via Firebase Authentication is only "
@"supported on the iOS "
@"platform.");
completion(nil, nil);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same silent-failure pattern as verifyPhoneNumberApp. The port added && !TARGET_OS_TV to the #if TARGET_OS_IPHONE guard; since tvOS isTARGET_OS_IPHONE, this is a genuine new divergence (tvOS previously took the real branch). The Pigeon return type is non-nullable InternalUserDetails, so completion(nil, nil) produces the same deterministic null-error throw on the Dart side.

Return an unsupported-platformFlutterError here too, matching the sibling stubs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Resolved in 26b67fc — now returns the unsupported-platformFlutterError. Verified against the diff.

#else
NSLog(@"The Firebase Phone Authentication provider is not supported on this "
@"platform.");
completion(nil, nil);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Milder variant (optional). This branch also returns completion(nil, nil), but here it does not throw — every caller (signInWithCredential/linkWithCredential/reauthenticateWithCredentialApp) guards if (credential == nil) and surfaces invalid-credential. So it fails gracefully, but the surfaced error implies "you passed bad data" rather than "phone credentials are unavailable on tvOS." Consider returning a distinct unsupported-platform error from this branch so the caller-side message is honest.

- firebase_auth: verifyPhoneNumberApp / updatePhoneNumberApp now return an
`unsupported-platform` FlutterError on tvOS instead of `completion(nil, nil)`,
which threw an opaque `null-error` on the non-nullable Pigeon return type.
- Commit the leaf packages' `pubspec_overrides.yaml` (previously gitignored) so
`flutter pub get` / `dart pub publish --dry-run` resolve `firebase_core_tvos`
before it is published; add `.pubignore` so they stay out of the published
archive.
- Remove `false_secrets` from all pubspecs — the example configs are now
placeholder values, not real keys.
- Docs: note in the firebase_auth README that only anonymous sign-in is
runtime-verified end-to-end; note in the firebase_messaging README that
onMessage / onMessageOpenedApp / getInitialMessage never emit on tvOS.
@MAUstaoglu

Copy link
Copy Markdown
MemberAuthor

Addressed in 26b67fc:

  • Phone-authverifyPhoneNumberApp / updatePhoneNumberApp now return an unsupported-platformFlutterError instead of completion(nil, nil); auth example still builds.
  • pubspec_overrides.yaml — committed for the four leaf packages so pub get / dry-run resolve firebase_core_tvos locally, with a .pubignore so they stay out of the published archive.
  • false_secrets — removed; the example configs are placeholders now.
  • Docs — auth README notes only anonymous sign-in is runtime-verified; messaging README notes onMessage / onMessageOpenedApp / getInitialMessage never emit on tvOS.
  • Storage/messaging examples confirmed resolving.

The milder credential branch is left graceful (invalid-credential, no throw) — can return a distinct error if you prefer. Dead locals / podspec pins / placeholder tests noted for a follow-up.

@MAUstaoglu
MAUstaoglu requested a review from DenisovAVJuly 2, 2026 16:41
@DenisovAV

Copy link
Copy Markdown
Contributor

Thanks for the fast turnaround — went through 26b67fc against the code, everything checks out:

One small follow-up on the credential branch (FLTFirebaseAuthPlugin.m:891): leaving it graceful is fine, but the caller currently surfaces invalid-credential, which reads as "you passed bad data" rather than "phone credentials are unavailable on tvOS." If you're up for it, returning a distinct unsupported-platform error there (same ~4-line shape as the two you just fixed) would make the diagnostic honest. Not a blocker.

The minor items (dead capturedCompletion locals, ~> vs = podspec pins, placeholder tests) are fine to defer to the follow-up as you noted.

That covers all the review findings — the credential-branch tweak is the only optional item left. Nice work on the port.

@MAUstaoglu

Copy link
Copy Markdown
MemberAuthor

Thanks for the re-review. Rolling the credential-branch tweak into the follow-up with the other minor items (dead capturedCompletion locals, ~> vs = podspec pins, placeholder tests) — worth doing as a batch. Note it's slightly more than the sibling shape: the shared getFIRAuthCredentialFromArguments caller returns invalid-credential on the credential == nil guard before ever reaching the error branch, so making the phone path report unsupported-platform means checking error first (and adding the missing return) at the four call sites, not just the branch itself. In practice it's near-unreachable on tvOS anyway since there's no FIRPhoneAuthProvider to mint a phone credential, but it'll read honestly once reordered.

@DenisovAV

Copy link
Copy Markdown
Contributor

Good catch — you're right, and it's a bigger change than my "same 4-line shape" made it sound. I re-checked the call sites (:1739, :2114, :2231, :2471): each guards if (credential == nil) { … invalid-credential; return; }before the if (error) check, so just returning unsupported-platform from the :891 branch would get overwritten by the nil guard — the honest error never reaches Dart unless the two checks are reordered at all four sites, exactly as you said.

And nice spot on the missing return in the if (error) block — as written it falls through into signInWithCredential: / reauthenticateWithCredential: with a nil credential. It's masked today only because the credential == nil guard fires first; reordering the checks is what would expose it, so the return has to land in the same pass. Agreed that's follow-up-batch material, not a one-liner — and the phone path is effectively unreachable on tvOS anyway (no FIRPhoneAuthProvider to mint the credential), so there's no functional regression waiting on it.

Batching it with the dead locals / podspec pins / placeholder tests makes sense. Nothing else outstanding from my side.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MAUstaoglu@DenisovAV