Skip to content

feat(firebase): tvOS support for firebase_performance - #9

Open
TheNoumanDev wants to merge 2 commits into
fluttertv:mainfrom
TheNoumanDev:feat/firebase-performance-tvos
Open

feat(firebase): tvOS support for firebase_performance#9
TheNoumanDev wants to merge 2 commits into
fluttertv:mainfrom
TheNoumanDev:feat/firebase-performance-tvos

Conversation

@TheNoumanDev

Copy link
Copy Markdown

What does this PR do?

Adds federated firebase_performance_tvos — Firebase Performance Monitoring for Apple TV, built on the Firebase Apple SDK. Re-exports the firebase_performance Dart API and ships the native tvOS pluginClass; depends on firebase_core_tvos. Full Performance API (custom traces + HTTP metrics); no features disabled.

Package(s) touched: firebase_performance_tvos (new)

How was it tested?

Verified against a live Firebase project on both the tvOS simulator and a physical Apple TV 4K (release/AOT). Firebase Performance Monitoring is successfully initialized!, custom traces + an HTTP metric were captured and sent, and the traces were confirmed in the Firebase Performance dashboard (Custom traces tab) — tvos_smoke_trace (206 ms, simulator) and tvos_device_trace (207 ms, physical Apple TV). No features are disabled (Performance is UI-free). Platform.operatingSystem == "tvos" / Platform.isIOS == true.

  • Ran the package's example/ app
  • Verified on tvOS simulator (version: 26.2) — trace + HTTP metric confirmed in the Performance dashboard (tvos_smoke_trace, 206 ms)
  • Verified on a physical Apple TV 4K (tvOS 26.2, release/AOT) — custom trace confirmed in the Performance dashboard (tvos_device_trace, 207 ms)
  • dart analyze is clean for the package

Versioning & changelog

  • version: set to 0.0.1 (new package)
  • Matching ## 0.0.1 entry at the top of CHANGELOG.md
  • No behaviour change (new package)
  • Semver 0.x: initial 0.0.1

Checklist

  • Only firebase_performance_tvos files are touched
  • No secrets, absolute local paths, or TODO/debug leftovers
  • README.md documents tvOS behaviour (no feature limitations; version-alignment note)
  • Sibling-package constraint noted below

Notes for reviewers

  • Version alignment (affects all Firebase _tvos leaf packages): the native code matches firebase_performance 0.11.4+3 on the firebase_core_platform_interface 7.1.0 train (firebase_core 4.11.x). Mixing firebase_core_tvos with a different firebase_core whose FirebaseOptions list differs can crash in CoreFirebaseOptions.fromList at launch (Dart options: init).
  • Manual work beyond the auto-port: podspec deps (Firebase/Performance + firebase_core_tvos, tvOS 15); Swift firebase_core import repointed to firebase_core_tvos; generated tvos/Package.swift removed (route via podspec); Dart re-export.

@DenisovAV DenisovAV 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

Clean port — the discipline here is exactly right, and the verification is the most convincing part. Three small changes requested, none of them in the ported native code.


1. plugins/README.md Ports table is not updated

AUTHORING.md §4 makes this a step of its own — "Edit plugins/README.md, add the new row to the Ports table" — and the porting checklist repeats it. All sixteen existing packages have a row; this one doesn't, so the package is undiscoverable from the repo index.

I see the PR checklist says only firebase_performance_tvos files are touched, so this looks deliberate. It's the one place where touching a file outside the package is the documented expectation.

