diff --git a/packages/firebase_analytics_tvos/.gitignore b/packages/firebase_analytics_tvos/.gitignore new file mode 100644 index 0000000..fd30348 --- /dev/null +++ b/packages/firebase_analytics_tvos/.gitignore @@ -0,0 +1,26 @@ +# Dart / Flutter +.dart_tool/ +build/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub/ + +# CocoaPods +tvos/Pods/ +tvos/Podfile.lock +tvos/.symlinks/ +tvos/Flutter/Flutter.framework +tvos/Flutter/Flutter.podspec + +# Xcode / SwiftPM (per-user, generated when tvos/Package.swift is opened) +**/.swiftpm/ +**/xcuserdata/ + +# IDE +.idea/ +.vscode/ +*.iml + +# macOS +.DS_Store diff --git a/packages/firebase_analytics_tvos/.pubignore b/packages/firebase_analytics_tvos/.pubignore new file mode 100644 index 0000000..671e1c9 --- /dev/null +++ b/packages/firebase_analytics_tvos/.pubignore @@ -0,0 +1 @@ +pubspec_overrides.yaml diff --git a/packages/firebase_analytics_tvos/CHANGELOG.md b/packages/firebase_analytics_tvos/CHANGELOG.md new file mode 100644 index 0000000..7d46e8b --- /dev/null +++ b/packages/firebase_analytics_tvos/CHANGELOG.md @@ -0,0 +1,8 @@ +## 0.0.1 + +- Initial tvOS (Apple TV) implementation of `firebase_analytics`, ported from + `firebase_analytics` 12.4.3 via `flutter-tvos plugin port` and finished by + hand (podspec `Firebase/Analytics` + `firebase_core_tvos` deps, tvOS 15.0 + floor, `firebase_core_tvos` import, Dart re-export). +- Verified on the tvOS simulator and a physical Apple TV 4K (tvOS 26.2): + Firebase initializes and Analytics events reach the backend. diff --git a/packages/firebase_analytics_tvos/LICENSE b/packages/firebase_analytics_tvos/LICENSE new file mode 100644 index 0000000..5b8ff62 --- /dev/null +++ b/packages/firebase_analytics_tvos/LICENSE @@ -0,0 +1,26 @@ +Copyright 2017, the Chromium project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/firebase_analytics_tvos/PORTING_REPORT.md b/packages/firebase_analytics_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..eda1ad5 --- /dev/null +++ b/packages/firebase_analytics_tvos/PORTING_REPORT.md @@ -0,0 +1,85 @@ +# firebase_analytics_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-08-07. + +Source: `firebase_analytics` 12.4.3 (Swift). Base platform: ios. +Output: `./firebase_analytics_tvos` + +> ✅ No tvOS-incompatible APIs detected at type level — the generated package is expected to compile on tvOS (still review stubbed/partial items below). + +## Summary + +| Status | Count | +| ------------------------------- | ---------------------- | +| Methods ported as-is | 0 | +| Methods stubbed (iOS-only) | 0 | +| Native regions disabled on tvOS | 0 | +| tvOS build outlook | ✅ expected to compile | +| Manual review items | 0 | + +## Methods + +No `case "":` handlers were detected in the source. Either the plugin dispatches method calls in a non-standard way (review `tvos/Classes/` by hand) or it has no method channel. + +## Imports removed + +None. Every `import` in the source compiles on tvOS. + +## Cross-platform Dart pruned + +None. The source ships no Dart files for non-Apple platforms — nothing had to be removed. + +## Disabled on tvOS + +None. No type-level tvOS-incompatible API was found; nothing had to be compiled out. + +## Manual review items + +None flagged automatically. You should still skim `tvos/Classes/` — regex-based porting is best-effort and cannot catch every obfuscated API use. + +## Checklist + +- [x] Stubbed methods — **N/A**: no methods were stubbed (analytics has no tvOS-incompatible APIs). +- [x] Partial methods — **N/A**: none. Full API is available on tvOS. +- [x] Removed imports — **N/A**: none removed. +- [x] `flutter-tvos build tvos` compiles the generated registrant — **verified** (simulator + physical Apple TV 4K, tvOS 26.2). +- [x] Version set (`0.0.1`) and `CHANGELOG.md` updated. + +--- + +Manual review complete (2026-08-09) — see the addendum below for the applied fixes and runtime verification. + +--- + +## Addendum: manual fixes + verification (2026-08-09) + +The automated port compiles clean (analytics has no tvOS-incompatible APIs — no +stubs, no `#if !TARGET_OS_TV` disables). Manual work applied on top: + +- **Podspec:** depend on `Firebase/Analytics '~> 12.15.0'` + `firebase_core_tvos` + (not upstream `firebase_core`, which declares no tvOS platform); bump + `s.platform` to tvOS 15.0 (Firebase 12.x floor); `static_framework = true`. + No Flutter-CocoaPod dependency (resolved via `FRAMEWORK_SEARCH_PATHS`). +- **`FirebaseAnalyticsPlugin.swift`:** `#if canImport(firebase_core) … #else +firebase_core_shared` repointed to a plain `import firebase_core_tvos`. +- **Swift package:** deleted the generated `tvos/Package.swift` (route via the + podspec, like `firebase_storage_tvos`; the SPM manifest lacks the Firebase + + `firebase_core_tvos` deps). +- **`lib/firebase_analytics_tvos.dart`:** one-line re-export of + `package:firebase_analytics/firebase_analytics.dart` (the Dart API is + method-channel-only, no per-platform override — duplicating it would create + incompatible types). + +**Version alignment (important for consumers).** This package's native Pigeon +matches `firebase_analytics 12.4.3` on the **`firebase_core_platform_interface` +7.1.0** train (i.e. `firebase_core 4.11.x`). An app mixing `firebase_core_tvos` +(built on that train, 15-field `FirebaseOptions`) with an older/newer +`firebase_core` whose options list differs will crash in +`CoreFirebaseOptions.fromList` at launch when initializing via Dart `options:`. +Keep the whole Firebase stack on one consistent FlutterFire release. + +**Verified.** `flutter-tvos build tvos` (simulator + physical **Apple TV 4K, +tvOS 26.2, arm64**); Firebase `initializeApp(options:)` succeeds, Analytics +`12.15.0` starts, collection enabled, and events reach the Firebase project +(confirmed as an active user in the Analytics overview). `Platform.operatingSystem +== "tvos"`, `Platform.isIOS == true`. diff --git a/packages/firebase_analytics_tvos/README.md b/packages/firebase_analytics_tvos/README.md new file mode 100644 index 0000000..6215ba4 --- /dev/null +++ b/packages/firebase_analytics_tvos/README.md @@ -0,0 +1,40 @@ +# firebase_analytics_tvos + +The tvOS implementation of [`firebase_analytics`](https://pub.dev/packages/firebase_analytics). + +> Ported with [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `firebase_analytics` 12.4.3, then finished + verified by hand. See +> `PORTING_REPORT.md`. + +## Usage + +Federated plugin implementation — no imports needed from app code; it registers +automatically. Apps that already use `firebase_analytics` and target tvOS add: + +```yaml +dependencies: + firebase_analytics: ^12.4.3 + firebase_analytics_tvos: ^0.0.1 + firebase_core: ^4.11.0 + firebase_core_tvos: ^0.0.1 # tvOS core (this package depends on it) +``` + +> **Version alignment matters.** This package's native code matches the +> `firebase_core_platform_interface` **7.1.0** train (`firebase_core 4.11.x`). +> Keep your whole Firebase stack on one consistent FlutterFire release, or Dart +> `initializeApp(options:)` can crash in `CoreFirebaseOptions.fromList` at launch +> (a `FirebaseOptions` field-count mismatch). See `PORTING_REPORT.md`. + +## Status + +| Platform | Implemented | Verified | +| --------------------------------------- | ----------- | ----------------------------------------------------------------------------- | +| Apple TV (`appletvos`) | yes | ✅ physical Apple TV 4K, tvOS 26.2 — Firebase inits, events reach the backend | +| Apple TV simulator (`appletvsimulator`) | yes | ✅ builds + runs | + +All `firebase_analytics` APIs are available (no tvOS feature disables). Events +appear in Firebase **DebugView** when the app is launched with `-FIRDebugEnabled`. + +## License + +fluttertv under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/firebase_analytics_tvos/analysis_options.yaml b/packages/firebase_analytics_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/firebase_analytics_tvos/analysis_options.yaml @@ -0,0 +1,7 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true diff --git a/packages/firebase_analytics_tvos/example/.gitignore b/packages/firebase_analytics_tvos/example/.gitignore new file mode 100644 index 0000000..d5fead7 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/.gitignore @@ -0,0 +1 @@ +GoogleService-Info.plist diff --git a/packages/firebase_analytics_tvos/example/README.md b/packages/firebase_analytics_tvos/example/README.md new file mode 100644 index 0000000..ab187cb --- /dev/null +++ b/packages/firebase_analytics_tvos/example/README.md @@ -0,0 +1,68 @@ +# firebase_analytics_tvos example (Apple TV) + +Minimal app that logs Firebase Analytics events from tvOS, for verifying the +`firebase_analytics_tvos` federated plugin on the Simulator and a physical +Apple TV. + +## 1. Connect it to Firebase + +On tvOS, `defaultTargetPlatform == TargetPlatform.iOS`, so **the iOS Firebase +app config is what tvOS uses** — you do _not_ need a separate tvOS app in the +Firebase console (though you may register one with the bundle id +`com.example.firebaseAnalyticsExample` if you prefer a clean split). + +Two ways to supply the config: + +**A. FlutterFire CLI (recommended — regenerates `lib/firebase_options.dart`):** + +```bash +cd packages/firebase_analytics_tvos/example +flutterfire configure # pick your project; select the iOS app +``` + +This overwrites the placeholder `lib/firebase_options.dart` with real values. +tvOS reads the `ios` entry automatically. + +**B. Hand-fill:** copy `apiKey` / `appId` (GOOGLE_APP_ID) / `messagingSenderId` +(GCM_SENDER_ID) / `projectId` / `storageBucket` from your iOS app's +`GoogleService-Info.plist` (or Firebase console → Project settings) into the +`ios` block of `lib/firebase_options.dart`. + +> **Optional — GoogleService-Info.plist:** the Dart options above are enough for +> Analytics. If you also want to drop the plist in, put your iOS app's +> `GoogleService-Info.plist` at `tvos/Runner/GoogleService-Info.plist` and add it +> to the Runner target in Xcode (drag it into the Runner group, tick +> "Copy items if needed" + the Runner target). Not required. + +## 2. Run it + +```bash +export PATH="/path/to/flutter-tvos/bin:$PATH" + +# Simulator: +flutter-tvos pub get +open -a Simulator # boot a tvOS simulator +flutter-tvos run -d "Apple TV" # or: flutter-tvos build tvos --simulator --debug + +# Physical Apple TV (must be paired to Xcode: Xcode ▸ Devices & Simulators): +flutter-tvos run -d +``` + +## 3. See events in Firebase DebugView (near-real-time) + +Analytics normally batches events for ~1 hour. **DebugView** shows them within +seconds. Enable debug mode by passing a launch argument: + +- **From Xcode:** open `tvos/Runner.xcworkspace` → scheme **Runner** ▸ Edit + Scheme ▸ Run ▸ Arguments ▸ _Arguments Passed On Launch_ → add `-FIRDebugEnabled`. + Run the Runner scheme. +- (To stop: use `-FIRDebugDisabled` once.) + +Then in the app press **"logEvent: test_event"** (or any button) with the Siri +Remote. In the Firebase console open **Analytics ▸ DebugView** and select your +debug device — you'll see `test_event`, `screen_view`, `login`, etc. stream in +with their parameters within a few seconds. + +> Simulator note: Analytics runs on the Simulator, but for a true end-to-end +> check use a **physical Apple TV in profile/AOT** (`flutter-tvos run --profile`) +> — that's what confirms the plugin under release-mode compilation. diff --git a/packages/firebase_analytics_tvos/example/lib/firebase_options.dart b/packages/firebase_analytics_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..6589034 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/lib/firebase_options.dart @@ -0,0 +1,39 @@ +// PLACEHOLDER — replace by running `flutterfire configure` in this example dir. +// +// On tvOS, `defaultTargetPlatform == TargetPlatform.iOS`, so the `ios` entry +// below is what the Apple TV build uses — i.e. your existing iOS Firebase app's +// config is reused for tvOS. Running `flutterfire configure` regenerates this +// file with real values pulled from your Firebase project. +// +// ignore_for_file: lines_longer_than_80_chars +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError('This example targets Apple TV (tvOS) only.'); + } + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: // tvOS reports iOS + return ios; + default: + throw UnsupportedError( + 'This example targets Apple TV (tvOS) only.', + ); + } + } + + // Placeholders — run `flutterfire configure` (select your project's iOS app), + // or copy the values from your iOS app's GoogleService-Info.plist. tvOS reuses + // this iOS config because `defaultTargetPlatform == TargetPlatform.iOS`. + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'REPLACE_ME', + appId: 'REPLACE_ME', // GOOGLE_APP_ID + messagingSenderId: 'REPLACE_ME', // GCM_SENDER_ID + projectId: 'REPLACE_ME', + storageBucket: 'REPLACE_ME', + iosBundleId: 'com.example.firebaseAnalyticsExample', + ); +} diff --git a/packages/firebase_analytics_tvos/example/lib/main.dart b/packages/firebase_analytics_tvos/example/lib/main.dart new file mode 100644 index 0000000..1c5e193 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/lib/main.dart @@ -0,0 +1,134 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; + +import 'firebase_options.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + runApp(const AnalyticsExampleApp()); +} + +class AnalyticsExampleApp extends StatelessWidget { + const AnalyticsExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + final analytics = FirebaseAnalytics.instance; + return MaterialApp( + title: 'firebase_analytics tvOS example', + theme: ThemeData.dark(useMaterial3: true), + // The FirebaseAnalyticsObserver logs screen_view events automatically. + navigatorObservers: [ + FirebaseAnalyticsObserver(analytics: analytics), + ], + home: AnalyticsHomePage(analytics: analytics), + ); + } +} + +class AnalyticsHomePage extends StatefulWidget { + const AnalyticsHomePage({super.key, required this.analytics}); + + final FirebaseAnalytics analytics; + + @override + State createState() => _AnalyticsHomePageState(); +} + +class _AnalyticsHomePageState extends State { + String _status = 'Ready. Pick an action with the Siri Remote.'; + + Future _run(String label, Future Function() action) async { + try { + await action(); + setState(() => _status = '✅ $label — sent to Firebase.'); + } catch (e) { + setState(() => _status = '❌ $label — $e'); + } + } + + @override + void initState() { + super.initState(); + // Make sure collection is on (it is by default unless disabled remotely). + widget.analytics.setAnalyticsCollectionEnabled(true); + } + + @override + Widget build(BuildContext context) { + final a = widget.analytics; + return Scaffold( + appBar: AppBar(title: const Text('firebase_analytics · tvOS')), + body: Padding( + padding: const EdgeInsets.all(48), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text(_status, style: const TextStyle(fontSize: 28)), + const SizedBox(height: 32), + Wrap( + spacing: 24, + runSpacing: 24, + children: [ + ElevatedButton( + autofocus: true, + onPressed: () => _run( + 'logEvent(test_event)', + () => a.logEvent( + name: 'test_event', + parameters: { + 'source': 'apple_tv', + 'string': 'hello_tvos', + 'int': 42, + }, + ), + ), + child: const Text('logEvent: test_event'), + ), + ElevatedButton( + onPressed: () => _run( + 'logLogin', + () => a.logLogin(loginMethod: 'demo'), + ), + child: const Text('logLogin'), + ), + ElevatedButton( + onPressed: () => _run( + 'logScreenView', + () => a.logScreenView(screenName: 'Home_tvOS'), + ), + child: const Text('logScreenView'), + ), + ElevatedButton( + onPressed: () => _run( + 'setUserProperty', + () => a.setUserProperty(name: 'tier', value: 'demo'), + ), + child: const Text('setUserProperty'), + ), + ElevatedButton( + onPressed: () => _run( + 'getAppInstanceId', + () async { + final id = await a.appInstanceId; + setState(() => _status = 'appInstanceId = $id'); + }, + ), + child: const Text('getAppInstanceId'), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_analytics_tvos/example/pubspec.yaml b/packages/firebase_analytics_tvos/example/pubspec.yaml new file mode 100755 index 0000000..4066cf4 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: firebase_analytics_example +description: Demonstrates how to use the firebase_analytics plugin on tvOS. +publish_to: none + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + # Pinned to the train the published _tvos packages were built from + # (firebase_core 4.11.0 -> firebase_core_platform_interface 7.1.0, whose + # FirebaseOptions has 15 fields incl. recaptchaSiteKey, matching the native + # Pigeon in firebase_core_tvos). No patch, no plist needed: Dart sends 15 + # fields, native reads 15. This is the same train flutter_pca will bump to. + firebase_core: 4.11.0 + firebase_analytics: 12.4.3 + firebase_analytics_tvos: + path: ../ + firebase_core_tvos: + path: ../../firebase_core_tvos + flutter: + sdk: flutter + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/firebase_analytics_tvos/example/pubspec_overrides.yaml b/packages/firebase_analytics_tvos/example/pubspec_overrides.yaml new file mode 100644 index 0000000..e45a494 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/pubspec_overrides.yaml @@ -0,0 +1,7 @@ +# Local development override for the example app. Forces firebase_core_tvos +# to the sibling package so the example resolves against the plugin's hosted +# `^0.0.1` constraint before that package is published. Gitignored; examples +# are not published. Remove once firebase_core_tvos 0.0.1 is on pub.dev. +dependency_overrides: + firebase_core_tvos: + path: ../../firebase_core_tvos diff --git a/packages/firebase_analytics_tvos/example/tvos/.gitignore b/packages/firebase_analytics_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/firebase_analytics_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/firebase_analytics_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/firebase_analytics_tvos/example/tvos/Flutter/Release.xcconfig b/packages/firebase_analytics_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/firebase_analytics_tvos/example/tvos/Podfile b/packages/firebase_analytics_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Podfile @@ -0,0 +1,45 @@ +# Flutter tvOS Podfile — auto-generated by flutter-tvos create. +# Reads .flutter-plugins-dependencies and adds local pods for each plugin. + +platform :tvos, '15.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +target 'Runner' do + use_frameworks! + + # Install plugin pods from .flutter-plugins-dependencies + flutter_plugins_deps = File.expand_path(File.join('..', '.flutter-plugins-dependencies'), File.dirname(__FILE__)) + if File.exist?(flutter_plugins_deps) + require 'json' + deps = JSON.parse(File.read(flutter_plugins_deps)) + tvos_plugins = deps.dig('plugins', 'tvos') || [] + tvos_plugins.each do |plugin| + plugin_name = plugin['name'] + plugin_path = plugin['path'] + tvos_dir = File.join(plugin_path, 'tvos') + # Plugins that ship a Package.swift are resolved via Swift Package Manager + # (see flutter-tvos's generated FlutterGeneratedPluginSwiftPackage). Skip + # them here so they are never linked twice (SPM + CocoaPods). + has_spm = File.exist?(File.join(tvos_dir, 'Package.swift')) + if File.directory?(tvos_dir) && !has_spm && File.exist?(File.join(tvos_dir, "#{plugin_name}.podspec")) + pod plugin_name, :path => tvos_dir + end + end + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['TVOS_DEPLOYMENT_TARGET'] = '15.0' + end + end +end diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..551dcde --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 69FC96ED025011896A40B981 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C376E448723773D75EC4B7DB /* Pods_Runner.framework */; }; + 97C146FB1CF9000082B4168C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000082B4168C /* AppDelegate.swift */; }; + 97C1470A1CF9000082B4168D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000082B4168D /* Main.storyboard */; }; + 97C1470B1CF9000082B4168D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000082B4168E /* LaunchScreen.storyboard */; }; + 97C1470F1CF9000082B4168C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000082B4168C /* Assets.xcassets */; }; + AAF20000000000000000F00D /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = AAF30000000000000000F00D /* FlutterGeneratedPluginSwiftPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAB004F5970 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 5C2882EE07DB51AF11121F93 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000082B41680 /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FA1CF9000082B4168C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 97C146FD1CF9000082B4168C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C146FE1CF9000082B4168C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 97C146FF1CF9000082B4168D /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FF1CF9000082B4168E /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + AAA000000000000000000003 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + C376E448723773D75EC4B7DB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C5D40B00A3F9A1FE37D78EA0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + E5FDE818A65A37D84788B042 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000082B4168C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AAF20000000000000000F00D /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + 69FC96ED025011896A40B981 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 72F2B22472AF13926912E010 /* Pods */ = { + isa = PBXGroup; + children = ( + E5FDE818A65A37D84788B042 /* Pods-Runner.debug.xcconfig */, + 5C2882EE07DB51AF11121F93 /* Pods-Runner.release.xcconfig */, + C5D40B00A3F9A1FE37D78EA0 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 97C146E51CF9000082B4168C = { + isa = PBXGroup; + children = ( + 97C146F01CF9000082B4168C /* Runner */, + 97C146F01CF9000082B4168E /* Flutter */, + 97C146F01CF9000082B4168F /* Frameworks */, + 97C146EF1CF9000082B41690 /* Products */, + 72F2B22472AF13926912E010 /* Pods */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000082B41690 /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000082B41680 /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000082B4168C /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000082B4168C /* AppDelegate.swift */, + AAA000000000000000000003 /* Runner-Bridging-Header.h */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 97C146FD1CF9000082B4168C /* Assets.xcassets */, + 97C146FE1CF9000082B4168C /* Info.plist */, + 97C146FF1CF9000082B4168D /* Main.storyboard */, + 97C146FF1CF9000082B4168E /* LaunchScreen.storyboard */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F01CF9000082B4168E /* Flutter */ = { + isa = PBXGroup; + children = ( + 74858FAE1ED2DC5600515810 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146F01CF9000082B4168F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAB004F5970 /* Flutter.framework */, + C376E448723773D75EC4B7DB /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000082B41690 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + FF571599D98BF3C1DDB19F8C /* [CP] Check Pods Manifest.lock */, + 97C146EA1CF9000082B4168C /* Sources */, + 97C146EB1CF9000082B4168C /* Frameworks */, + 97C146EC1CF9000082B4168C /* Resources */, + AAF10000000000000000F00D /* Embed App.framework */, + 9740EEB31CF901A200538489 /* Copy flutter_assets */, + 2E4ECA833C8DE363BE0B80E1 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + packageProductDependencies = ( + AAF30000000000000000F00D /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 97C146EE1CF9000082B41680 /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000082B4168C /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1510; + LastUpgradeCheck = 1510; + TargetAttributes = { + 97C146ED1CF9000082B41690 = { + CreatedOnToolsVersion = 15.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000082B4168C /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000082B4168C; + packageReferences = ( + AAF40000000000000000F00D /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 97C146EF1CF9000082B41690 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000082B41690 /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000082B4168C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C1470F1CF9000082B4168C /* Assets.xcassets in Resources */, + 97C1470A1CF9000082B4168D /* Main.storyboard in Resources */, + 97C1470B1CF9000082B4168D /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2E4ECA833C8DE363BE0B80E1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB31CF901A200538489 /* Copy flutter_assets */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/Flutter/flutter_assets", + ); + name = "Copy flutter_assets"; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(PRODUCT_NAME).app/flutter_assets", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/sh\n# Copy flutter_assets into the app bundle\nFLUTTER_ASSETS_SRC=\"${PROJECT_DIR}/Flutter/flutter_assets\"\nDEST=\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/flutter_assets\"\nif [ -d \"${FLUTTER_ASSETS_SRC}\" ]; then\n echo \"Copying flutter_assets to app bundle...\"\n rsync -av --delete \"${FLUTTER_ASSETS_SRC}/\" \"${DEST}/\"\nelse\n echo \"warning: flutter_assets not found at ${FLUTTER_ASSETS_SRC}\"\nfi\n"; + }; + AAF10000000000000000F00D /* Embed App.framework */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/Flutter/App.framework", + ); + name = "Embed App.framework"; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(PRODUCT_NAME).app/Frameworks/App.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/sh\n# Embed App.framework (AOT Dart snapshots) into the app bundle.\n# Present only for release/profile (AOT) builds; debug/JIT has no App.framework.\n# Runs for build, run, AND archive, so TestFlight/App Store builds get it too.\nAPP_FRAMEWORK_SRC=\"${PROJECT_DIR}/Flutter/App.framework\"\nDEST_FRAMEWORKS=\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Frameworks\"\nif [ -d \"${APP_FRAMEWORK_SRC}\" ]; then\n echo \"Embedding App.framework...\"\n mkdir -p \"${DEST_FRAMEWORKS}\"\n rsync -av --delete \"${APP_FRAMEWORK_SRC}\" \"${DEST_FRAMEWORKS}/\"\n if [ \"${CODE_SIGNING_REQUIRED}\" != \"NO\" ] && [ -n \"${EXPANDED_CODE_SIGN_IDENTITY}\" ]; then\n echo \"Codesigning App.framework with ${EXPANDED_CODE_SIGN_IDENTITY}...\"\n codesign --force --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" --timestamp=none --generate-entitlement-der \"${DEST_FRAMEWORKS}/App.framework\"\n fi\nelse\n echo \"No App.framework to embed (debug/JIT build).\"\nfi\n"; + }; + FF571599D98BF3C1DDB19F8C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000082B4168C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C146FB1CF9000082B4168C /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "Apple Development"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = appletvos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TVOS_DEPLOYMENT_TARGET = 15.0; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.firebaseAnalyticsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000082B41691 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "Apple Development"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TVOS_DEPLOYMENT_TARGET = 15.0; + }; + name = Debug; + }; + 97C147031CF9000082B41692 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.firebaseAnalyticsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147041CF9000082B41691 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "Apple Development"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = appletvos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TVOS_DEPLOYMENT_TARGET = 15.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147041CF9000082B41692 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.firebaseAnalyticsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000082B4168C /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000082B41691 /* Debug */, + 97C147041CF9000082B41691 /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000082B41692 /* Debug */, + 97C147041CF9000082B41692 /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + AAF40000000000000000F00D /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + AAF30000000000000000F00D /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 97C146E61CF9000082B4168C /* Project object */; +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..d3911dc --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/AppDelegate.swift b/packages/firebase_analytics_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/AppDelegate.swift @@ -0,0 +1,20 @@ +import UIKit +import Flutter + +@main +class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil) + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = flutterViewController + window.makeKeyAndVisible() + self.window = window + + GeneratedPluginRegistrant.register(with: self) + + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..c6a0bc3 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large_back.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png new file mode 100644 index 0000000..b89e77a Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json new file mode 100644 index 0000000..de59d88 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..f7cf529 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large_front.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png new file mode 100644 index 0000000..d1bf0b6 Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..fedb0ad --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "large_middle.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png new file mode 100644 index 0000000..eca5900 Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..1d59796 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small_back.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png new file mode 100644 index 0000000..eac8b47 Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json new file mode 100644 index 0000000..de59d88 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..e8f0da2 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small_front.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png new file mode 100644 index 0000000..71eb8ae Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..9d01973 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "small_middle.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png new file mode 100644 index 0000000..624d2bb Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json @@ -0,0 +1,26 @@ +{ + "assets" : [ + { + "filename" : "App Icon - Large.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "1280x768" + }, + { + "filename" : "App Icon - Small.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "400x240" + }, + { + "filename" : "Top Shelf Image.imageset", + "idiom" : "tv", + "role" : "top-shelf-image", + "size" : "1920x720" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000..74f7c24 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "tv", + "filename" : "top_shelf.png", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png new file mode 100644 index 0000000..cbbebf8 Binary files /dev/null and b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Info.plist b/packages/firebase_analytics_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..4e54d18 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Firebase_storage_example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + firebase_storage_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + + FLTAssetsPath + flutter_assets + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/packages/firebase_analytics_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/firebase_analytics_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/firebase_analytics_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_analytics_tvos/lib/firebase_analytics_tvos.dart b/packages/firebase_analytics_tvos/lib/firebase_analytics_tvos.dart new file mode 100644 index 0000000..0f3d245 --- /dev/null +++ b/packages/firebase_analytics_tvos/lib/firebase_analytics_tvos.dart @@ -0,0 +1,14 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// firebase_analytics's public Dart API (FirebaseAnalytics, the navigator +// observer, AnalyticsCallOptions, …) has no per-platform Dart override — it +// talks to native through firebase_analytics_platform_interface's +// MethodChannel implementation regardless of platform. Duplicating those +// classes here would create incompatible types vs. apps that import +// package:firebase_analytics/firebase_analytics.dart directly. This package +// only supplies the native tvOS pluginClass (tvos/Classes/); apps depend on +// firebase_analytics (Dart API) and firebase_analytics_tvos (native +// registration) side by side — see example/. +export 'package:firebase_analytics/firebase_analytics.dart'; diff --git a/packages/firebase_analytics_tvos/pubspec.yaml b/packages/firebase_analytics_tvos/pubspec.yaml new file mode 100644 index 0000000..f14e13a --- /dev/null +++ b/packages/firebase_analytics_tvos/pubspec.yaml @@ -0,0 +1,38 @@ +name: firebase_analytics_tvos +description: >- + tvOS (Apple TV) implementation of the firebase_analytics Flutter plugin, + providing Firebase Analytics event logging on Apple TV. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/firebase_analytics_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`, then finished by hand. See PORTING_REPORT.md. +# License holder: fluttertv + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + firebase_analytics: ^12.4.3 + # Transitive Dart dependency so the app's dependency graph includes + # firebase_core_tvos — plugin discovery (tvos_plugins.dart) only adds a + # package's native pod to the generated Podfile if it's reachable here, + # which the podspec's `s.dependency 'firebase_core_tvos'` then resolves + # against locally instead of failing over to upstream's iOS-only firebase_core + # pod. Hosted constraint for pub.dev (path deps can't be published); local + # development resolves it via pubspec_overrides.yaml until it's published. + firebase_core_tvos: ^0.0.1 + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + +flutter: + plugin: + platforms: + tvos: + pluginClass: FirebaseAnalyticsPlugin diff --git a/packages/firebase_analytics_tvos/pubspec_overrides.yaml b/packages/firebase_analytics_tvos/pubspec_overrides.yaml new file mode 100644 index 0000000..e0a245e --- /dev/null +++ b/packages/firebase_analytics_tvos/pubspec_overrides.yaml @@ -0,0 +1,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 diff --git a/packages/firebase_analytics_tvos/test/firebase_analytics_tvos_test.dart b/packages/firebase_analytics_tvos/test/firebase_analytics_tvos_test.dart new file mode 100644 index 0000000..9f5267e --- /dev/null +++ b/packages/firebase_analytics_tvos/test/firebase_analytics_tvos_test.dart @@ -0,0 +1,14 @@ +// Copyright 2026 fluttertv. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Generated on 2026-08-07 by `flutter-tvos plugin port`. +// Source plugin: firebase_analytics + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/firebase_analytics_tvos/tvos/Classes/Constants.swift b/packages/firebase_analytics_tvos/tvos/Classes/Constants.swift new file mode 100644 index 0000000..d25ebe1 --- /dev/null +++ b/packages/firebase_analytics_tvos/tvos/Classes/Constants.swift @@ -0,0 +1,6 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Auto-generated file. Do not edit. +public let versionNumber = "11.4.6" diff --git a/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsMessages.g.swift b/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsMessages.g.swift new file mode 100644 index 0000000..fd3e6de --- /dev/null +++ b/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsMessages.g.swift @@ -0,0 +1,557 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if (os(iOS) || os(tvOS)) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(Swift.type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +private func doubleEqualsFirebaseAnalyticsMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseAnalyticsMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + +func deepEqualsFirebaseAnalyticsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: + return true + + case is (Void, Void): + return true + + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebaseAnalyticsMessages(element, rhsArray[index]) { + return false + } + } + return true + + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebaseAnalyticsMessages(element, rhsArray[index]) { + return false + } + } + return true + + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebaseAnalyticsMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseAnalyticsMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseAnalyticsMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + + default: + return false + } +} + +func deepHashFirebaseAnalyticsMessages(value: Any?, hasher: inout Hasher) { + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseAnalyticsMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseAnalyticsMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseAnalyticsMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseAnalyticsMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseAnalyticsMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) + } + } else { + hasher.combine(0) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct AnalyticsEvent: Hashable { + var name: String + var parameters: [String?: Any?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> AnalyticsEvent? { + let name = pigeonVar_list[0] as! String + let parameters: [String?: Any?]? = nilOrValue(pigeonVar_list[1]) + + return AnalyticsEvent( + name: name, + parameters: parameters + ) + } + + func toList() -> [Any?] { + [ + name, + parameters, + ] + } + + static func == (lhs: AnalyticsEvent, rhs: AnalyticsEvent) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseAnalyticsMessages(lhs.name, rhs.name) + && deepEqualsFirebaseAnalyticsMessages( + lhs.parameters, + rhs.parameters + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("AnalyticsEvent") + deepHashFirebaseAnalyticsMessages(value: name, hasher: &hasher) + deepHashFirebaseAnalyticsMessages(value: parameters, hasher: &hasher) + } +} + +private class FirebaseAnalyticsMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return AnalyticsEvent.fromList(readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class FirebaseAnalyticsMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? AnalyticsEvent { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class FirebaseAnalyticsMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirebaseAnalyticsMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirebaseAnalyticsMessagesPigeonCodecWriter(data: data) + } +} + +class FirebaseAnalyticsMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = + FirebaseAnalyticsMessagesPigeonCodec( + readerWriter: FirebaseAnalyticsMessagesPigeonCodecReaderWriter() + ) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseAnalyticsHostApi { + func logEvent(event: [String: Any?], completion: @escaping (Result) -> Void) + func setUserId(userId: String?, completion: @escaping (Result) -> Void) + func setUserProperty( + name: String, value: String?, + completion: @escaping (Result) -> Void) + func setAnalyticsCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void) + func resetAnalyticsData(completion: @escaping (Result) -> Void) + func setSessionTimeoutDuration( + timeout: Int64, + completion: @escaping (Result) -> Void) + func setConsent(consent: [String: Bool?], completion: @escaping (Result) -> Void) + func setDefaultEventParameters( + parameters: [String: Any?]?, + completion: @escaping (Result) -> Void) + func getAppInstanceId(completion: @escaping (Result) -> Void) + func getSessionId(completion: @escaping (Result) -> Void) + func initiateOnDeviceConversionMeasurement( + arguments: [String: String?], + completion: @escaping (Result) -> Void) + func logTransaction(transactionId: String, completion: @escaping (Result) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseAnalyticsHostApiSetup { + static var codec: FlutterStandardMessageCodec { + FirebaseAnalyticsMessagesPigeonCodec.shared + } + + /// Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseAnalyticsHostApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let logEventChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + logEventChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let eventArg = args[0] as! [String: Any?] + api.logEvent(event: eventArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + logEventChannel.setMessageHandler(nil) + } + let setUserIdChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setUserIdChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let userIdArg: String? = nilOrValue(args[0]) + api.setUserId(userId: userIdArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setUserIdChannel.setMessageHandler(nil) + } + let setUserPropertyChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setUserPropertyChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let nameArg = args[0] as! String + let valueArg: String? = nilOrValue(args[1]) + api.setUserProperty(name: nameArg, value: valueArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setUserPropertyChannel.setMessageHandler(nil) + } + let setAnalyticsCollectionEnabledChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setAnalyticsCollectionEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + api.setAnalyticsCollectionEnabled(enabled: enabledArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setAnalyticsCollectionEnabledChannel.setMessageHandler(nil) + } + let resetAnalyticsDataChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + resetAnalyticsDataChannel.setMessageHandler { _, reply in + api.resetAnalyticsData { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + resetAnalyticsDataChannel.setMessageHandler(nil) + } + let setSessionTimeoutDurationChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setSessionTimeoutDurationChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let timeoutArg = args[0] as! Int64 + api.setSessionTimeoutDuration(timeout: timeoutArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setSessionTimeoutDurationChannel.setMessageHandler(nil) + } + let setConsentChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setConsentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let consentArg = args[0] as! [String: Bool?] + api.setConsent(consent: consentArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setConsentChannel.setMessageHandler(nil) + } + let setDefaultEventParametersChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setDefaultEventParametersChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let parametersArg: [String: Any?]? = nilOrValue(args[0]) + api.setDefaultEventParameters(parameters: parametersArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setDefaultEventParametersChannel.setMessageHandler(nil) + } + let getAppInstanceIdChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getAppInstanceIdChannel.setMessageHandler { _, reply in + api.getAppInstanceId { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getAppInstanceIdChannel.setMessageHandler(nil) + } + let getSessionIdChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getSessionIdChannel.setMessageHandler { _, reply in + api.getSessionId { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getSessionIdChannel.setMessageHandler(nil) + } + let initiateOnDeviceConversionMeasurementChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + initiateOnDeviceConversionMeasurementChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let argumentsArg = args[0] as! [String: String?] + api.initiateOnDeviceConversionMeasurement(arguments: argumentsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + initiateOnDeviceConversionMeasurementChannel.setMessageHandler(nil) + } + let logTransactionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + logTransactionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let transactionIdArg = args[0] as! String + api.logTransaction(transactionId: transactionIdArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + logTransactionChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsPlugin.swift b/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsPlugin.swift new file mode 100644 index 0000000..404bcb6 --- /dev/null +++ b/packages/firebase_analytics_tvos/tvos/Classes/FirebaseAnalyticsPlugin.swift @@ -0,0 +1,295 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FirebaseAnalytics +import StoreKit + +#if canImport(FlutterMacOS) + import FlutterMacOS +#else + import Flutter +#endif + +// Point at our own tvOS core pod (upstream firebase_core has no tvOS platform). +import firebase_core_tvos + +let kFLTFirebaseAnalyticsName = "name" +let kFLTFirebaseAnalyticsValue = "value" +let kFLTFirebaseAnalyticsEnabled = "enabled" +let kFLTFirebaseAnalyticsEventName = "eventName" +let kFLTFirebaseAnalyticsParameters = "parameters" +let kFLTFirebaseAnalyticsAdStorageConsentGranted = "adStorageConsentGranted" +let kFLTFirebaseAnalyticsStorageConsentGranted = "analyticsStorageConsentGranted" +let kFLTFirebaseAdPersonalizationSignalsConsentGranted = "adPersonalizationSignalsConsentGranted" +let kFLTFirebaseAdUserDataConsentGranted = "adUserDataConsentGranted" +let kFLTFirebaseAnalyticsUserId = "userId" + +// swift-format-ignore: AlwaysUseLowerCamelCase +let FLTFirebaseAnalyticsChannelName = "plugins.flutter.io/firebase_analytics" + +extension FlutterError: Error {} + +public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin, + FirebaseAnalyticsHostApi +{ + public static func register(with registrar: any FlutterPluginRegistrar) { + let binaryMessenger: FlutterBinaryMessenger + + #if os(macOS) + binaryMessenger = registrar.messenger + #elseif (os(iOS) || os(tvOS)) + binaryMessenger = registrar.messenger() + #endif + + let instance = FirebaseAnalyticsPlugin() + FirebaseAnalyticsHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) + } + + func logEvent(event: [String: Any?], completion: @escaping (Result) -> Void) { + guard let eventName = event[kFLTFirebaseAnalyticsEventName] as? String else { + completion(.success(())) + return + } + let parameters = event[kFLTFirebaseAnalyticsParameters] as? [String: Any] + Analytics.logEvent(eventName, parameters: parameters) + completion(.success(())) + } + + func setUserId(userId: String?, completion: @escaping (Result) -> Void) { + Analytics.setUserID(userId) + completion(.success(())) + } + + func setUserProperty( + name: String, value: String?, + completion: @escaping (Result) -> Void + ) { + Analytics.setUserProperty(value, forName: name) + completion(.success(())) + } + + func setAnalyticsCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void + ) { + Analytics.setAnalyticsCollectionEnabled(enabled) + completion(.success(())) + } + + func resetAnalyticsData(completion: @escaping (Result) -> Void) { + Analytics.resetAnalyticsData() + completion(.success(())) + } + + func setSessionTimeoutDuration( + timeout: Int64, + completion: @escaping (Result) -> Void + ) { + Analytics.setSessionTimeoutInterval(TimeInterval(timeout)) + completion(.success(())) + } + + func setConsent( + consent: [String: Bool?], + completion: @escaping (Result) -> Void + ) { + var parameters: [ConsentType: ConsentStatus] = [:] + if let adStorage = consent[kFLTFirebaseAnalyticsAdStorageConsentGranted] as? Bool { + parameters[.adStorage] = adStorage ? .granted : .denied + } + if let analyticsStorage = consent[kFLTFirebaseAnalyticsStorageConsentGranted] as? Bool { + parameters[.analyticsStorage] = analyticsStorage ? .granted : .denied + } + if let adPersonalization = + consent[kFLTFirebaseAdPersonalizationSignalsConsentGranted] as? Bool + { + parameters[.adPersonalization] = adPersonalization ? .granted : .denied + } + if let adUserData = consent[kFLTFirebaseAdUserDataConsentGranted] as? Bool { + parameters[.adUserData] = adUserData ? .granted : .denied + } + Analytics.setConsent(parameters) + completion(.success(())) + } + + func setDefaultEventParameters( + parameters: [String: Any?]?, + completion: @escaping (Result) -> Void + ) { + Analytics.setDefaultEventParameters(parameters) + completion(.success(())) + } + + func getAppInstanceId(completion: @escaping (Result) -> Void) { + let instanceID = Analytics.appInstanceID() + completion(.success(instanceID)) + } + + func getSessionId(completion: @escaping (Result) -> Void) { + Analytics.sessionID { sessionID, error in + if let error { + completion(.failure(error)) + } else { + completion(.success(sessionID)) + } + } + } + + func initiateOnDeviceConversionMeasurement( + arguments: [String: String?], + completion: + @escaping (Result) + -> Void + ) { + if let emailAddress = arguments["emailAddress"] as? String { + Analytics.initiateOnDeviceConversionMeasurement(emailAddress: emailAddress) + } + if let phoneNumber = arguments["phoneNumber"] as? String { + Analytics.initiateOnDeviceConversionMeasurement(phoneNumber: phoneNumber) + } + if let hashedEmailAddress = arguments["hashedEmailAddress"] as? String, + let data = hexStringToData(hashedEmailAddress) + { + Analytics.initiateOnDeviceConversionMeasurement(hashedEmailAddress: data) + } + if let hashedPhoneNumber = arguments["hashedPhoneNumber"] as? String, + let data = hexStringToData(hashedPhoneNumber) + { + Analytics.initiateOnDeviceConversionMeasurement(hashedPhoneNumber: data) + } + completion(.success(())) + } + + func logTransaction( + transactionId: String, + completion: @escaping (Result) -> Void + ) { + #if os(macOS) + if #available(macOS 12.0, *) { + logTransactionWithStoreKit(transactionId: transactionId, completion: completion) + } else { + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "logTransaction() is only supported on macOS 12.0 or newer", + details: nil + ) + ) + ) + } + #else + if #available(iOS 15.0, tvOS 15.0, *) { + logTransactionWithStoreKit(transactionId: transactionId, completion: completion) + } else { + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "logTransaction() is only supported on iOS 15.0 or newer", + details: nil + ) + ) + ) + } + #endif + } + + #if os(macOS) + @available(macOS 12.0, *) + #else + @available(iOS 15.0, tvOS 15.0, *) + #endif + private func logTransactionWithStoreKit( + transactionId: String, + completion: @escaping (Result) -> Void + ) { + Task { + do { + guard let id = UInt64(transactionId) else { + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "Invalid transactionId", + details: nil + ) + ) + ) + return + } + + var foundTransaction: Transaction? + for await result in Transaction.all { + switch result { + case .verified(let transaction): + if transaction.id == id { + foundTransaction = transaction + break + } + case .unverified: + continue + } + } + + guard let transaction = foundTransaction else { + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "Transaction not found", + details: nil + ) + ) + ) + return + } + + Analytics.logTransaction(transaction) + completion(.success(())) + } catch { + completion(.failure(error)) + } + } + } + + private func hexStringToData(_ hexString: String) -> Data? { + let length = hexString.count + guard length % 2 == 0 else { return nil } + + var data = Data(capacity: length / 2) + var index = hexString.startIndex + + for _ in 0..<(length / 2) { + let nextIndex = hexString.index(index, offsetBy: 2) + guard let byte = UInt8(hexString[index.. Void) { + completion() + } + + public func pluginConstants(for firebaseApp: FirebaseApp) -> [AnyHashable: Any] { + [:] + } + + public func firebaseLibraryName() -> String { + "flutter-fire-analytics" + } + + public func firebaseLibraryVersion() -> String { + versionNumber + } + + public func flutterChannelName() -> String { + FLTFirebaseAnalyticsChannelName + } +} diff --git a/packages/firebase_analytics_tvos/tvos/firebase_analytics_tvos.podspec b/packages/firebase_analytics_tvos/tvos/firebase_analytics_tvos.podspec new file mode 100644 index 0000000..de2ca13 --- /dev/null +++ b/packages/firebase_analytics_tvos/tvos/firebase_analytics_tvos.podspec @@ -0,0 +1,43 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint firebase_analytics_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`, then finished by hand. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'firebase_analytics_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of firebase_analytics.' + s.description = <<-DESC +tvOS implementation of firebase_analytics, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/firebase_analytics_tvos' + s.license = { :file => '../LICENSE' } + s.author = { 'fluttertv' => 'noreply@fluttertv.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + # Firebase/Analytics 12.x requires tvOS 15.0+ — bumped from the porter's + # generic 13.0 default to satisfy that dependency. + s.platform = :tvos, '15.0' + s.swift_version = '5.0' + + # IMPORTANT: this podspec must not depend on the Flutter CocoaPod. That + # pod does not declare tvOS support, so adding a dependency on it breaks + # `pod install` for tvOS consumers. Flutter.framework is resolved via + # FRAMEWORK_SEARCH_PATHS, populated by the host app's Podfile. + s.xcconfig = { + 'FRAMEWORK_SEARCH_PATHS' => '"${PODS_ROOT}/../Flutter"', + 'OTHER_SWIFT_FLAGS' => '$(inherited) -DTARGET_OS_TV', + } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + + # The ported Classes/ call into FIRAnalytics (Firebase/Analytics) and into + # our own firebase_core_tvos pod, not upstream's "firebase_core" pod — that + # pod's own podspec declares only `s.platform = :ios`, so depending on it + # directly would make `pod install` fail to find a tvOS-compatible spec. + s.dependency 'Firebase/Analytics', '~> 12.15.0' + s.dependency 'firebase_core_tvos' + s.static_framework = true +end