| [`firebase_performance_tvos`](packages/firebase_performance_tvos) [![pub](https://img.shields.io/pub/v/firebase_performance_tvos.svg)](https://pub.dev/packages/firebase_performance_tvos) | [`firebase_performance`](https://pub.dev/packages/firebase_performance) |

2. Both pubspec_overrides.yaml files are obsolete — by their own comment

Each says "Remove once firebase_core_tvos 0.0.1 is on pub.dev." It is:

$ curl -s https://pub.dev/api/packages/firebase_core_tvos | jq -r .latest.version
0.0.1

They aren't merely redundant — the example one is load-bearing today, because example/pubspec.yaml declares firebase_core_tvos as a path: dependency while this package requires it as hosted ^0.0.1, and pub refuses to mix sources for one package. The override is what collapses them. So removing the overrides alone breaks the example:

$ flutter pub get          # overrides removed, example still on path:
Because firebase_performance_example depends on firebase_performance_tvos from path
which depends on firebase_core_tvos ^0.0.1, firebase_core_tvos from hosted is required.
So, because firebase_performance_example depends on firebase_core_tvos from path,
version solving failed.

Removing both overrides and switching the example to the hosted constraint resolves cleanly — verified:

 # example/pubspec.yaml
-  firebase_core_tvos:
-    path: ../../firebase_core_tvos
+  firebase_core_tvos: ^0.0.1
$ flutter pub get
Got dependencies!
Resolving dependencies in `./example`...
Got dependencies in `./example`.

Two notes on scope: the same stale pair sits in cloud_firestore_tvos, firebase_auth_tvos, firebase_messaging_tvos and firebase_storage_tvos, so cleaning those up is a separate change, not yours. And the example override's comment says "Gitignored" — it is committed, and the package .gitignore doesn't list it. (.pubignore does exclude it, so publishing was never at risk.)

3. The example's Info.plist still identifies itself as the storage example

example/tvos/Runner/Info.plist is byte-identical to firebase_storage_tvos's, display strings included:

<key>CFBundleDisplayName</key>
<string>Firebase_storage_example</string>
...
<key>CFBundleName</key>
<string>firebase_storage_example</string>

Installed on an Apple TV, this example shows up as "Firebase_storage_example" on the home screen and in Settings — so anyone running both example apps gets two tiles they can't tell apart. PRODUCT_BUNDLE_IDENTIFIER is correctly com.example.firebasePerformanceExample, so Firebase app matching is unaffected; only the human-facing strings are wrong.

These are the only two stale references in the package — I grepped all 59 files, and the sole other storage hit is the legitimate storageBucket field in firebase_options.dart.


What I verified

The repository has no CI — there is no .github/workflows, and no PR in it has ever reported a check, despite AUTHORING.md §6 saying "CI will run flutter pub get + flutter analyze on the new package." So I ran the checks by hand and looked at the port mechanically.

The port is minimal and faithful. Diffed all three Swift files against firebase_performance 0.11.4+3 as published on pub.dev. Exactly three deltas, every one necessary:

# FirebasePerformanceMessages.g.swift
-#if os(iOS)
+#if (os(iOS) || os(tvOS))

# FirebasePerformancePlugin.swift
-#if canImport(firebase_core)
-  import firebase_core
-#else
-  import firebase_core_shared
-#endif
+import firebase_core_tvos

-    #elseif os(iOS)
+    #elseif (os(iOS) || os(tvOS))

Constants.swift is byte-identical to upstream. No stubs, no disabled regions, nothing rewritten that didn't have to be — which is what makes the next rebase onto a new firebase_performance cheap.

Podspec. Structurally matches the merged firebase_storage_tvos, and Firebase/Performance '~> 12.15.0' is aligned with every other Firebase _tvos package in the repo, firebase_core_tvos included — so CocoaPods resolves one Firebase version across the stack.

The tvOS 15.0 floor is right, not a guess. Firebase Performance 12.15.0's own podspec declares tvos_deployment_target = '15.0'.

"No feature disables" holds more strongly than the README claims. The README reads as a statement about the plugin's API surface, but the automatic instrumentation is fine too: FPRScreenTraceTracker.m in the Firebase Apple SDK carries explicit #if TARGET_OS_TV branches — it even recomputes the slow-frame budget on UIScreenModeDidChangeNotification, because a tvOS refresh rate can change with the display mode. Screen traces are supported on tvOS by design, not tolerated.

Hygiene. firebase_options.dart is all REPLACE_ME — no project credentials committed. No absolute paths or personal identifiers anywhere in the 59 files.

Static checks (Flutter 3.44.0 stable):

$ flutter pub get     # package + example
Got dependencies!
$ dart analyze        # package
No issues found!
$ (cd example && dart analyze)
No issues found!
$ flutter test
00:00 +1: All tests passed!

A repo-level note your README raised, and I went and checked

Not a request on this PR — it applies equally to all five existing Firebase packages, and you inherited it. But your "Version alignment matters" warning is more load-bearing than it reads, so it's worth writing down where the sharp edge actually is.

firebase_core_tvos encodes FirebaseOptions as a positional list of exactly fifteen elements, decoded with an unchecked index:

pigeonResult.recaptchaSiteKey = GetNullableObjectAtIndex(list, 14);
…
static id GetNullableObjectAtIndex(NSArray<id> *array, NSInteger key) {
  id result = array[key];        // no bounds check

initializeApp sends those options back to Dart, and the Dart side is equally positional — firebase_core_platform_interface 7.1.0, lib/src/pigeon/messages.pigeon.dart:186-205:

static CoreFirebaseOptions decode(Object result) {
  result as List<Object?>;
  return CoreFirebaseOptions(
    apiKey: result[0]! as String,
    …
    recaptchaSiteKey: result[14] as String?,
  );
}

Fifteen on each side today, so it works. The two directions are not symmetric: a Dart side with fewer fields ignores the trailing element harmlessly, while a Dart side with more reads result[15] off a fifteen-element list and throws — a RangeError at Firebase.initializeApp(), which is the launch crash your README describes in prose.

The guard against that is one line in firebase_core_tvos:

firebase_core: ^4.11.0

It cannot do the job, and tightening it doesn't help either. firebase_core 4.11.0 declares firebase_core_platform_interface: ^7.1.0, so the arity floats across all of 7.x no matter how narrowly firebase_core itself is pinned — even an exact firebase_core: 4.11.0 leaves the field count free to change under it. The only constraint that actually pins the arity is a direct one on firebase_core_platform_interface in firebase_core_tvos, since that is the package the generated code's shape comes from.

Worth a separate issue rather than anything here — it applies to all five Firebase _tvos packages, and only firebase_core_tvos needs the change.


One more note

test/firebase_performance_tvos_test.dart is the porter's placeholder (expect(1 + 1, 2)). That matches every sibling and AUTHORING.md §5 says plugin testing in this repo is app-driven, so it isn't something to fix here. Worth saying out loud, though: with no CI and no unit tests, your manual verification is the only gate this package has — and it is a good one. Two distinct named traces with durations, on the simulator and a physical Apple TV 4K in release/AOT, confirmed in the dashboard rather than inferred from a log line, is more evidence than most ports arrive with.

Happy to approve once the README row, the override cleanup and the Info.plist strings land.

Comment on lines +9 to +17
<string>Firebase_storage_example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>firebase_storage_example</string>

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.

Copy-paste leftover: this file is byte-identical to firebase_storage_tvos's example Info.plist, display strings included. Installed on an Apple TV the example shows up as Firebase_storage_example on the home screen and in Settings, so running both examples side by side gives two tiles you can't tell apart.

PRODUCT_BUNDLE_IDENTIFIER is correct (com.example.firebasePerformanceExample), so Firebase app matching is unaffected — only the human-facing strings.

Suggested change
<string>Firebase_storage_example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>firebase_storage_example</string>
<string>Firebase_performance_example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>firebase_performance_example</string>

(I grepped all 59 files — these two lines are the only stale references; the other storage hit is the legitimate storageBucket field.)

Comment on lines +1 to +6
# Local development only — resolves the unpublished firebase_core_tvos from the
# sibling package in this monorepo. Excluded from the published archive via
# .pubignore so consumers get the hosted `^0.0.1` instead.
dependency_overrides:
firebase_core_tvos:
path: ../firebase_core_tvos

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.

This file and example/pubspec_overrides.yaml are both obsolete by their own comment — "Remove once firebase_core_tvos 0.0.1 is on pub.dev" — and it is:

$ curl -s https://pub.dev/api/packages/firebase_core_tvos | jq -r .latest.version
0.0.1

Careful with the order, though: the example one is load-bearing right now. example/pubspec.yaml declares firebase_core_tvos as a path: dep while this package requires it hosted as ^0.0.1, and pub won't mix sources for one package — the override is what collapses them. Deleting the overrides alone breaks the example:

$ flutter pub get
Because firebase_performance_example depends on firebase_performance_tvos from path
which depends on firebase_core_tvos ^0.0.1, firebase_core_tvos from hosted is required.
So, because firebase_performance_example depends on firebase_core_tvos from path,
version solving failed.

Deleting both and moving the example to the hosted constraint resolves cleanly — verified:

 # example/pubspec.yaml
-  firebase_core_tvos:
-    path: ../../firebase_core_tvos
+  firebase_core_tvos: ^0.0.1

The same stale pair sits in cloud_firestore_tvos, firebase_auth_tvos, firebase_messaging_tvos and firebase_storage_tvos, so cleaning those is a separate change, not yours. Also: the example override's comment says "Gitignored" — it is committed, and the package .gitignore doesn't list it. (.pubignore does exclude it, so publishing was never at risk.)

Sign up for free to 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