diff --git a/packages/cloud_firestore_tvos/.gitignore b/packages/cloud_firestore_tvos/.gitignore new file mode 100644 index 0000000..c83dba5 --- /dev/null +++ b/packages/cloud_firestore_tvos/.gitignore @@ -0,0 +1,32 @@ +# 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 + +# Local-dev dependency override resolving firebase_core_tvos from the sibling +# package before it is published to pub.dev. Not committed, not published; +# remove it at release time (after firebase_core_tvos is live). See pubspec.yaml. +pubspec_overrides.yaml +example/pubspec_overrides.yaml diff --git a/packages/cloud_firestore_tvos/CHANGELOG.md b/packages/cloud_firestore_tvos/CHANGELOG.md new file mode 100644 index 0000000..42767ce --- /dev/null +++ b/packages/cloud_firestore_tvos/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.0.1 + +* Initial tvOS scaffolding generated by `flutter-tvos plugin port` from + `cloud_firestore`. diff --git a/packages/cloud_firestore_tvos/LICENSE b/packages/cloud_firestore_tvos/LICENSE new file mode 100644 index 0000000..5b8ff62 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/PORTING_REPORT.md b/packages/cloud_firestore_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..c6ef6b1 --- /dev/null +++ b/packages/cloud_firestore_tvos/PORTING_REPORT.md @@ -0,0 +1,446 @@ +# cloud_firestore_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-06-30. + +Source: `cloud_firestore` 6.6.0 (path: `/Users/aliustaoglu/.pub-cache/hosted/pub.dev/cloud_firestore-6.6.0`) +Base platform: ios (Objective-C) +Output: `./cloud_firestore_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 | 92 | +| Methods stubbed (iOS-only) | 0 | +| Native regions disabled on tvOS | 0 | +| tvOS build outlook | ✅ expected to compile | +| Manual review items | 0 | + +## Methods + +### `!=` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `<` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `<=` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `==` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `>` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `>=` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `AND` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `OR` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `add_fields` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `aggregate` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `aggregate_with_options` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `alias` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `and` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array-contains` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array-contains-any` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `arrayContains` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `arrayContainsAny` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_concat` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_concat_multiple` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_contains` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_contains_all` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_contains_any` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_filter` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_slice` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_transform` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `array_transform_with_index` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `as_boolean` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `asc` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `bit_xor` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `cache` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `collection` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `collection_group` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `concat` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `conditional` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `constant` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `count_all` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `current_timestamp` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `database` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `distinct` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `document_id_from_ref` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `document_matches` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `documents` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `equal_any` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `estimate` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `expression` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `field` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `filter` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `find_nearest` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `if_absent` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `if_error` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `in` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isEqualTo` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isGreaterThan` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isGreaterThanOrEqualTo` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isLessThan` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isLessThanOrEqualTo` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isNotEqualTo` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isNotNull` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `isNull` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `limit` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `map` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `map_get` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `maximum` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `minimum` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `modulo` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `not` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `not-in` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `null` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `offset` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `percentage` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `previous` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `remove_fields` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `replace` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `replace_with` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `sample` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `search` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `select` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `server` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `sort` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `split` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `string` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `substring` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `timestamp_add` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `timestamp_truncate` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `to_lower_case` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `to_upper_case` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `union` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `unnest` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `where` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `whereNotIn` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `xor` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +## 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 + +- [ ] Read every `✗ stubbed` method above and confirm returning `FlutterMethodNotImplemented` is acceptable on tvOS. +- [ ] Review every `⚠️ partial` method against a real Apple TV (behaviour differs from iOS). +- [ ] Confirm the removed imports were not load-bearing for still-supported code paths. +- [ ] `flutter-tvos build tvos --simulator --debug` from the plugin's example app compiles the generated registrant. +- [ ] Bump the version and update `CHANGELOG.md` before publishing. + +--- + +## Addendum: manual fixes beyond the automated port (2026-06-30) + +The automated port reported a clean compile, and unlike `firebase_auth_tvos`, Firestore's SDK +surface turned out to be fully exported for tvOS — no `#if !TARGET_OS_TV` disables were needed +here. Fixed by hand (the same mechanical gaps every port in this batch hit, none Firestore-specific): + +- **7 headers using `#import "../Public/X.h"`** (relative-parent-directory imports) fixed to + `#import "X.h"` — CocoaPods flattens `Public/`/`Private/` into one directory when building a + framework (`use_frameworks!`). +- **6 source/header files importing ``** repointed to + ``, with the flat-fallback branch fixed from angle-bracket + `` to quoted `"FLTFirebasePlugin.h"` (angle-bracket flat imports aren't + resolvable across pod/framework boundaries the way quoted same-directory imports are). +- **Podspec**: added `Firebase/Firestore` and `firebase_core_tvos` dependencies (not upstream's + `firebase_core`), bumped `s.platform` to tvOS 15.0, added the `LIBRARY_NAME`/`LIBRARY_VERSION` + preprocessor defines `FLTFirebaseFirestorePlugin.m` expects. +- **`lib/cloud_firestore_tvos.dart`**: replaced the porter's copied Dart classes (`Query`, + `DocumentSnapshot`, the whole Pipeline API surface, …) with a one-line re-export of + `package:cloud_firestore/cloud_firestore.dart` — same reasoning as `firebase_core_tvos` / + `firebase_auth_tvos`: no per-platform Dart override exists, so duplicating would create + incompatible types. + +Verified: `flutter-tvos build tvos --simulator --debug` against the example (unmodified from the +porter's `--include-example` output, aside from the `firebase_core_tvos` dependency and deployment +target bumps) completes with no compiler errors — native pod (~6 min compile, this SDK is large), +Dart kernel, and `GeneratedPluginRegistrant` all link. Not verified: a live `Firestore` round-trip +against a real project, or the Pipeline API specifically (it's the newest/most complex part of the +SDK surface — worth extra scrutiny before relying on it), or behavior on a physical Apple TV. + +Manual review required. Read this report top-to-bottom before publishing `cloud_firestore_tvos`. diff --git a/packages/cloud_firestore_tvos/README.md b/packages/cloud_firestore_tvos/README.md new file mode 100644 index 0000000..3644864 --- /dev/null +++ b/packages/cloud_firestore_tvos/README.md @@ -0,0 +1,51 @@ +# cloud_firestore_tvos + +The tvOS (Apple TV) implementation of [`cloud_firestore`](https://pub.dev/packages/cloud_firestore), +provided by the [flutter-tvos](https://github.com/fluttertv/flutter-tvos) toolchain. + +> Generated by [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `cloud_firestore`, then completed by hand. See `PORTING_REPORT.md` for the +> full list of what was changed. + +## Usage + +This is a federated plugin implementation. An app that already depends on +`cloud_firestore` and targets Apple TV only needs to add this package alongside +it: + +```yaml +dependencies: + cloud_firestore: ^6.6.0 + cloud_firestore_tvos: ^0.0.1 +``` + +The native plugin registers automatically through flutter-tvos' plugin +registrant — no extra imports or setup in app code. Use the normal +`cloud_firestore` API; it routes to the Apple TV native side. + +## tvOS support + +The Firebase Apple SDK exposes the full Firestore surface on tvOS, so no +Firestore features are disabled here: + +| Capability | tvOS | +|---|:---:| +| Reads / writes / queries / filters | ✅ | +| Real-time snapshot listeners | ✅ | +| Transactions & batched writes | ✅ | +| Aggregate queries (`count`, `sum`, `average`) | ✅ | +| Offline persistence | ✅ | +| Bundles & the Pipeline API | ✅ (see note) | + +> The newest parts of the SDK surface (the Pipeline API) compile and link on +> tvOS but have had the least real-device verification — exercise them on actual +> Apple TV hardware before relying on them. See `PORTING_REPORT.md`. + +## Requirements + +- Apple TV running tvOS 15.0 or later (the Firebase Apple SDK's minimum). +- `firebase_core_tvos` (pulled in automatically). + +## License + +fluttertv, under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/cloud_firestore_tvos/analysis_options.yaml b/packages/cloud_firestore_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/README.md b/packages/cloud_firestore_tvos/example/README.md new file mode 100755 index 0000000..2a26c0e --- /dev/null +++ b/packages/cloud_firestore_tvos/example/README.md @@ -0,0 +1,8 @@ +# firestore_example + +Demonstrates how to use the firestore plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/cloud_firestore_tvos/example/firebase.json b/packages/cloud_firestore_tvos/example/firebase.json new file mode 100644 index 0000000..63508b6 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"your-project-id","appId":"YOUR_APP_ID","fileOutput":"android/app/google-services.json"}}}}} \ No newline at end of file diff --git a/packages/cloud_firestore_tvos/example/lib/firebase_options.dart b/packages/cloud_firestore_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..06fe56f --- /dev/null +++ b/packages/cloud_firestore_tvos/example/lib/firebase_options.dart @@ -0,0 +1,92 @@ +// 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. + +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + return switch (defaultTargetPlatform) { + TargetPlatform.android => android, + TargetPlatform.iOS => ios, + TargetPlatform.macOS => macos, + TargetPlatform.windows => android, + TargetPlatform.linux => throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ), + _ => throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ) + }; + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.firestore.example', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.firestore.example', + ); +} diff --git a/packages/cloud_firestore_tvos/example/lib/main.dart b/packages/cloud_firestore_tvos/example/lib/main.dart new file mode 100755 index 0000000..df1d30d --- /dev/null +++ b/packages/cloud_firestore_tvos/example/lib/main.dart @@ -0,0 +1,548 @@ +// Copyright 2020, 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. + +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +import 'firebase_options.dart'; + +/// Requires that a Firestore emulator is running locally. +/// See https://firebase.google.com/docs/firestore/quickstart#optional_prototype_and_test_with +bool shouldUseFirestoreEmulator = true; + +Future loadBundleSetup(int number) async { + // endpoint serves a bundle with 3 documents each containing + // a 'number' property that increments in value 1-3. + final url = + Uri.https('api.rnfirebase.io', '/firestore/e2e-tests/bundle-$number'); + final response = await http.get(url); + String string = response.body; + return Uint8List.fromList(string.codeUnits); +} + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + FirebaseFirestore.instance.settings = const Settings( + persistenceEnabled: true, + ); + if (shouldUseFirestoreEmulator) { + FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080); + } + + runApp(FirestoreExampleApp()); +} + +/// A reference to the list of movies. +/// We are using `withConverter` to ensure that interactions with the collection +/// are type-safe. +final moviesRef = FirebaseFirestore.instance + .collection('firestore-example-app') + .withConverter( + fromFirestore: (snapshots, _) => Movie.fromJson(snapshots.data()!), + toFirestore: (movie, _) => movie.toJson(), + ); + +/// The different ways that we can filter/sort movies. +enum MovieQuery { + year, + likesAsc, + likesDesc, + rated, + sciFi, + fantasy, +} + +extension on Query { + /// Create a firebase query from a [MovieQuery] + Query queryBy(MovieQuery query) { + return switch (query) { + MovieQuery.fantasy => where('genre', arrayContainsAny: ['fantasy']), + MovieQuery.sciFi => where('genre', arrayContainsAny: ['sci-fi']), + MovieQuery.likesAsc || + MovieQuery.likesDesc => + orderBy('likes', descending: query == MovieQuery.likesDesc), + MovieQuery.year => orderBy('year', descending: true), + MovieQuery.rated => orderBy('rated', descending: true) + }; + } +} + +/// The entry point of the application. +/// +/// Returns a [MaterialApp]. +class FirestoreExampleApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Firestore Example App', + theme: ThemeData.dark(), + home: const Scaffold( + body: Center(child: FilmList()), + ), + ); + } +} + +/// Holds all example app films +class FilmList extends StatefulWidget { + const FilmList({Key? key}) : super(key: key); + + @override + _FilmListState createState() => _FilmListState(); +} + +class _FilmListState extends State { + MovieQuery query = MovieQuery.year; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text('Firestore Example: Movies'), + + // This is a example use for 'snapshots in sync'. + // The view reflects the time of the last Firestore sync; which happens any time a field is updated. + StreamBuilder( + stream: FirebaseFirestore.instance.snapshotsInSync(), + builder: (context, _) { + return Text( + 'Latest Snapshot: ${DateTime.now()}', + style: Theme.of(context).textTheme.bodySmall, + ); + }, + ), + ], + ), + actions: [ + PopupMenuButton( + onSelected: (value) => setState(() => query = value), + icon: const Icon(Icons.sort), + itemBuilder: (BuildContext context) { + return [ + const PopupMenuItem( + value: MovieQuery.year, + child: Text('Sort by Year'), + ), + const PopupMenuItem( + value: MovieQuery.rated, + child: Text('Sort by Rated'), + ), + const PopupMenuItem( + value: MovieQuery.likesAsc, + child: Text('Sort by Likes ascending'), + ), + const PopupMenuItem( + value: MovieQuery.likesDesc, + child: Text('Sort by Likes descending'), + ), + const PopupMenuItem( + value: MovieQuery.fantasy, + child: Text('Filter genre fantasy'), + ), + const PopupMenuItem( + value: MovieQuery.sciFi, + child: Text('Filter genre sci-fi'), + ), + ]; + }, + ), + PopupMenuButton( + onSelected: (value) async { + switch (value) { + case 'reset_likes': + return _resetLikes(); + case 'aggregate': + // Count the number of movies + final _count = await FirebaseFirestore.instance + .collection('firestore-example-app') + .count() + .get(); + + print('Count: ${_count.count}'); + + // Average the number of likes + final _average = await FirebaseFirestore.instance + .collection('firestore-example-app') + .aggregate(average('likes')) + .get(); + + print('Average: ${_average.getAverage('likes')}'); + + // Sum the number of likes + final _sum = await FirebaseFirestore.instance + .collection('firestore-example-app') + .aggregate(sum('likes')) + .get(); + + print('Sum: ${_sum.getSum('likes')}'); + + // In one query + final _all = await FirebaseFirestore.instance + .collection('firestore-example-app') + .aggregate( + average('likes'), + sum('likes'), + count(), + ) + .get(); + + print('Average: ${_all.getAverage('likes')} ' + 'Sum: ${_all.getSum('likes')} ' + 'Count: ${_all.count}'); + + return; + case 'load_bundle': + Uint8List buffer = await loadBundleSetup(2); + LoadBundleTask task = + FirebaseFirestore.instance.loadBundle(buffer); + + final list = await task.stream.toList(); + + print( + list.map((e) => e.totalDocuments), + ); + print( + list.map((e) => e.bytesLoaded), + ); + print( + list.map((e) => e.documentsLoaded), + ); + print( + list.map((e) => e.totalBytes), + ); + print( + list, + ); + + LoadBundleTaskSnapshot lastSnapshot = list.removeLast(); + print(lastSnapshot.taskState); + + print( + list.map((e) => e.taskState), + ); + return; + case 'vectorValue': + const vectorValue = VectorValue([1.0, 2.0, 3.0]); + final vectorValueDoc = await FirebaseFirestore.instance + .collection('firestore-example-app') + .add({'vectorValue': vectorValue}); + + final snapshot = await vectorValueDoc.get(); + print(snapshot.data()); + return; + default: + return; + } + }, + itemBuilder: (BuildContext context) { + return [ + const PopupMenuItem( + value: 'reset_likes', + child: Text('Reset like counts (WriteBatch)'), + ), + const PopupMenuItem( + value: 'aggregate', + child: Text('Get aggregate data'), + ), + const PopupMenuItem( + value: 'load_bundle', + child: Text('Load bundle'), + ), + const PopupMenuItem( + value: 'vectorValue', + child: Text('Test Vector Value'), + ), + ]; + }, + ), + ], + ), + body: StreamBuilder>( + stream: moviesRef.queryBy(query).snapshots(), + builder: (context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Text(snapshot.error.toString()), + ); + } + + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + final data = snapshot.requireData; + + return ListView.builder( + itemCount: data.size, + itemBuilder: (context, index) { + return _MovieItem( + data.docs[index].data(), + data.docs[index].reference, + ); + }, + ); + }, + ), + ); + } + + Future _resetLikes() async { + final movies = await moviesRef.get( + const GetOptions( + serverTimestampBehavior: ServerTimestampBehavior.previous, + ), + ); + + WriteBatch batch = FirebaseFirestore.instance.batch(); + + for (final movie in movies.docs) { + batch.update(movie.reference, {'likes': 0}); + } + await batch.commit(); + } +} + +/// A single movie row. +class _MovieItem extends StatelessWidget { + _MovieItem(this.movie, this.reference); + + final Movie movie; + final DocumentReference reference; + + /// Returns the movie poster. + Widget get poster { + return SizedBox( + width: 100, + child: Image.network(movie.poster), + ); + } + + /// Returns movie details. + Widget get details { + return Padding( + padding: const EdgeInsets.only(left: 8, right: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title, + metadata, + genres, + Likes( + reference: reference, + currentLikes: movie.likes, + ), + ], + ), + ); + } + + /// Return the movie title. + Widget get title { + return Text( + '${movie.title} (${movie.year})', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ); + } + + /// Returns metadata about the movie. + Widget get metadata { + return Padding( + padding: const EdgeInsets.only(top: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: Text('Rated: ${movie.rated}'), + ), + Text('Runtime: ${movie.runtime}'), + ], + ), + ); + } + + /// Returns a list of genre movie tags. + List get genreItems { + return [ + for (final genre in movie.genre) + Padding( + padding: const EdgeInsets.only(right: 2), + child: Chip( + backgroundColor: Colors.lightBlue, + label: Text( + genre, + style: const TextStyle(color: Colors.white), + ), + ), + ), + ]; + } + + /// Returns all genres. + Widget get genres { + return Padding( + padding: const EdgeInsets.only(top: 8), + child: Wrap( + children: genreItems, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 4, top: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + poster, + Flexible(child: details), + ], + ), + ); + } +} + +/// Displays and manages the movie 'like' count. +class Likes extends StatefulWidget { + /// Constructs a new [Likes] instance with a given [DocumentReference] and + /// current like count. + Likes({ + Key? key, + required this.reference, + required this.currentLikes, + }) : super(key: key); + + /// The reference relating to the counter. + final DocumentReference reference; + + /// The number of current likes (before manipulation). + final int currentLikes; + + @override + _LikesState createState() => _LikesState(); +} + +class _LikesState extends State { + /// A local cache of the current likes, used to immediately render the updated + /// likes count after an update, even while the request isn't completed yet. + late int _likes = widget.currentLikes; + + Future _onLike() async { + final currentLikes = _likes; + + // Increment the 'like' count straight away to show feedback to the user. + setState(() { + _likes = currentLikes + 1; + }); + + try { + // Update the likes using a transaction. + // We use a transaction because multiple users could update the likes count + // simultaneously. As such, our likes count may be different from the likes + // count on the server. + int newLikes = await FirebaseFirestore.instance + .runTransaction((transaction) async { + DocumentSnapshot movie = + await transaction.get(widget.reference); + + if (!movie.exists) { + throw Exception('Document does not exist!'); + } + + int updatedLikes = movie.data()!.likes + 1; + transaction.update(widget.reference, {'likes': updatedLikes}); + return updatedLikes; + }); + + // Update with the real count once the transaction has completed. + setState(() => _likes = newLikes); + } catch (e, s) { + print(s); + print('Failed to update likes for document! $e'); + + // If the transaction fails, revert back to the old count + setState(() => _likes = currentLikes); + } + } + + @override + void didUpdateWidget(Likes oldWidget) { + super.didUpdateWidget(oldWidget); + // The likes on the server changed, so we need to update our local cache to + // keep things in sync. Otherwise if another user updates the likes, + // we won't see the update. + if (widget.currentLikes != oldWidget.currentLikes) { + _likes = widget.currentLikes; + } + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + IconButton( + iconSize: 20, + onPressed: _onLike, + icon: const Icon(Icons.favorite), + ), + Text('$_likes likes'), + ], + ); + } +} + +@immutable +class Movie { + Movie({ + required this.genre, + required this.likes, + required this.poster, + required this.rated, + required this.runtime, + required this.title, + required this.year, + }); + + Movie.fromJson(Map json) + : this( + genre: (json['genre']! as List).cast(), + likes: json['likes']! as int, + poster: json['poster']! as String, + rated: json['rated']! as String, + runtime: json['runtime']! as String, + title: json['title']! as String, + year: json['year']! as int, + ); + + final String poster; + final int likes; + final String title; + final int year; + final String runtime; + final String rated; + final List genre; + + Map toJson() { + return { + 'genre': genre, + 'likes': likes, + 'poster': poster, + 'rated': rated, + 'runtime': runtime, + 'title': title, + 'year': year, + }; + } +} diff --git a/packages/cloud_firestore_tvos/example/pubspec.yaml b/packages/cloud_firestore_tvos/example/pubspec.yaml new file mode 100755 index 0000000..6a95e80 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/pubspec.yaml @@ -0,0 +1,24 @@ +name: cloud_firestore_example +description: Demonstrates how to use the firestore plugin. + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + cloud_firestore: ^6.6.0 + cloud_firestore_tvos: + path: ../ + firebase_core: ^4.11.0 + firebase_core_tvos: + path: ../../firebase_core_tvos + flutter: + sdk: flutter + http: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/cloud_firestore_tvos/example/tvos/.gitignore b/packages/cloud_firestore_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/cloud_firestore_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Flutter/Release.xcconfig b/packages/cloud_firestore_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Podfile b/packages/cloud_firestore_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..79ddee7 --- /dev/null +++ b/packages/cloud_firestore_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 */; }; + 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 */; }; + EA919B9D5B63F0890E2693D2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 735EAE15467C7F25161A2B0C /* Pods_Runner.framework */; }; +/* 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 = ""; }; + 197783A38A691FFC6148E0D2 /* 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 = ""; }; + 3B3967151E833CAB004F5970 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 551CA5C1BAF98ADA9651F99E /* 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 = ""; }; + 735EAE15467C7F25161A2B0C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + FAEFB9D7FC178343B4995C83 /* 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 */, + EA919B9D5B63F0890E2693D2 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 97C146E51CF9000082B4168C = { + isa = PBXGroup; + children = ( + 97C146F01CF9000082B4168C /* Runner */, + 97C146F01CF9000082B4168E /* Flutter */, + 97C146F01CF9000082B4168F /* Frameworks */, + 97C146EF1CF9000082B41690 /* Products */, + E5066B45EC1BAE9685C82F39 /* 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 */, + 735EAE15467C7F25161A2B0C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + E5066B45EC1BAE9685C82F39 /* Pods */ = { + isa = PBXGroup; + children = ( + FAEFB9D7FC178343B4995C83 /* Pods-Runner.debug.xcconfig */, + 197783A38A691FFC6148E0D2 /* Pods-Runner.release.xcconfig */, + 551CA5C1BAF98ADA9651F99E /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000082B41690 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 32A12D531A8734FDA50DC439 /* [CP] Check Pods Manifest.lock */, + 97C146EA1CF9000082B4168C /* Sources */, + 97C146EB1CF9000082B4168C /* Frameworks */, + 97C146EC1CF9000082B4168C /* Resources */, + AAF10000000000000000F00D /* Embed App.framework */, + 9740EEB31CF901A200538489 /* Copy flutter_assets */, + 72A92458B6187A4D7EC33E28 /* [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 */ + 32A12D531A8734FDA50DC439 /* [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; + }; + 72A92458B6187A4D7EC33E28 /* [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"; + }; +/* 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.cloudFirestoreExample; + 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)"; + 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.cloudFirestoreExample; + 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)"; + DEVELOPMENT_TEAM = JF8S44WVTV; + 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.cloudFirestoreExample; + 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/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ee3561d --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/AppDelegate.swift b/packages/cloud_firestore_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/cloud_firestore_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/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/cloud_firestore_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/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Info.plist b/packages/cloud_firestore_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..48b3acc --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Cloud_firestore_example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + cloud_firestore_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + + FLTAssetsPath + flutter_assets + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/packages/cloud_firestore_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/cloud_firestore_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/cloud_firestore_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/cloud_firestore_tvos/lib/cloud_firestore_tvos.dart b/packages/cloud_firestore_tvos/lib/cloud_firestore_tvos.dart new file mode 100644 index 0000000..248e8ac --- /dev/null +++ b/packages/cloud_firestore_tvos/lib/cloud_firestore_tvos.dart @@ -0,0 +1,14 @@ +// Copyright 2020, 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. + +// Same reasoning as firebase_core_tvos: cloud_firestore's public Dart API +// (FirebaseFirestore, Query, DocumentSnapshot, …) has no per-platform Dart +// override — it talks to native through cloud_firestore_platform_interface's +// MethodChannel implementation regardless of platform. Duplicating it here +// would create incompatible types vs. apps that import +// package:cloud_firestore/cloud_firestore.dart directly. This package only +// supplies the native tvOS pluginClass (tvos/Classes/); apps depend on +// cloud_firestore (Dart API) and cloud_firestore_tvos (native registration) +// side by side — see example/. +export 'package:cloud_firestore/cloud_firestore.dart'; diff --git a/packages/cloud_firestore_tvos/pubspec.yaml b/packages/cloud_firestore_tvos/pubspec.yaml new file mode 100644 index 0000000..b7e4c6b --- /dev/null +++ b/packages/cloud_firestore_tvos/pubspec.yaml @@ -0,0 +1,44 @@ +name: cloud_firestore_tvos +description: >- + tvOS (Apple TV) implementation of the cloud_firestore Flutter plugin, + bringing Cloud Firestore to Apple TV apps via the flutter-tvos toolchain. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/cloud_firestore_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`. See PORTING_REPORT.md. +# License holder: fluttertv + +# The example ships the standard FlutterFire demo-project GoogleService +# values (client-side Firebase identifiers, not secrets). Tell pub's secret +# scanner they are intentional, matching upstream cloud_firestore's pubspec. +false_secrets: + - example/** + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + cloud_firestore: ^6.6.0 + # Transitive Dart dependency so the app's dependency graph includes + # firebase_core_tvos — our 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 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: FLTFirebaseFirestorePlugin diff --git a/packages/cloud_firestore_tvos/test/cloud_firestore_tvos_test.dart b/packages/cloud_firestore_tvos/test/cloud_firestore_tvos_test.dart new file mode 100644 index 0000000..87c5fe6 --- /dev/null +++ b/packages/cloud_firestore_tvos/test/cloud_firestore_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-06-30 by `flutter-tvos plugin port`. +// Source plugin: cloud_firestore + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTDocumentSnapshotStreamHandler.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTDocumentSnapshotStreamHandler.m new file mode 100644 index 0000000..18539e9 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTDocumentSnapshotStreamHandler.m @@ -0,0 +1,84 @@ +// 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 FirebaseFirestore; +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import "include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FirestorePigeonParser.h" +#import "include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h" + +@interface FLTDocumentSnapshotStreamHandler () +@property(readwrite, strong) id listenerRegistration; +@end + +@implementation FLTDocumentSnapshotStreamHandler + +- (nonnull instancetype)initWithFirestore:(nonnull FIRFirestore *)firestore + reference:(nonnull FIRDocumentReference *)reference + includeMetadataChanges:(BOOL)includeMetadataChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior + source:(FIRListenSource)source { + self = [super init]; + if (self) { + self.firestore = firestore; + self.reference = reference; + self.includeMetadataChanges = includeMetadataChanges; + self.serverTimestampBehavior = serverTimestampBehavior; + self.source = source; + } + return self; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + id listener = ^(FIRDocumentSnapshot *snapshot, NSError *_Nullable error) { + if (error) { + NSArray *codeAndMessage = [FLTFirebaseFirestoreUtils ErrorCodeAndMessageFromNSError:error]; + NSString *code = codeAndMessage[0]; + NSString *message = codeAndMessage[1]; + NSDictionary *details = @{ + @"code" : code, + @"message" : message, + }; + dispatch_async(dispatch_get_main_queue(), ^{ + events([FLTFirebasePlugin createFlutterErrorFromCode:code + message:message + optionalDetails:details + andOptionalNSError:error]); + }); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + // Emit the Pigeon object directly; the Pigeon-aware codec on the + // MessageChannel serializes it end-to-end. Pigeon 26 no longer flattens + // nested types via `toList`. + events([FirestorePigeonParser toPigeonDocumentSnapshot:snapshot + serverTimestampBehavior:self.serverTimestampBehavior]); + }); + } + }; + + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + FIRSnapshotListenOptions *optionsWithSourceAndMetadata = [[options + optionsWithIncludeMetadataChanges:_includeMetadataChanges] optionsWithSource:_source]; + + self.listenerRegistration = + [_reference addSnapshotListenerWithOptions:optionsWithSourceAndMetadata listener:listener]; + + return nil; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + [self.listenerRegistration remove]; + self.listenerRegistration = nil; + + return nil; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreExtension.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreExtension.m new file mode 100644 index 0000000..33fd922 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreExtension.m @@ -0,0 +1,27 @@ +// Copyright 2023 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 FirebaseFirestore; + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h" + +@interface FLTFirebaseFirestoreExtension () + +@property(nonatomic, strong, readwrite) FIRFirestore *instance; +@property(nonatomic, strong, readwrite) NSString *databaseURL; + +@end + +@implementation FLTFirebaseFirestoreExtension + +- (instancetype)initWithFirestoreInstance:(FIRFirestore *)firestore + databaseURL:(NSString *)databaseURL { + self = [super init]; + if (self) { + _instance = firestore; + _databaseURL = [databaseURL copy]; + } + return self; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestorePlugin.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestorePlugin.m new file mode 100644 index 0000000..725a101 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestorePlugin.m @@ -0,0 +1,975 @@ +// Copyright 2020 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 FirebaseFirestore; +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import +#import "FirebaseFirestoreInternal/FIRPersistentCacheIndexManager.h" +#import "include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h" +#import "include/cloud_firestore/Private/FLTPipelineParser.h" +#import "include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h" +#import "include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h" +#import "include/cloud_firestore/Private/FLTTransactionStreamHandler.h" +#import "include/cloud_firestore/Private/FirestorePigeonParser.h" +#import "include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h" +#import "include/cloud_firestore/Public/FirestoreMessages.g.h" + +// Forward-declare the Pigeon-generated reader/writer defined in +// `FirestoreMessages.g.m`. It bundles `FLTFirebaseFirestoreReader/Writer` with +// Pigeon type serialization, so it's safe to use on the plugin's method/event +// channels. +@interface FirebaseFirestoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end + +NSString *const kFLTFirebaseFirestoreChannelName = @"plugins.flutter.io/firebase_firestore"; +NSString *const kFLTFirebaseFirestoreQuerySnapshotEventChannelName = + @"plugins.flutter.io/firebase_firestore/query"; +NSString *const kFLTFirebaseFirestoreDocumentSnapshotEventChannelName = + @"plugins.flutter.io/firebase_firestore/document"; +NSString *const kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName = + @"plugins.flutter.io/firebase_firestore/snapshotsInSync"; +NSString *const kFLTFirebaseFirestoreTransactionChannelName = + @"plugins.flutter.io/firebase_firestore/transaction"; +NSString *const kFLTFirebaseFirestoreLoadBundleChannelName = + @"plugins.flutter.io/firebase_firestore/loadBundle"; + +@interface FLTFirestoreClientLanguage : NSObject ++ (void)setClientLanguage:(NSString *)language; +@end + +@interface FLTFirebaseFirestorePlugin () +@property(nonatomic, retain) NSMutableDictionary *transactions; + +/// Registers a unique event channel based on a channel prefix. +/// +/// Once registered, the plugin will take care of removing the stream handler and cleaning up, +/// if the engine is detached. +/// +/// This function generates a random ID. +/// +/// @param prefix Channel prefix onto which the unique ID will be appended on. The convention is +/// "namespace/component" whereas the last / is added internally. +/// @param handler The handler object for responding to channel events and submitting data. +/// @return The generated identifier. +/// @see #registerEventChannel(String, String, StreamHandler) +- (NSString *)registerEventChannelWithPrefix:(NSString *)prefix + streamHandler:(NSObject *)handler; + +/// Registers a unique event channel based on a channel prefix. +/// +/// Once registered, the plugin will take care of removing the stream handler and cleaning up, +/// if the engine is detached. +/// +/// @param prefix Channel prefix onto which the unique ID will be appended on. The convention is +/// "namespace/component" whereas the last / is added internally. +/// @param identifier A identifier which will be appended to the prefix. +/// @param handler The handler object for responding to channel events and submitting data. +/// @return The passed identifier. +/// @see #registerEventChannel(String, String, StreamHandler) +- (NSString *)registerEventChannelWithPrefix:(NSString *)prefix + identifier:(NSString *)identifier + streamHandler:(NSObject *)handler; +@end + +static NSCache *_serverTimestampMap; + +static id _Nullable FLTPipelineNullSafe(id value) { + return (value == nil || [value isKindOfClass:[NSNull class]]) ? nil : value; +} + +static NSNumber *_Nullable FLTPipelineTimestampToMs(id value) { + if (!value) return nil; + if ([value isKindOfClass:[NSNumber class]]) return value; + if ([value isKindOfClass:[FIRTimestamp class]]) { + FIRTimestamp *ts = value; + return @((int64_t)ts.seconds * 1000 + (int64_t)ts.nanoseconds / 1000000); + } + return nil; +} + +@implementation FLTFirebaseFirestorePlugin { + NSMutableDictionary *_eventChannels; + NSMutableDictionary *> *_streamHandlers; + NSMutableDictionary *_transactionHandlers; + NSObject *_binaryMessenger; +} + +FlutterStandardMethodCodec *_codec; + ++ (NSCache *)serverTimestampMap { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _serverTimestampMap = [NSCache new]; + }); + return _serverTimestampMap; +} + ++ (void)initialize { + // Use the Pigeon-generated reader/writer for MethodChannel/EventChannels so + // Pigeon types emitted by stream handlers (e.g. `InternalDocumentSnapshot`, + // `InternalSnapshotMetadata`) serialize correctly. The reader/writer extend + // `FLTFirebaseFirestoreReader/Writer`, so Firestore-specific types + // (Timestamp, GeoPoint, FieldValue, ...) still round-trip. + _codec = [FlutterStandardMethodCodec + codecWithReaderWriter:[[FirebaseFirestoreHostApiCodecReaderWriter alloc] init]]; +} + +#pragma mark - FlutterPlugin + +// Returns a singleton instance of the Firebase Firestore plugin. +//+ (instancetype)sharedInstance { +// static dispatch_once_t onceToken; +// static FLTFirebaseFirestorePlugin *instance; +// +// dispatch_once(&onceToken, ^{ +// instance = [[FLTFirebaseFirestorePlugin alloc] init]; +// // Register with the Flutter Firebase plugin registry. +// [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:instance]; +// }); +// +// return instance; +//} + +- (instancetype)init:(NSObject *)messenger { + self = [super init]; + if (self) { + _binaryMessenger = messenger; + _transactions = [NSMutableDictionary dictionary]; + _eventChannels = [NSMutableDictionary dictionary]; + _streamHandlers = [NSMutableDictionary dictionary]; + _transactionHandlers = [NSMutableDictionary dictionary]; + } + return self; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FLTFirebaseFirestorePlugin *instance = + [[FLTFirebaseFirestorePlugin alloc] init:[registrar messenger]]; +#if TARGET_OS_IPHONE + [FLTFirestoreClientLanguage + setClientLanguage:[NSString stringWithFormat:@"gl-dart/%@", @LIBRARY_VERSION]]; +#endif + +#if TARGET_OS_OSX +// TODO(Salakar): Publish does not exist on MacOS version of FlutterPluginRegistrar. +#else + [registrar publish:instance]; +#endif + SetUpFirebaseFirestoreHostApi(registrar.messenger, instance); +} + +- (void)cleanupEventListeners { + for (FlutterEventChannel *channel in self->_eventChannels.allValues) { + [channel setStreamHandler:nil]; + } + [self->_eventChannels removeAllObjects]; + for (NSObject *handler in self->_streamHandlers.allValues) { + [handler onCancelWithArguments:nil]; + } + [self->_streamHandlers removeAllObjects]; + + @synchronized(self->_transactions) { + [self->_transactions removeAllObjects]; + } +} + +- (void)cleanupFirestoreInstances:(void (^)(void))completion { + if ([FLTFirebaseFirestoreUtils count] > 0) { + [FLTFirebaseFirestoreUtils cleanupFirestoreInstances:completion]; + } else { + if (completion != nil) completion(); + } +} + +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { + [self cleanupEventListeners]; +} + +#pragma mark - FLTFirebasePlugin + +- (void)didReinitializeFirebaseCore:(void (^)(void))completion { + [self cleanupEventListeners]; + [self cleanupFirestoreInstances:completion]; +} + +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *)firebase_app { + return @{}; +} + +- (NSString *_Nonnull)firebaseLibraryName { + return @LIBRARY_NAME; +} + +- (NSString *_Nonnull)firebaseLibraryVersion { + return @LIBRARY_VERSION; +} + +- (NSString *_Nonnull)flutterChannelName { + return kFLTFirebaseFirestoreChannelName; +} + +#pragma mark - Firestore API + +- (NSString *)registerEventChannelWithPrefix:(NSString *)prefix + streamHandler:(NSObject *)handler { + return [self registerEventChannelWithPrefix:prefix + identifier:[[[NSUUID UUID] UUIDString] lowercaseString] + streamHandler:handler]; +} + +- (NSString *)registerEventChannelWithPrefix:(NSString *)prefix + identifier:(NSString *)identifier + streamHandler:(NSObject *)handler { + NSString *channelName = [NSString stringWithFormat:@"%@/%@", prefix, identifier]; + + FlutterEventChannel *channel = [[FlutterEventChannel alloc] initWithName:channelName + binaryMessenger:_binaryMessenger + codec:_codec]; + + [channel setStreamHandler:handler]; + [_eventChannels setObject:channel forKey:identifier]; + [_streamHandlers setObject:handler forKey:identifier]; + + return identifier; +} + +- (FIRFirestore *_Nullable)getFIRFirestoreFromAppNameFromPigeon: + (FirestorePigeonFirebaseApp *)pigeonApp { + @synchronized(self) { + NSString *appNameDart = pigeonApp.appName; + NSString *databaseUrl = pigeonApp.databaseURL; + + FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appNameDart]; + + if ([FLTFirebaseFirestoreUtils getFirestoreInstanceByName:app.name + databaseURL:databaseUrl] != nil) { + return [FLTFirebaseFirestoreUtils getFirestoreInstanceByName:app.name + databaseURL:databaseUrl]; + } + + FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; + if (pigeonApp.settings.persistenceEnabled != nil) { + bool persistEnabled = [pigeonApp.settings.persistenceEnabled boolValue]; + + // We default to the maximum amount of cache allowed. + NSNumber *size = @(kFIRFirestoreCacheSizeUnlimited); + + if (pigeonApp.settings.cacheSizeBytes) { + NSNumber *cacheSizeBytes = pigeonApp.settings.cacheSizeBytes; + if ([cacheSizeBytes intValue] != -1) { + size = cacheSizeBytes; + } + } + + if (persistEnabled) { + settings.cacheSettings = [[FIRPersistentCacheSettings alloc] initWithSizeBytes:size]; + } else { + settings.cacheSettings = [[FIRMemoryCacheSettings alloc] + initWithGarbageCollectorSettings:[[FIRMemoryLRUGCSettings alloc] init]]; + } + } + + if (pigeonApp.settings.host != nil) { + settings.host = pigeonApp.settings.host; + // Only allow changing ssl if host is also specified. + if (pigeonApp.settings.sslEnabled != nil) { + settings.sslEnabled = [pigeonApp.settings.sslEnabled boolValue]; + } + } + + settings.dispatchQueue = [FLTFirebaseFirestoreReader getFirestoreQueue]; + + FIRFirestore *firestore = [FIRFirestore firestoreForApp:app database:databaseUrl]; + firestore.settings = settings; + + [FLTFirebaseFirestoreUtils setCachedFIRFirestoreInstance:firestore + forAppName:app.name + databaseURL:databaseUrl]; + return firestore; + } +} + +- (FlutterError *)convertToFlutterError:(NSError *)error { + NSArray *codeAndMessage = [FLTFirebaseFirestoreUtils ErrorCodeAndMessageFromNSError:error]; + NSString *_Nullable code = codeAndMessage[0]; + NSString *_Nullable message = codeAndMessage[1]; + NSDictionary *_Nullable details = @{ + @"code" : code, + @"message" : message, + }; + + return [FlutterError errorWithCode:code message:message details:details]; +} + +- (void)clearPersistenceApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + [firestore clearPersistenceWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)disableNetworkApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + [firestore disableNetworkWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)documentReferenceDeleteApp:(nonnull FirestorePigeonFirebaseApp *)app + request:(nonnull DocumentReferenceRequest *)request + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:request.path]; + + [document deleteDocumentWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)terminate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRFirestore *firestore = arguments[@"firestore"]; + [firestore terminateWithCompletion:^(NSError *error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + FLTFirebaseFirestoreExtension *firestoreExtension = + [FLTFirebaseFirestoreUtils getCachedInstanceForFirestore:firestore]; + [FLTFirebaseFirestoreUtils destroyCachedInstanceForFirestore:firestore.app.name + databaseURL:firestoreExtension.databaseURL]; + result.success(nil); + } + }]; +} + +- (void)documentReferenceGetApp:(nonnull FirestorePigeonFirebaseApp *)app + request:(nonnull DocumentReferenceRequest *)request + completion:(nonnull void (^)(InternalDocumentSnapshot *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:request.path]; + FIRFirestoreSource source = [FirestorePigeonParser parseSource:request.source.value]; + FIRServerTimestampBehavior serverTimestampBehavior = + [FirestorePigeonParser parseServerTimestampBehavior:request.serverTimestampBehavior.value]; + + id completionGet = ^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [self convertToFlutterError:error]); + } else { + completion([FirestorePigeonParser toPigeonDocumentSnapshot:snapshot + serverTimestampBehavior:serverTimestampBehavior], + nil); + } + }; + + [document getDocumentWithSource:source completion:completionGet]; +} + +- (void)documentReferenceSetApp:(nonnull FirestorePigeonFirebaseApp *)app + request:(nonnull DocumentReferenceRequest *)request + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + id data = request.data; + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:request.path]; + + void (^completionBlock)(NSError *) = ^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }; + + if ([request.option.merge isEqual:@YES]) { + [document setData:data merge:YES completion:completionBlock]; + } else if (request.option.mergeFields) { + [document setData:data + mergeFields:[FirestorePigeonParser parseFieldPath:request.option.mergeFields] + completion:completionBlock]; + } else { + [document setData:data completion:completionBlock]; + } +} + +- (void)documentReferenceSnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app + parameters:(nonnull DocumentReferenceRequest *)parameters + includeMetadataChanges:(BOOL)includeMetadataChanges + source:(ListenSource)source + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:parameters.path]; + FIRServerTimestampBehavior serverTimestampBehavior = + [FirestorePigeonParser parseServerTimestampBehavior:parameters.serverTimestampBehavior.value]; + FIRListenSource listenSource = [FirestorePigeonParser parseListenSource:source]; + + completion( + [self registerEventChannelWithPrefix:kFLTFirebaseFirestoreDocumentSnapshotEventChannelName + streamHandler:[[FLTDocumentSnapshotStreamHandler alloc] + initWithFirestore:firestore + reference:document + includeMetadataChanges:includeMetadataChanges + serverTimestampBehavior:serverTimestampBehavior + source:listenSource]], + nil); +} + +- (void)documentReferenceUpdateApp:(nonnull FirestorePigeonFirebaseApp *)app + request:(nonnull DocumentReferenceRequest *)request + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + id data = request.data; + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:request.path]; + + [document updateData:data + completion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)enableNetworkApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + [firestore enableNetworkWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)loadBundleApp:(nonnull FirestorePigeonFirebaseApp *)app + bundle:(nonnull FlutterStandardTypedData *)bundle + completion:(nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + completion([self registerEventChannelWithPrefix:kFLTFirebaseFirestoreLoadBundleChannelName + streamHandler:[[FLTLoadBundleStreamHandler alloc] + initWithFirestore:firestore + bundle:bundle]], + nil); +} + +- (void)namedQueryGetApp:(nonnull FirestorePigeonFirebaseApp *)app + name:(nonnull NSString *)name + options:(nonnull InternalGetOptions *)options + completion:(nonnull void (^)(InternalQuerySnapshot *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + FIRFirestoreSource source = [FirestorePigeonParser parseSource:options.source]; + FIRServerTimestampBehavior serverTimestampBehavior = + [FirestorePigeonParser parseServerTimestampBehavior:options.serverTimestampBehavior]; + + [firestore + getQueryNamed:name + completion:^(FIRQuery *_Nullable query) { + if (query == nil) { + completion(nil, + [FlutterError errorWithCode:@"non-existent-named-query" + message:@"Named query has not been found. Please check " + @"it has been loaded properly via loadBundle()." + details:nil]); + + return; + } + [query getDocumentsWithSource:source + completion:^(FIRQuerySnapshot *_Nullable snapshot, + NSError *_Nullable error) { + if (error != nil) { + completion(nil, [self convertToFlutterError:error]); + } else { + completion([FirestorePigeonParser + toPigeonQuerySnapshot:snapshot + serverTimestampBehavior:serverTimestampBehavior], + nil); + } + }]; + }]; +} + +- (void)queryGetApp:(nonnull FirestorePigeonFirebaseApp *)app + path:(nonnull NSString *)path + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(nonnull InternalQueryParameters *)parameters + options:(nonnull InternalGetOptions *)options + completion:(nonnull void (^)(InternalQuerySnapshot *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters + firestore:firestore + path:path + isCollectionGroup:isCollectionGroup]; + if (query == nil) { + completion(nil, [FlutterError errorWithCode:@"error-parsing" + message:@"An error occurred while parsing query arguments, " + @"this is most likely an error with this SDK." + details:nil]); + return; + } + + FIRFirestoreSource source = [FirestorePigeonParser parseSource:options.source]; + FIRServerTimestampBehavior serverTimestampBehavior = + [FirestorePigeonParser parseServerTimestampBehavior:options.serverTimestampBehavior]; + + [query getDocumentsWithSource:source + completion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [self convertToFlutterError:error]); + } else { + completion( + [FirestorePigeonParser toPigeonQuerySnapshot:snapshot + serverTimestampBehavior:serverTimestampBehavior], + nil); + } + }]; +} + +- (void)querySnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app + path:(nonnull NSString *)path + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(nonnull InternalQueryParameters *)parameters + options:(nonnull InternalGetOptions *)options + includeMetadataChanges:(BOOL)includeMetadataChanges + source:(ListenSource)source + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters + firestore:firestore + path:path + isCollectionGroup:isCollectionGroup]; + if (query == nil) { + completion(nil, [FlutterError errorWithCode:@"error-parsing" + message:@"An error occurred while parsing query arguments, " + @"this is most likely an error with this SDK." + details:nil]); + return; + } + + FIRServerTimestampBehavior serverTimestampBehavior = + [FirestorePigeonParser parseServerTimestampBehavior:options.serverTimestampBehavior]; + FIRListenSource listenSource = [FirestorePigeonParser parseListenSource:source]; + + completion( + [self registerEventChannelWithPrefix:kFLTFirebaseFirestoreQuerySnapshotEventChannelName + streamHandler:[[FLTQuerySnapshotStreamHandler alloc] + initWithFirestore:firestore + query:query + includeMetadataChanges:includeMetadataChanges + serverTimestampBehavior:serverTimestampBehavior + source:listenSource]], + nil); +} + +- (void)setIndexConfigurationApp:(nonnull FirestorePigeonFirebaseApp *)app + indexConfiguration:(nonnull NSString *)indexConfiguration + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + [firestore setIndexConfigurationFromJSON:indexConfiguration + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app + request:(PersistenceCacheIndexManagerRequest)request + completion:(void (^)(FlutterError *_Nullable))completion { + FIRPersistentCacheIndexManager *persistentCacheIndexManager = + [self getFIRFirestoreFromAppNameFromPigeon:app].persistentCacheIndexManager; + + if (persistentCacheIndexManager) { + switch (request) { + case PersistenceCacheIndexManagerRequestEnableIndexAutoCreation: + [persistentCacheIndexManager enableIndexAutoCreation]; + break; + case PersistenceCacheIndexManagerRequestDisableIndexAutoCreation: + [persistentCacheIndexManager disableIndexAutoCreation]; + break; + case PersistenceCacheIndexManagerRequestDeleteAllIndexes: + [persistentCacheIndexManager deleteAllIndexes]; + break; + } + } else { + // Put because `persistentCacheIndexManager` is a nullable property + NSLog(@"FLTFirebaseFirestore: `PersistentCacheIndexManager` is not available."); + } + completion(nil); +} + +- (void)setLoggingEnabledLoggingEnabled:(BOOL)loggingEnabled + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + [FIRFirestore enableLogging:loggingEnabled]; + completion(nil); +} + +- (void)terminateApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + [firestore terminateWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + FLTFirebaseFirestoreExtension *firestoreExtension = + [FLTFirebaseFirestoreUtils getCachedInstanceForFirestore:firestore]; + [FLTFirebaseFirestoreUtils destroyCachedInstanceForFirestore:firestore.app.name + databaseURL:firestoreExtension.databaseURL]; + completion(nil); + } + }]; +} + +- (void)transactionGetApp:(nonnull FirestorePigeonFirebaseApp *)app + transactionId:(nonnull NSString *)transactionId + path:(nonnull NSString *)path + completion:(nonnull void (^)(InternalDocumentSnapshot *_Nullable, + FlutterError *_Nullable))completion { + // Dispatching to main thread allow us to ensure that the auth token are fetched in time + // for the transaction + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRDocumentReference *document = [firestore documentWithPath:path]; + + FIRTransaction *transaction = self->_transactions[transactionId]; + + if (transaction == nil) { + completion( + nil, + [FlutterError + errorWithCode:@"missing-transaction" + message:@"An error occurred while getting the native transaction. " + @"It could be caused by a timeout in a preceding transaction operation." + details:nil]); + return; + } + + NSError *error = nil; + FIRDocumentSnapshot *snapshot = [transaction getDocument:document error:&error]; + + if (error != nil) { + completion(nil, [self convertToFlutterError:error]); + } else if (snapshot != nil) { + completion([FirestorePigeonParser toPigeonDocumentSnapshot:snapshot + serverTimestampBehavior:FIRServerTimestampBehaviorNone], + nil); + } else { + completion(nil, nil); + } + }); +} + +- (void)transactionStoreResultTransactionId:(nonnull NSString *)transactionId + resultType:(InternalTransactionResult)resultType + commands: + (nullable NSArray *)commands + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + [_transactionHandlers[transactionId] receiveTransactionResponse:resultType commands:commands]; + + completion(nil); +} + +- (void)waitForPendingWritesApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + [firestore waitForPendingWritesWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)writeBatchCommitApp:(nonnull FirestorePigeonFirebaseApp *)app + writes:(nonnull NSArray *)writes + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + FIRWriteBatch *batch = [firestore batch]; + + for (InternalTransactionCommand *write in writes) { + InternalTransactionType type = write.type; + NSString *path = write.path; + FIRDocumentReference *reference = [firestore documentWithPath:path]; + + switch (type) { + case InternalTransactionTypeGet: + break; + case InternalTransactionTypeDeleteType: + [batch deleteDocument:reference]; + break; + case InternalTransactionTypeUpdate: + [batch updateData:write.data forDocument:reference]; + break; + case InternalTransactionTypeSet: + if ([write.option.merge isEqual:@YES]) { + [batch setData:write.data forDocument:reference merge:YES]; + } else if (write.option.mergeFields) { + [batch setData:write.data + forDocument:reference + mergeFields:[FirestorePigeonParser parseFieldPath:write.option.mergeFields]]; + } else { + [batch setData:write.data forDocument:reference]; + } + break; + } + } + + [batch commitWithCompletion:^(NSError *error) { + if (error != nil) { + completion([self convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)snapshotsInSyncSetupApp:(nonnull FirestorePigeonFirebaseApp *)app + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + completion( + [self registerEventChannelWithPrefix:kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName + streamHandler:[[FLTSnapshotsInSyncStreamHandler alloc] + initWithFirestore:firestore]], + nil); +} + +- (void)transactionCreateApp:(nonnull FirestorePigeonFirebaseApp *)app + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + NSString *transactionId = [[[NSUUID UUID] UUIDString] lowercaseString]; + + FLTTransactionStreamHandler *handler = + [[FLTTransactionStreamHandler alloc] initWithId:transactionId + firestore:firestore + timeout:timeout + maxAttempts:maxAttempts + started:^(FIRTransaction *_Nonnull transaction) { + self->_transactions[transactionId] = transaction; + } + ended:^{ + self->_transactions[transactionId] = nil; + }]; + + _transactionHandlers[transactionId] = handler; + + completion([self registerEventChannelWithPrefix:kFLTFirebaseFirestoreTransactionChannelName + identifier:transactionId + streamHandler:handler], + nil); +} + +- (void)aggregateQueryApp:(nonnull FirestorePigeonFirebaseApp *)app + path:(nonnull NSString *)path + parameters:(nonnull InternalQueryParameters *)parameters + source:(AggregateSource)source + queries:(nonnull NSArray *)queries + isCollectionGroup:(BOOL)isCollectionGroup + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters + firestore:firestore + path:path + isCollectionGroup:isCollectionGroup]; + if (query == nil) { + completion(nil, [FlutterError errorWithCode:@"error-parsing" + message:@"An error occurred while parsing query arguments, " + @"this is most likely an error with this SDK." + details:nil]); + return; + } + + NSMutableArray *aggregateFields = + [[NSMutableArray alloc] init]; + + for (AggregateQuery *queryRequest in queries) { + switch ([queryRequest type]) { + case AggregateTypeCount: + [aggregateFields addObject:[FIRAggregateField aggregateFieldForCount]]; + break; + case AggregateTypeSum: + [aggregateFields + addObject:[FIRAggregateField aggregateFieldForSumOfField:[queryRequest field]]]; + break; + case AggregateTypeAverage: + [aggregateFields + addObject:[FIRAggregateField aggregateFieldForAverageOfField:[queryRequest field]]]; + break; + default: + // Handle the default case + break; + } + } + + FIRAggregateQuery *aggregateQuery = [query aggregate:aggregateFields]; + + [aggregateQuery + aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *_Nullable snapshot, + NSError *_Nullable error) { + if (error != nil) { + completion(nil, [self convertToFlutterError:error]); + return; + } + NSMutableArray *aggregateResponses = + [[NSMutableArray alloc] init]; + + for (AggregateQuery *queryRequest in queries) { + switch (queryRequest.type) { + case AggregateTypeCount: { + double doubleValue = [snapshot.count doubleValue]; + + [aggregateResponses + addObject:[AggregateQueryResponse + makeWithType:AggregateTypeCount + field:nil + value:[NSNumber numberWithDouble:doubleValue]]]; + break; + } + case AggregateTypeSum: { + NSNumber *value = [snapshot + valueForAggregateField:[FIRAggregateField + aggregateFieldForSumOfField:[queryRequest + field]]]; + + [aggregateResponses + addObject:[AggregateQueryResponse + makeWithType:AggregateTypeSum + field:queryRequest.field + // This passes either a double (wrapped in + // NSNumber) or null value + value:value != ((id)[NSNull null]) + ? [NSNumber + numberWithDouble:[value + doubleValue]] + : value]]; + break; + } + case AggregateTypeAverage: { + NSNumber *value = [snapshot + valueForAggregateField: + [FIRAggregateField + aggregateFieldForAverageOfField:[queryRequest field]]]; + + [aggregateResponses + addObject:[AggregateQueryResponse + makeWithType:AggregateTypeAverage + field:queryRequest.field + // This passes either a double (wrapped in + // NSNumber) or null value + value:value != ((id)[NSNull null]) + ? [NSNumber + numberWithDouble:[value + doubleValue]] + : value]]; + break; + } + } + } + + completion(aggregateResponses, nil); + }]; +} + +- (void)executePipelineApp:(nonnull FirestorePigeonFirebaseApp *)app + stages:(nonnull NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(nonnull void (^)(InternalPipelineSnapshot *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + [FLTPipelineParser + executePipelineWithFirestore:firestore + stages:stages + options:options + completion:^(id _Nullable snapshot, NSError *_Nullable error) { + if (error) { + completion(nil, [self convertToFlutterError:error]); + return; + } + if (snapshot == nil) { + completion( + nil, + [FlutterError errorWithCode:@"error" + message:@"Pipeline execution returned no result" + details:nil]); + return; + } + + NSMutableArray *pigeonResults = + [NSMutableArray array]; + NSArray *results = [snapshot results]; + if ([results isKindOfClass:[NSArray class]]) { + for (id result in results) { + id ref = [result reference]; + NSString *path = (ref && [ref respondsToSelector:@selector(path)]) + ? [ref path] + : FLTPipelineNullSafe([result documentID]); + NSNumber *createTime = + FLTPipelineTimestampToMs([result valueForKey:@"create_time"]); + NSNumber *updateTime = + FLTPipelineTimestampToMs([result valueForKey:@"update_time"]); + NSDictionary *data = FLTPipelineNullSafe([result data]); + InternalPipelineResult *pigeonResult = + [InternalPipelineResult makeWithDocumentPath:path + createTime:createTime + updateTime:updateTime + data:data]; + [pigeonResults addObject:pigeonResult]; + } + } + + NSNumber *executionTime = + FLTPipelineTimestampToMs([snapshot execution_time]); + if (executionTime == nil) { + executionTime = + @((int64_t)([[NSDate date] timeIntervalSince1970] * 1000)); + } + + InternalPipelineSnapshot *pigeonSnapshot = [InternalPipelineSnapshot + makeWithResults:pigeonResults + executionTime:[executionTime longLongValue]]; + completion(pigeonSnapshot, nil); + }]; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreReader.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreReader.m new file mode 100644 index 0000000..9387c9d --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreReader.m @@ -0,0 +1,318 @@ +// Copyright 2020 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 FirebaseFirestore; +@import FirebaseCore; + +#if __has_include() +#import +#else +#import "FLTFirebasePlugin.h" +#endif +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" + +@implementation FLTFirebaseFirestoreReader + +- (id)readValueOfType:(UInt8)type { + switch (type) { + case FirestoreDataTypeDateTime: { + SInt64 value; + [self readBytes:&value length:8]; + return [NSDate dateWithTimeIntervalSince1970:(value / 1000.0)]; + } + case FirestoreDataTypeTimestamp: { + SInt64 seconds; + int nanoseconds; + [self readBytes:&seconds length:8]; + [self readBytes:&nanoseconds length:4]; + return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds]; + } + case FirestoreDataTypeGeoPoint: { + Float64 latitude; + Float64 longitude; + [self readAlignment:8]; + [self readBytes:&latitude length:8]; + [self readBytes:&longitude length:8]; + return [[FIRGeoPoint alloc] initWithLatitude:latitude longitude:longitude]; + } + case FirestoreDataTypeVectorValue: { + return [[FIRVectorValue alloc] initWithArray:[self readValue]]; + } + case FirestoreDataTypeDocumentReference: { + FIRFirestore *firestore = [self readValue]; + NSString *documentPath = [self readValue]; + return [firestore documentWithPath:documentPath]; + } + case FirestoreDataTypeFieldPath: { + UInt32 length = [self readSize]; + NSMutableArray *array = [NSMutableArray arrayWithCapacity:length]; + for (UInt32 i = 0; i < length; i++) { + id value = [self readValue]; + [array addObject:(value == nil ? [NSNull null] : value)]; + } + return [[FIRFieldPath alloc] initWithFields:array]; + } + case FirestoreDataTypeBlob: + return [self readData:[self readSize]]; + case FirestoreDataTypeArrayUnion: + return [FIRFieldValue fieldValueForArrayUnion:[self readValue]]; + case FirestoreDataTypeArrayRemove: + return [FIRFieldValue fieldValueForArrayRemove:[self readValue]]; + case FirestoreDataTypeDelete: + return [FIRFieldValue fieldValueForDelete]; + case FirestoreDataTypeServerTimestamp: + return [FIRFieldValue fieldValueForServerTimestamp]; + case FirestoreDataTypeIncrementDouble: + return + [FIRFieldValue fieldValueForDoubleIncrement:((NSNumber *)[self readValue]).doubleValue]; + case FirestoreDataTypeIncrementInteger: + return [FIRFieldValue fieldValueForIntegerIncrement:((NSNumber *)[self readValue]).intValue]; + case FirestoreDataTypeDocumentId: + return [FIRFieldPath documentID]; + case FirestoreDataTypeFirestoreInstance: + return [self FIRFirestore]; + case FirestoreDataTypeFirestoreQuery: + return [self FIRQuery]; + case FirestoreDataTypeFirestoreSettings: + return [self FIRFirestoreSettings]; + case FirestoreDataTypeNaN: + return @(NAN); + case FirestoreDataTypeInfinity: + return @(INFINITY); + case FirestoreDataTypeNegativeInfinity: + return @(-INFINITY); + default: + return [super readValueOfType:type]; + } +} + ++ (dispatch_queue_t)getFirestoreQueue { + static dispatch_queue_t firestoreQueue; + static dispatch_once_t once; + dispatch_once(&once, ^{ + firestoreQueue = dispatch_queue_create("dev.flutter.firebase.firestore", DISPATCH_QUEUE_SERIAL); + }); + return firestoreQueue; +} + +- (FIRFirestoreSettings *)FIRFirestoreSettings { + NSDictionary *values = [self readValue]; + FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; + + if (![values[@"persistenceEnabled"] isEqual:[NSNull null]]) { + bool persistEnabled = [((NSNumber *)values[@"persistenceEnabled"]) boolValue]; + + // We default to the maximum amount of cache allowed. + NSNumber *size = @(kFIRFirestoreCacheSizeUnlimited); + + if (![values[@"cacheSizeBytes"] isEqual:[NSNull null]]) { + NSNumber *cacheSizeBytes = ((NSNumber *)values[@"cacheSizeBytes"]); + if ([cacheSizeBytes intValue] != -1) { + size = cacheSizeBytes; + } + } + + if (persistEnabled) { + settings.cacheSettings = [[FIRPersistentCacheSettings alloc] initWithSizeBytes:size]; + } else { + settings.cacheSettings = [[FIRMemoryCacheSettings alloc] + initWithGarbageCollectorSettings:[[FIRMemoryLRUGCSettings alloc] init]]; + } + } + + if (![values[@"host"] isEqual:[NSNull null]]) { + settings.host = (NSString *)values[@"host"]; + // Only allow changing ssl if host is also specified. + if (![values[@"sslEnabled"] isEqual:[NSNull null]]) { + settings.sslEnabled = [((NSNumber *)values[@"sslEnabled"]) boolValue]; + } + } + + settings.dispatchQueue = [FLTFirebaseFirestoreReader getFirestoreQueue]; + + return settings; +} + +- (FIRFilter *)filterFromJson:(NSDictionary *)map { + if (map[@"fieldPath"]) { + // Deserialize a FilterQuery + NSString *op = map[@"op"]; + FIRFieldPath *fieldPath = map[@"fieldPath"]; + id value = map[@"value"]; + + // All the operators from Firebase + if ([op isEqualToString:@"=="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isEqualTo:value]; + } else if ([op isEqualToString:@"!="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isNotEqualTo:value]; + } else if ([op isEqualToString:@"<"]) { + return [FIRFilter filterWhereFieldPath:fieldPath isLessThan:value]; + } else if ([op isEqualToString:@"<="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isLessThanOrEqualTo:value]; + } else if ([op isEqualToString:@">"]) { + return [FIRFilter filterWhereFieldPath:fieldPath isGreaterThan:value]; + } else if ([op isEqualToString:@">="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value]; + } else if ([op isEqualToString:@"array-contains"]) { + return [FIRFilter filterWhereFieldPath:fieldPath arrayContains:value]; + } else if ([op isEqualToString:@"array-contains-any"]) { + return [FIRFilter filterWhereFieldPath:fieldPath arrayContainsAny:value]; + } else if ([op isEqualToString:@"in"]) { + return [FIRFilter filterWhereFieldPath:fieldPath in:value]; + } else if ([op isEqualToString:@"not-in"]) { + return [FIRFilter filterWhereFieldPath:fieldPath notIn:value]; + } else { + @throw [NSException exceptionWithName:@"InvalidOperator" + reason:@"Invalid operator" + userInfo:nil]; + } + } + // Deserialize a FilterOperator + NSString *op = map[@"op"]; + NSArray *> *queries = map[@"queries"]; + + // Map queries recursively + NSMutableArray *parsedFilters = [NSMutableArray array]; + for (NSDictionary *query in queries) { + [parsedFilters addObject:[self filterFromJson:query]]; + } + + if ([op isEqualToString:@"OR"]) { + return [FIRFilter orFilterWithFilters:parsedFilters]; + } else if ([op isEqualToString:@"AND"]) { + return [FIRFilter andFilterWithFilters:parsedFilters]; + } + + @throw [NSException exceptionWithName:@"InvalidOperator" reason:@"Invalid operator" userInfo:nil]; +} + +- (FIRQuery *)FIRQuery { + @try { + FIRQuery *query; + NSDictionary *values = [self readValue]; + FIRFirestore *firestore = values[@"firestore"]; + + NSDictionary *parameters = values[@"parameters"]; + NSArray *whereConditions = parameters[@"where"]; + BOOL isCollectionGroup = ((NSNumber *)values[@"isCollectionGroup"]).boolValue; + + if (isCollectionGroup) { + query = [firestore collectionGroupWithID:values[@"path"]]; + } else { + query = (FIRQuery *)[firestore collectionWithPath:values[@"path"]]; + } + + BOOL isFilterQuery = [parameters objectForKey:@"filters"] != nil; + if (isFilterQuery) { + FIRFilter *filter = + [self filterFromJson:(NSDictionary *)parameters[@"filters"]]; + query = [query queryWhereFilter:filter]; + } + + // Filters + for (id item in whereConditions) { + NSArray *condition = item; + FIRFieldPath *fieldPath = (FIRFieldPath *)condition[0]; + NSString *operator= condition[1]; + id value = condition[2]; + if ([operator isEqualToString:@"=="]) { + query = [query queryWhereFieldPath:fieldPath isEqualTo:value]; + } else if ([operator isEqualToString:@"!="]) { + query = [query queryWhereFieldPath:fieldPath isNotEqualTo:value]; + } else if ([operator isEqualToString:@"<"]) { + query = [query queryWhereFieldPath:fieldPath isLessThan:value]; + } else if ([operator isEqualToString:@"<="]) { + query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value]; + } else if ([operator isEqualToString:@">"]) { + query = [query queryWhereFieldPath:fieldPath isGreaterThan:value]; + } else if ([operator isEqualToString:@">="]) { + query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value]; + } else if ([operator isEqualToString:@"array-contains"]) { + query = [query queryWhereFieldPath:fieldPath arrayContains:value]; + } else if ([operator isEqualToString:@"array-contains-any"]) { + query = [query queryWhereFieldPath:fieldPath arrayContainsAny:value]; + } else if ([operator isEqualToString:@"in"]) { + query = [query queryWhereFieldPath:fieldPath in:value]; + } else if ([operator isEqualToString:@"not-in"]) { + query = [query queryWhereFieldPath:fieldPath notIn:value]; + } else { + NSLog(@"FLTFirebaseFirestore: An invalid query operator %@ was received but not handled.", + operator); + } + } + + // Limit + id limit = parameters[@"limit"]; + if (![limit isEqual:[NSNull null]]) { + query = [query queryLimitedTo:((NSNumber *)limit).intValue]; + } + + // Limit To Last + id limitToLast = parameters[@"limitToLast"]; + if (![limitToLast isEqual:[NSNull null]]) { + query = [query queryLimitedToLast:((NSNumber *)limitToLast).intValue]; + } + + // Ordering + NSArray *orderBy = parameters[@"orderBy"]; + if ([orderBy isEqual:[NSNull null]]) { + // We return early if no ordering set as cursor queries below require at least one orderBy set + return query; + } + + for (NSArray *orderByParameters in orderBy) { + FIRFieldPath *fieldPath = (FIRFieldPath *)orderByParameters[0]; + NSNumber *descending = orderByParameters[1]; + query = [query queryOrderedByFieldPath:fieldPath descending:[descending boolValue]]; + } + + // Start At + id startAt = parameters[@"startAt"]; + if (![startAt isEqual:[NSNull null]]) query = [query queryStartingAtValues:(NSArray *)startAt]; + // Start After + id startAfter = parameters[@"startAfter"]; + if (![startAfter isEqual:[NSNull null]]) + query = [query queryStartingAfterValues:(NSArray *)startAfter]; + // End At + id endAt = parameters[@"endAt"]; + if (![endAt isEqual:[NSNull null]]) query = [query queryEndingAtValues:(NSArray *)endAt]; + // End Before + id endBefore = parameters[@"endBefore"]; + if (![endBefore isEqual:[NSNull null]]) + query = [query queryEndingBeforeValues:(NSArray *)endBefore]; + + return query; + } @catch (NSException *exception) { + NSLog(@"An error occurred while parsing query arguments, this is most likely an error with " + @"this SDK. %@", + [exception callStackSymbols]); + return nil; + } +} + +- (FIRFirestore *)FIRFirestore { + @synchronized(self) { + NSString *appNameDart = [self readValue]; + NSString *databaseUrl = [self readValue]; + FIRFirestoreSettings *settings = [self readValue]; + FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appNameDart]; + + if ([FLTFirebaseFirestoreUtils getFirestoreInstanceByName:app.name + databaseURL:databaseUrl] != nil) { + return [FLTFirebaseFirestoreUtils getFirestoreInstanceByName:app.name + databaseURL:databaseUrl]; + } + + FIRFirestore *firestore = [FIRFirestore firestoreForApp:app database:databaseUrl]; + firestore.settings = settings; + + [FLTFirebaseFirestoreUtils setCachedFIRFirestoreInstance:firestore + forAppName:app.name + databaseURL:databaseUrl]; + return firestore; + } +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreUtils.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreUtils.m new file mode 100644 index 0000000..db06c89 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreUtils.m @@ -0,0 +1,259 @@ +// Copyright 2020 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 FirebaseFirestore; +@import FirebaseCore; + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h" + +@implementation FLTFirebaseFirestoreReaderWriter +- (FlutterStandardWriter *_Nonnull)writerWithData:(NSMutableData *)data { + return [[FLTFirebaseFirestoreWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *_Nonnull)readerWithData:(NSData *)data { + return [[FLTFirebaseFirestoreReader alloc] initWithData:data]; +} +@end + +NSMutableDictionary *firestoreInstanceCache; + +const NSInteger FLTFirebaseFirestoreErrorCodePipelineParse = -1; + +@implementation FLTFirebaseFirestoreUtils + ++ (NSString *)generateKeyForAppName:(NSString *)appName andDatabaseURL:(NSString *)databaseURL { + return [NSString stringWithFormat:@"%@|%@", appName, databaseURL]; +} + ++ (FLTFirebaseFirestoreExtension *_Nullable) + getCachedFIRFirestoreInstanceForAppName:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)url { + @synchronized(firestoreInstanceCache) { + if (firestoreInstanceCache == nil) { + firestoreInstanceCache = [NSMutableDictionary dictionary]; + return nil; + } else { + NSString *key = [self generateKeyForAppName:appName andDatabaseURL:url]; + return firestoreInstanceCache[key]; + } + } +} + ++ (void)setCachedFIRFirestoreInstance:(FIRFirestore *_Nonnull)firestore + forAppName:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)url { + @synchronized(firestoreInstanceCache) { + if (firestoreInstanceCache == nil) { + firestoreInstanceCache = [NSMutableDictionary dictionary]; + } + NSString *key = [self generateKeyForAppName:appName andDatabaseURL:url]; + firestoreInstanceCache[key] = + [[FLTFirebaseFirestoreExtension alloc] initWithFirestoreInstance:firestore databaseURL:url]; + } +} + ++ (void)destroyCachedInstanceForFirestore:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)databaseURL { + @synchronized(firestoreInstanceCache) { + if (firestoreInstanceCache != nil) { + NSString *key = [self generateKeyForAppName:appName andDatabaseURL:databaseURL]; + FLTFirebaseFirestoreExtension *extension = firestoreInstanceCache[key]; + + if (extension != nil) { + [firestoreInstanceCache removeObjectForKey:key]; + } + } + } +} + ++ (FIRFirestore *)getFirestoreInstanceByName:(NSString *)appName + databaseURL:(NSString *)databaseURL { + @synchronized(firestoreInstanceCache) { + if (firestoreInstanceCache == nil) { + firestoreInstanceCache = [NSMutableDictionary dictionary]; + } + NSString *key = [self generateKeyForAppName:appName andDatabaseURL:databaseURL]; + FLTFirebaseFirestoreExtension *extension = firestoreInstanceCache[key]; + + if (extension != nil) { + return extension.instance; + } + + return nil; + } +} + ++ (NSUInteger)count { + return [firestoreInstanceCache count]; +} + +// Require this method when we don't have access to the "databaseURL" ++ (FLTFirebaseFirestoreExtension *_Nullable)getCachedInstanceForFirestore: + (FIRFirestore *_Nonnull)firestore { + @synchronized(firestoreInstanceCache) { + if (firestoreInstanceCache != nil) { + NSEnumerator *enumerator = [firestoreInstanceCache keyEnumerator]; + NSString *key; + + while ((key = [enumerator nextObject])) { + FLTFirebaseFirestoreExtension *value = firestoreInstanceCache[key]; + + if (value.instance == firestore) { + return value; + } + } + } + @throw [NSException exceptionWithName:@"NoCachedInstance" + reason:@"No cached instance of Firestore" + userInfo:nil]; + } +} + ++ (void)cleanupFirestoreInstances:(void (^)(void))completion { + __block int instancesTerminated = 0; + NSUInteger numberOfInstances = [firestoreInstanceCache count]; + void (^firestoreTerminateInstanceCompletion)(NSError *) = ^void(NSError *error) { + instancesTerminated++; + if (instancesTerminated == numberOfInstances && completion != nil) { + completion(); + } + }; + + if (numberOfInstances > 0) { + for (NSString *key in firestoreInstanceCache) { + FLTFirebaseFirestoreExtension *firestoreExtension = firestoreInstanceCache[key]; + FIRFirestore *firestore = firestoreExtension.instance; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + [firestore terminateWithCompletion:^(NSError *error) { + [FLTFirebaseFirestoreUtils + destroyCachedInstanceForFirestore:firestore.app.name + databaseURL:firestoreExtension.databaseURL]; + firestoreTerminateInstanceCompletion(error); + }]; + }); + } + } +} + ++ (FIRFirestoreSource)FIRFirestoreSourceFromArguments:(NSDictionary *)arguments { + NSString *source = arguments[@"source"]; + if ([@"server" isEqualToString:source]) { + return FIRFirestoreSourceServer; + } + + if ([@"cache" isEqualToString:source]) { + return FIRFirestoreSourceCache; + } + + return FIRFirestoreSourceDefault; +} + ++ (NSArray *)ErrorCodeAndMessageFromNSError:(NSError *)error { + NSString *code = @"unknown"; + + if (error == nil) { + return @[ code, @"An unknown error has occurred." ]; + } + + NSString *message; + + switch (error.code) { + case FIRFirestoreErrorCodeAborted: + code = @"aborted"; + message = @"The operation was aborted, typically due to a concurrency issue like transaction " + @"aborts, etc."; + break; + case FIRFirestoreErrorCodeAlreadyExists: + code = @"already-exists"; + message = @"Some document that we attempted to create already exists."; + break; + case FIRFirestoreErrorCodeCancelled: + code = @"cancelled"; + message = @"The operation was cancelled (typically by the caller)."; + break; + case FIRFirestoreErrorCodeDataLoss: + code = @"data-loss"; + message = @"Unrecoverable data loss or corruption."; + break; + case FIRFirestoreErrorCodeDeadlineExceeded: + code = @"deadline-exceeded"; + message = @"Deadline expired before operation could complete. For operations that change the " + @"state of the system, this error may be returned even if the operation has " + @"completed successfully. For example, a successful response from a server could " + @"have been delayed long enough for the deadline to expire."; + break; + case FIRFirestoreErrorCodeFailedPrecondition: + code = @"failed-precondition"; + if ([error.localizedDescription containsString:@"index"]) { + message = error.localizedDescription; + } else { + message = @"Operation was rejected because the system is not in a state required for the " + @"operation's execution. If performing a query, ensure it has been indexed via " + @"the Firebase console."; + } + break; + case FIRFirestoreErrorCodeInternal: + code = @"internal"; + message = @"Internal errors. Means some invariants expected by underlying system has been " + @"broken. If you see one of these errors, something is very broken."; + break; + case FIRFirestoreErrorCodeInvalidArgument: + code = @"invalid-argument"; + message = @"Client specified an invalid argument. Note that this differs from " + @"failed-precondition. invalid-argument indicates arguments that are problematic " + @"regardless of the state of the system (e.g., an invalid field name)."; + break; + case FIRFirestoreErrorCodeNotFound: + code = @"not-found"; + message = @"Some requested document was not found."; + break; + case FIRFirestoreErrorCodeOutOfRange: + code = @"out-of-range"; + message = @"Operation was attempted past the valid range."; + break; + case FIRFirestoreErrorCodePermissionDenied: + code = @"permission-denied"; + message = @"The caller does not have permission to execute the specified operation."; + break; + case FIRFirestoreErrorCodeResourceExhausted: + code = @"resource-exhausted"; + message = @"Some resource has been exhausted, perhaps a per-user quota, or perhaps the " + @"entire file system is out of space."; + break; + case FIRFirestoreErrorCodeUnauthenticated: + code = @"unauthenticated"; + message = @"The request does not have valid authentication credentials for the operation."; + break; + case FIRFirestoreErrorCodeUnavailable: + code = @"unavailable"; + message = @"The service is currently unavailable. This is a most likely a transient " + @"condition and may be corrected by retrying with a backoff."; + break; + case FIRFirestoreErrorCodeUnimplemented: + code = @"unimplemented"; + message = @"Operation is not implemented or not supported/enabled."; + break; + case FIRFirestoreErrorCodeUnknown: + code = @"unknown"; + message = @"Unknown error or an error from a different error domain."; + break; + case FLTFirebaseFirestoreErrorCodePipelineParse: + code = @"parse-error"; + message = (error.localizedDescription.length > 0) ? error.localizedDescription + : @"An unknown error occurred."; + break; + default: + code = @"unknown"; + message = @"An unknown error occurred."; + break; + } + + return @[ code, message ]; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreWriter.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreWriter.m new file mode 100644 index 0000000..f2656d3 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirebaseFirestoreWriter.m @@ -0,0 +1,241 @@ +// Copyright 2020 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 FirebaseFirestore; +@import FirebaseCore; + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h" + +@implementation FLTFirebaseFirestoreWriter : FlutterStandardWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[NSDate class]]) { + [self writeByte:FirestoreDataTypeDateTime]; + NSDate *date = value; + NSTimeInterval time = date.timeIntervalSince1970; + SInt64 ms = (SInt64)(time * 1000.0); + [self writeBytes:&ms length:8]; + } else if ([value isKindOfClass:[FIRTimestamp class]]) { + FIRTimestamp *timestamp = value; + SInt64 seconds = timestamp.seconds; + int nanoseconds = timestamp.nanoseconds; + [self writeByte:FirestoreDataTypeTimestamp]; + [self writeBytes:(UInt8 *)&seconds length:8]; + [self writeBytes:(UInt8 *)&nanoseconds length:4]; + } else if ([value isKindOfClass:[FIRGeoPoint class]]) { + FIRGeoPoint *geoPoint = value; + Float64 latitude = geoPoint.latitude; + Float64 longitude = geoPoint.longitude; + [self writeByte:FirestoreDataTypeGeoPoint]; + [self writeAlignment:8]; + [self writeBytes:(UInt8 *)&latitude length:8]; + [self writeBytes:(UInt8 *)&longitude length:8]; + } else if ([value isKindOfClass:[FIRVectorValue class]]) { + FIRVectorValue *vector = value; + [self writeByte:FirestoreDataTypeVectorValue]; + [self writeValue:vector.array]; + } else if ([value isKindOfClass:[FIRDocumentReference class]]) { + FIRDocumentReference *document = value; + NSString *documentPath = [document path]; + NSString *appName = [FLTFirebasePlugin firebaseAppNameFromIosName:document.firestore.app.name]; + [self writeByte:FirestoreDataTypeDocumentReference]; + [self writeValue:appName]; + [self writeValue:documentPath]; + + FIRFirestore *firestore = document.firestore; + + FLTFirebaseFirestoreExtension *extension = + [FLTFirebaseFirestoreUtils getCachedInstanceForFirestore:firestore]; + [self writeValue:extension.databaseURL]; + + } else if ([value isKindOfClass:[FIRDocumentSnapshot class]]) { + [super writeValue:[self FIRDocumentSnapshot:value]]; + } else if ([value isKindOfClass:[FIRLoadBundleTaskProgress class]]) { + [super writeValue:[self FIRLoadBundleTaskProgress:value]]; + } else if ([value isKindOfClass:[FIRQuerySnapshot class]]) { + [super writeValue:[self FIRQuerySnapshot:value]]; + } else if ([value isKindOfClass:[FIRDocumentChange class]]) { + [super writeValue:[self FIRDocumentChange:value]]; + } else if ([value isKindOfClass:[FIRSnapshotMetadata class]]) { + [super writeValue:[self FIRSnapshotMetadata:value]]; + } else if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *number = (NSNumber *)value; + + // Infinity + if ([number isEqual:@(INFINITY)]) { + [self writeByte:FirestoreDataTypeInfinity]; + return; + } + + // -Infinity + if ([number isEqual:@(-INFINITY)]) { + [self writeByte:FirestoreDataTypeNegativeInfinity]; + return; + } + + // NaN + if ([[value description].lowercaseString isEqual:@"nan"]) { + [self writeByte:FirestoreDataTypeNaN]; + return; + } + + [super writeValue:value]; + } else if ([value isKindOfClass:[NSData class]]) { + NSData *blob = value; + [self writeByte:FirestoreDataTypeBlob]; + [self writeSize:(UInt32)blob.length]; + [self writeData:blob]; + } else { + [super writeValue:value]; + } +} + +- (NSDictionary *)FIRSnapshotMetadata:(FIRSnapshotMetadata *)snapshotMetadata { + return @{ + @"hasPendingWrites" : @(snapshotMetadata.hasPendingWrites), + @"isFromCache" : @(snapshotMetadata.isFromCache), + }; +} + +- (NSDictionary *)FIRDocumentChange:(FIRDocumentChange *)documentChange { + NSString *type; + + switch (documentChange.type) { + case FIRDocumentChangeTypeAdded: + type = @"DocumentChangeType.added"; + break; + case FIRDocumentChangeTypeModified: + type = @"DocumentChangeType.modified"; + break; + case FIRDocumentChangeTypeRemoved: + type = @"DocumentChangeType.removed"; + break; + } + + NSNumber *oldIndex; + NSNumber *newIndex; + + // Note the Firestore C++ SDK here returns a maxed UInt that is != NSUIntegerMax, so we make one + // ourselves so we can convert to -1 for Dart. + NSUInteger MAX_VAL = (NSUInteger)[@(-1) integerValue]; + + if (documentChange.newIndex == NSNotFound || documentChange.newIndex == 4294967295 || + documentChange.newIndex == MAX_VAL) { + newIndex = @([@(-1) intValue]); + } else { + newIndex = @([@(documentChange.newIndex) intValue]); + } + + if (documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4294967295 || + documentChange.oldIndex == MAX_VAL) { + oldIndex = @([@(-1) intValue]); + } else { + oldIndex = @([@(documentChange.oldIndex) intValue]); + } + + return @{ + @"type" : type, + @"data" : documentChange.document.data, + @"path" : documentChange.document.reference.path, + @"oldIndex" : oldIndex, + @"newIndex" : newIndex, + @"metadata" : documentChange.document.metadata, + }; +} + +- (FIRServerTimestampBehavior)toServerTimestampBehavior:(NSString *)serverTimestampBehavior { + if (serverTimestampBehavior == nil) { + return FIRServerTimestampBehaviorNone; + } + + if ([serverTimestampBehavior isEqualToString:@"estimate"]) { + return FIRServerTimestampBehaviorEstimate; + } else if ([serverTimestampBehavior isEqualToString:@"previous"]) { + return FIRServerTimestampBehaviorPrevious; + } else { + return FIRServerTimestampBehaviorNone; + } +} + +- (NSDictionary *)FIRDocumentSnapshot:(FIRDocumentSnapshot *)documentSnapshot { + if (documentSnapshot == nil) { + NSLog(@"Error: documentSnapshot is nil"); + return nil; + } + + NSNumber *documentSnapshotHash = @([documentSnapshot hash]); + NSString *timestampBehaviorString = + [FLTFirebaseFirestorePlugin.serverTimestampMap objectForKey:documentSnapshotHash]; + + FIRServerTimestampBehavior serverTimestampBehavior = + [self toServerTimestampBehavior:timestampBehaviorString]; + + [FLTFirebaseFirestorePlugin.serverTimestampMap removeObjectForKey:documentSnapshotHash]; + + return @{ + @"path" : documentSnapshot.reference.path, + @"data" : documentSnapshot.exists + ? (id)[documentSnapshot dataWithServerTimestampBehavior:serverTimestampBehavior] + : [NSNull null], + @"metadata" : documentSnapshot.metadata, + }; +} +- (NSDictionary *)FIRLoadBundleTaskProgress:(FIRLoadBundleTaskProgress *)progress { + NSString *state; + + switch (progress.state) { + case FIRLoadBundleTaskStateError: + state = @"error"; + break; + case FIRLoadBundleTaskStateSuccess: + state = @"success"; + break; + case FIRLoadBundleTaskStateInProgress: + state = @"running"; + break; + } + return @{ + @"bytesLoaded" : @(progress.bytesLoaded), + @"documentsLoaded" : @(progress.documentsLoaded), + @"totalBytes" : @(progress.totalBytes), + @"totalDocuments" : @(progress.totalDocuments), + @"taskState" : state, + }; +} + +- (NSDictionary *)FIRQuerySnapshot:(FIRQuerySnapshot *)querySnapshot { + if (querySnapshot == nil) { + NSLog(@"Error: querySnapshot is nil"); + return nil; + } + + NSNumber *querySnapshotHash = @([querySnapshot hash]); + + NSMutableArray *paths = [NSMutableArray array]; + NSMutableArray *documents = [NSMutableArray array]; + NSMutableArray *metadatas = [NSMutableArray array]; + NSString *timestampBehaviorString = + [FLTFirebaseFirestorePlugin.serverTimestampMap objectForKey:querySnapshotHash]; + + FIRServerTimestampBehavior serverTimestampBehavior = + [self toServerTimestampBehavior:timestampBehaviorString]; + + [FLTFirebaseFirestorePlugin.serverTimestampMap removeObjectForKey:querySnapshotHash]; + + for (FIRDocumentSnapshot *document in querySnapshot.documents) { + [paths addObject:document.reference.path]; + [documents addObject:[document dataWithServerTimestampBehavior:serverTimestampBehavior]]; + [metadatas addObject:document.metadata]; + } + + return @{ + @"paths" : paths, + @"documentChanges" : querySnapshot.documentChanges, + @"documents" : documents, + @"metadatas" : metadatas, + @"metadata" : querySnapshot.metadata, + }; +} +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTFirestoreClientLanguage.mm b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirestoreClientLanguage.mm new file mode 100644 index 0000000..399944e --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTFirestoreClientLanguage.mm @@ -0,0 +1,33 @@ +// 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 +#import + +namespace firebase { +namespace firestore { +namespace api { + +class Firestore { + public: + static void SetClientLanguage(std::string language_token); +}; + +} // namespace api +} // namespace firestore +} // namespace firebase + +@interface FLTFirestoreClientLanguage : NSObject ++ (void)setClientLanguage:(NSString *)language; +@end + +@implementation FLTFirestoreClientLanguage ++ (void)setClientLanguage:(NSString *)language { + if (language == nil) { + return; + } + std::string token = std::string([language UTF8String]); + firebase::firestore::api::Firestore::SetClientLanguage(token); +} +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTLoadBundleStreamHandler.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTLoadBundleStreamHandler.m new file mode 100644 index 0000000..2aabe2f --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTLoadBundleStreamHandler.m @@ -0,0 +1,80 @@ +// Copyright 2022, 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. + +// +// FLTLoadBundleStreamHandler.m +// cloud_firestore +// +// Created by Russell Wheatley on 05/05/2021. +// + +@import FirebaseFirestore; +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h" + +@interface FLTLoadBundleStreamHandler () +@property(readwrite, strong) FIRLoadBundleTask *task; +@end + +@implementation FLTLoadBundleStreamHandler + +- (nonnull instancetype)initWithFirestore:(nonnull FIRFirestore *)firestore + bundle:(FlutterStandardTypedData *)bundle { + self = [super init]; + if (self) { + _firestore = firestore; + _bundle = bundle; + } + return self; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + // use completion handler to inform user of platform error. + self.task = [_firestore + loadBundle:_bundle.data + completion:^(FIRLoadBundleTaskProgress *_Nullable snapshot, NSError *_Nullable error) { + if (error != nil) { + NSArray *codeAndMessage = + [FLTFirebaseFirestoreUtils ErrorCodeAndMessageFromNSError:error]; + NSString *code = codeAndMessage[0]; + NSString *message = codeAndMessage[1]; + NSDictionary *details = @{ + @"code" : code, + @"message" : message, + }; + + dispatch_async(dispatch_get_main_queue(), ^{ + events([FLTFirebasePlugin createFlutterErrorFromCode:code + message:message + optionalDetails:details + andOptionalNSError:error]); + }); + } + }]; + // use addObserver to update user with snapshot progress + [self.task addObserver:^(FIRLoadBundleTaskProgress *_Nullable progress) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (progress.state != FIRLoadBundleTaskStateError) { + events(progress); + } + }); + }]; + + return nil; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + [self.task removeAllObservers]; + self.task = nil; + + return nil; +} +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTPipelineParser.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTPipelineParser.m new file mode 100644 index 0000000..c0ca562 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTPipelineParser.m @@ -0,0 +1,1541 @@ +/* + * Copyright 2026, 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. + */ + +#import "include/cloud_firestore/Private/FLTPipelineParser.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" + +#if TARGET_OS_OSX +#import +#import "FirebaseFirestoreInternal/FIRPipelineBridge.h" +#else +@import FirebaseFirestore; +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") +#import "FirebaseFirestoreInternal/FIRPipelineBridge.h" +#elif __has_include("FIRPipelineBridge.h") +#import "FIRPipelineBridge.h" +#endif +#endif + +#if __has_include("FirebaseFirestoreInternal/FIRVectorValue.h") +#import "FirebaseFirestoreInternal/FIRVectorValue.h" +#elif __has_include() +#import +#endif + +#import + +static NSString *const kPipelineNotAvailable = + @"Pipeline API is not available. Firestore Pipelines require Firebase iOS SDK with pipeline " + "support."; + +static NSError *pipelineUnavailableError(void) { + return [NSError errorWithDomain:@"FLTFirebaseFirestore" + code:FLTFirebaseFirestoreErrorCodePipelineParse + userInfo:@{NSLocalizedDescriptionKey : kPipelineNotAvailable}]; +} + +#if TARGET_OS_OSX +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") +#define FLT_PIPELINE_AVAILABLE 1 +#endif +#else +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") || \ + __has_include("FIRPipelineBridge.h") +#define FLT_PIPELINE_AVAILABLE 1 +#endif +#endif + +#if FLT_PIPELINE_AVAILABLE + +// Firebase iOS SDK versions differ: some expose initWithName:Args:Options:, others +// initWithName:Args:. +@interface FIRFunctionExprBridge (FLTSDKCompat) +- (instancetype)initWithName:(NSString *)name + Args:(NSArray *)args + Options:(NSDictionary *)options; +- (instancetype)initWithName:(NSString *)name Args:(NSArray *)args; +@end + +static FIRFunctionExprBridge *FLTNewFunctionExprBridge(NSString *name, + NSArray *args) { + FIRFunctionExprBridge *obj = [FIRFunctionExprBridge alloc]; + if ([obj respondsToSelector:@selector(initWithName:Args:Options:)]) { + return [obj initWithName:name Args:args Options:nil]; + } + return [obj initWithName:name Args:args]; +} + +static NSError *parseError(NSString *message) { + return [NSError errorWithDomain:@"FLTFirebaseFirestore" + code:FLTFirebaseFirestoreErrorCodePipelineParse + userInfo:@{NSLocalizedDescriptionKey : message}]; +} + +@interface FLTPipelineExpressionParser : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +- (instancetype)initWithFirestore:(FIRFirestore *)firestore; +- (FIRExprBridge *)parseExpression:(NSDictionary *)map error:(NSError **)error; +- (FIRExprBridge *)parseBooleanExpression:(NSDictionary *)map + error:(NSError **)error; +- (FIRExprBridge *)rightExprFromValue:(id)value error:(NSError **)error; +@end + +@implementation FLTPipelineExpressionParser + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore { + self = [super init]; + if (self) { + _firestore = firestore; + } + return self; +} + +- (FIRExprBridge *)parseExpression:(NSDictionary *)map error:(NSError **)error { + NSString *name = map[@"name"]; + if (!name) { + NSDictionary *args = map[@"args"]; + if ([args isKindOfClass:[NSDictionary class]] && args[@"field"]) { + return [[FIRFieldBridge alloc] initWithName:args[@"field"]]; + } + if (error) *error = parseError(@"Expression must have a 'name' field"); + return nil; + } + + NSDictionary *args = map[@"args"]; + if (![args isKindOfClass:[NSDictionary class]]) args = @{}; + + if ([name isEqualToString:@"field"]) { + NSString *field = args[@"field"]; + if (!field) { + if (error) *error = parseError(@"Field expression requires 'field' argument"); + return nil; + } + return [[FIRFieldBridge alloc] initWithName:field]; + } + + if ([name isEqualToString:@"constant"]) { + id value = args[@"value"]; + if (value == nil) { + if (error) *error = parseError(@"Constant requires 'value' argument"); + return nil; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSString *path = ((NSDictionary *)value)[@"path"]; + if ([path isKindOfClass:[NSString class]] && self.firestore) { + FIRDocumentReference *docRef = [self.firestore documentWithPath:path]; + return [[FIRConstantBridge alloc] init:docRef]; + } + } + return [[FIRConstantBridge alloc] init:value]; + } + + if ([name isEqualToString:@"alias"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Alias requires 'expression'"); + return nil; + } + // No explicit AliasedExpression type in ObjC; aliases are dict keys when building stages. + // Parse and return the inner expression; the caller uses args[@"alias"] as the dict key. + return [self parseExpression:exprMap error:error]; + } + + if ([name isEqualToString:@"null"]) { + return [[FIRConstantBridge alloc] init:[NSNull null]]; + } + + if ([name isEqualToString:@"document_id_from_ref"]) { + NSString *path = args[@"doc_ref"]; + if (![path isKindOfClass:[NSString class]] || path.length == 0) { + if (error) *error = parseError(@"document_id_from_ref requires doc_ref path"); + return nil; + } + if (!self.firestore) { + if (error) *error = parseError(@"document_id_from_ref requires firestore"); + return nil; + } + FIRDocumentReference *docRef = [self.firestore documentWithPath:path]; + FIRExprBridge *refExpr = [[FIRConstantBridge alloc] init:docRef]; + return FLTNewFunctionExprBridge(@"document_id", @[ refExpr ]); + } + + // Swift asBoolean() is a type coercion, not a pipeline function named "as_boolean". + // Dart still sends as_boolean + expression; unwrap to the inner FIRExprBridge. + if ([name isEqualToString:@"as_boolean"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"as_boolean requires expression"); + return nil; + } + return [self parseExpression:(NSDictionary *)exprMap error:error]; + } + + if ([name isEqualToString:@"document_matches"]) { + NSString *query = args[@"query"]; + if (![query isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"document_matches requires query"); + return nil; + } + FIRExprBridge *queryExpr = [[FIRConstantBridge alloc] init:query]; + return FLTNewFunctionExprBridge(@"document_matches", @[ queryExpr ]); + } + + // Map Dart names to iOS SDK names where they differ + NSString *sdkName = name; + if ([name isEqualToString:@"bit_xor"]) sdkName = @"xor"; + if ([name isEqualToString:@"modulo"]) sdkName = @"mod"; + + // ------------------------------------------------------------------------- + // Binary expressions (left + right): comparisons, arithmetic, bitwise + // ------------------------------------------------------------------------- + static NSArray *binaryNames = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + binaryNames = @[ + @"equal", @"not_equal", @"greater_than", @"greater_than_or_equal", @"less_than", + @"less_than_or_equal", @"add", @"subtract", @"multiply", @"divide", @"mod", @"bit_and", + @"bit_or", @"bit_left_shift", @"bit_right_shift" + ]; + }); + if ([binaryNames containsObject:sdkName] || [name isEqualToString:@"bit_xor"]) { + id leftMap = args[@"left"]; + id rightMap = args[@"right"]; + if (![leftMap isKindOfClass:[NSDictionary class]] || + ![rightMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires left and right expressions", name]); + return nil; + } + FIRExprBridge *left = [self parseExpression:leftMap error:error]; + FIRExprBridge *right = [self parseExpression:rightMap error:error]; + if (!left || !right) return nil; + return FLTNewFunctionExprBridge(sdkName, @[ left, right ]); + } + + // ------------------------------------------------------------------------- + // Unary expressions (single expression): exists, is_error, is_absent, not + // (as_boolean is handled above — unwrap only, not a pipeline function.) + // ------------------------------------------------------------------------- + NSArray *unaryNames = @[ @"exists", @"is_error", @"is_absent", @"not" ]; + if ([unaryNames containsObject:name]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires expression", name]); + return nil; + } + FIRExprBridge *expr = [name isEqualToString:@"not"] + ? [self parseBooleanExpression:exprMap error:error] + : [self parseExpression:exprMap error:error]; + if (!expr) return nil; + return FLTNewFunctionExprBridge(name, @[ expr ]); + } + + // ------------------------------------------------------------------------- + // Unary with optional SDK name mapping: length, to_lower, to_upper, trim, + // abs, array_length, array_reverse, bit_not, document_id, collection_id + // ------------------------------------------------------------------------- + NSArray *unaryWithSdkName = @[ + @"length", @"to_lower_case", @"to_upper_case", @"trim", @"abs", @"array_length", + @"array_reverse", @"bit_not", @"document_id", @"collection_id" + ]; + if ([unaryWithSdkName containsObject:name]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires expression", name]); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + if (!expr) return nil; + NSString *unarySdkName = name; + if ([name isEqualToString:@"to_lower_case"]) unarySdkName = @"to_lower"; + if ([name isEqualToString:@"to_upper_case"]) unarySdkName = @"to_upper"; + return FLTNewFunctionExprBridge(unarySdkName, @[ expr ]); + } + + // ------------------------------------------------------------------------- + // N-ary logical (expressions array): and, or, xor + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"and"] || [name isEqualToString:@"or"] || + [name isEqualToString:@"xor"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires at least one expression", name]); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseBooleanExpression:em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires at least one expression", name]); + return nil; + } + return FLTNewFunctionExprBridge(name, all); + } + + // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"equal_any"] || [name isEqualToString:@"not_equal_any"]) { + id valueMap = args[@"value"]; + NSArray *valuesMaps = args[@"values"]; + if (![valueMap isKindOfClass:[NSDictionary class]] || + ![valuesMaps isKindOfClass:[NSArray class]] || valuesMaps.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires value and non-empty values", name]); + return nil; + } + FIRExprBridge *valueExpr = [self parseExpression:valueMap error:error]; + if (!valueExpr) return nil; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id vm in valuesMaps) { + if (![vm isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *ve = [self parseExpression:vm error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) + *error = parseError([NSString stringWithFormat:@"%@ requires at least one value", name]); + return nil; + } + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(name, @[ valueExpr, valuesArrayExpr ]); + } + + // ------------------------------------------------------------------------- + // array + element: array_contains + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_contains"]) { + id arrayMap = args[@"array"]; + id elementMap = args[@"element"]; + if (![arrayMap isKindOfClass:[NSDictionary class]] || + ![elementMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_contains requires array and element"); + return nil; + } + FIRExprBridge *arrayExpr = [self parseExpression:arrayMap error:error]; + FIRExprBridge *elementExpr = [self parseExpression:elementMap error:error]; + if (!arrayExpr || !elementExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, elementExpr ]); + } + + // ------------------------------------------------------------------------- + // array + values[]: array_contains_all, array_contains_any + // SDK expects: array_contains_any(field, array(val1, val2, ...)) — two args. + // Reuse the "array" expression parser to build the values array. + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_contains_all"] || + [name isEqualToString:@"array_contains_any"]) { + id arrayMap = args[@"array"]; + if (![arrayMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires array", name]); + return nil; + } + FIRExprBridge *arrayExpr = [self parseExpression:arrayMap error:error]; + if (!arrayExpr) return nil; + + NSArray *valuesMaps = args[@"values"]; + if (![valuesMaps isKindOfClass:[NSArray class]]) valuesMaps = args[@"elements"]; + BOOL hasValues = [valuesMaps isKindOfClass:[NSArray class]] && valuesMaps.count > 0; + + if (hasValues) { + NSDictionary *arrayExprMap = @{@"name" : @"array", @"args" : @{@"elements" : valuesMaps}}; + FIRExprBridge *valuesArrayExpr = [self parseExpression:arrayExprMap error:error]; + if (!valuesArrayExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, valuesArrayExpr ]); + } + + if ([name isEqualToString:@"array_contains_all"]) { + id arrayExpressionMap = args[@"array_expression"]; + if ([arrayExpressionMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *requiredArrayExpr = [self parseExpression:arrayExpressionMap error:error]; + if (!requiredArrayExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, requiredArrayExpr ]); + } + } + + if (error) + *error = parseError([NSString + stringWithFormat: + @"%@ requires array and values/elements, or array_contains_all with array_expression", + name]); + return nil; + } + + // ------------------------------------------------------------------------- + // expressions[]: concat (SDK: concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"concat"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"concat requires non-empty expressions"); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) *error = parseError(@"concat requires at least one expression"); + return nil; + } + return FLTNewFunctionExprBridge(@"concat", all); + } + + // ------------------------------------------------------------------------- + // expression + start + end: substring (SDK: substring) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"substring"]) { + id exprMap = args[@"expression"]; + id startMap = args[@"start"]; + id endMap = args[@"end"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![startMap isKindOfClass:[NSDictionary class]] || + ![endMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"substring requires expression, start, and end"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *start = [self parseExpression:startMap error:error]; + FIRExprBridge *end = [self parseExpression:endMap error:error]; + if (!expr || !start || !end) return nil; + return FLTNewFunctionExprBridge(@"substring", @[ expr, start, end ]); + } + + // ------------------------------------------------------------------------- + // expression + find + replacement: replace (SDK: string_replace) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"replace"]) { + id exprMap = args[@"expression"]; + id findMap = args[@"find"]; + id replacementMap = args[@"replacement"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![findMap isKindOfClass:[NSDictionary class]] || + ![replacementMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"replace requires expression, find, and replacement"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *find = [self parseExpression:findMap error:error]; + FIRExprBridge *replacement = [self parseExpression:replacementMap error:error]; + if (!expr || !find || !replacement) return nil; + return FLTNewFunctionExprBridge(@"string_replace", @[ expr, find, replacement ]); + } + + // ------------------------------------------------------------------------- + // expression + delimiter: split, join (SDK: split, join) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"split"] || [name isEqualToString:@"join"]) { + id exprMap = args[@"expression"]; + id delimiterMap = args[@"delimiter"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![delimiterMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires expression and delimiter", name]); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *delimiter = [self parseExpression:delimiterMap error:error]; + if (!expr || !delimiter) return nil; + return FLTNewFunctionExprBridge(name, @[ expr, delimiter ]); + } + + // ------------------------------------------------------------------------- + // first + second: array_concat (SDK: array_concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_concat"]) { + id firstMap = args[@"first"]; + id secondMap = args[@"second"]; + if (![firstMap isKindOfClass:[NSDictionary class]] || + ![secondMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_concat requires first and second"); + return nil; + } + FIRExprBridge *first = [self parseExpression:firstMap error:error]; + FIRExprBridge *second = [self parseExpression:secondMap error:error]; + if (!first || !second) return nil; + return FLTNewFunctionExprBridge(@"array_concat", @[ first, second ]); + } + + // ------------------------------------------------------------------------- + // arrays[]: array_concat_multiple (SDK: array_concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_concat_multiple"]) { + NSArray *arraysMaps = args[@"arrays"]; + if (![arraysMaps isKindOfClass:[NSArray class]] || arraysMaps.count == 0) { + if (error) *error = parseError(@"array_concat_multiple requires non-empty arrays"); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id am in arraysMaps) { + if (![am isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:am error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) *error = parseError(@"array_concat_multiple requires at least one array"); + return nil; + } + return FLTNewFunctionExprBridge(@"array_concat", all); + } + + // ------------------------------------------------------------------------- + // expression + offset (+ optional length): array_slice + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_slice"]) { + id exprMap = args[@"expression"]; + id offsetMap = args[@"offset"]; + id lengthMap = args[@"length"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![offsetMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_slice requires expression and offset"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *offset = [self parseExpression:offsetMap error:error]; + if (!expr || !offset) return nil; + NSMutableArray *sliceArgs = + [NSMutableArray arrayWithObjects:expr, offset, nil]; + if ([lengthMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *length = [self parseExpression:lengthMap error:error]; + if (!length) return nil; + [sliceArgs addObject:length]; + } + return FLTNewFunctionExprBridge(@"array_slice", sliceArgs); + } + + // ------------------------------------------------------------------------- + // expression + alias + filter: array_filter + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_filter"]) { + id exprMap = args[@"expression"]; + NSString *alias = args[@"alias"]; + id filterMap = args[@"filter"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || ![alias isKindOfClass:[NSString class]] || + ![filterMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_filter requires expression, alias, and filter"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *filter = [self parseBooleanExpression:filterMap error:error]; + if (!expr || !filter) return nil; + return FLTNewFunctionExprBridge(@"array_filter", + @[ expr, [[FIRConstantBridge alloc] init:alias], filter ]); + } + + // ------------------------------------------------------------------------- + // expression + aliases + transform: array_transform / array_transform_with_index + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_transform"] || + [name isEqualToString:@"array_transform_with_index"]) { + id exprMap = args[@"expression"]; + NSString *elementAlias = args[@"element_alias"]; + NSString *indexAlias = args[@"index_alias"]; + id transformMap = args[@"transform"]; + BOOL withIndex = [name isEqualToString:@"array_transform_with_index"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![elementAlias isKindOfClass:[NSString class]] || + (withIndex && ![indexAlias isKindOfClass:[NSString class]]) || + ![transformMap isKindOfClass:[NSDictionary class]]) { + if (error) { + NSString *message = + withIndex + ? @"array_transform_with_index requires expression, element_alias, index_alias, " + @"and transform" + : @"array_transform requires expression, element_alias, and transform"; + *error = parseError(message); + } + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *transform = [self parseExpression:transformMap error:error]; + if (!expr || !transform) return nil; + NSMutableArray *transformArgs = + [NSMutableArray arrayWithObjects:expr, [[FIRConstantBridge alloc] init:elementAlias], nil]; + if (withIndex) { + [transformArgs addObject:[[FIRConstantBridge alloc] init:indexAlias]]; + } + [transformArgs addObject:transform]; + return FLTNewFunctionExprBridge(name, transformArgs); + } + + // ------------------------------------------------------------------------- + // elements[]: array (construct) — Expression.array([...]) from Dart + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array"]) { + NSArray *elementsMaps = args[@"elements"]; + if (![elementsMaps isKindOfClass:[NSArray class]] || elementsMaps.count == 0) { + if (error) *error = parseError(@"array requires non-empty elements"); + return nil; + } + NSMutableArray *elementExprs = [NSMutableArray array]; + for (id em in elementsMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:em error:error]; + if (!e) return nil; + [elementExprs addObject:e]; + } + if (elementExprs.count == 0) { + if (error) *error = parseError(@"array requires at least one element"); + return nil; + } + return FLTNewFunctionExprBridge(@"array", elementExprs); + } + + // ------------------------------------------------------------------------- + // data: map (construct) — Expression.map({ "k": expr, ... }) from Dart + // SDK expects Args as alternating key (constant), value (expression). + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"map"]) { + NSDictionary *dataMap = args[@"data"]; + if (![dataMap isKindOfClass:[NSDictionary class]] || dataMap.count == 0) { + if (error) *error = parseError(@"map requires non-empty data"); + return nil; + } + NSMutableArray *mapArgs = [NSMutableArray array]; + for (NSString *key in dataMap) { + id valueMap = dataMap[key]; + if (![valueMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *keyExpr = [[FIRConstantBridge alloc] init:key]; + FIRExprBridge *valueExpr = [self parseExpression:valueMap error:error]; + if (!valueExpr) return nil; + [mapArgs addObject:keyExpr]; + [mapArgs addObject:valueExpr]; + } + if (mapArgs.count == 0) { + if (error) *error = parseError(@"map requires at least one key-value pair"); + return nil; + } + return FLTNewFunctionExprBridge(@"map", mapArgs); + } + + // ------------------------------------------------------------------------- + // map + key: map_get (SDK: map_get) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"map_get"]) { + id mapMap = args[@"map"]; + id keyMap = args[@"key"]; + if (![mapMap isKindOfClass:[NSDictionary class]] || + ![keyMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"map_get requires map and key"); + return nil; + } + FIRExprBridge *mapExpr = [self parseExpression:mapMap error:error]; + FIRExprBridge *keyExpr = [self parseExpression:keyMap error:error]; + if (!mapExpr || !keyExpr) return nil; + return FLTNewFunctionExprBridge(@"map_get", @[ mapExpr, keyExpr ]); + } + + // ------------------------------------------------------------------------- + // expression + else: if_absent (SDK: if_absent) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"if_absent"]) { + id exprMap = args[@"expression"]; + id elseMap = args[@"else"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![elseMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"if_absent requires expression and else"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *elseExpr = [self parseExpression:elseMap error:error]; + if (!expr || !elseExpr) return nil; + return FLTNewFunctionExprBridge(@"if_absent", @[ expr, elseExpr ]); + } + + // ------------------------------------------------------------------------- + // expression + catch: if_error (SDK: if_error) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"if_error"]) { + id exprMap = args[@"expression"]; + id catchMap = args[@"catch"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![catchMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"if_error requires expression and catch"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *catchExpr = [self parseExpression:catchMap error:error]; + if (!expr || !catchExpr) return nil; + return FLTNewFunctionExprBridge(@"if_error", @[ expr, catchExpr ]); + } + + // ------------------------------------------------------------------------- + // condition + then + else: conditional (SDK: conditional) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"conditional"]) { + id conditionMap = args[@"condition"]; + id thenMap = args[@"then"]; + id elseMap = args[@"else"]; + if (![conditionMap isKindOfClass:[NSDictionary class]] || + ![thenMap isKindOfClass:[NSDictionary class]] || + ![elseMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"conditional requires condition, then, and else"); + return nil; + } + FIRExprBridge *condition = [self parseBooleanExpression:conditionMap error:error]; + FIRExprBridge *thenExpr = [self parseExpression:thenMap error:error]; + FIRExprBridge *elseExpr = [self parseExpression:elseMap error:error]; + if (!condition || !thenExpr || !elseExpr) return nil; + return FLTNewFunctionExprBridge(@"conditional", @[ condition, thenExpr, elseExpr ]); + } + + // ------------------------------------------------------------------------- + // timestamp + amount + unit: timestamp_add, timestamp_subtract (SDK: Args ts, amount, unit) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"timestamp_add"] || [name isEqualToString:@"timestamp_subtract"]) { + id timestampMap = args[@"timestamp"]; + id unitVal = args[@"unit"]; + id amountMap = args[@"amount"]; + if (![timestampMap isKindOfClass:[NSDictionary class]] || !unitVal || + ![amountMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = parseError( + [NSString stringWithFormat:@"%@ requires timestamp, unit, and amount", name]); + return nil; + } + FIRExprBridge *timestampExpr = [self parseExpression:timestampMap error:error]; + FIRExprBridge *amountExpr = [self parseExpression:amountMap error:error]; + if (!timestampExpr || !amountExpr) return nil; + FIRExprBridge *unitExpr = [[FIRConstantBridge alloc] init:unitVal]; + return FLTNewFunctionExprBridge(name, @[ timestampExpr, unitExpr, amountExpr ]); + } + + // ------------------------------------------------------------------------- + // No args: current_timestamp (SDK: current_timestamp with empty Args) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"current_timestamp"]) { + return FLTNewFunctionExprBridge(@"current_timestamp", @[]); + } + + // ------------------------------------------------------------------------- + // timestamp + unit: timestamp_truncate (SDK: timestamp_trunc) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"timestamp_truncate"]) { + id timestampMap = args[@"timestamp"]; + id unitVal = args[@"unit"]; + if (![timestampMap isKindOfClass:[NSDictionary class]] || !unitVal) { + if (error) *error = parseError(@"timestamp_truncate requires timestamp and unit"); + return nil; + } + FIRExprBridge *timestampExpr = [self parseExpression:timestampMap error:error]; + if (!timestampExpr) return nil; + FIRExprBridge *unitExpr = [[FIRConstantBridge alloc] init:unitVal]; + return FLTNewFunctionExprBridge(@"timestamp_trunc", @[ timestampExpr, unitExpr ]); + } + + // ------------------------------------------------------------------------- + // PipelineFilter (name "filter"): operator-based (and/or) or field-based + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"filter"]) { + return [self parseFilterExpressionWithArgs:args error:error]; + } + + if (error) *error = parseError([NSString stringWithFormat:@"Unsupported expression: %@", name]); + return nil; +} + +- (FIRExprBridge *)rightExprFromValue:(id)value error:(NSError **)error { + if ([value isKindOfClass:[NSDictionary class]]) { + return [self parseExpression:(NSDictionary *)value error:error]; + } + return [[FIRConstantBridge alloc] init:value]; +} + +- (FIRExprBridge *)parseFilterExpressionWithArgs:(NSDictionary *)args error:(NSError **)error { + // Operator-based: and/or with expressions array (from PipelineFilter.and / .or) + NSString *operator= args[@"operator"]; + NSArray *exprMaps = args[@"expressions"]; + if ([operator isKindOfClass:[NSString class]] && [exprMaps isKindOfClass:[NSArray class]]) { + if (exprMaps.count == 0) { + if (error) *error = parseError(@"filter with operator requires at least one expression"); + return nil; + } + if (exprMaps.count == 1) { + id em = exprMaps[0]; + if (![em isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"filter expressions must be maps"); + return nil; + } + return [self parseBooleanExpression:(NSDictionary *)em error:error]; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseBooleanExpression:(NSDictionary *)em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) return nil; + return FLTNewFunctionExprBridge(operator, all); + } + + // Field-based: field + isEqualTo, isGreaterThan, etc. + NSString *fieldName = args[@"field"]; + if (![fieldName isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"filter requires operator+expressions or field"); + return nil; + } + FIRExprBridge *fieldExpr = [[FIRFieldBridge alloc] initWithName:fieldName]; + + static NSArray *filterComparisonKeys = nil; + static dispatch_once_t filterOnce; + dispatch_once(&filterOnce, ^{ + filterComparisonKeys = @[ + @"isEqualTo", @"isNotEqualTo", @"isGreaterThan", @"isGreaterThanOrEqualTo", @"isLessThan", + @"isLessThanOrEqualTo", @"arrayContains", @"arrayContainsAny", @"whereIn", @"whereNotIn", + @"isNull", @"isNotNull" + ]; + }); + for (NSString *key in filterComparisonKeys) { + id value = args[key]; + if (value == nil) continue; + + if ([key isEqualToString:@"isEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isNotEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"not_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isGreaterThan"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"greater_than", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isGreaterThanOrEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"greater_than_or_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isLessThan"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"less_than", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isLessThanOrEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"less_than_or_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"arrayContains"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"array_contains", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"arrayContainsAny"] || [key isEqualToString:@"whereIn"]) { + NSArray *valuesList = [value isKindOfClass:[NSArray class]] ? value : @[]; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id v in valuesList) { + FIRExprBridge *ve = [self rightExprFromValue:v error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) *error = parseError(@"arrayContainsAny/whereIn requires non-empty list"); + return nil; + } + // SDK expects (value, array) not (value, v1, v2, ...); wrap in "array" expr. + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(@"equal_any", @[ fieldExpr, valuesArrayExpr ]); + } + if ([key isEqualToString:@"whereNotIn"]) { + NSArray *valuesList = [value isKindOfClass:[NSArray class]] ? value : @[]; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id v in valuesList) { + FIRExprBridge *ve = [self rightExprFromValue:v error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) *error = parseError(@"whereNotIn requires non-empty list"); + return nil; + } + // SDK expects (value, array) not (value, v1, v2, ...); wrap in "array" expr. + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(@"not_equal_any", @[ fieldExpr, valuesArrayExpr ]); + } + if ([key isEqualToString:@"isNull"]) { + FIRExprBridge *right = [[FIRConstantBridge alloc] init:[NSNull null]]; + return FLTNewFunctionExprBridge(@"equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isNotNull"]) { + FIRExprBridge *right = [[FIRConstantBridge alloc] init:[NSNull null]]; + return FLTNewFunctionExprBridge(@"not_equal", @[ fieldExpr, right ]); + } + } + + if (error) + *error = + parseError(@"filter requires at least one comparison (isEqualTo, isGreaterThan, etc.)"); + return nil; +} + +- (FIRExprBridge *)parseBooleanExpression:(NSDictionary *)map + error:(NSError **)error { + return [self parseExpression:map error:error]; +} + +@end + +@implementation FLTPipelineParser + +/// Returns the key (alias or field name) for an expression map in select/distinct stages. +/// Uses args.alias if present; otherwise for "field" expressions uses args.field. Returns nil if +/// no key can be determined (caller should error). ++ (NSString *)keyForExpressionMap:(NSDictionary *)em error:(NSError **)error { + NSString *alias = [em valueForKeyPath:@"args.alias"]; + if ([alias isKindOfClass:[NSString class]] && alias.length > 0) { + return alias; + } + if ([em[@"name"] isEqualToString:@"field"]) { + NSString *field = [em valueForKeyPath:@"args.field"]; + if ([field isKindOfClass:[NSString class]]) return field; + if (error) *error = parseError(@"field expression must have args.field"); + return nil; + } + if (error) *error = parseError(@"expression must have alias or be a field reference"); + return nil; +} + ++ (NSDictionary *) + parseSearchFieldsWithExpressionMaps:(NSArray *> *)exprMaps + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + NSError *parseErr = nil; + + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + + NSString *key = [self keyForExpressionMap:em error:error]; + if (![key isKindOfClass:[NSString class]] || key.length == 0) return nil; + fields[key] = expr; + } + + return fields; +} + ++ (FIRStageBridge *)parseSearchStageWithArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSString *queryType = args[@"query_type"]; + id query = args[@"query"]; + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + NSError *parseErr = nil; + + if ([queryType isEqualToString:@"string"]) { + if (![query isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"search query_type 'string' requires string query"); + return nil; + } + FIRExprBridge *queryExpr = [[FIRConstantBridge alloc] init:query]; + options[@"query"] = FLTNewFunctionExprBridge(@"document_matches", @[ queryExpr ]); + } else if ([queryType isEqualToString:@"expression"]) { + if (![query isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"search query_type 'expression' requires expression query"); + return nil; + } + FIRExprBridge *queryExpr = [exprParser parseBooleanExpression:query error:&parseErr]; + if (!queryExpr) { + if (error) *error = parseErr; + return nil; + } + options[@"query"] = queryExpr; + } else { + if (error) *error = parseError(@"search requires query_type to be 'string' or 'expression'"); + return nil; + } + + NSNumber *limit = [args[@"limit"] isKindOfClass:[NSNumber class]] ? args[@"limit"] : nil; + if (limit) options[@"limit"] = [[FIRConstantBridge alloc] init:limit]; + + NSNumber *offset = [args[@"offset"] isKindOfClass:[NSNumber class]] ? args[@"offset"] : nil; + if (offset) options[@"offset"] = [[FIRConstantBridge alloc] init:offset]; + + NSNumber *retrievalDepth = + [args[@"retrieval_depth"] isKindOfClass:[NSNumber class]] ? args[@"retrieval_depth"] : nil; + if (retrievalDepth) { + options[@"retrieval_depth"] = [[FIRConstantBridge alloc] init:retrievalDepth]; + } + + NSString *languageCode = + [args[@"language_code"] isKindOfClass:[NSString class]] ? args[@"language_code"] : nil; + if (languageCode) { + options[@"language_code"] = [[FIRConstantBridge alloc] init:languageCode]; + } + + NSMutableArray *sort = [NSMutableArray array]; + NSArray *orderingMaps = args[@"sort"]; + if ([orderingMaps isKindOfClass:[NSArray class]]) { + for (id om in orderingMaps) { + if (![om isKindOfClass:[NSDictionary class]]) continue; + id exprMap = ((NSDictionary *)om)[@"expression"]; + NSString *dir = ((NSDictionary *)om)[@"order_direction"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *direction = [dir isEqualToString:@"asc"] ? @"ascending" : @"descending"; + [sort addObject:[[FIROrderingBridge alloc] initWithExpr:expr Direction:direction]]; + } + } + + NSDictionary *addFields = @{}; + NSArray *addFieldMaps = args[@"add_fields"]; + if ([addFieldMaps isKindOfClass:[NSArray class]] && addFieldMaps.count > 0) { + addFields = [self parseSearchFieldsWithExpressionMaps:addFieldMaps + exprParser:exprParser + error:error]; + if (!addFields) return nil; + } + + return [[FIRSearchStageBridge alloc] initWithOptions:options + addFields:addFields + select:@{} + sort:sort]; +} + ++ (NSArray *) + parseStagesWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + error:(NSError **)error { + FLTPipelineExpressionParser *exprParser = + [[FLTPipelineExpressionParser alloc] initWithFirestore:firestore]; + NSMutableArray *stageBridges = [NSMutableArray array]; + NSError *parseErr = nil; + + for (NSUInteger i = 0; i < stages.count; i++) { + NSDictionary *stageMap = stages[i]; + if (![stageMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Stage must be a map"); + return nil; + } + NSString *stageName = stageMap[@"stage"]; + if (![stageName isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"Stage must have a 'stage' field"); + return nil; + } + id argsObj = stageMap[@"args"]; + NSDictionary *args = [argsObj isKindOfClass:[NSDictionary class]] ? argsObj : @{}; + NSArray *argsArray = [argsObj isKindOfClass:[NSArray class]] ? argsObj : nil; + + FIRStageBridge *stage = nil; + + if (i == 0) { + if ([stageName isEqualToString:@"collection"]) { + NSString *path = args[@"path"]; + if (!path) { + if (error) *error = parseError(@"collection requires 'path'"); + return nil; + } + FIRCollectionReference *ref = [firestore collectionWithPath:path]; + stage = [[FIRCollectionSourceStageBridge alloc] initWithRef:ref + firestore:firestore + forceIndex:nil]; + } else if ([stageName isEqualToString:@"collection_group"]) { + NSString *path = args[@"path"]; + if (!path) { + if (error) *error = parseError(@"collection_group requires 'path'"); + return nil; + } + stage = [[FIRCollectionGroupSourceStageBridge alloc] initWithCollectionId:path + forceIndex:nil]; + } else if ([stageName isEqualToString:@"database"]) { + stage = [[FIRDatabaseSourceStageBridge alloc] init]; + } else if ([stageName isEqualToString:@"documents"]) { + NSArray *docMaps = argsArray; + if (!docMaps || docMaps.count == 0) { + if (error) *error = parseError(@"documents requires array of document refs"); + return nil; + } + NSMutableArray *refs = [NSMutableArray array]; + for (id docMap in docMaps) { + if (![docMap isKindOfClass:[NSDictionary class]]) continue; + NSString *path = ((NSDictionary *)docMap)[@"path"]; + if (path) [refs addObject:[firestore documentWithPath:path]]; + } + stage = [[FIRDocumentsSourceStageBridge alloc] initWithDocuments:refs firestore:firestore]; + } else { + if (error) + *error = parseError( + [NSString stringWithFormat:@"First stage must be collection, collection_group, " + @"documents, or database. Got: %@", + stageName]); + return nil; + } + } else { + if ([stageName isEqualToString:@"where"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"where requires expression"); + return nil; + } + FIRExprBridge *expr = [exprParser parseBooleanExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRWhereStageBridge alloc] initWithExpr:expr]; + } else if ([stageName isEqualToString:@"search"]) { + stage = [self parseSearchStageWithArgs:args exprParser:exprParser error:error]; + if (!stage) return nil; + } else if ([stageName isEqualToString:@"limit"]) { + NSNumber *limit = args[@"limit"]; + if (![limit isKindOfClass:[NSNumber class]]) { + if (error) *error = parseError(@"limit requires numeric limit"); + return nil; + } + stage = [[FIRLimitStageBridge alloc] initWithLimit:limit.intValue]; + } else if ([stageName isEqualToString:@"offset"]) { + NSNumber *offset = args[@"offset"]; + if (![offset isKindOfClass:[NSNumber class]]) { + if (error) *error = parseError(@"offset requires numeric offset"); + return nil; + } + stage = [[FIROffsetStageBridge alloc] initWithOffset:offset.intValue]; + } else if ([stageName isEqualToString:@"sort"]) { + NSArray *orderingMaps = args[@"orderings"]; + if (![orderingMaps isKindOfClass:[NSArray class]] || orderingMaps.count == 0) { + if (error) *error = parseError(@"sort requires at least one ordering"); + return nil; + } + NSMutableArray *orderings = [NSMutableArray array]; + for (id om in orderingMaps) { + if (![om isKindOfClass:[NSDictionary class]]) continue; + id exprMap = ((NSDictionary *)om)[@"expression"]; + NSString *dir = ((NSDictionary *)om)[@"order_direction"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *direction = [dir isEqualToString:@"asc"] ? @"ascending" : @"descending"; + FIROrderingBridge *ordering = [[FIROrderingBridge alloc] initWithExpr:expr + Direction:direction]; + [orderings addObject:ordering]; + } + if (orderings.count == 0) { + if (error) *error = parseError(@"sort requires at least one ordering"); + return nil; + } + stage = [[FIRSorStageBridge alloc] initWithOrderings:orderings]; + } else if ([stageName isEqualToString:@"select"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"select requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *key = [self keyForExpressionMap:em error:error]; + if (!key) return nil; + fields[key] = expr; + } + stage = [[FIRSelectStageBridge alloc] initWithSelections:fields]; + } else if ([stageName isEqualToString:@"add_fields"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"add_fields requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *alias = [em valueForKeyPath:@"args.alias"]; + if (!alias) { + if (error) *error = parseError(@"add_fields expressions must have alias"); + return nil; + } + fields[alias] = expr; + } + stage = [[FIRAddFieldsStageBridge alloc] initWithFields:fields]; + } else if ([stageName isEqualToString:@"remove_fields"]) { + NSArray *paths = args[@"field_paths"]; + if (![paths isKindOfClass:[NSArray class]] || paths.count == 0) { + if (error) *error = parseError(@"remove_fields requires field_paths"); + return nil; + } + stage = [[FIRRemoveFieldsStageBridge alloc] initWithFields:paths]; + } else if ([stageName isEqualToString:@"distinct"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"distinct requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *key = [self keyForExpressionMap:em error:error]; + if (!key) return nil; + fields[key] = expr; + } + stage = [[FIRDistinctStageBridge alloc] initWithGroups:fields]; + } else if ([stageName isEqualToString:@"replace_with"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"replace_with requires expression"); + return nil; + } + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRReplaceWithStageBridge alloc] initWithExpr:expr]; + } else if ([stageName isEqualToString:@"union"]) { + NSArray *nestedStages = args[@"pipeline"]; + if (![nestedStages isKindOfClass:[NSArray class]] || nestedStages.count == 0) { + if (error) *error = parseError(@"union requires non-empty pipeline"); + return nil; + } + id otherPipeline = [self buildPipelineWithFirestore:firestore + stages:nestedStages + error:&parseErr]; + if (!otherPipeline) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRUnionStageBridge alloc] initWithOther:otherPipeline]; + } else if ([stageName isEqualToString:@"sample"]) { + NSString *type = args[@"type"]; + id val = args[@"value"]; + if ([type isEqualToString:@"percentage"]) { + double v = [val isKindOfClass:[NSNumber class]] ? [(NSNumber *)val doubleValue] : 0; + stage = [[FIRSampleStageBridge alloc] initWithPercentage:v]; + } else { + int v = [val isKindOfClass:[NSNumber class]] ? [(NSNumber *)val intValue] : 0; + stage = [[FIRSampleStageBridge alloc] initWithCount:v]; + } + } else if ([stageName isEqualToString:@"aggregate"]) { + stage = [self parseAggregateStageWithArgs:args exprParser:exprParser error:error]; + } else if ([stageName isEqualToString:@"aggregate_with_options"]) { + stage = [self parseAggregateStageWithOptionsArgs:args exprParser:exprParser error:error]; + } else if ([stageName isEqualToString:@"unnest"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"unnest requires expression"); + return nil; + } + FIRExprBridge *fieldExpr = nil; + FIRExprBridge *aliasExpr = nil; + NSDictionary *exprDict = (NSDictionary *)exprMap; + NSString *aliasStr = nil; + if ([exprDict[@"name"] isEqualToString:@"alias"]) { + NSDictionary *aliasArgs = exprDict[@"args"]; + if ([aliasArgs isKindOfClass:[NSDictionary class]] && aliasArgs[@"expression"]) { + fieldExpr = [exprParser parseExpression:aliasArgs[@"expression"] error:&parseErr]; + if (!fieldExpr) { + if (error) *error = parseErr; + return nil; + } + aliasStr = + [aliasArgs[@"alias"] isKindOfClass:[NSString class]] ? aliasArgs[@"alias"] : nil; + } + } + if (!fieldExpr) { + fieldExpr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!fieldExpr) { + if (error) *error = parseErr; + return nil; + } + if (!aliasStr && [exprDict[@"name"] isEqualToString:@"field"]) { + NSDictionary *fieldArgs = exprDict[@"args"]; + aliasStr = + [fieldArgs[@"field"] isKindOfClass:[NSString class]] ? fieldArgs[@"field"] : @"_"; + } + } + if (!aliasStr) aliasStr = @"_"; + aliasExpr = [[FIRFieldBridge alloc] initWithName:aliasStr]; + NSString *indexFieldStr = + [args[@"index_field"] isKindOfClass:[NSString class]] ? args[@"index_field"] : nil; + FIRExprBridge *indexFieldExpr = + (indexFieldStr.length > 0) ? [[FIRFieldBridge alloc] initWithName:indexFieldStr] : nil; + stage = [[FIRUnnestStageBridge alloc] initWithField:fieldExpr + alias:aliasExpr + indexField:indexFieldExpr]; + } else if ([stageName isEqualToString:@"find_nearest"]) { + NSString *vectorFieldName = args[@"vector_field"]; + NSArray *vectorValueArray = args[@"vector_value"]; + NSString *distanceMeasure = args[@"distance_measure"]; + NSNumber *limit = [args[@"limit"] isKindOfClass:[NSNumber class]] ? args[@"limit"] : nil; + NSString *distanceField = [args[@"distance_field"] isKindOfClass:[NSString class]] + ? args[@"distance_field"] + : nil; + if (![vectorFieldName isKindOfClass:[NSString class]] || vectorFieldName.length == 0) { + if (error) *error = parseError(@"find_nearest requires 'vector_field'"); + return nil; + } + if (![vectorValueArray isKindOfClass:[NSArray class]] || vectorValueArray.count == 0) { + if (error) *error = parseError(@"find_nearest requires non-empty 'vector_value'"); + return nil; + } + if (![distanceMeasure isKindOfClass:[NSString class]] || distanceMeasure.length == 0) { + if (error) *error = parseError(@"find_nearest requires 'distance_measure'"); + return nil; + } + FIRFieldBridge *embeddingField = [[FIRFieldBridge alloc] initWithName:vectorFieldName]; + NSMutableArray *numbers = + [NSMutableArray arrayWithCapacity:vectorValueArray.count]; + for (id v in vectorValueArray) { + if ([v isKindOfClass:[NSNumber class]]) { + [numbers addObject:(NSNumber *)v]; + } + } + if (numbers.count != (NSUInteger)vectorValueArray.count) { + if (error) *error = parseError(@"find_nearest vector_value must be an array of numbers"); + return nil; + } + FIRVectorValue *queryVector = [[FIRVectorValue alloc] initWithArray:numbers]; + stage = [[FIRFindNearestStageBridge alloc] initWithField:embeddingField + vectorValue:queryVector + distanceMeasure:distanceMeasure + limit:limit + distanceField:distanceField]; + } else { + if (error) + *error = parseError([NSString stringWithFormat:@"Unknown pipeline stage: %@", stageName]); + return nil; + } + } + + if (stage) [stageBridges addObject:stage]; + } + + if (stageBridges.count == 0) { + if (error && !*error) *error = parseError(@"No valid stages"); + return nil; + } + + return stageBridges; +} + ++ (FIRAggregateFunctionBridge *)aggregateFunctionFromMap:(NSDictionary *)funcMap + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSString *name = funcMap[@"name"]; + if (![name isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"Aggregate function must have a 'name'"); + return nil; + } + // Map Dart aggregate function names to iOS SDK names (count_all -> count with no args; minimum -> + // min; maximum -> max) + NSString *iosName = name; + if ([name isEqualToString:@"count_all"]) { + iosName = @"count"; + } else if ([name isEqualToString:@"minimum"]) { + iosName = @"min"; + } else if ([name isEqualToString:@"maximum"]) { + iosName = @"max"; + } + NSDictionary *argsDict = funcMap[@"args"]; + NSMutableArray *argsArray = [NSMutableArray array]; + if ([argsDict isKindOfClass:[NSDictionary class]]) { + id exprMap = argsDict[@"expression"]; + if ([exprMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:error]; + if (!expr) return nil; + [argsArray addObject:expr]; + } + } + return [[FIRAggregateFunctionBridge alloc] initWithName:iosName Args:argsArray]; +} + ++ (FIRStageBridge *)parseAggregateStageWithArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSArray *accumulatorMaps = args[@"aggregate_functions"]; + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + if (error) *error = parseError(@"aggregate requires aggregate_functions"); + return nil; + } + return [self parseAggregateStageWithAccumulatorMaps:accumulatorMaps + groupMaps:nil + exprParser:exprParser + error:error]; +} + ++ (FIRStageBridge *)parseAggregateStageWithOptionsArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSDictionary *stageMap = args[@"aggregate_stage"]; + if (![stageMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"aggregate_with_options requires aggregate_stage"); + return nil; + } + NSArray *accumulatorMaps = stageMap[@"accumulators"]; + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + accumulatorMaps = stageMap[@"aggregate_functions"]; + } + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + if (error) *error = parseError(@"aggregate_stage requires accumulators or aggregate_functions"); + return nil; + } + NSArray *groupMaps = stageMap[@"groups"]; + return [self parseAggregateStageWithAccumulatorMaps:accumulatorMaps + groupMaps:groupMaps + exprParser:exprParser + error:error]; +} + ++ (FIRStageBridge *)parseAggregateStageWithAccumulatorMaps:(NSArray *)accumulatorMaps + groupMaps:(nullable NSArray *)groupMaps + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSError *parseErr = nil; + NSMutableDictionary *accumulators = + [NSMutableDictionary dictionary]; + for (id accMap in accumulatorMaps) { + if (![accMap isKindOfClass:[NSDictionary class]]) continue; + NSString *alias = nil; + NSDictionary *funcMap = nil; + if ([accMap[@"name"] isEqualToString:@"alias"]) { + NSDictionary *accArgs = accMap[@"args"]; + if (![accArgs isKindOfClass:[NSDictionary class]]) continue; + alias = accArgs[@"alias"]; + funcMap = accArgs[@"aggregate_function"]; + } + if (![alias isKindOfClass:[NSString class]] || ![funcMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Each accumulator must have alias and aggregate_function"); + return nil; + } + FIRAggregateFunctionBridge *func = [self aggregateFunctionFromMap:funcMap + exprParser:exprParser + error:&parseErr]; + if (!func) { + if (error) *error = parseErr; + return nil; + } + accumulators[alias] = func; + } + if (accumulators.count == 0) { + if (error) *error = parseError(@"aggregate requires at least one valid accumulator"); + return nil; + } + + NSMutableDictionary *groups = [NSMutableDictionary dictionary]; + if ([groupMaps isKindOfClass:[NSArray class]] && groupMaps.count > 0) { + for (NSUInteger g = 0; g < groupMaps.count; g++) { + id gm = groupMaps[g]; + if (![gm isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:gm error:&parseErr]; + if (!expr) continue; + NSError *groupKeyError = nil; + NSString *groupKey = [self keyForExpressionMap:gm error:&groupKeyError]; + if (![groupKey isKindOfClass:[NSString class]] || groupKey.length == 0) { + if (error) + *error = + groupKeyError + ?: parseError( + @"aggregate group expression must be a field reference or have an alias"); + return nil; + } + groups[groupKey] = expr; + } + } + + return [[FIRAggregateStageBridge alloc] initWithAccumulators:accumulators groups:groups]; +} + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(id _Nullable snapshot, + NSError *_Nullable error))completion { + if (!stages || stages.count == 0) { + completion(nil, parseError(@"Pipeline requires at least one stage")); + return; + } + + NSError *parseErr = nil; + NSArray *stageBridges = [self parseStagesWithFirestore:firestore + stages:stages + error:&parseErr]; + if (!stageBridges) { + completion(nil, parseErr); + return; + } + + FIRPipelineBridge *pipeline = [[FIRPipelineBridge alloc] initWithStages:stageBridges + db:firestore]; + [pipeline executeWithCompletion:^(id snapshot, NSError *execError) { + if (execError) { + completion(nil, execError); + return; + } + completion(snapshot, nil); + }]; +} + ++ (id)buildPipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + error:(NSError **)error { + NSArray *stageBridges = [self parseStagesWithFirestore:firestore + stages:stages + error:error]; + if (!stageBridges) return nil; + return [[FIRPipelineBridge alloc] initWithStages:stageBridges db:firestore]; +} + +@end + +#else + +@implementation FLTPipelineParser + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(id _Nullable snapshot, + NSError *_Nullable error))completion { + completion(nil, pipelineUnavailableError()); +} + +@end + +#endif diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTQuerySnapshotStreamHandler.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTQuerySnapshotStreamHandler.m new file mode 100644 index 0000000..2800493 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTQuerySnapshotStreamHandler.m @@ -0,0 +1,95 @@ +// 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 FirebaseFirestore; +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h" +#import "include/cloud_firestore/Private/FirestorePigeonParser.h" +#import "include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h" + +@interface FLTQuerySnapshotStreamHandler () +@property(readwrite, strong) id listenerRegistration; +@end + +@implementation FLTQuerySnapshotStreamHandler + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore + query:(FIRQuery *)query + includeMetadataChanges:(BOOL)includeMetadataChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior + source:(FIRListenSource)source { + self = [super init]; + if (self) { + _firestore = firestore; + _query = query; + _includeMetadataChanges = includeMetadataChanges; + _serverTimestampBehavior = serverTimestampBehavior; + _source = source; + } + return self; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + FIRQuery *query = self.query; + + if (query == nil) { + return [FlutterError + errorWithCode:@"sdk-error" + message:@"An error occurred while parsing query arguments, see native logs for more " + @"information. Please report this issue." + details:nil]; + } + + id listener = ^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) { + if (error) { + NSArray *codeAndMessage = [FLTFirebaseFirestoreUtils ErrorCodeAndMessageFromNSError:error]; + NSString *code = codeAndMessage[0]; + NSString *message = codeAndMessage[1]; + NSDictionary *details = @{ + @"code" : code, + @"message" : message, + }; + dispatch_async(dispatch_get_main_queue(), ^{ + events([FLTFirebasePlugin createFlutterErrorFromCode:code + message:message + optionalDetails:details + andOptionalNSError:error]); + }); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + // Emit the Pigeon object directly; the Pigeon-aware codec serializes nested + // `InternalDocumentSnapshot` / `InternalDocumentChange` / `InternalSnapshotMetadata` + // with their proper type codes. Pigeon 26 no longer flattens nested types + // via `toList`. + events([FirestorePigeonParser toPigeonQuerySnapshot:snapshot + serverTimestampBehavior:self.serverTimestampBehavior]); + }); + } + }; + + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + FIRSnapshotListenOptions *optionsWithSourceAndMetadata = [[options + optionsWithIncludeMetadataChanges:_includeMetadataChanges] optionsWithSource:_source]; + + self.listenerRegistration = [query addSnapshotListenerWithOptions:optionsWithSourceAndMetadata + listener:listener]; + + return nil; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + [self.listenerRegistration remove]; + self.listenerRegistration = nil; + + return nil; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTSnapshotsInSyncStreamHandler.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTSnapshotsInSyncStreamHandler.m new file mode 100644 index 0000000..1003be6 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTSnapshotsInSyncStreamHandler.m @@ -0,0 +1,44 @@ +// 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 FirebaseFirestore; + +#import "include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" + +@interface FLTSnapshotsInSyncStreamHandler () +@property(readwrite, strong) id listenerRegistration; +@end + +@implementation FLTSnapshotsInSyncStreamHandler + +- (nonnull instancetype)initWithFirestore:(nonnull FIRFirestore *)firestore { + self = [super init]; + if (self) { + _firestore = firestore; + } + return self; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + id listener = ^() { + dispatch_async(dispatch_get_main_queue(), ^{ + events(nil); + }); + }; + + self.listenerRegistration = [_firestore addSnapshotsInSyncListener:listener]; + + return nil; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + [self.listenerRegistration remove]; + self.listenerRegistration = nil; + + return nil; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FLTTransactionStreamHandler.m b/packages/cloud_firestore_tvos/tvos/Classes/FLTTransactionStreamHandler.m new file mode 100644 index 0000000..68d8acd --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FLTTransactionStreamHandler.m @@ -0,0 +1,167 @@ +// 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 FirebaseFirestore; +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" +#import "include/cloud_firestore/Private/FLTTransactionStreamHandler.h" +#import "include/cloud_firestore/Private/FirestorePigeonParser.h" + +@interface FLTTransactionStreamHandler () +@property(nonatomic, copy, nonnull) void (^started)(FIRTransaction *); +@property(nonatomic, copy, nonnull) void (^ended)(void); +@property(strong) dispatch_semaphore_t semaphore; +@property InternalTransactionResult resultType; +@property NSArray *commands; + +@end + +@implementation FLTTransactionStreamHandler { + NSString *_transactionId; +} + +- (instancetype)initWithId:(NSString *)transactionId + firestore:(FIRFirestore *)firestore + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts + started:(void (^)(FIRTransaction *))startedListener + ended:(void (^)(void))endedListener { + self = [super init]; + if (self) { + _transactionId = transactionId; + self.firestore = firestore; + self.maxAttempts = maxAttempts; + self.timeout = timeout; + self.started = startedListener; + self.ended = endedListener; + self.semaphore = dispatch_semaphore_create(0); + } + return self; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + __weak FLTTransactionStreamHandler *weakSelf = self; + + id transactionRunBlock = ^id(FIRTransaction *transaction, NSError **pError) { + FLTTransactionStreamHandler *strongSelf = weakSelf; + + strongSelf.started(transaction); + + dispatch_async(dispatch_get_main_queue(), ^{ + events( + @{@"appName" : [FLTFirebasePlugin firebaseAppNameFromIosName:self.firestore.app.name]}); + }); + + long timedOut = dispatch_semaphore_wait( + strongSelf.semaphore, dispatch_time(DISPATCH_TIME_NOW, self.timeout * NSEC_PER_MSEC)); + + if (timedOut) { + NSArray *codeAndMessage = [FLTFirebaseFirestoreUtils + ErrorCodeAndMessageFromNSError:[NSError + errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeDeadlineExceeded + userInfo:@{}]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + events(@{ + @"error" : @{ + @"code" : codeAndMessage[0], + @"message" : codeAndMessage[1], + } + }); + }); + } + + if (self.resultType == InternalTransactionResultFailure) { + // Do nothing - already handled in Dart land. + return nil; + } + + for (InternalTransactionCommand *command in self.commands) { + InternalTransactionType commandType = command.type; + NSString *documentPath = command.path; + FIRDocumentReference *reference = [self.firestore documentWithPath:documentPath]; + + switch (commandType) { + case InternalTransactionTypeDeleteType: + [transaction deleteDocument:reference]; + break; + case InternalTransactionTypeUpdate: + [transaction updateData:command.data forDocument:reference]; + break; + case InternalTransactionTypeSet: + if ([command.option.merge isEqual:@YES]) { + [transaction setData:command.data forDocument:reference merge:YES]; + } else if (command.option.mergeFields) { + [transaction setData:command.data + forDocument:reference + mergeFields:[FirestorePigeonParser parseFieldPath:command.option.mergeFields]]; + } else { + [transaction setData:command.data forDocument:reference]; + } + break; + default: + break; + } + } + + return nil; + }; + + id transactionCompleteBlock = ^(id transactionResult, NSError *error) { + FLTTransactionStreamHandler *strongSelf = weakSelf; + if (error) { + NSArray *details = [FLTFirebaseFirestoreUtils ErrorCodeAndMessageFromNSError:error]; + + dispatch_async(dispatch_get_main_queue(), ^{ + events(@{ + @"error" : @{ + @"code" : details[0], + @"message" : details[1], + } + }); + }); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + events(@{@"complete" : [NSNumber numberWithBool:YES]}); + }); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + events(FlutterEndOfEventStream); + }); + + strongSelf.ended(); + }; + FIRTransactionOptions *options = [[FIRTransactionOptions alloc] init]; + options.maxAttempts = _maxAttempts; + + [_firestore runTransactionWithOptions:options + block:transactionRunBlock + completion:transactionCompleteBlock]; + + return nil; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + dispatch_semaphore_signal(self.semaphore); + + return nil; +} + +- (void)receiveTransactionResponse:(InternalTransactionResult)resultType + commands:(NSArray *)commands { + self.resultType = resultType; + self.commands = commands; + + dispatch_semaphore_signal(self.semaphore); +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FirestoreMessages.g.m b/packages/cloud_firestore_tvos/tvos/Classes/FirestoreMessages.g.m new file mode 100644 index 0000000..f9594c6 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FirestoreMessages.g.m @@ -0,0 +1,2033 @@ +// Copyright 2023, 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 "FirestoreMessages.g.h" +#import "FLTFirebaseFirestoreReader.h" +#import "FLTFirebaseFirestoreWriter.h" + +#if TARGET_OS_OSX +@import FlutterMacOS; +#else +@import Flutter; +#endif + +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} + +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +/// An enumeration of document change types. +@implementation DocumentChangeTypeBox +- (instancetype)initWithValue:(DocumentChangeType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// An enumeration of firestore source types. +@implementation SourceBox +- (instancetype)initWithValue:(Source)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// The listener retrieves data and listens to updates from the local Firestore cache only. +/// If the cache is empty, an empty snapshot will be returned. +/// Snapshot events will be triggered on cache updates, like local mutations or load bundles. +/// +/// Note that the data might be stale if the cache hasn't synchronized with recent server-side +/// changes. +@implementation ListenSourceBox +- (instancetype)initWithValue:(ListenSource)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation ServerTimestampBehaviorBox +- (instancetype)initWithValue:(ServerTimestampBehavior)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// [AggregateSource] represents the source of data for an [AggregateQuery]. +@implementation AggregateSourceBox +- (instancetype)initWithValue:(AggregateSource)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache +/// index manager. +@implementation PersistenceCacheIndexManagerRequestBox +- (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation InternalTransactionResultBox +- (instancetype)initWithValue:(InternalTransactionResult)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation InternalTransactionTypeBox +- (instancetype)initWithValue:(InternalTransactionType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation AggregateTypeBox +- (instancetype)initWithValue:(AggregateType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@interface InternalFirebaseSettings () ++ (InternalFirebaseSettings *)fromList:(NSArray *)list; ++ (nullable InternalFirebaseSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FirestorePigeonFirebaseApp () ++ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list; ++ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalSnapshotMetadata () ++ (InternalSnapshotMetadata *)fromList:(NSArray *)list; ++ (nullable InternalSnapshotMetadata *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalDocumentSnapshot () ++ (InternalDocumentSnapshot *)fromList:(NSArray *)list; ++ (nullable InternalDocumentSnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalDocumentChange () ++ (InternalDocumentChange *)fromList:(NSArray *)list; ++ (nullable InternalDocumentChange *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalQuerySnapshot () ++ (InternalQuerySnapshot *)fromList:(NSArray *)list; ++ (nullable InternalQuerySnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalPipelineResult () ++ (InternalPipelineResult *)fromList:(NSArray *)list; ++ (nullable InternalPipelineResult *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalPipelineSnapshot () ++ (InternalPipelineSnapshot *)fromList:(NSArray *)list; ++ (nullable InternalPipelineSnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalGetOptions () ++ (InternalGetOptions *)fromList:(NSArray *)list; ++ (nullable InternalGetOptions *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalDocumentOption () ++ (InternalDocumentOption *)fromList:(NSArray *)list; ++ (nullable InternalDocumentOption *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalTransactionCommand () ++ (InternalTransactionCommand *)fromList:(NSArray *)list; ++ (nullable InternalTransactionCommand *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface DocumentReferenceRequest () ++ (DocumentReferenceRequest *)fromList:(NSArray *)list; ++ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalQueryParameters () ++ (InternalQueryParameters *)fromList:(NSArray *)list; ++ (nullable InternalQueryParameters *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface AggregateQuery () ++ (AggregateQuery *)fromList:(NSArray *)list; ++ (nullable AggregateQuery *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface AggregateQueryResponse () ++ (AggregateQueryResponse *)fromList:(NSArray *)list; ++ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation InternalFirebaseSettings ++ (instancetype)makeWithPersistenceEnabled:(nullable NSNumber *)persistenceEnabled + host:(nullable NSString *)host + sslEnabled:(nullable NSNumber *)sslEnabled + cacheSizeBytes:(nullable NSNumber *)cacheSizeBytes + ignoreUndefinedProperties:(BOOL)ignoreUndefinedProperties { + InternalFirebaseSettings *pigeonResult = [[InternalFirebaseSettings alloc] init]; + pigeonResult.persistenceEnabled = persistenceEnabled; + pigeonResult.host = host; + pigeonResult.sslEnabled = sslEnabled; + pigeonResult.cacheSizeBytes = cacheSizeBytes; + pigeonResult.ignoreUndefinedProperties = ignoreUndefinedProperties; + return pigeonResult; +} ++ (InternalFirebaseSettings *)fromList:(NSArray *)list { + InternalFirebaseSettings *pigeonResult = [[InternalFirebaseSettings alloc] init]; + pigeonResult.persistenceEnabled = GetNullableObjectAtIndex(list, 0); + pigeonResult.host = GetNullableObjectAtIndex(list, 1); + pigeonResult.sslEnabled = GetNullableObjectAtIndex(list, 2); + pigeonResult.cacheSizeBytes = GetNullableObjectAtIndex(list, 3); + pigeonResult.ignoreUndefinedProperties = [GetNullableObjectAtIndex(list, 4) boolValue]; + return pigeonResult; +} ++ (nullable InternalFirebaseSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalFirebaseSettings fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.persistenceEnabled ?: [NSNull null], + self.host ?: [NSNull null], + self.sslEnabled ?: [NSNull null], + self.cacheSizeBytes ?: [NSNull null], + @(self.ignoreUndefinedProperties), + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalFirebaseSettings *other = (InternalFirebaseSettings *)object; + return FLTPigeonDeepEquals(self.persistenceEnabled, other.persistenceEnabled) && + FLTPigeonDeepEquals(self.host, other.host) && + FLTPigeonDeepEquals(self.sslEnabled, other.sslEnabled) && + FLTPigeonDeepEquals(self.cacheSizeBytes, other.cacheSizeBytes) && + self.ignoreUndefinedProperties == other.ignoreUndefinedProperties; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.persistenceEnabled); + result = result * 31 + FLTPigeonDeepHash(self.host); + result = result * 31 + FLTPigeonDeepHash(self.sslEnabled); + result = result * 31 + FLTPigeonDeepHash(self.cacheSizeBytes); + result = result * 31 + @(self.ignoreUndefinedProperties).hash; + return result; +} +@end + +@implementation FirestorePigeonFirebaseApp ++ (instancetype)makeWithAppName:(NSString *)appName + settings:(InternalFirebaseSettings *)settings + databaseURL:(NSString *)databaseURL { + FirestorePigeonFirebaseApp *pigeonResult = [[FirestorePigeonFirebaseApp alloc] init]; + pigeonResult.appName = appName; + pigeonResult.settings = settings; + pigeonResult.databaseURL = databaseURL; + return pigeonResult; +} ++ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list { + FirestorePigeonFirebaseApp *pigeonResult = [[FirestorePigeonFirebaseApp alloc] init]; + pigeonResult.appName = GetNullableObjectAtIndex(list, 0); + pigeonResult.settings = GetNullableObjectAtIndex(list, 1); + pigeonResult.databaseURL = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list { + return (list) ? [FirestorePigeonFirebaseApp fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.appName ?: [NSNull null], + self.settings ?: [NSNull null], + self.databaseURL ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + FirestorePigeonFirebaseApp *other = (FirestorePigeonFirebaseApp *)object; + return FLTPigeonDeepEquals(self.appName, other.appName) && + FLTPigeonDeepEquals(self.settings, other.settings) && + FLTPigeonDeepEquals(self.databaseURL, other.databaseURL); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appName); + result = result * 31 + FLTPigeonDeepHash(self.settings); + result = result * 31 + FLTPigeonDeepHash(self.databaseURL); + return result; +} +@end + +@implementation InternalSnapshotMetadata ++ (instancetype)makeWithHasPendingWrites:(BOOL)hasPendingWrites isFromCache:(BOOL)isFromCache { + InternalSnapshotMetadata *pigeonResult = [[InternalSnapshotMetadata alloc] init]; + pigeonResult.hasPendingWrites = hasPendingWrites; + pigeonResult.isFromCache = isFromCache; + return pigeonResult; +} ++ (InternalSnapshotMetadata *)fromList:(NSArray *)list { + InternalSnapshotMetadata *pigeonResult = [[InternalSnapshotMetadata alloc] init]; + pigeonResult.hasPendingWrites = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.isFromCache = [GetNullableObjectAtIndex(list, 1) boolValue]; + return pigeonResult; +} ++ (nullable InternalSnapshotMetadata *)nullableFromList:(NSArray *)list { + return (list) ? [InternalSnapshotMetadata fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.hasPendingWrites), + @(self.isFromCache), + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalSnapshotMetadata *other = (InternalSnapshotMetadata *)object; + return self.hasPendingWrites == other.hasPendingWrites && self.isFromCache == other.isFromCache; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.hasPendingWrites).hash; + result = result * 31 + @(self.isFromCache).hash; + return result; +} +@end + +@implementation InternalDocumentSnapshot ++ (instancetype)makeWithPath:(NSString *)path + data:(nullable NSDictionary *)data + metadata:(InternalSnapshotMetadata *)metadata { + InternalDocumentSnapshot *pigeonResult = [[InternalDocumentSnapshot alloc] init]; + pigeonResult.path = path; + pigeonResult.data = data; + pigeonResult.metadata = metadata; + return pigeonResult; +} ++ (InternalDocumentSnapshot *)fromList:(NSArray *)list { + InternalDocumentSnapshot *pigeonResult = [[InternalDocumentSnapshot alloc] init]; + pigeonResult.path = GetNullableObjectAtIndex(list, 0); + pigeonResult.data = GetNullableObjectAtIndex(list, 1); + pigeonResult.metadata = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable InternalDocumentSnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentSnapshot fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.metadata ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentSnapshot *other = (InternalDocumentSnapshot *)object; + return FLTPigeonDeepEquals(self.path, other.path) && FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.metadata, other.metadata); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.metadata); + return result; +} +@end + +@implementation InternalDocumentChange ++ (instancetype)makeWithType:(DocumentChangeType)type + document:(InternalDocumentSnapshot *)document + oldIndex:(NSInteger)oldIndex + newIndex:(NSInteger)newIndex { + InternalDocumentChange *pigeonResult = [[InternalDocumentChange alloc] init]; + pigeonResult.type = type; + pigeonResult.document = document; + pigeonResult.oldIndex = oldIndex; + pigeonResult.newIndex = newIndex; + return pigeonResult; +} ++ (InternalDocumentChange *)fromList:(NSArray *)list { + InternalDocumentChange *pigeonResult = [[InternalDocumentChange alloc] init]; + DocumentChangeTypeBox *boxedDocumentChangeType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedDocumentChangeType.value; + pigeonResult.document = GetNullableObjectAtIndex(list, 1); + pigeonResult.oldIndex = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.newIndex = [GetNullableObjectAtIndex(list, 3) integerValue]; + return pigeonResult; +} ++ (nullable InternalDocumentChange *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentChange fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[DocumentChangeTypeBox alloc] initWithValue:self.type], + self.document ?: [NSNull null], + @(self.oldIndex), + @(self.newIndex), + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentChange *other = (InternalDocumentChange *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.document, other.document) && + self.oldIndex == other.oldIndex && self.newIndex == other.newIndex; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.document); + result = result * 31 + @(self.oldIndex).hash; + result = result * 31 + @(self.newIndex).hash; + return result; +} +@end + +@implementation InternalQuerySnapshot ++ (instancetype)makeWithDocuments:(NSArray *)documents + documentChanges:(NSArray *)documentChanges + metadata:(InternalSnapshotMetadata *)metadata { + InternalQuerySnapshot *pigeonResult = [[InternalQuerySnapshot alloc] init]; + pigeonResult.documents = documents; + pigeonResult.documentChanges = documentChanges; + pigeonResult.metadata = metadata; + return pigeonResult; +} ++ (InternalQuerySnapshot *)fromList:(NSArray *)list { + InternalQuerySnapshot *pigeonResult = [[InternalQuerySnapshot alloc] init]; + pigeonResult.documents = GetNullableObjectAtIndex(list, 0); + pigeonResult.documentChanges = GetNullableObjectAtIndex(list, 1); + pigeonResult.metadata = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable InternalQuerySnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalQuerySnapshot fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.documents ?: [NSNull null], + self.documentChanges ?: [NSNull null], + self.metadata ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalQuerySnapshot *other = (InternalQuerySnapshot *)object; + return FLTPigeonDeepEquals(self.documents, other.documents) && + FLTPigeonDeepEquals(self.documentChanges, other.documentChanges) && + FLTPigeonDeepEquals(self.metadata, other.metadata); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.documents); + result = result * 31 + FLTPigeonDeepHash(self.documentChanges); + result = result * 31 + FLTPigeonDeepHash(self.metadata); + return result; +} +@end + +@implementation InternalPipelineResult ++ (instancetype)makeWithDocumentPath:(nullable NSString *)documentPath + createTime:(nullable NSNumber *)createTime + updateTime:(nullable NSNumber *)updateTime + data:(nullable NSDictionary *)data { + InternalPipelineResult *pigeonResult = [[InternalPipelineResult alloc] init]; + pigeonResult.documentPath = documentPath; + pigeonResult.createTime = createTime; + pigeonResult.updateTime = updateTime; + pigeonResult.data = data; + return pigeonResult; +} ++ (InternalPipelineResult *)fromList:(NSArray *)list { + InternalPipelineResult *pigeonResult = [[InternalPipelineResult alloc] init]; + pigeonResult.documentPath = GetNullableObjectAtIndex(list, 0); + pigeonResult.createTime = GetNullableObjectAtIndex(list, 1); + pigeonResult.updateTime = GetNullableObjectAtIndex(list, 2); + pigeonResult.data = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalPipelineResult *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPipelineResult fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.documentPath ?: [NSNull null], + self.createTime ?: [NSNull null], + self.updateTime ?: [NSNull null], + self.data ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPipelineResult *other = (InternalPipelineResult *)object; + return FLTPigeonDeepEquals(self.documentPath, other.documentPath) && + FLTPigeonDeepEquals(self.createTime, other.createTime) && + FLTPigeonDeepEquals(self.updateTime, other.updateTime) && + FLTPigeonDeepEquals(self.data, other.data); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.documentPath); + result = result * 31 + FLTPigeonDeepHash(self.createTime); + result = result * 31 + FLTPigeonDeepHash(self.updateTime); + result = result * 31 + FLTPigeonDeepHash(self.data); + return result; +} +@end + +@implementation InternalPipelineSnapshot ++ (instancetype)makeWithResults:(NSArray *)results + executionTime:(NSInteger)executionTime { + InternalPipelineSnapshot *pigeonResult = [[InternalPipelineSnapshot alloc] init]; + pigeonResult.results = results; + pigeonResult.executionTime = executionTime; + return pigeonResult; +} ++ (InternalPipelineSnapshot *)fromList:(NSArray *)list { + InternalPipelineSnapshot *pigeonResult = [[InternalPipelineSnapshot alloc] init]; + pigeonResult.results = GetNullableObjectAtIndex(list, 0); + pigeonResult.executionTime = [GetNullableObjectAtIndex(list, 1) integerValue]; + return pigeonResult; +} ++ (nullable InternalPipelineSnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPipelineSnapshot fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.results ?: [NSNull null], + @(self.executionTime), + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPipelineSnapshot *other = (InternalPipelineSnapshot *)object; + return FLTPigeonDeepEquals(self.results, other.results) && + self.executionTime == other.executionTime; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.results); + result = result * 31 + @(self.executionTime).hash; + return result; +} +@end + +@implementation InternalGetOptions ++ (instancetype)makeWithSource:(Source)source + serverTimestampBehavior:(ServerTimestampBehavior)serverTimestampBehavior { + InternalGetOptions *pigeonResult = [[InternalGetOptions alloc] init]; + pigeonResult.source = source; + pigeonResult.serverTimestampBehavior = serverTimestampBehavior; + return pigeonResult; +} ++ (InternalGetOptions *)fromList:(NSArray *)list { + InternalGetOptions *pigeonResult = [[InternalGetOptions alloc] init]; + SourceBox *boxedSource = GetNullableObjectAtIndex(list, 0); + pigeonResult.source = boxedSource.value; + ServerTimestampBehaviorBox *boxedServerTimestampBehavior = GetNullableObjectAtIndex(list, 1); + pigeonResult.serverTimestampBehavior = boxedServerTimestampBehavior.value; + return pigeonResult; +} ++ (nullable InternalGetOptions *)nullableFromList:(NSArray *)list { + return (list) ? [InternalGetOptions fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[SourceBox alloc] initWithValue:self.source], + [[ServerTimestampBehaviorBox alloc] initWithValue:self.serverTimestampBehavior], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalGetOptions *other = (InternalGetOptions *)object; + return self.source == other.source && + self.serverTimestampBehavior == other.serverTimestampBehavior; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.source).hash; + result = result * 31 + @(self.serverTimestampBehavior).hash; + return result; +} +@end + +@implementation InternalDocumentOption ++ (instancetype)makeWithMerge:(nullable NSNumber *)merge + mergeFields:(nullable NSArray *> *)mergeFields { + InternalDocumentOption *pigeonResult = [[InternalDocumentOption alloc] init]; + pigeonResult.merge = merge; + pigeonResult.mergeFields = mergeFields; + return pigeonResult; +} ++ (InternalDocumentOption *)fromList:(NSArray *)list { + InternalDocumentOption *pigeonResult = [[InternalDocumentOption alloc] init]; + pigeonResult.merge = GetNullableObjectAtIndex(list, 0); + pigeonResult.mergeFields = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable InternalDocumentOption *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentOption fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.merge ?: [NSNull null], + self.mergeFields ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentOption *other = (InternalDocumentOption *)object; + return FLTPigeonDeepEquals(self.merge, other.merge) && + FLTPigeonDeepEquals(self.mergeFields, other.mergeFields); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.merge); + result = result * 31 + FLTPigeonDeepHash(self.mergeFields); + return result; +} +@end + +@implementation InternalTransactionCommand ++ (instancetype)makeWithType:(InternalTransactionType)type + path:(NSString *)path + data:(nullable NSDictionary *)data + option:(nullable InternalDocumentOption *)option { + InternalTransactionCommand *pigeonResult = [[InternalTransactionCommand alloc] init]; + pigeonResult.type = type; + pigeonResult.path = path; + pigeonResult.data = data; + pigeonResult.option = option; + return pigeonResult; +} ++ (InternalTransactionCommand *)fromList:(NSArray *)list { + InternalTransactionCommand *pigeonResult = [[InternalTransactionCommand alloc] init]; + InternalTransactionTypeBox *boxedInternalTransactionType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedInternalTransactionType.value; + pigeonResult.path = GetNullableObjectAtIndex(list, 1); + pigeonResult.data = GetNullableObjectAtIndex(list, 2); + pigeonResult.option = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalTransactionCommand *)nullableFromList:(NSArray *)list { + return (list) ? [InternalTransactionCommand fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[InternalTransactionTypeBox alloc] initWithValue:self.type], + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.option ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalTransactionCommand *other = (InternalTransactionCommand *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.path, other.path) && + FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.option, other.option); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.option); + return result; +} +@end + +@implementation DocumentReferenceRequest ++ (instancetype)makeWithPath:(NSString *)path + data:(nullable NSDictionary *)data + option:(nullable InternalDocumentOption *)option + source:(nullable SourceBox *)source + serverTimestampBehavior:(nullable ServerTimestampBehaviorBox *)serverTimestampBehavior { + DocumentReferenceRequest *pigeonResult = [[DocumentReferenceRequest alloc] init]; + pigeonResult.path = path; + pigeonResult.data = data; + pigeonResult.option = option; + pigeonResult.source = source; + pigeonResult.serverTimestampBehavior = serverTimestampBehavior; + return pigeonResult; +} ++ (DocumentReferenceRequest *)fromList:(NSArray *)list { + DocumentReferenceRequest *pigeonResult = [[DocumentReferenceRequest alloc] init]; + pigeonResult.path = GetNullableObjectAtIndex(list, 0); + pigeonResult.data = GetNullableObjectAtIndex(list, 1); + pigeonResult.option = GetNullableObjectAtIndex(list, 2); + pigeonResult.source = GetNullableObjectAtIndex(list, 3); + pigeonResult.serverTimestampBehavior = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list { + return (list) ? [DocumentReferenceRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.option ?: [NSNull null], + self.source ?: [NSNull null], + self.serverTimestampBehavior ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + DocumentReferenceRequest *other = (DocumentReferenceRequest *)object; + return FLTPigeonDeepEquals(self.path, other.path) && FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.option, other.option) && + FLTPigeonDeepEquals(self.source, other.source) && + FLTPigeonDeepEquals(self.serverTimestampBehavior, other.serverTimestampBehavior); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.option); + result = result * 31 + FLTPigeonDeepHash(self.source); + result = result * 31 + FLTPigeonDeepHash(self.serverTimestampBehavior); + return result; +} +@end + +@implementation InternalQueryParameters ++ (instancetype)makeWithWhere:(nullable NSArray *> *)where + orderBy:(nullable NSArray *> *)orderBy + limit:(nullable NSNumber *)limit + limitToLast:(nullable NSNumber *)limitToLast + startAt:(nullable NSArray *)startAt + startAfter:(nullable NSArray *)startAfter + endAt:(nullable NSArray *)endAt + endBefore:(nullable NSArray *)endBefore + filters:(nullable NSDictionary *)filters { + InternalQueryParameters *pigeonResult = [[InternalQueryParameters alloc] init]; + pigeonResult.where = where; + pigeonResult.orderBy = orderBy; + pigeonResult.limit = limit; + pigeonResult.limitToLast = limitToLast; + pigeonResult.startAt = startAt; + pigeonResult.startAfter = startAfter; + pigeonResult.endAt = endAt; + pigeonResult.endBefore = endBefore; + pigeonResult.filters = filters; + return pigeonResult; +} ++ (InternalQueryParameters *)fromList:(NSArray *)list { + InternalQueryParameters *pigeonResult = [[InternalQueryParameters alloc] init]; + pigeonResult.where = GetNullableObjectAtIndex(list, 0); + pigeonResult.orderBy = GetNullableObjectAtIndex(list, 1); + pigeonResult.limit = GetNullableObjectAtIndex(list, 2); + pigeonResult.limitToLast = GetNullableObjectAtIndex(list, 3); + pigeonResult.startAt = GetNullableObjectAtIndex(list, 4); + pigeonResult.startAfter = GetNullableObjectAtIndex(list, 5); + pigeonResult.endAt = GetNullableObjectAtIndex(list, 6); + pigeonResult.endBefore = GetNullableObjectAtIndex(list, 7); + pigeonResult.filters = GetNullableObjectAtIndex(list, 8); + return pigeonResult; +} ++ (nullable InternalQueryParameters *)nullableFromList:(NSArray *)list { + return (list) ? [InternalQueryParameters fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.where ?: [NSNull null], + self.orderBy ?: [NSNull null], + self.limit ?: [NSNull null], + self.limitToLast ?: [NSNull null], + self.startAt ?: [NSNull null], + self.startAfter ?: [NSNull null], + self.endAt ?: [NSNull null], + self.endBefore ?: [NSNull null], + self.filters ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalQueryParameters *other = (InternalQueryParameters *)object; + return FLTPigeonDeepEquals(self.where, other.where) && + FLTPigeonDeepEquals(self.orderBy, other.orderBy) && + FLTPigeonDeepEquals(self.limit, other.limit) && + FLTPigeonDeepEquals(self.limitToLast, other.limitToLast) && + FLTPigeonDeepEquals(self.startAt, other.startAt) && + FLTPigeonDeepEquals(self.startAfter, other.startAfter) && + FLTPigeonDeepEquals(self.endAt, other.endAt) && + FLTPigeonDeepEquals(self.endBefore, other.endBefore) && + FLTPigeonDeepEquals(self.filters, other.filters); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.where); + result = result * 31 + FLTPigeonDeepHash(self.orderBy); + result = result * 31 + FLTPigeonDeepHash(self.limit); + result = result * 31 + FLTPigeonDeepHash(self.limitToLast); + result = result * 31 + FLTPigeonDeepHash(self.startAt); + result = result * 31 + FLTPigeonDeepHash(self.startAfter); + result = result * 31 + FLTPigeonDeepHash(self.endAt); + result = result * 31 + FLTPigeonDeepHash(self.endBefore); + result = result * 31 + FLTPigeonDeepHash(self.filters); + return result; +} +@end + +@implementation AggregateQuery ++ (instancetype)makeWithType:(AggregateType)type field:(nullable NSString *)field { + AggregateQuery *pigeonResult = [[AggregateQuery alloc] init]; + pigeonResult.type = type; + pigeonResult.field = field; + return pigeonResult; +} ++ (AggregateQuery *)fromList:(NSArray *)list { + AggregateQuery *pigeonResult = [[AggregateQuery alloc] init]; + AggregateTypeBox *boxedAggregateType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedAggregateType.value; + pigeonResult.field = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable AggregateQuery *)nullableFromList:(NSArray *)list { + return (list) ? [AggregateQuery fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[AggregateTypeBox alloc] initWithValue:self.type], + self.field ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AggregateQuery *other = (AggregateQuery *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.field, other.field); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.field); + return result; +} +@end + +@implementation AggregateQueryResponse ++ (instancetype)makeWithType:(AggregateType)type + field:(nullable NSString *)field + value:(nullable NSNumber *)value { + AggregateQueryResponse *pigeonResult = [[AggregateQueryResponse alloc] init]; + pigeonResult.type = type; + pigeonResult.field = field; + pigeonResult.value = value; + return pigeonResult; +} ++ (AggregateQueryResponse *)fromList:(NSArray *)list { + AggregateQueryResponse *pigeonResult = [[AggregateQueryResponse alloc] init]; + AggregateTypeBox *boxedAggregateType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedAggregateType.value; + pigeonResult.field = GetNullableObjectAtIndex(list, 1); + pigeonResult.value = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list { + return (list) ? [AggregateQueryResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[AggregateTypeBox alloc] initWithValue:self.type], + self.field ?: [NSNull null], + self.value ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AggregateQueryResponse *other = (AggregateQueryResponse *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.field, other.field) && + FLTPigeonDeepEquals(self.value, other.value); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.field); + result = result * 31 + FLTPigeonDeepHash(self.value); + return result; +} +@end + +@interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader +@end +@implementation FirebaseFirestoreHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 129: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[DocumentChangeTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 130: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[SourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 131: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ListenSourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 132: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ServerTimestampBehaviorBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 133: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[AggregateSourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 134: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[PersistenceCacheIndexManagerRequestBox alloc] + initWithValue:[enumAsNumber integerValue]]; + } + case 135: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[InternalTransactionResultBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 136: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[InternalTransactionTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 137: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[AggregateTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 138: + return [InternalFirebaseSettings fromList:[self readValue]]; + case 139: + return [FirestorePigeonFirebaseApp fromList:[self readValue]]; + case 140: + return [InternalSnapshotMetadata fromList:[self readValue]]; + case 141: + return [InternalDocumentSnapshot fromList:[self readValue]]; + case 142: + return [InternalDocumentChange fromList:[self readValue]]; + case 143: + return [InternalQuerySnapshot fromList:[self readValue]]; + case 144: + return [InternalPipelineResult fromList:[self readValue]]; + case 145: + return [InternalPipelineSnapshot fromList:[self readValue]]; + case 146: + return [InternalGetOptions fromList:[self readValue]]; + case 147: + return [InternalDocumentOption fromList:[self readValue]]; + case 148: + return [InternalTransactionCommand fromList:[self readValue]]; + case 149: + return [DocumentReferenceRequest fromList:[self readValue]]; + case 150: + return [InternalQueryParameters fromList:[self readValue]]; + case 151: + return [AggregateQuery fromList:[self readValue]]; + case 152: + return [AggregateQueryResponse fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FirebaseFirestoreHostApiCodecWriter : FLTFirebaseFirestoreWriter +@end +@implementation FirebaseFirestoreHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[DocumentChangeTypeBox class]]) { + DocumentChangeTypeBox *box = (DocumentChangeTypeBox *)value; + [self writeByte:129]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[SourceBox class]]) { + SourceBox *box = (SourceBox *)value; + [self writeByte:130]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[ListenSourceBox class]]) { + ListenSourceBox *box = (ListenSourceBox *)value; + [self writeByte:131]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[ServerTimestampBehaviorBox class]]) { + ServerTimestampBehaviorBox *box = (ServerTimestampBehaviorBox *)value; + [self writeByte:132]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[AggregateSourceBox class]]) { + AggregateSourceBox *box = (AggregateSourceBox *)value; + [self writeByte:133]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[PersistenceCacheIndexManagerRequestBox class]]) { + PersistenceCacheIndexManagerRequestBox *box = (PersistenceCacheIndexManagerRequestBox *)value; + [self writeByte:134]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalTransactionResultBox class]]) { + InternalTransactionResultBox *box = (InternalTransactionResultBox *)value; + [self writeByte:135]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalTransactionTypeBox class]]) { + InternalTransactionTypeBox *box = (InternalTransactionTypeBox *)value; + [self writeByte:136]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[AggregateTypeBox class]]) { + AggregateTypeBox *box = (AggregateTypeBox *)value; + [self writeByte:137]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalFirebaseSettings class]]) { + [self writeByte:138]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FirestorePigeonFirebaseApp class]]) { + [self writeByte:139]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalSnapshotMetadata class]]) { + [self writeByte:140]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentSnapshot class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentChange class]]) { + [self writeByte:142]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalQuerySnapshot class]]) { + [self writeByte:143]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalPipelineResult class]]) { + [self writeByte:144]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalPipelineSnapshot class]]) { + [self writeByte:145]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalGetOptions class]]) { + [self writeByte:146]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentOption class]]) { + [self writeByte:147]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalTransactionCommand class]]) { + [self writeByte:148]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[DocumentReferenceRequest class]]) { + [self writeByte:149]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalQueryParameters class]]) { + [self writeByte:150]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[AggregateQuery class]]) { + [self writeByte:151]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[AggregateQueryResponse class]]) { + [self writeByte:152]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FirebaseFirestoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FirebaseFirestoreHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FirebaseFirestoreHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FirebaseFirestoreHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *GetFirebaseFirestoreHostApiCodec(void) { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + FirebaseFirestoreHostApiCodecReaderWriter *readerWriter = + [[FirebaseFirestoreHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} +void SetUpFirebaseFirestoreHostApi(id binaryMessenger, + NSObject *api) { + SetUpFirebaseFirestoreHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseFirestoreHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.loadBundle", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadBundleApp:bundle:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(loadBundleApp:bundle:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + FlutterStandardTypedData *arg_bundle = GetNullableObjectAtIndex(args, 1); + [api loadBundleApp:arg_app + bundle:arg_bundle + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.namedQueryGet", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(namedQueryGetApp:name:options:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(namedQueryGetApp:name:options:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_name = GetNullableObjectAtIndex(args, 1); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 2); + [api namedQueryGetApp:arg_app + name:arg_name + options:arg_options + completion:^(InternalQuerySnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.clearPersistence", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(clearPersistenceApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(clearPersistenceApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api clearPersistenceApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.disableNetwork", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(disableNetworkApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(disableNetworkApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api disableNetworkApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.enableNetwork", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(enableNetworkApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(enableNetworkApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api enableNetworkApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.terminate", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(terminateApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(terminateApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api terminateApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.waitForPendingWrites", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(waitForPendingWritesApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(waitForPendingWritesApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api waitForPendingWritesApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.setIndexConfiguration", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + setIndexConfigurationApp:indexConfiguration:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(setIndexConfigurationApp:indexConfiguration:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_indexConfiguration = GetNullableObjectAtIndex(args, 1); + [api setIndexConfigurationApp:arg_app + indexConfiguration:arg_indexConfiguration + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.setLoggingEnabled", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setLoggingEnabledLoggingEnabled:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(setLoggingEnabledLoggingEnabled:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + BOOL arg_loggingEnabled = [GetNullableObjectAtIndex(args, 0) boolValue]; + [api setLoggingEnabledLoggingEnabled:arg_loggingEnabled + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.snapshotsInSyncSetup", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(snapshotsInSyncSetupApp:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(snapshotsInSyncSetupApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api snapshotsInSyncSetupApp:arg_app + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.transactionCreate", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(transactionCreateApp:timeout:maxAttempts:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(transactionCreateApp:timeout:maxAttempts:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSInteger arg_timeout = [GetNullableObjectAtIndex(args, 1) integerValue]; + NSInteger arg_maxAttempts = [GetNullableObjectAtIndex(args, 2) integerValue]; + [api transactionCreateApp:arg_app + timeout:arg_timeout + maxAttempts:arg_maxAttempts + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.transactionStoreResult", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(transactionStoreResultTransactionId:resultType: + commands:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(transactionStoreResultTransactionId:resultType:commands:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_transactionId = GetNullableObjectAtIndex(args, 0); + InternalTransactionResultBox *boxedInternalTransactionResult = + GetNullableObjectAtIndex(args, 1); + InternalTransactionResult arg_resultType = boxedInternalTransactionResult.value; + NSArray *arg_commands = GetNullableObjectAtIndex(args, 2); + [api transactionStoreResultTransactionId:arg_transactionId + resultType:arg_resultType + commands:arg_commands + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.transactionGet", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(transactionGetApp:transactionId:path:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(transactionGetApp:transactionId:path:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_transactionId = GetNullableObjectAtIndex(args, 1); + NSString *arg_path = GetNullableObjectAtIndex(args, 2); + [api transactionGetApp:arg_app + transactionId:arg_transactionId + path:arg_path + completion:^(InternalDocumentSnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceSet", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(documentReferenceSetApp:request:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(documentReferenceSetApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); + [api documentReferenceSetApp:arg_app + request:arg_request + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceUpdate", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(documentReferenceUpdateApp:request:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(documentReferenceUpdateApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); + [api documentReferenceUpdateApp:arg_app + request:arg_request + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceGet", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(documentReferenceGetApp:request:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(documentReferenceGetApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); + [api documentReferenceGetApp:arg_app + request:arg_request + completion:^(InternalDocumentSnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceDelete", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(documentReferenceDeleteApp:request:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(documentReferenceDeleteApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); + [api documentReferenceDeleteApp:arg_app + request:arg_request + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.queryGet", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(queryGetApp:path:isCollectionGroup:parameters: + options:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(queryGetApp:path:isCollectionGroup:parameters:options:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_path = GetNullableObjectAtIndex(args, 1); + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 2) boolValue]; + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); + [api queryGetApp:arg_app + path:arg_path + isCollectionGroup:arg_isCollectionGroup + parameters:arg_parameters + options:arg_options + completion:^(InternalQuerySnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.aggregateQuery", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(aggregateQueryApp:path:parameters:source:queries: + isCollectionGroup:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(aggregateQueryApp:path:parameters:source:queries:isCollectionGroup:" + @"completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_path = GetNullableObjectAtIndex(args, 1); + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 2); + AggregateSourceBox *boxedAggregateSource = GetNullableObjectAtIndex(args, 3); + AggregateSource arg_source = boxedAggregateSource.value; + NSArray *arg_queries = GetNullableObjectAtIndex(args, 4); + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 5) boolValue]; + [api aggregateQueryApp:arg_app + path:arg_path + parameters:arg_parameters + source:arg_source + queries:arg_queries + isCollectionGroup:arg_isCollectionGroup + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.writeBatchCommit", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(writeBatchCommitApp:writes:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(writeBatchCommitApp:writes:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSArray *arg_writes = GetNullableObjectAtIndex(args, 1); + [api writeBatchCommitApp:arg_app + writes:arg_writes + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.querySnapshot", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(querySnapshotApp:path:isCollectionGroup:parameters: + options:includeMetadataChanges:source:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(querySnapshotApp:path:isCollectionGroup:parameters:options:" + @"includeMetadataChanges:source:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_path = GetNullableObjectAtIndex(args, 1); + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 2) boolValue]; + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); + BOOL arg_includeMetadataChanges = [GetNullableObjectAtIndex(args, 5) boolValue]; + ListenSourceBox *boxedListenSource = GetNullableObjectAtIndex(args, 6); + ListenSource arg_source = boxedListenSource.value; + [api querySnapshotApp:arg_app + path:arg_path + isCollectionGroup:arg_isCollectionGroup + parameters:arg_parameters + options:arg_options + includeMetadataChanges:arg_includeMetadataChanges + source:arg_source + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceSnapshot", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(documentReferenceSnapshotApp:parameters: + includeMetadataChanges:source:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(documentReferenceSnapshotApp:parameters:includeMetadataChanges:source:" + @"completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + DocumentReferenceRequest *arg_parameters = GetNullableObjectAtIndex(args, 1); + BOOL arg_includeMetadataChanges = [GetNullableObjectAtIndex(args, 2) boolValue]; + ListenSourceBox *boxedListenSource = GetNullableObjectAtIndex(args, 3); + ListenSource arg_source = boxedListenSource.value; + [api documentReferenceSnapshotApp:arg_app + parameters:arg_parameters + includeMetadataChanges:arg_includeMetadataChanges + source:arg_source + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + persistenceCacheIndexManagerRequestApp:request:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(persistenceCacheIndexManagerRequestApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + PersistenceCacheIndexManagerRequestBox *boxedPersistenceCacheIndexManagerRequest = + GetNullableObjectAtIndex(args, 1); + PersistenceCacheIndexManagerRequest arg_request = + boxedPersistenceCacheIndexManagerRequest.value; + [api persistenceCacheIndexManagerRequestApp:arg_app + request:arg_request + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.executePipeline", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(executePipelineApp:stages:options:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(executePipelineApp:stages:options:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSArray *> *arg_stages = GetNullableObjectAtIndex(args, 1); + NSDictionary *arg_options = GetNullableObjectAtIndex(args, 2); + [api executePipelineApp:arg_app + stages:arg_stages + options:arg_options + completion:^(InternalPipelineSnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/cloud_firestore_tvos/tvos/Classes/FirestorePigeonParser.m b/packages/cloud_firestore_tvos/tvos/Classes/FirestorePigeonParser.m new file mode 100644 index 0000000..0178847 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/FirestorePigeonParser.m @@ -0,0 +1,311 @@ +// Copyright 2023, 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. + +#import "FirestorePigeonParser.h" +#import + +@implementation FirestorePigeonParser + ++ (FIRFilter *_Nonnull)filterFromJson:(NSDictionary *_Nullable)map { + if (map[@"fieldPath"]) { + // Deserialize a FilterQuery + NSString *op = map[@"op"]; + FIRFieldPath *fieldPath = map[@"fieldPath"]; + id value = map[@"value"]; + + // All the operators from Firebase + if ([op isEqualToString:@"=="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isEqualTo:value]; + } else if ([op isEqualToString:@"!="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isNotEqualTo:value]; + } else if ([op isEqualToString:@"<"]) { + return [FIRFilter filterWhereFieldPath:fieldPath isLessThan:value]; + } else if ([op isEqualToString:@"<="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isLessThanOrEqualTo:value]; + } else if ([op isEqualToString:@">"]) { + return [FIRFilter filterWhereFieldPath:fieldPath isGreaterThan:value]; + } else if ([op isEqualToString:@">="]) { + return [FIRFilter filterWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value]; + } else if ([op isEqualToString:@"array-contains"]) { + return [FIRFilter filterWhereFieldPath:fieldPath arrayContains:value]; + } else if ([op isEqualToString:@"array-contains-any"]) { + return [FIRFilter filterWhereFieldPath:fieldPath arrayContainsAny:value]; + } else if ([op isEqualToString:@"in"]) { + return [FIRFilter filterWhereFieldPath:fieldPath in:value]; + } else if ([op isEqualToString:@"not-in"]) { + return [FIRFilter filterWhereFieldPath:fieldPath notIn:value]; + } else { + @throw [NSException exceptionWithName:@"InvalidOperator" + reason:@"Invalid operator" + userInfo:nil]; + } + } + // Deserialize a FilterOperator + NSString *op = map[@"op"]; + NSArray *> *queries = map[@"queries"]; + + // Map queries recursively + NSMutableArray *parsedFilters = [NSMutableArray array]; + for (NSDictionary *query in queries) { + [parsedFilters addObject:[self filterFromJson:query]]; + } + + if ([op isEqualToString:@"OR"]) { + return [FIRFilter orFilterWithFilters:parsedFilters]; + } else if ([op isEqualToString:@"AND"]) { + return [FIRFilter andFilterWithFilters:parsedFilters]; + } + + @throw [NSException exceptionWithName:@"InvalidOperator" reason:@"Invalid operator" userInfo:nil]; +} + ++ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull InternalQueryParameters *)parameters + firestore:(nonnull FIRFirestore *)firestore + path:(nonnull NSString *)path + isCollectionGroup:(Boolean)isCollectionGroup { + @try { + FIRQuery *query; + + NSArray *whereConditions = parameters.where; + + if (isCollectionGroup) { + query = [firestore collectionGroupWithID:path]; + } else { + query = (FIRQuery *)[firestore collectionWithPath:path]; + } + + BOOL isFilterQuery = parameters.filters != nil; + if (isFilterQuery) { + FIRFilter *filter = [FirestorePigeonParser filterFromJson:parameters.filters]; + query = [query queryWhereFilter:filter]; + } + + // Filters + for (id item in whereConditions) { + NSArray *condition = item; + FIRFieldPath *fieldPath = (FIRFieldPath *)condition[0]; + NSString *operator= condition[1]; + id value = condition[2]; + if ([operator isEqualToString:@"=="]) { + query = [query queryWhereFieldPath:fieldPath isEqualTo:value]; + } else if ([operator isEqualToString:@"!="]) { + query = [query queryWhereFieldPath:fieldPath isNotEqualTo:value]; + } else if ([operator isEqualToString:@"<"]) { + query = [query queryWhereFieldPath:fieldPath isLessThan:value]; + } else if ([operator isEqualToString:@"<="]) { + query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value]; + } else if ([operator isEqualToString:@">"]) { + query = [query queryWhereFieldPath:fieldPath isGreaterThan:value]; + } else if ([operator isEqualToString:@">="]) { + query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value]; + } else if ([operator isEqualToString:@"array-contains"]) { + query = [query queryWhereFieldPath:fieldPath arrayContains:value]; + } else if ([operator isEqualToString:@"array-contains-any"]) { + query = [query queryWhereFieldPath:fieldPath arrayContainsAny:value]; + } else if ([operator isEqualToString:@"in"]) { + query = [query queryWhereFieldPath:fieldPath in:value]; + } else if ([operator isEqualToString:@"not-in"]) { + query = [query queryWhereFieldPath:fieldPath notIn:value]; + } else { + NSLog( + @"FLTFirebaseFirestore: An invalid query operator %@ was received but not handled.", + operator); + } + } + + // Limit + id limit = parameters.limit; + if (limit) { + query = [query queryLimitedTo:((NSNumber *)limit).intValue]; + } + + // Limit To Last + id limitToLast = parameters.limitToLast; + if (limitToLast) { + query = [query queryLimitedToLast:((NSNumber *)limitToLast).intValue]; + } + + // Ordering + NSArray *orderBy = parameters.orderBy; + if (!orderBy) { + // We return early if no ordering set as cursor queries below require at least one orderBy + // set + return query; + } + + for (NSArray *orderByParameters in orderBy) { + FIRFieldPath *fieldPath = (FIRFieldPath *)orderByParameters[0]; + NSNumber *descending = orderByParameters[1]; + query = [query queryOrderedByFieldPath:fieldPath descending:[descending boolValue]]; + } + + // Start At + id startAt = parameters.startAt; + if (startAt) query = [query queryStartingAtValues:(NSArray *)startAt]; + // Start After + id startAfter = parameters.startAfter; + if (startAfter) query = [query queryStartingAfterValues:(NSArray *)startAfter]; + // End At + id endAt = parameters.endAt; + if (endAt) query = [query queryEndingAtValues:(NSArray *)endAt]; + // End Before + id endBefore = parameters.endBefore; + if (endBefore) query = [query queryEndingBeforeValues:(NSArray *)endBefore]; + + return query; + } @catch (NSException *exception) { + NSLog(@"An error occurred while parsing query arguments, this is most likely an error with " + @"this SDK. %@", + [exception callStackSymbols]); + return nil; + } +} + ++ (FIRFirestoreSource)parseSource:(Source)source { + switch (source) { + case SourceServerAndCache: + return FIRFirestoreSourceDefault; + case SourceServer: + return FIRFirestoreSourceServer; + case SourceCache: + return FIRFirestoreSourceCache; + default: + @throw [NSException exceptionWithName:@"Invalid Source" + reason:@"This source is not supported by the SDK" + userInfo:nil]; + } +} + ++ (NSArray *_Nonnull)parseFieldPath: + (NSArray *> *_Nonnull)fieldPaths { + NSMutableArray *paths = [NSMutableArray arrayWithCapacity:[fieldPaths count]]; + for (NSArray *fieldPath in fieldPaths) { + FIRFieldPath *parsed = [[FIRFieldPath alloc] initWithFields:fieldPath]; + [paths addObject:parsed]; + } + return [NSArray arrayWithArray:paths]; +} + ++ (FIRServerTimestampBehavior)parseServerTimestampBehavior: + (ServerTimestampBehavior)serverTimestampBehavior { + switch (serverTimestampBehavior) { + case ServerTimestampBehaviorNone: + return FIRServerTimestampBehaviorNone; + case ServerTimestampBehaviorEstimate: + return FIRServerTimestampBehaviorEstimate; + case ServerTimestampBehaviorPrevious: + return FIRServerTimestampBehaviorPrevious; + default: + @throw [NSException + exceptionWithName:@"Invalid Server Timestamp Behavior" + reason:@"This Server Timestamp Behavior is not supported by the SDK" + userInfo:nil]; + } +} + ++ (FIRListenSource)parseListenSource:(ListenSource)source { + switch (source) { + case ListenSourceDefaultSource: + return FIRListenSourceDefault; + case ListenSourceCache: + return FIRListenSourceCache; + default: + @throw + [NSException exceptionWithName:@"Invalid ListenSource" + reason:@"This ListenSource Behavior is not supported by the SDK" + userInfo:nil]; + } +} + ++ (InternalSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: + (FIRSnapshotMetadata *_Nonnull)snapshotMetadata { + return [InternalSnapshotMetadata makeWithHasPendingWrites:snapshotMetadata.hasPendingWrites + isFromCache:snapshotMetadata.isFromCache]; +} + ++ (InternalDocumentSnapshot *_Nonnull) + toPigeonDocumentSnapshot:(FIRDocumentSnapshot *_Nonnull)documentSnapshot + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { + return [InternalDocumentSnapshot + makeWithPath:documentSnapshot.reference.path + data:[documentSnapshot dataWithServerTimestampBehavior:serverTimestampBehavior] + metadata:[FirestorePigeonParser toPigeonSnapshotMetadata:documentSnapshot.metadata]]; +} + ++ (DocumentChangeType)toPigeonDocumentChangeType:(FIRDocumentChangeType)documentChangeType { + switch (documentChangeType) { + case FIRDocumentChangeTypeAdded: + return DocumentChangeTypeAdded; + case FIRDocumentChangeTypeModified: + return DocumentChangeTypeModified; + case FIRDocumentChangeTypeRemoved: + return DocumentChangeTypeRemoved; + default: + @throw [NSException exceptionWithName:@"InvalidDocumentChangeType" + reason:@"Invalid document change type" + userInfo:nil]; + } +} + ++ (InternalDocumentChange *_Nonnull) + toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { + NSInteger oldIndex; + NSInteger newIndex; + + // Note the Firestore C++ SDK here returns a maxed UInt that is != NSUIntegerMax, so we make one + // ourselves so we can convert to -1 for Dart. + NSUInteger MAX_VAL = (NSUInteger)[@(-1) integerValue]; + + if (documentChange.newIndex == NSNotFound || documentChange.newIndex == 4294967295 || + documentChange.newIndex == MAX_VAL) { + newIndex = -1; + } else { + newIndex = (NSInteger)documentChange.newIndex; + } + + if (documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4294967295 || + documentChange.oldIndex == MAX_VAL) { + oldIndex = -1; + } else { + oldIndex = (NSInteger)documentChange.oldIndex; + } + + return [InternalDocumentChange + makeWithType:[FirestorePigeonParser toPigeonDocumentChangeType:documentChange.type] + document:[FirestorePigeonParser toPigeonDocumentSnapshot:documentChange.document + serverTimestampBehavior:serverTimestampBehavior] + oldIndex:oldIndex + newIndex:newIndex]; +} + ++ (NSArray *_Nonnull) + toPigeonDocumentChanges:(NSArray *_Nonnull)documentChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { + NSMutableArray *pigeonDocumentChanges = [NSMutableArray array]; + for (FIRDocumentChange *documentChange in documentChanges) { + [pigeonDocumentChanges + addObject:[FirestorePigeonParser toPigeonDocumentChange:documentChange + serverTimestampBehavior:serverTimestampBehavior]]; + } + return pigeonDocumentChanges; +} + ++ (InternalQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot + serverTimestampBehavior: + (FIRServerTimestampBehavior)serverTimestampBehavior { + NSMutableArray *documentSnapshots = [NSMutableArray array]; + for (FIRDocumentSnapshot *documentSnapshot in querySnaphot.documents) { + [documentSnapshots + addObject:[FirestorePigeonParser toPigeonDocumentSnapshot:documentSnapshot + serverTimestampBehavior:serverTimestampBehavior]]; + } + return [InternalQuerySnapshot + makeWithDocuments:documentSnapshots + documentChanges:[FirestorePigeonParser toPigeonDocumentChanges:querySnaphot.documentChanges + serverTimestampBehavior:serverTimestampBehavior] + metadata:[FirestorePigeonParser toPigeonSnapshotMetadata:querySnaphot.metadata]]; +} + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h new file mode 100644 index 0000000..fc97756 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h @@ -0,0 +1,37 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#if TARGET_OS_OSX +#import +#else +@import FirebaseFirestore; +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTDocumentSnapshotStreamHandler : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +@property(nonatomic, strong) FIRDocumentReference *reference; +@property(nonatomic, assign) BOOL includeMetadataChanges; +@property(nonatomic, assign) FIRListenSource source; +@property(nonatomic, assign) FIRServerTimestampBehavior serverTimestampBehavior; + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore + reference:(FIRDocumentReference *)reference + includeMetadataChanges:(BOOL)includeMetadataChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior + source:(FIRListenSource)source; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h new file mode 100644 index 0000000..522a879 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h @@ -0,0 +1,15 @@ +// Copyright 2023 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 +#import + +@interface FLTFirebaseFirestoreExtension : NSObject + +@property(nonatomic, strong, readonly) FIRFirestore *instance; +@property(nonatomic, strong, readonly) NSString *databaseURL; + +- (instancetype)initWithFirestoreInstance:(FIRFirestore *)instance + databaseURL:(NSString *)databaseURL; + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h new file mode 100644 index 0000000..7f49b71 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h @@ -0,0 +1,17 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +@interface FLTFirebaseFirestoreReader : FlutterStandardReader +- (id)readValueOfType:(UInt8)type; ++ (dispatch_queue_t)getFirestoreQueue; +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h new file mode 100644 index 0000000..e04b30b --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h @@ -0,0 +1,70 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#if TARGET_OS_OSX +#import +#else +@import FirebaseFirestore; +#endif +#import +#import "FLTFirebaseFirestoreExtension.h" + +/// Error code used by the pipeline parser for parse/unsupported expression errors. +/// Handled in ErrorCodeAndMessageFromNSError to return code "parse-error" and the error message. +FOUNDATION_EXPORT const NSInteger FLTFirebaseFirestoreErrorCodePipelineParse; + +typedef NS_ENUM(UInt8, FirestoreDataType) { + FirestoreDataTypeDateTime = 180, + FirestoreDataTypeGeoPoint = 181, + FirestoreDataTypeDocumentReference = 182, + FirestoreDataTypeBlob = 183, + FirestoreDataTypeArrayUnion = 184, + FirestoreDataTypeArrayRemove = 185, + FirestoreDataTypeDelete = 186, + FirestoreDataTypeServerTimestamp = 187, + FirestoreDataTypeTimestamp = 188, + FirestoreDataTypeIncrementDouble = 189, + FirestoreDataTypeIncrementInteger = 190, + FirestoreDataTypeDocumentId = 191, + FirestoreDataTypeFieldPath = 192, + FirestoreDataTypeNaN = 193, + FirestoreDataTypeInfinity = 194, + FirestoreDataTypeNegativeInfinity = 195, + FirestoreDataTypeFirestoreInstance = 196, + FirestoreDataTypeFirestoreQuery = 197, + FirestoreDataTypeFirestoreSettings = 198, + FirestoreDataTypeVectorValue = 199, +}; + +@interface FLTFirebaseFirestoreReaderWriter : FlutterStandardReaderWriter +- (FlutterStandardWriter *_Nonnull)writerWithData:(NSMutableData *_Nullable)data; +- (FlutterStandardReader *_Nonnull)readerWithData:(NSData *_Nullable)data; +@end + +@interface FLTFirebaseFirestoreUtils : NSObject ++ (FIRFirestoreSource)FIRFirestoreSourceFromArguments:(NSDictionary *_Nonnull)arguments; ++ (NSArray *_Nonnull)ErrorCodeAndMessageFromNSError:(NSError *_Nonnull)error; ++ (FLTFirebaseFirestoreExtension *_Nullable) + getCachedFIRFirestoreInstanceForAppName:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)url; ++ (void)setCachedFIRFirestoreInstance:(FIRFirestore *_Nonnull)firestore + forAppName:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)url; ++ (void)destroyCachedInstanceForFirestore:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)databaseURL; ++ (FIRFirestore *_Nullable)getFirestoreInstanceByName:(NSString *_Nonnull)appName + databaseURL:(NSString *_Nonnull)databaseURL; ++ (void)cleanupFirestoreInstances:(void (^_Nullable)(void))completion; ++ (NSUInteger)count; ++ (FLTFirebaseFirestoreExtension *_Nullable)getCachedInstanceForFirestore: + (FIRFirestore *_Nonnull)firestore; +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h new file mode 100644 index 0000000..f40262d --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h @@ -0,0 +1,16 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +@interface FLTFirebaseFirestoreWriter : FlutterStandardWriter +- (void)writeValue:(id)value; +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h new file mode 100644 index 0000000..30dbfcd --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022, 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. + */ + +// +// FLTLoadBundleStreamHandler.h +// Pods +// +// Created by Russell Wheatley on 05/05/2021. +// +#import + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#if TARGET_OS_OSX +#import +#else +@import FirebaseFirestore; +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTLoadBundleStreamHandler : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +@property(nonatomic, strong) FlutterStandardTypedData *bundle; + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore + bundle:(FlutterStandardTypedData *)bundle; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTPipelineParser.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTPipelineParser.h new file mode 100644 index 0000000..97c77f0 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTPipelineParser.h @@ -0,0 +1,23 @@ +/* + * Copyright 2026, 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. + */ + +#import + +@class FIRFirestore; + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTPipelineParser : NSObject + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion: + (void (^)(id _Nullable snapshot, NSError *_Nullable error))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h new file mode 100644 index 0000000..8528b72 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h @@ -0,0 +1,31 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTQuerySnapshotStreamHandler : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +@property(nonatomic, strong) FIRQuery *query; +@property(nonatomic, assign) BOOL includeMetadataChanges; +@property(nonatomic, assign) FIRListenSource source; +@property(nonatomic, assign) FIRServerTimestampBehavior serverTimestampBehavior; + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore + query:(FIRQuery *)query + includeMetadataChanges:(BOOL)includeMetadataChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior + source:(FIRListenSource)source; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h new file mode 100644 index 0000000..1a05f12 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h @@ -0,0 +1,23 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTSnapshotsInSyncStreamHandler : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTTransactionStreamHandler.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTTransactionStreamHandler.h new file mode 100644 index 0000000..a5511bf --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FLTTransactionStreamHandler.h @@ -0,0 +1,42 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#if TARGET_OS_OSX +#import +#else +@import FirebaseFirestore; +#endif +#if __has_include() +#import +#else +#import "FirestoreMessages.g.h" +#endif +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTTransactionStreamHandler : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +@property(nonatomic, assign) NSInteger timeout; +@property(nonatomic, assign) NSInteger maxAttempts; + +- (instancetype)initWithId:(NSString *)transactionId + firestore:(FIRFirestore *)firestore + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts + started:(void (^)(FIRTransaction *))startedListener + ended:(void (^)(void))endedListener; +- (void)receiveTransactionResponse:(InternalTransactionResult)resultType + commands:(NSArray *)commands; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FirestorePigeonParser.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FirestorePigeonParser.h new file mode 100644 index 0000000..0bb5806 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Private/FirestorePigeonParser.h @@ -0,0 +1,58 @@ +/* + * Copyright 2023, 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. + */ + +#if TARGET_OS_OSX +#import +#else +@import FirebaseFirestore; +#endif +#import +#if __has_include() +#import +#else +#import "FirestoreMessages.g.h" +#endif +@interface FirestorePigeonParser : NSObject + ++ (FIRFilter *_Nonnull)filterFromJson:(NSDictionary *_Nullable)map; + ++ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull InternalQueryParameters *)parameters + firestore:(nonnull FIRFirestore *)firestore + path:(nonnull NSString *)path + isCollectionGroup:(Boolean)isCollectionGroup; + ++ (FIRFirestoreSource)parseSource:(Source)source; + ++ (NSArray *_Nonnull)parseFieldPath: + (NSArray *> *_Nonnull)fieldPaths; + ++ (FIRServerTimestampBehavior)parseServerTimestampBehavior: + (ServerTimestampBehavior)serverTimestampBehavior; + ++ (FIRListenSource)parseListenSource:(ListenSource)source; + ++ (InternalSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: + (FIRSnapshotMetadata *_Nonnull)snapshotMetadata; + ++ (InternalDocumentSnapshot *_Nonnull) + toPigeonDocumentSnapshot:(FIRDocumentSnapshot *_Nonnull)documentSnapshot + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; + ++ (DocumentChangeType)toPigeonDocumentChangeType:(FIRDocumentChangeType)documentChangeType; + ++ (InternalDocumentChange *_Nonnull) + toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; + ++ (NSArray *_Nonnull) + toPigeonDocumentChanges:(NSArray *_Nonnull)documentChanges + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; + ++ (InternalQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot + serverTimestampBehavior: + (FIRServerTimestampBehavior)serverTimestampBehavior; + +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h new file mode 100644 index 0000000..7127b00 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h @@ -0,0 +1,16 @@ +// 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 "FirestoreMessages.g.h" + +@interface InternalDocumentSnapshot (Map) +- (NSDictionary *)toList; +@end + +@interface InternalDocumentChange (Map) +- (NSDictionary *)toList; +@end + +@interface InternalSnapshotMetadata (Map) +- (NSDictionary *)toList; +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h new file mode 100644 index 0000000..96c6e65 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h @@ -0,0 +1,23 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import +#if __has_include() +#import +#else +#import "FLTFirebasePlugin.h" +#endif +#import "FirestoreMessages.g.h" + +@interface FLTFirebaseFirestorePlugin + : FLTFirebasePlugin ++ (NSMutableDictionary *)serverTimestampMap; +@end diff --git a/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FirestoreMessages.g.h b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FirestoreMessages.g.h new file mode 100644 index 0000000..7fd40da --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/Classes/include/cloud_firestore/Public/FirestoreMessages.g.h @@ -0,0 +1,457 @@ +// Copyright 2023, 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; + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +/// An enumeration of document change types. +typedef NS_ENUM(NSUInteger, DocumentChangeType) { + /// Indicates a new document was added to the set of documents matching the + /// query. + DocumentChangeTypeAdded = 0, + /// Indicates a document within the query was modified. + DocumentChangeTypeModified = 1, + /// Indicates a document within the query was removed (either deleted or no + /// longer matches the query. + DocumentChangeTypeRemoved = 2, +}; + +/// Wrapper for DocumentChangeType to allow for nullability. +@interface DocumentChangeTypeBox : NSObject +@property(nonatomic, assign) DocumentChangeType value; +- (instancetype)initWithValue:(DocumentChangeType)value; +@end + +/// An enumeration of firestore source types. +typedef NS_ENUM(NSUInteger, Source) { + /// Causes Firestore to try to retrieve an up-to-date (server-retrieved) snapshot, but fall back + /// to + /// returning cached data if the server can't be reached. + SourceServerAndCache = 0, + /// Causes Firestore to avoid the cache, generating an error if the server cannot be reached. Note + /// that the cache will still be updated if the server request succeeds. Also note that + /// latency-compensation still takes effect, so any pending write operations will be visible in + /// the + /// returned data (merged into the server-provided data). + SourceServer = 1, + /// Causes Firestore to immediately return a value from the cache, ignoring the server completely + /// (implying that the returned value may be stale with respect to the value on the server). If + /// there is no data in the cache to satisfy the `get` call, + /// [DocumentReference.get] will throw a [FirebaseException] and + /// [Query.get] will return an empty [QuerySnapshotPlatform] with no documents. + SourceCache = 2, +}; + +/// Wrapper for Source to allow for nullability. +@interface SourceBox : NSObject +@property(nonatomic, assign) Source value; +- (instancetype)initWithValue:(Source)value; +@end + +/// The listener retrieves data and listens to updates from the local Firestore cache only. +/// If the cache is empty, an empty snapshot will be returned. +/// Snapshot events will be triggered on cache updates, like local mutations or load bundles. +/// +/// Note that the data might be stale if the cache hasn't synchronized with recent server-side +/// changes. +typedef NS_ENUM(NSUInteger, ListenSource) { + /// The default behavior. The listener attempts to return initial snapshot from cache and retrieve + /// up-to-date snapshots from the Firestore server. + /// Snapshot events will be triggered on local mutations and server side updates. + ListenSourceDefaultSource = 0, + /// The listener retrieves data and listens to updates from the local Firestore cache only. + /// If the cache is empty, an empty snapshot will be returned. + /// Snapshot events will be triggered on cache updates, like local mutations or load bundles. + ListenSourceCache = 1, +}; + +/// Wrapper for ListenSource to allow for nullability. +@interface ListenSourceBox : NSObject +@property(nonatomic, assign) ListenSource value; +- (instancetype)initWithValue:(ListenSource)value; +@end + +typedef NS_ENUM(NSUInteger, ServerTimestampBehavior) { + /// Return null for [FieldValue.serverTimestamp()] values that have not yet + ServerTimestampBehaviorNone = 0, + /// Return local estimates for [FieldValue.serverTimestamp()] values that have not yet been set to + /// their final value. + ServerTimestampBehaviorEstimate = 1, + /// Return the previous value for [FieldValue.serverTimestamp()] values that have not yet been set + /// to their final value. + ServerTimestampBehaviorPrevious = 2, +}; + +/// Wrapper for ServerTimestampBehavior to allow for nullability. +@interface ServerTimestampBehaviorBox : NSObject +@property(nonatomic, assign) ServerTimestampBehavior value; +- (instancetype)initWithValue:(ServerTimestampBehavior)value; +@end + +/// [AggregateSource] represents the source of data for an [AggregateQuery]. +typedef NS_ENUM(NSUInteger, AggregateSource) { + /// Indicates that the data should be retrieved from the server. + AggregateSourceServer = 0, +}; + +/// Wrapper for AggregateSource to allow for nullability. +@interface AggregateSourceBox : NSObject +@property(nonatomic, assign) AggregateSource value; +- (instancetype)initWithValue:(AggregateSource)value; +@end + +/// [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache +/// index manager. +typedef NS_ENUM(NSUInteger, PersistenceCacheIndexManagerRequest) { + PersistenceCacheIndexManagerRequestEnableIndexAutoCreation = 0, + PersistenceCacheIndexManagerRequestDisableIndexAutoCreation = 1, + PersistenceCacheIndexManagerRequestDeleteAllIndexes = 2, +}; + +/// Wrapper for PersistenceCacheIndexManagerRequest to allow for nullability. +@interface PersistenceCacheIndexManagerRequestBox : NSObject +@property(nonatomic, assign) PersistenceCacheIndexManagerRequest value; +- (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value; +@end + +typedef NS_ENUM(NSUInteger, InternalTransactionResult) { + InternalTransactionResultSuccess = 0, + InternalTransactionResultFailure = 1, +}; + +/// Wrapper for InternalTransactionResult to allow for nullability. +@interface InternalTransactionResultBox : NSObject +@property(nonatomic, assign) InternalTransactionResult value; +- (instancetype)initWithValue:(InternalTransactionResult)value; +@end + +typedef NS_ENUM(NSUInteger, InternalTransactionType) { + InternalTransactionTypeGet = 0, + InternalTransactionTypeUpdate = 1, + InternalTransactionTypeSet = 2, + InternalTransactionTypeDeleteType = 3, +}; + +/// Wrapper for InternalTransactionType to allow for nullability. +@interface InternalTransactionTypeBox : NSObject +@property(nonatomic, assign) InternalTransactionType value; +- (instancetype)initWithValue:(InternalTransactionType)value; +@end + +typedef NS_ENUM(NSUInteger, AggregateType) { + AggregateTypeCount = 0, + AggregateTypeSum = 1, + AggregateTypeAverage = 2, +}; + +/// Wrapper for AggregateType to allow for nullability. +@interface AggregateTypeBox : NSObject +@property(nonatomic, assign) AggregateType value; +- (instancetype)initWithValue:(AggregateType)value; +@end + +@class InternalFirebaseSettings; +@class FirestorePigeonFirebaseApp; +@class InternalSnapshotMetadata; +@class InternalDocumentSnapshot; +@class InternalDocumentChange; +@class InternalQuerySnapshot; +@class InternalPipelineResult; +@class InternalPipelineSnapshot; +@class InternalGetOptions; +@class InternalDocumentOption; +@class InternalTransactionCommand; +@class DocumentReferenceRequest; +@class InternalQueryParameters; +@class AggregateQuery; +@class AggregateQueryResponse; + +@interface InternalFirebaseSettings : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPersistenceEnabled:(nullable NSNumber *)persistenceEnabled + host:(nullable NSString *)host + sslEnabled:(nullable NSNumber *)sslEnabled + cacheSizeBytes:(nullable NSNumber *)cacheSizeBytes + ignoreUndefinedProperties:(BOOL)ignoreUndefinedProperties; +@property(nonatomic, strong, nullable) NSNumber *persistenceEnabled; +@property(nonatomic, copy, nullable) NSString *host; +@property(nonatomic, strong, nullable) NSNumber *sslEnabled; +@property(nonatomic, strong, nullable) NSNumber *cacheSizeBytes; +@property(nonatomic, assign) BOOL ignoreUndefinedProperties; +@end + +@interface FirestorePigeonFirebaseApp : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAppName:(NSString *)appName + settings:(InternalFirebaseSettings *)settings + databaseURL:(NSString *)databaseURL; +@property(nonatomic, copy) NSString *appName; +@property(nonatomic, strong) InternalFirebaseSettings *settings; +@property(nonatomic, copy) NSString *databaseURL; +@end + +@interface InternalSnapshotMetadata : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithHasPendingWrites:(BOOL)hasPendingWrites isFromCache:(BOOL)isFromCache; +@property(nonatomic, assign) BOOL hasPendingWrites; +@property(nonatomic, assign) BOOL isFromCache; +@end + +@interface InternalDocumentSnapshot : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPath:(NSString *)path + data:(nullable NSDictionary *)data + metadata:(InternalSnapshotMetadata *)metadata; +@property(nonatomic, copy) NSString *path; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong) InternalSnapshotMetadata *metadata; +@end + +@interface InternalDocumentChange : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(DocumentChangeType)type + document:(InternalDocumentSnapshot *)document + oldIndex:(NSInteger)oldIndex + newIndex:(NSInteger)newIndex; +@property(nonatomic, assign) DocumentChangeType type; +@property(nonatomic, strong) InternalDocumentSnapshot *document; +@property(nonatomic, assign) NSInteger oldIndex; +@property(nonatomic, assign) NSInteger newIndex; +@end + +@interface InternalQuerySnapshot : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithDocuments:(NSArray *)documents + documentChanges:(NSArray *)documentChanges + metadata:(InternalSnapshotMetadata *)metadata; +@property(nonatomic, copy) NSArray *documents; +@property(nonatomic, copy) NSArray *documentChanges; +@property(nonatomic, strong) InternalSnapshotMetadata *metadata; +@end + +@interface InternalPipelineResult : NSObject ++ (instancetype)makeWithDocumentPath:(nullable NSString *)documentPath + createTime:(nullable NSNumber *)createTime + updateTime:(nullable NSNumber *)updateTime + data:(nullable NSDictionary *)data; +@property(nonatomic, copy, nullable) NSString *documentPath; +@property(nonatomic, strong, nullable) NSNumber *createTime; +@property(nonatomic, strong, nullable) NSNumber *updateTime; +/// All fields in the result (from PipelineResult.data() on Android). +@property(nonatomic, copy, nullable) NSDictionary *data; +@end + +@interface InternalPipelineSnapshot : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithResults:(NSArray *)results + executionTime:(NSInteger)executionTime; +@property(nonatomic, copy) NSArray *results; +@property(nonatomic, assign) NSInteger executionTime; +@end + +@interface InternalGetOptions : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithSource:(Source)source + serverTimestampBehavior:(ServerTimestampBehavior)serverTimestampBehavior; +@property(nonatomic, assign) Source source; +@property(nonatomic, assign) ServerTimestampBehavior serverTimestampBehavior; +@end + +@interface InternalDocumentOption : NSObject ++ (instancetype)makeWithMerge:(nullable NSNumber *)merge + mergeFields:(nullable NSArray *> *)mergeFields; +@property(nonatomic, strong, nullable) NSNumber *merge; +@property(nonatomic, copy, nullable) NSArray *> *mergeFields; +@end + +@interface InternalTransactionCommand : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(InternalTransactionType)type + path:(NSString *)path + data:(nullable NSDictionary *)data + option:(nullable InternalDocumentOption *)option; +@property(nonatomic, assign) InternalTransactionType type; +@property(nonatomic, copy) NSString *path; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong, nullable) InternalDocumentOption *option; +@end + +@interface DocumentReferenceRequest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPath:(NSString *)path + data:(nullable NSDictionary *)data + option:(nullable InternalDocumentOption *)option + source:(nullable SourceBox *)source + serverTimestampBehavior:(nullable ServerTimestampBehaviorBox *)serverTimestampBehavior; +@property(nonatomic, copy) NSString *path; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong, nullable) InternalDocumentOption *option; +@property(nonatomic, strong, nullable) SourceBox *source; +@property(nonatomic, strong, nullable) ServerTimestampBehaviorBox *serverTimestampBehavior; +@end + +@interface InternalQueryParameters : NSObject ++ (instancetype)makeWithWhere:(nullable NSArray *> *)where + orderBy:(nullable NSArray *> *)orderBy + limit:(nullable NSNumber *)limit + limitToLast:(nullable NSNumber *)limitToLast + startAt:(nullable NSArray *)startAt + startAfter:(nullable NSArray *)startAfter + endAt:(nullable NSArray *)endAt + endBefore:(nullable NSArray *)endBefore + filters:(nullable NSDictionary *)filters; +@property(nonatomic, copy, nullable) NSArray *> *where; +@property(nonatomic, copy, nullable) NSArray *> *orderBy; +@property(nonatomic, strong, nullable) NSNumber *limit; +@property(nonatomic, strong, nullable) NSNumber *limitToLast; +@property(nonatomic, copy, nullable) NSArray *startAt; +@property(nonatomic, copy, nullable) NSArray *startAfter; +@property(nonatomic, copy, nullable) NSArray *endAt; +@property(nonatomic, copy, nullable) NSArray *endBefore; +@property(nonatomic, copy, nullable) NSDictionary *filters; +@end + +@interface AggregateQuery : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(AggregateType)type field:(nullable NSString *)field; +@property(nonatomic, assign) AggregateType type; +@property(nonatomic, copy, nullable) NSString *field; +@end + +@interface AggregateQueryResponse : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(AggregateType)type + field:(nullable NSString *)field + value:(nullable NSNumber *)value; +@property(nonatomic, assign) AggregateType type; +@property(nonatomic, copy, nullable) NSString *field; +@property(nonatomic, strong, nullable) NSNumber *value; +@end + +/// The codec used by all APIs. +NSObject *GetFirebaseFirestoreHostApiCodec(void); + +@protocol FirebaseFirestoreHostApi +- (void)loadBundleApp:(FirestorePigeonFirebaseApp *)app + bundle:(FlutterStandardTypedData *)bundle + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)namedQueryGetApp:(FirestorePigeonFirebaseApp *)app + name:(NSString *)name + options:(InternalGetOptions *)options + completion: + (void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; +- (void)clearPersistenceApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)disableNetworkApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)enableNetworkApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)terminateApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)waitForPendingWritesApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)setIndexConfigurationApp:(FirestorePigeonFirebaseApp *)app + indexConfiguration:(NSString *)indexConfiguration + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)setLoggingEnabledLoggingEnabled:(BOOL)loggingEnabled + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)snapshotsInSyncSetupApp:(FirestorePigeonFirebaseApp *)app + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)transactionCreateApp:(FirestorePigeonFirebaseApp *)app + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)transactionStoreResultTransactionId:(NSString *)transactionId + resultType:(InternalTransactionResult)resultType + commands: + (nullable NSArray *)commands + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)transactionGetApp:(FirestorePigeonFirebaseApp *)app + transactionId:(NSString *)transactionId + path:(NSString *)path + completion:(void (^)(InternalDocumentSnapshot *_Nullable, + FlutterError *_Nullable))completion; +- (void)documentReferenceSetApp:(FirestorePigeonFirebaseApp *)app + request:(DocumentReferenceRequest *)request + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)documentReferenceUpdateApp:(FirestorePigeonFirebaseApp *)app + request:(DocumentReferenceRequest *)request + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)documentReferenceGetApp:(FirestorePigeonFirebaseApp *)app + request:(DocumentReferenceRequest *)request + completion:(void (^)(InternalDocumentSnapshot *_Nullable, + FlutterError *_Nullable))completion; +- (void)documentReferenceDeleteApp:(FirestorePigeonFirebaseApp *)app + request:(DocumentReferenceRequest *)request + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)queryGetApp:(FirestorePigeonFirebaseApp *)app + path:(NSString *)path + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(InternalQueryParameters *)parameters + options:(InternalGetOptions *)options + completion: + (void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; +- (void)aggregateQueryApp:(FirestorePigeonFirebaseApp *)app + path:(NSString *)path + parameters:(InternalQueryParameters *)parameters + source:(AggregateSource)source + queries:(NSArray *)queries + isCollectionGroup:(BOOL)isCollectionGroup + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)writeBatchCommitApp:(FirestorePigeonFirebaseApp *)app + writes:(NSArray *)writes + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)querySnapshotApp:(FirestorePigeonFirebaseApp *)app + path:(NSString *)path + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(InternalQueryParameters *)parameters + options:(InternalGetOptions *)options + includeMetadataChanges:(BOOL)includeMetadataChanges + source:(ListenSource)source + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)documentReferenceSnapshotApp:(FirestorePigeonFirebaseApp *)app + parameters:(DocumentReferenceRequest *)parameters + includeMetadataChanges:(BOOL)includeMetadataChanges + source:(ListenSource)source + completion: + (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app + request:(PersistenceCacheIndexManagerRequest)request + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)executePipelineApp:(FirestorePigeonFirebaseApp *)app + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(InternalPipelineSnapshot *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void SetUpFirebaseFirestoreHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFirebaseFirestoreHostApiWithSuffix( + id binaryMessenger, NSObject *_Nullable api, + NSString *messageChannelSuffix); + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore_tvos/tvos/cloud_firestore_tvos.podspec b/packages/cloud_firestore_tvos/tvos/cloud_firestore_tvos.podspec new file mode 100644 index 0000000..ee8b123 --- /dev/null +++ b/packages/cloud_firestore_tvos/tvos/cloud_firestore_tvos.podspec @@ -0,0 +1,53 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint cloud_firestore_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'cloud_firestore_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of cloud_firestore.' + s.description = <<-DESC +tvOS implementation of cloud_firestore, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/cloud_firestore_tvos' + s.license = { :file => '../LICENSE' } + s.author = { 'fluttertv' => 'noreply@fluttertv.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*.{h,m,mm,swift}' + s.public_header_files = 'Classes/**/include/**/*.h' + # Firebase/Firestore 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', + } + + # FLTFirebaseFirestorePlugin.m reads @LIBRARY_NAME / @LIBRARY_VERSION as + # preprocessor token-pasted string literals (same as upstream + # cloud_firestore's podspec) — without these the file fails to compile + # with "unexpected '@' in program". + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) LIBRARY_VERSION=\"0.0.1\" LIBRARY_NAME=\"flutter-fire-fst-tvos\"', + } + + # The ported Classes/ call into FIRFirestore (Firebase/Firestore) 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/Firestore', '~> 12.15.0' + s.dependency 'firebase_core_tvos' + s.static_framework = true +end diff --git a/packages/firebase_auth_tvos/.gitignore b/packages/firebase_auth_tvos/.gitignore new file mode 100644 index 0000000..c83dba5 --- /dev/null +++ b/packages/firebase_auth_tvos/.gitignore @@ -0,0 +1,32 @@ +# 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 + +# Local-dev dependency override resolving firebase_core_tvos from the sibling +# package before it is published to pub.dev. Not committed, not published; +# remove it at release time (after firebase_core_tvos is live). See pubspec.yaml. +pubspec_overrides.yaml +example/pubspec_overrides.yaml diff --git a/packages/firebase_auth_tvos/CHANGELOG.md b/packages/firebase_auth_tvos/CHANGELOG.md new file mode 100644 index 0000000..c7d8626 --- /dev/null +++ b/packages/firebase_auth_tvos/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.0.1 + +* Initial tvOS scaffolding generated by `flutter-tvos plugin port` from + `firebase_auth`. diff --git a/packages/firebase_auth_tvos/LICENSE b/packages/firebase_auth_tvos/LICENSE new file mode 100644 index 0000000..000b461 --- /dev/null +++ b/packages/firebase_auth_tvos/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium 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. \ No newline at end of file diff --git a/packages/firebase_auth_tvos/PORTING_REPORT.md b/packages/firebase_auth_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..f50f0e5 --- /dev/null +++ b/packages/firebase_auth_tvos/PORTING_REPORT.md @@ -0,0 +1,141 @@ +# firebase_auth_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-06-30. + +Source: `firebase_auth` 6.5.4 (path: `/Users/aliustaoglu/.pub-cache/hosted/pub.dev/firebase_auth-6.5.4`) +Base platform: ios (Objective-C) +Output: `./firebase_auth_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 + +- `#import ` (`tvos/Classes/FLTFirebaseAuthPlugin.m:22`) +- `#import ` (`tvos/Classes/include/Public/FLTFirebaseAuthPlugin.h:13`) + +## 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 + +- [ ] Read every `✗ stubbed` method above and confirm returning `FlutterMethodNotImplemented` is acceptable on tvOS. +- [ ] Review every `⚠️ partial` method against a real Apple TV (behaviour differs from iOS). +- [ ] Confirm the removed imports were not load-bearing for still-supported code paths. +- [ ] `flutter-tvos build tvos --simulator --debug` from the plugin's example app compiles the generated registrant. +- [ ] Bump the version and update `CHANGELOG.md` before publishing. + +--- + +## Addendum: manual fixes beyond the automated port (2026-06-30) + +The automated port reported a clean compile, but `flutter-tvos build tvos --simulator --debug` +against the real Firebase SDK surfaced several issues the regex-based porter couldn't see — it +only checks for known-bad *imports*, not for which symbols a given SDK build actually exports. +Fixed by hand: + +- **Restored `#import `** in + `FLTFirebaseAuthPlugin.m`/`.h` — the porter strips this import broadly, but Sign in with Apple + (`ASAuthorizationController` et al., used extensively here) is part of the same framework and + *is* available on tvOS 13+. Only `ASWebAuthenticationSession`-style browser flows are iOS/macOS-only. +- **Disabled the entire Multi-Factor Auth surface** (TOTP, phone-factor enrollment/resolution, + `FIRMultiFactorResolver`/`FIRMultiFactorAssertion`/`FIRMultiFactorInfo`) behind + `#if !TARGET_OS_TV`. Firebase's CocoaPods `Firebase/Auth` subspec doesn't export these types' + interfaces for tvOS at all (some aren't even forward-declared). No tvOS workaround exists short + of Firebase shipping it. +- **Disabled phone-number sign-in/verification** (`FIRPhoneAuthProvider`, `verifyPhoneNumberApp`, + `updatePhoneNumberApp`, the `FLTPhoneNumberVerificationStreamHandler` event stream, APNs silent-push + plumbing for it, and the `canHandleURL:`/`canHandleNotification:` universal-link continuation + handlers) — same SDK-export gap, and there's no SMS/phone-number entry UI on tvOS anyway. +- **Disabled `signInWithProvider`/`linkWithProvider`/`reauthenticateWithProvider` for every OAuth + provider except Apple** (Google, GitHub, Twitter, Yahoo, Microsoft, generic OAuth/SAML) — these + route through `getCredentialWithUIDelegate:`, a browser-redirect (`ASWebAuthenticationSession`) + flow tvOS has no WebKit for. Apple Sign In doesn't need a browser (`ASAuthorizationController` + drives it directly) and still works. +- **Disabled `initializeRecaptchaConfigApp`** — reCAPTCHA app-verification also needs WebKit. +- **Fixed 4 headers using `#import "../Public/X.h"`** (relative-parent-directory imports) to + `#import "X.h"` — CocoaPods flattens `Public/` and `Private/` into one directory when building + a framework (`use_frameworks!`), so the original relative paths only worked while compiling the + pod itself, not when the Runner target consumed it via `@import firebase_auth_tvos;`. +- **Podspec**: added `Firebase/Auth` and `firebase_core_tvos` dependencies (not upstream's + `firebase_core`, which has no tvOS platform declaration), bumped `s.platform` to tvOS 15.0 + (Firebase 12.x's floor), added the `LIBRARY_NAME`/`LIBRARY_VERSION` preprocessor defines the + plugin file expects. +- **`lib/firebase_auth_tvos.dart`**: replaced the porter's copied `FirebaseAuth`/`User`/etc. Dart + classes with a one-line re-export of `package:firebase_auth/firebase_auth.dart` — those classes + have no per-platform override even on iOS (method-channel only), so duplicating them would have + created a second, incompatible type other Firebase plugins don't recognize. +- **Example app**: trimmed from upstream's kitchen-sink example (Facebook/Google/FontAwesome + social-sign-in UI, none of which has a tvOS implementation, and `font_awesome_flutter` was + separately incompatible with this monorepo's pinned Flutter SDK) down to a minimal app + exercising anonymous and email/password sign-in — the auth flows that work on tvOS without a + browser. + +Verified: `flutter-tvos build tvos --simulator --debug` against the trimmed example completes +with no compiler errors (native pod + Dart kernel + GeneratedPluginRegistrant all link). Not +verified: a live `FirebaseAuth` round-trip against a real Firebase project (no test project +configured), and behavior on a physical Apple TV. + +## Addendum: runtime verification & a launch-crash fix (2026-07-01) + +Ran the plugin on a tvOS simulator against a real Firebase project. This caught a **critical bug +that a compile-only check completely missed and that would have crashed every app using this +plugin at launch**: + +- **Pigeon registration asserts every protocol selector exists.** The generated + `firebase_auth_messages.g.m` `SetUpWithSuffix` functions call + `NSCAssert([api respondsToSelector:@selector(...)])` for **every** method of + `MultiFactorUserHostApi` / `MultiFactoResolverHostApi` / `MultiFactorTotpHostApi` / + `MultiFactorTotpSecretHostApi` at registration time. By `#if !TARGET_OS_TV`-**removing** the + disabled MFA/TOTP methods entirely (see the earlier addendum), the plugin no longer responded to + `enrollTotpApp:…`/`getSessionApp:…`/etc., so `+[FLTFirebaseAuthPlugin registerWithRegistrar:]` + aborted with `NSInternalInconsistencyException` **before any Dart ran** — the whole app SIGABRT'd + on launch. This compiled perfectly. + - **Fix:** the disabled MFA/TOTP methods are now kept present on tvOS as **stubs** that return an + `unsupported-platform` `FlutterError`, so `respondsToSelector` is satisfied while the + tvOS-absent Firebase types are never touched. The real implementations remain under + `#if !TARGET_OS_TV`. (Lesson: on tvOS, *stub* Pigeon methods — never remove them.) + +Runtime smoke results on Apple TV 4K simulator (tvOS 17.5), throwaway project `fluttertv-smoke-*`: + +- `core.initializeApp` → **PASS** +- `auth.signInAnonymously` → **PASS**: after enabling Anonymous sign-in in the Firebase console, the + plugin returned a real anonymous user (`uid=JgrhZiTbpNMhNIi1QkzB0hA375B3`). Full end-to-end round + trip — registers, calls the Identity Toolkit backend, parses the credential, and surfaces the + `User` to Dart. (Before the toggle it cleanly round-tripped the `[firebase_auth/internal-error]` + config error instead — proving the plugin worked either way.) +- Confirmed `Platform.operatingSystem == "tvos"` and `Platform.isIOS == true` at runtime. + +**Physical device (2026-07-01):** the same smoke harness was also run on a **real Apple TV 4K +(tvOS 26.5) in profile/AOT mode** — `signInAnonymously` returned a real anonymous user on hardware +(`uid=3OdiW1zBB5…`), core+firestore PASS alongside it, no registration crash. This confirms the +Pigeon-stub fix and platform identity (`operatingSystem=="tvos"`, `isIOS==true`) hold under AOT on +hardware, not just JIT/simulator. + +Still not verified: the disabled paths (MFA, phone, browser-OAuth providers) which return +`unsupported-platform` by design — these are inherently untestable without the tvOS-absent Firebase +APIs. + +Manual review required. Read this report top-to-bottom before publishing `firebase_auth_tvos`. diff --git a/packages/firebase_auth_tvos/README.md b/packages/firebase_auth_tvos/README.md new file mode 100644 index 0000000..5a5004f --- /dev/null +++ b/packages/firebase_auth_tvos/README.md @@ -0,0 +1,57 @@ +# firebase_auth_tvos + +The tvOS (Apple TV) implementation of [`firebase_auth`](https://pub.dev/packages/firebase_auth), +provided by the [flutter-tvos](https://github.com/fluttertv/flutter-tvos) toolchain. + +> Generated by [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `firebase_auth`, then completed by hand. See `PORTING_REPORT.md` for the +> full list of what was changed and disabled. + +## Usage + +This is a federated plugin implementation. An app that already depends on +`firebase_auth` and targets Apple TV only needs to add this package alongside +it: + +```yaml +dependencies: + firebase_auth: ^6.5.4 + firebase_auth_tvos: ^0.0.1 +``` + +The native plugin registers automatically through flutter-tvos' plugin +registrant — no extra imports or setup in app code. Use the normal +`firebase_auth` API; it routes to the Apple TV native side. + +## tvOS support + +Apple TV has no web browser (no `ASWebAuthenticationSession`/`SFSafariViewController`), +no SMS/telephony, and the Firebase Apple SDK does not expose multi-factor or +phone APIs for tvOS. As a result this plugin supports the auth methods that work +without a browser redirect, and throws/no-ops for the rest. + +| Auth method | tvOS | Notes | +|---|:---:|---| +| Anonymous sign-in | ✅ | | +| Email / password (sign-in, create user, reset) | ✅ | | +| Email link sign-in | ✅ | | +| Sign in with Apple | ✅ | Uses `ASAuthorizationController` (no browser needed) | +| Custom token sign-in | ✅ | | +| Token refresh, `authStateChanges`, sign-out, reload, profile update | ✅ | | +| Google / Facebook / GitHub / Twitter / Yahoo / Microsoft / generic OAuth | ❌ | Require a browser-redirect (`signInWithProvider`) tvOS can't show | +| Phone number / SMS verification | ❌ | No telephony on tvOS; not in the Firebase tvOS SDK | +| Multi-factor auth (TOTP & phone) | ❌ | Not exposed by the Firebase Apple SDK on tvOS | +| reCAPTCHA app verification | ❌ | Requires WebKit, absent on tvOS | + +Calls into the unsupported methods return an `unsupported-platform`/ +`second-factor-required` `FirebaseAuthException` or are no-ops rather than +crashing. See `PORTING_REPORT.md` for the exact per-method behaviour. + +## Requirements + +- Apple TV running tvOS 15.0 or later (the Firebase Apple SDK's minimum). +- `firebase_core_tvos` (pulled in automatically). + +## License + +fluttertv, under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/firebase_auth_tvos/analysis_options.yaml b/packages/firebase_auth_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/README.md b/packages/firebase_auth_tvos/example/README.md new file mode 100644 index 0000000..8a9c4d3 --- /dev/null +++ b/packages/firebase_auth_tvos/example/README.md @@ -0,0 +1,58 @@ +# Firebase Auth Example + +[![pub package](https://img.shields.io/pub/v/firebase_auth.svg)](https://pub.dev/packages/firebase_auth) + +Demonstrates how to use the `firebase_auth` plugin and enable multiple auth providers. + +## Phone Auth + +1. Enable phone authentication in the [Firebase console]((https://console.firebase.google.com/u/0/project/_/authentication/providers)). +2. Add test phone number and verification code to the Firebase console. + - For this sample the number `+1 408-555-6969` and verification code `888888` are used. +3. For iOS set the `URL Schemes` to the `REVERSE_CLIENT_ID` from the `GoogleServices-Info.plist` file. +4. Enter the phone number `+1 408-555-6969` and press the `Verify phone number` button. +5. Once the phone number is verified the app displays the test + verification code. +6. Enter the verficication code `888888` and press "Sign in with phone number" +7. Signed in user ID is now displayed in the UI. + +## Google Sign-In + +1. Enable Google authentication in the [Firebase console](https://console.firebase.google.com/u/0/project/_/authentication/providers). +2. For Android, add your app's package name and SHA-1 fingerprint to the [Settings page](https://console.firebase.google.com/project/_/settings/general) of the Firebase console. Refer to [Authenticating Your Client]('https://developers.google.com/android/guides/client-auth') for details on how to get your app's SHA-1 fingerprint. +3. For iOS set the `URL Schemes` to the `REVERSE_CLIENT_ID` from the `GoogleServices-Info.plist` file (same step for `Phone Auth` above). +4. Select `Google` under `Social Authentication` and click the `Sign In With Google` button. +5. Signed in user's details are displayed in the UI. + +### Running on Web + +Make sure you run the example app on port 5000, since `localhost:5000` is +whitelisted for Google authentication. To do so, run: + +``` +flutter run -d web-server --web-port 5000 +``` + +## GitHub Sign-In +To get your `clientId` and `clientSecret`: +1. Visit https://github.com/settings/developers. +2. Create a new OAuth application. +3. Set **Home Page URL** to `https://react-native-firebase-testing.firebaseapp.com`. +4. Set **Authorization callback URL** to `https://react-native-firebase-testing.firebaseapp.com/__/auth/handler`. +5. After you register your app, add the `clientId` and `clientSecret` to the example app config in [`lib/config.dart`](./lib/config.dart). + +## Twitter Sign-In +Twitter sign in requires you to add keys from Twitter Developer API to Firebase Console, which means you cannot use the provided configurations with the example app, instead, **please create a new Firebase project**, then enable Twitter as an Auth provider (*optionally you can enable the rest of providers supported in this example*). + +To get your `apiKey` and `apiSecretKey` for Twitter: +1. Sign up for a developer account on [Twitter Developer](https://developer.twitter.com). +2. Create a new app and copy your keys. +3. From the dashboard, go to your app settings, then go to OAuth settings and turn on OAuth 1.0a, then add 2 callback URLs: + 1. `flutterfireauth://` + 2. `https://react-native-firebase-testing.firebaseapp.com/__/auth/handler` +4. Add your keys to the example app config in [`lib/config.dart`](./lib/config.dart). + +## Getting Started + +For help getting started with Flutter, view the online +[documentation](https://flutter.dev/). diff --git a/packages/firebase_auth_tvos/example/lib/firebase_options.dart b/packages/firebase_auth_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..3c968dc --- /dev/null +++ b/packages/firebase_auth_tvos/example/lib/firebase_options.dart @@ -0,0 +1,98 @@ +// Copyright 2022, 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. + +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return macos; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.auth.example', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.auth.example', + ); +} diff --git a/packages/firebase_auth_tvos/example/lib/main.dart b/packages/firebase_auth_tvos/example/lib/main.dart new file mode 100755 index 0000000..c1191ed --- /dev/null +++ b/packages/firebase_auth_tvos/example/lib/main.dart @@ -0,0 +1,95 @@ +// 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. + +// Trimmed down by hand from upstream firebase_auth's example app, which +// pulls in several third-party social-sign-in UI packages +// (flutter_facebook_auth, google_sign_in, font_awesome_flutter, …) that +// have no tvOS implementation and, separately, were incompatible with this +// monorepo's pinned Flutter SDK (font_awesome_flutter 10.x extends the now +// `final` IconData class). None of those providers work on tvOS anyway — +// they rely on browser-redirect flows (ASWebAuthenticationSession / +// SFSafariViewController) that tvOS doesn't support. This example instead +// exercises the auth flows that are tvOS-compatible: anonymous and +// email/password sign-in, both of which go straight to the Identity +// Toolkit REST API with no browser involved. + +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; +import 'firebase_options.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + Future _auth() async { + final app = await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + return FirebaseAuth.instanceFor(app: app); + } + + Future signInAnonymously() async { + final auth = await _auth(); + final credential = await auth.signInAnonymously(); + print('Signed in anonymously as ${credential.user?.uid}'); + } + + Future createUser() async { + final auth = await _auth(); + final credential = await auth.createUserWithEmailAndPassword( + email: 'demo@example.com', + password: 'correct horse battery staple', + ); + print('Created user ${credential.user?.uid}'); + } + + Future signOut() async { + final auth = await _auth(); + await auth.signOut(); + print('Signed out'); + } + + void authStateChanges() async { + final auth = await _auth(); + auth.authStateChanges().listen((user) { + print('Auth state changed: ${user?.uid}'); + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('Firebase Auth example app')), + body: Padding( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ElevatedButton( + onPressed: signInAnonymously, + child: const Text('Sign in anonymously'), + ), + ElevatedButton( + onPressed: createUser, + child: const Text('Create email/password user'), + ), + ElevatedButton( + onPressed: authStateChanges, + child: const Text('Listen for auth state changes'), + ), + ElevatedButton( + onPressed: signOut, + child: const Text('Sign out'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/firebase_auth_tvos/example/pubspec.yaml b/packages/firebase_auth_tvos/example/pubspec.yaml new file mode 100644 index 0000000..c019177 --- /dev/null +++ b/packages/firebase_auth_tvos/example/pubspec.yaml @@ -0,0 +1,19 @@ +name: firebase_auth_example +description: Demonstrates how to use the firebase_auth plugin on tvOS. + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + firebase_auth: ^6.5.4 + firebase_auth_tvos: + path: ../ + firebase_core: ^4.11.0 + firebase_core_tvos: + path: ../../firebase_core_tvos + flutter: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/firebase_auth_tvos/example/tvos/.gitignore b/packages/firebase_auth_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/firebase_auth_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Flutter/Release.xcconfig b/packages/firebase_auth_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Podfile b/packages/firebase_auth_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..39a9600 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 062DFA1BBDEB8540F972A8E2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10CD871C26472CFAFFA205DE /* Pods_Runner.framework */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 */ + 10CD871C26472CFAFFA205DE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 5BC9139FD6DD5391D9233377 /* 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 = ""; }; + 8E2E48CECC6385781C43980C /* 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 = ""; }; + 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 = ""; }; + DBCBCE32CC9BBC888FD97F22 /* 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 */, + 062DFA1BBDEB8540F972A8E2 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 97C146E51CF9000082B4168C = { + isa = PBXGroup; + children = ( + 97C146F01CF9000082B4168C /* Runner */, + 97C146F01CF9000082B4168E /* Flutter */, + 97C146F01CF9000082B4168F /* Frameworks */, + 97C146EF1CF9000082B41690 /* Products */, + AFFCD63F12B92A6E8146059E /* 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 */, + 10CD871C26472CFAFFA205DE /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + AFFCD63F12B92A6E8146059E /* Pods */ = { + isa = PBXGroup; + children = ( + DBCBCE32CC9BBC888FD97F22 /* Pods-Runner.debug.xcconfig */, + 5BC9139FD6DD5391D9233377 /* Pods-Runner.release.xcconfig */, + 8E2E48CECC6385781C43980C /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000082B41690 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 5FA7E62394ED5BE8C3D6323F /* [CP] Check Pods Manifest.lock */, + 97C146EA1CF9000082B4168C /* Sources */, + 97C146EB1CF9000082B4168C /* Frameworks */, + 97C146EC1CF9000082B4168C /* Resources */, + AAF10000000000000000F00D /* Embed App.framework */, + 9740EEB31CF901A200538489 /* Copy flutter_assets */, + FB20707660B344E9A6A65B1F /* [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 */ + 5FA7E62394ED5BE8C3D6323F /* [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; + }; + 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"; + }; + FB20707660B344E9A6A65B1F /* [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; + }; +/* 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.firebaseAuthExample; + 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)"; + 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.firebaseAuthExample; + 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.firebaseAuthExample; + 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_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ee3561d --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/AppDelegate.swift b/packages/firebase_auth_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/firebase_auth_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/firebase_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/firebase_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/firebase_auth_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_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/firebase_auth_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_auth_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/firebase_auth_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/Info.plist b/packages/firebase_auth_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..b751cc4 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Firebase_auth_example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + firebase_auth_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + + FLTAssetsPath + flutter_assets + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/packages/firebase_auth_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/firebase_auth_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/firebase_auth_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_auth_tvos/lib/firebase_auth_tvos.dart b/packages/firebase_auth_tvos/lib/firebase_auth_tvos.dart new file mode 100644 index 0000000..7bb8b9f --- /dev/null +++ b/packages/firebase_auth_tvos/lib/firebase_auth_tvos.dart @@ -0,0 +1,14 @@ +// Copyright 2019 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. + +// Same reasoning as firebase_core_tvos: firebase_auth's public Dart API +// (FirebaseAuth, User, UserCredential, …) has no per-platform Dart +// override — it talks to native through firebase_auth_platform_interface's +// MethodChannel implementation regardless of platform. Duplicating it here +// would create incompatible types vs. apps that import +// package:firebase_auth/firebase_auth.dart directly. This package only +// supplies the native tvOS pluginClass (tvos/Classes/); apps depend on +// firebase_auth (Dart API) and firebase_auth_tvos (native registration) +// side by side — see example/. +export 'package:firebase_auth/firebase_auth.dart'; diff --git a/packages/firebase_auth_tvos/pubspec.yaml b/packages/firebase_auth_tvos/pubspec.yaml new file mode 100644 index 0000000..0832ea7 --- /dev/null +++ b/packages/firebase_auth_tvos/pubspec.yaml @@ -0,0 +1,44 @@ +name: firebase_auth_tvos +description: >- + tvOS (Apple TV) implementation of the firebase_auth Flutter plugin, supporting + anonymous, email/password and Sign in with Apple authentication on Apple TV. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/firebase_auth_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`. See PORTING_REPORT.md. +# License holder: fluttertv + +# The example ships the standard FlutterFire demo-project GoogleService +# values (client-side Firebase identifiers, not secrets). Tell pub's secret +# scanner they are intentional, matching upstream firebase_auth's pubspec. +false_secrets: + - example/** + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + firebase_auth: ^6.5.4 + # Transitive Dart dependency so the app's dependency graph includes + # firebase_core_tvos — our 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 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: FLTFirebaseAuthPlugin diff --git a/packages/firebase_auth_tvos/test/firebase_auth_tvos_test.dart b/packages/firebase_auth_tvos/test/firebase_auth_tvos_test.dart new file mode 100644 index 0000000..93b77a2 --- /dev/null +++ b/packages/firebase_auth_tvos/test/firebase_auth_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-06-30 by `flutter-tvos plugin port`. +// Source plugin: firebase_auth + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/firebase_auth_tvos/tvos/Classes/FLTAuthStateChannelStreamHandler.m b/packages/firebase_auth_tvos/tvos/Classes/FLTAuthStateChannelStreamHandler.m new file mode 100644 index 0000000..5ef9ada --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/FLTAuthStateChannelStreamHandler.m @@ -0,0 +1,56 @@ +// 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 FirebaseAuth; +#import "include/Private/FLTAuthStateChannelStreamHandler.h" +#import +#import "include/Private/PigeonParser.h" +#import "include/Public/FLTFirebaseAuthPlugin.h" + +@implementation FLTAuthStateChannelStreamHandler { + FIRAuth *_auth; + FIRAuthStateDidChangeListenerHandle _listener; +} + +- (instancetype)initWithAuth:(FIRAuth *)auth { + self = [super init]; + if (self) { + _auth = auth; + } + return self; +} + +- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events { + bool __block initialAuthState = YES; + + _listener = [_auth addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, + FIRUser *_Nullable user) { + if (initialAuthState) { + initialAuthState = NO; + return; + } + + if (user) { + events(@{ + @"user" : [PigeonParser getManualList:[PigeonParser getPigeonDetails:[auth currentUser]]] + }); + } else { + events(@{ + @"user" : [NSNull null], + }); + } + }]; + + return nil; +} + +- (FlutterError *)onCancelWithArguments:(id)arguments { + if (_listener) { + [_auth removeAuthStateDidChangeListener:_listener]; + } + _listener = nil; + + return nil; +} + +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/FLTFirebaseAuthPlugin.m b/packages/firebase_auth_tvos/tvos/Classes/FLTFirebaseAuthPlugin.m new file mode 100644 index 0000000..d69410d --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/FLTFirebaseAuthPlugin.m @@ -0,0 +1,2606 @@ +// Copyright 2020 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 FirebaseAuth; +#import +#import +// Points at our own firebase_core_tvos pod, not upstream's "firebase_core" +// pod (which has no tvOS platform declaration) — see PORTING_REPORT.md. +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif + +#import "include/Private/FLTAuthStateChannelStreamHandler.h" +#import "include/Private/FLTIdTokenChannelStreamHandler.h" +#import "include/Private/FLTPhoneNumberVerificationStreamHandler.h" +#import "include/Private/PigeonParser.h" + +#import "include/Public/CustomPigeonHeader.h" +#import "include/Public/FLTFirebaseAuthPlugin.h" +@import CommonCrypto; +// Restored by hand after porting: the porter's compatibility database flags +// AuthenticationServices as tvOS-incompatible because most of its API +// (ASWebAuthenticationSession, SFSafariViewController-adjacent browser +// flows) isn't available on tvOS. But Sign in with Apple +// (ASAuthorizationController / ASAuthorizationAppleIDProvider, used +// extensively below) is part of the same framework and *is* available on +// tvOS 13+ — stripping the import broke a still-supported code path. +#import + +#if __has_include() +#import +#else +#import "FLTFirebaseCorePlugin.h" +#endif + +NSString *const kFLTFirebaseAuthChannelName = @"plugins.flutter.io/firebase_auth"; + +// Argument Keys +NSString *const kAppName = @"appName"; + +// Provider type keys. +NSString *const kSignInMethodPassword = @"password"; +NSString *const kSignInMethodEmailLink = @"emailLink"; +NSString *const kSignInMethodFacebook = @"facebook.com"; +NSString *const kSignInMethodGoogle = @"google.com"; +NSString *const kSignInMethodGameCenter = @"gc.apple.com"; +NSString *const kSignInMethodTwitter = @"twitter.com"; +NSString *const kSignInMethodGithub = @"github.com"; +NSString *const kSignInMethodApple = @"apple.com"; +NSString *const kSignInMethodPhone = @"phone"; +NSString *const kSignInMethodOAuth = @"oauth"; + +// Credential argument keys. +NSString *const kArgumentCredential = @"credential"; +NSString *const kArgumentProviderId = @"providerId"; +NSString *const kArgumentProviderScope = @"scopes"; +NSString *const kArgumentProviderCustomParameters = @"customParameters"; +NSString *const kArgumentSignInMethod = @"signInMethod"; +NSString *const kArgumentSecret = @"secret"; +NSString *const kArgumentIdToken = @"idToken"; +NSString *const kArgumentAccessToken = @"accessToken"; +NSString *const kArgumentRawNonce = @"rawNonce"; +NSString *const kArgumentEmail = @"email"; +NSString *const kArgumentCode = @"code"; +NSString *const kArgumentNewEmail = @"newEmail"; +NSString *const kArgumentEmailLink = kSignInMethodEmailLink; +NSString *const kArgumentToken = @"token"; +NSString *const kArgumentVerificationId = @"verificationId"; +NSString *const kArgumentSmsCode = @"smsCode"; +NSString *const kArgumentActionCodeSettings = @"actionCodeSettings"; +NSString *const kArgumentFamilyName = @"familyName"; +NSString *const kArgumentGivenName = @"givenName"; +NSString *const kArgumentMiddleName = @"middleName"; +NSString *const kArgumentNickname = @"nickname"; +NSString *const kArgumentNamePrefix = @"namePrefix"; +NSString *const kArgumentNameSuffix = @"nameSuffix"; + +// MultiFactor +NSString *const kArgumentMultiFactorHints = @"multiFactorHints"; +NSString *const kArgumentMultiFactorSessionId = @"multiFactorSessionId"; +NSString *const kArgumentMultiFactorResolverId = @"multiFactorResolverId"; +NSString *const kArgumentMultiFactorInfo = @"multiFactorInfo"; + +// Manual error codes & messages. +NSString *const kErrCodeNoCurrentUser = @"no-current-user"; +NSString *const kErrMsgNoCurrentUser = @"No user currently signed in."; +NSString *const kErrCodeInvalidCredential = @"invalid-credential"; +NSString *const kErrMsgInvalidCredential = + @"The supplied auth credential is malformed, has expired or is not " + @"currently supported."; + +// Used for caching credentials between Method Channel method calls. +static NSMutableDictionary *credentialsMap; + +@interface FLTFirebaseAuthPlugin () +@property(nonatomic, retain) NSObject *messenger; +@property(strong, nonatomic) FIROAuthProvider *authProvider; +// Used to keep the user who wants to link with Apple Sign In +@property(strong, nonatomic) FIRUser *linkWithAppleUser; +@property(strong, nonatomic) FIRAuth *signInWithAppleAuth; +@property BOOL isReauthenticatingWithApple; +@property(strong, nonatomic) NSString *currentNonce; +@property(strong, nonatomic) void (^appleCompletion) + (InternalUserCredential *_Nullable, FlutterError *_Nullable); +@property(strong, nonatomic) AuthPigeonFirebaseApp *appleArguments; +/// YES while an `ASAuthorizationController` Sign in with Apple flow is active. +@property(nonatomic, assign) BOOL appleSignInRequestInFlight; + +@end + +@implementation FLTFirebaseAuthPlugin { + // Map an id to a MultiFactorSession object. + NSMutableDictionary *_multiFactorSessionMap; + +// FIRMultiFactorResolver / FIRMultiFactorAssertion aren't even forward +// `@class`-declared in Firebase's tvOS build of FirebaseAuth (unlike +// FIRMultiFactorSession / FIRTOTPSecret above, which are forward-declared +// but lack a visible interface) — these two ivars don't compile at all on +// tvOS. +#if !TARGET_OS_TV + // Map an id to a MultiFactorResolver object. + NSMutableDictionary *_multiFactorResolverMap; + + // Map an id to a MultiFactorResolver object. + NSMutableDictionary *_multiFactorAssertionMap; +#endif // !TARGET_OS_TV + + // Map an id to a MultiFactorResolver object. + NSMutableDictionary *_multiFactorTotpSecretMap; + + // Emulator host/port per app, used to build REST URLs for workarounds. + NSMutableDictionary *_emulatorConfigs; + + NSObject *_binaryMessenger; + NSMutableDictionary *_eventChannels; + NSMutableDictionary *> *_streamHandlers; + NSData *_apnsToken; +} + +#pragma mark - FlutterPlugin + +- (instancetype)init:(NSObject *)messenger { + self = [super init]; + if (self) { + [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:self]; + credentialsMap = [NSMutableDictionary dictionary]; + _binaryMessenger = messenger; + _eventChannels = [NSMutableDictionary dictionary]; + _streamHandlers = [NSMutableDictionary dictionary]; + + _multiFactorSessionMap = [NSMutableDictionary dictionary]; +#if !TARGET_OS_TV + _multiFactorResolverMap = [NSMutableDictionary dictionary]; + _multiFactorAssertionMap = [NSMutableDictionary dictionary]; +#endif // !TARGET_OS_TV + _multiFactorTotpSecretMap = [NSMutableDictionary dictionary]; + _emulatorConfigs = [NSMutableDictionary dictionary]; + } + return self; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:kFLTFirebaseAuthChannelName + binaryMessenger:[registrar messenger]]; + FLTFirebaseAuthPlugin *instance = [[FLTFirebaseAuthPlugin alloc] init:registrar.messenger]; + + [registrar addMethodCallDelegate:instance channel:channel]; + + [registrar publish:instance]; + [registrar addApplicationDelegate:instance]; +#if !TARGET_OS_OSX + if (@available(iOS 13.0, tvOS 13.0, *)) { + if ([registrar respondsToSelector:@selector(addSceneDelegate:)]) { + [registrar performSelector:@selector(addSceneDelegate:) withObject:instance]; + } + } +#endif + SetUpFirebaseAuthHostApi(registrar.messenger, instance); + SetUpFirebaseAuthUserHostApi(registrar.messenger, instance); + SetUpMultiFactorUserHostApi(registrar.messenger, instance); + SetUpMultiFactoResolverHostApi(registrar.messenger, instance); + SetUpMultiFactorTotpHostApi(registrar.messenger, instance); + SetUpMultiFactorTotpSecretHostApi(registrar.messenger, instance); +} + ++ (FlutterError *)convertToFlutterError:(NSError *)error { + NSString *code = @"unknown"; + NSString *message = @"An unknown error has occurred."; + + if (error == nil) { + return [FlutterError errorWithCode:code message:message details:@{}]; + } + + // code + if ([error userInfo][FIRAuthErrorUserInfoNameKey] != nil) { + // See [FIRAuthErrorCodeString] for list of codes. + // Codes are in the format "ERROR_SOME_NAME", converting below to the format + // required in Dart. ERROR_SOME_NAME -> SOME_NAME + NSString *firebaseErrorCode = [error userInfo][FIRAuthErrorUserInfoNameKey]; + code = [firebaseErrorCode stringByReplacingOccurrencesOfString:@"ERROR_" withString:@""]; + // SOME_NAME -> SOME-NAME + code = [code stringByReplacingOccurrencesOfString:@"_" withString:@"-"]; + // SOME-NAME -> some-name + code = [code lowercaseString]; + } + + // message + if ([error userInfo][NSLocalizedDescriptionKey] != nil) { + message = [error userInfo][NSLocalizedDescriptionKey]; + } + + NSMutableDictionary *additionalData = [NSMutableDictionary dictionary]; + // additionalData.email + if ([error userInfo][FIRAuthErrorUserInfoEmailKey] != nil) { + additionalData[kArgumentEmail] = [error userInfo][FIRAuthErrorUserInfoEmailKey]; + } + // We want to store the credential if present for future sign in if the exception contains a + // credential, we pass a token back to Flutter to allow retrieval of the credential. + NSNumber *token = [FLTFirebaseAuthPlugin storeAuthCredentialIfPresent:error]; + + // additionalData.authCredential + if ([error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey] != nil) { + FIRAuthCredential *authCredential = [error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey]; + additionalData[@"authCredential"] = [PigeonParser getPigeonAuthCredential:authCredential + token:token]; + } + + // Manual message overrides to ensure messages/codes matches other platforms. + if ([message isEqual:@"The password must be 6 characters long or more."]) { + message = @"Password should be at least 6 characters"; + } + + return [FlutterError errorWithCode:code message:message details:additionalData]; +} + ++ (id)getNSDictionaryFromAuthCredential:(FIRAuthCredential *)authCredential { + if (authCredential == nil) { + return [NSNull null]; + } + + NSString *accessToken = nil; + if ([authCredential isKindOfClass:[FIROAuthCredential class]]) { + if (((FIROAuthCredential *)authCredential).accessToken != nil) { + accessToken = ((FIROAuthCredential *)authCredential).accessToken; + } else if (((FIROAuthCredential *)authCredential).IDToken != nil) { + // For Sign In With Apple, the token is stored in IDToken + accessToken = ((FIROAuthCredential *)authCredential).IDToken; + } + } + + return @{ + kArgumentProviderId : authCredential.provider, + // Note: "signInMethod" does not exist on iOS SDK, so using provider + // instead. + kArgumentSignInMethod : authCredential.provider, + kArgumentToken : @([authCredential hash]), + kArgumentAccessToken : accessToken ?: [NSNull null], + }; +} + +- (void)cleanupWithCompletion:(void (^)(void))completion { + // Cleanup credentials. + [credentialsMap removeAllObjects]; + + for (FlutterEventChannel *channel in self->_eventChannels.allValues) { + [channel setStreamHandler:nil]; + } + [self->_eventChannels removeAllObjects]; + for (NSObject *handler in self->_streamHandlers.allValues) { + [handler onCancelWithArguments:nil]; + } + [self->_streamHandlers removeAllObjects]; + + if (completion != nil) completion(); +} + +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { + [self cleanupWithCompletion:nil]; +} + +#pragma mark - AppDelegate + +#if TARGET_OS_IPHONE +#if !__has_include() +- (BOOL)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)notification + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { +// -[FIRAuth canHandleNotification:] / -canHandleURL: intercept the silent +// push / universal link continuation used by phone-auth verification, +// which isn't exported by Firebase's tvOS Auth pod (see PORTING_REPORT.md +// and the phone-auth disable above). They're simply not reachable on tvOS. +#if !TARGET_OS_TV + if ([[FIRAuth auth] canHandleNotification:notification]) { + completionHandler(UIBackgroundFetchResultNoData); + return YES; + } +#endif // !TARGET_OS_TV + return NO; +} +#endif + +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + _apnsToken = deviceToken; +} + +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { +#if TARGET_OS_TV + return NO; +#else + return [[FIRAuth auth] canHandleURL:url]; +#endif // TARGET_OS_TV +} + +#pragma mark - SceneDelegate + +- (BOOL)scene:(UIScene *)scene + openURLContexts:(NSSet *)URLContexts API_AVAILABLE(ios(13.0), tvos(13.0)) { +#if !TARGET_OS_TV + for (UIOpenURLContext *urlContext in URLContexts) { + if ([[FIRAuth auth] canHandleURL:urlContext.URL]) { + return YES; + } + } +#endif // !TARGET_OS_TV + return NO; +} +#endif + +#pragma mark - FLTFirebasePlugin + +- (void)didReinitializeFirebaseCore:(void (^_Nonnull)(void))completion { + [self cleanupWithCompletion:completion]; +} + +- (NSString *_Nonnull)firebaseLibraryName { + return @LIBRARY_NAME; +} + +- (NSString *_Nonnull)firebaseLibraryVersion { + return @LIBRARY_VERSION; +} + +- (NSString *_Nonnull)flutterChannelName { + return kFLTFirebaseAuthChannelName; +} + +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *_Nonnull)firebaseApp { + FIRAuth *auth = [FIRAuth authWithApp:firebaseApp]; + return @{ + @"APP_LANGUAGE_CODE" : (id)[auth languageCode] ?: [NSNull null], + @"APP_CURRENT_USER" : [auth currentUser] + ? [PigeonParser getManualList:[PigeonParser getPigeonDetails:[auth currentUser]]] + : [NSNull null], + }; +} + +#pragma mark - Firebase Auth API + +// Adapted from +// https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce Used +// for Apple Sign In +- (NSString *)randomNonce:(NSInteger)length { + NSAssert(length > 0, @"Expected nonce to have positive length"); + NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._"; + NSMutableString *result = [NSMutableString string]; + NSInteger remainingLength = length; + + while (remainingLength > 0) { + NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16]; + for (NSInteger i = 0; i < 16; i++) { + uint8_t random = 0; + int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random); + NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode); + + [randoms addObject:@(random)]; + } + + for (NSNumber *random in randoms) { + if (remainingLength == 0) { + break; + } + + if (random.unsignedIntValue < characterSet.length) { + unichar character = [characterSet characterAtIndex:random.unsignedIntValue]; + [result appendFormat:@"%C", character]; + remainingLength--; + } + } + } + + return [result copy]; +} + +- (NSString *)stringBySha256HashingString:(NSString *)input { + const char *string = [input UTF8String]; + unsigned char result[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(string, (CC_LONG)strlen(string), result); + + NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hashed appendFormat:@"%02x", result[i]]; + } + return hashed; +} + +static void handleSignInWithApple(FLTFirebaseAuthPlugin *object, FIRAuthDataResult *authResult, + NSString *authorizationCode, NSError *error) { + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + object.appleCompletion; + if (completion == nil) { + object.appleSignInRequestInFlight = NO; + return; + } + + if (error != nil) { + if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; + [object handleMultiFactorError:object.appleArguments completion:completion withError:error]; + } else { + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + return; + } + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:authorizationCode], + nil); +} + +- (void)authorizationController:(ASAuthorizationController *)controller + didCompleteWithAuthorization:(ASAuthorization *)authorization + API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) { + if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { + ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential; + NSString *rawNonce = self.currentNonce; + NSAssert(rawNonce != nil, + @"Invalid state: A login callback was received, but no login request was sent."); + + if (appleIDCredential.identityToken == nil) { + NSLog(@"Unable to fetch identity token."); + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + if (completion != nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + } + return; + } + + NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken + encoding:NSUTF8StringEncoding]; + if (idToken == nil) { + NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken); + } + + NSString *authorizationCode = nil; + if (appleIDCredential.authorizationCode != nil) { + authorizationCode = [[NSString alloc] initWithData:appleIDCredential.authorizationCode + encoding:NSUTF8StringEncoding]; + } + + FIROAuthCredential *credential = + [FIROAuthProvider appleCredentialWithIDToken:idToken + rawNonce:rawNonce + fullName:appleIDCredential.fullName]; + + if (self.isReauthenticatingWithApple == YES) { + self.isReauthenticatingWithApple = NO; + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + [[FIRAuth.auth currentUser] + reauthenticateWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + handleSignInWithApple(self, authResult, authorizationCode, error); + }]; + + } else if (self.linkWithAppleUser != nil) { + FIRUser *userToLink = self.linkWithAppleUser; + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + [userToLink linkWithCredential:credential + completion:^(FIRAuthDataResult *authResult, NSError *error) { + self.linkWithAppleUser = nil; + handleSignInWithApple(self, authResult, authorizationCode, error); + }]; + + } else { + FIRAuth *signInAuth = + self.signInWithAppleAuth != nil ? self.signInWithAppleAuth : FIRAuth.auth; + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + [signInAuth signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + self.signInWithAppleAuth = nil; + handleSignInWithApple(self, authResult, authorizationCode, error); + }]; + } + } else { + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + if (completion != nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + } + } +} + +- (void)authorizationController:(ASAuthorizationController *)controller + didCompleteWithError:(NSError *)error API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) { + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + + NSLog(@"Sign in with Apple errored: %@", error); + if (completion == nil) { + return; + } + + switch (error.code) { + case ASAuthorizationErrorCanceled: + completion(nil, [FlutterError errorWithCode:@"canceled" + message:@"The user canceled the authorization attempt." + details:nil]); + break; + + case ASAuthorizationErrorInvalidResponse: + completion(nil, [FlutterError + errorWithCode:@"invalid-response" + message:@"The authorization request received an invalid response." + details:nil]); + break; + + case ASAuthorizationErrorNotHandled: + completion(nil, [FlutterError errorWithCode:@"not-handled" + message:@"The authorization request wasn’t handled." + details:nil]); + break; + + case ASAuthorizationErrorFailed: + completion(nil, [FlutterError errorWithCode:@"failed" + message:@"The authorization attempt failed." + details:nil]); + break; + + case ASAuthorizationErrorUnknown: + default: + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + break; + } +} + +- (void)handleInternalError:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion + withError:(NSError *)error { + const NSError *underlyingError = error.userInfo[@"NSUnderlyingError"]; + if (underlyingError != nil) { + const NSDictionary *details = + underlyingError.userInfo[@"FIRAuthErrorUserInfoDeserializedResponseKey"]; + completion(nil, [FlutterError errorWithCode:@"internal-error" + message:error.description + details:details]); + return; + } + completion(nil, [FlutterError errorWithCode:@"internal-error" + message:error.description + details:nil]); +} + +// Multi-factor auth disabled on tvOS by hand (not caught by the porter): +// FIRMultiFactorResolver / FIRMultiFactorAssertion / FIRMultiFactorInfo +// aren't even forward-declared in Firebase's tvOS build of FirebaseAuth +// (build errors were "unknown type name", not just "property not found on +// forward class" as with FIRTOTPSecret) — the whole MFA surface is absent +// from the SDK on this platform, not just TOTP/phone factors specifically. +// See also: getAppMultiFactorFromPigeon, enrollPhoneApp, getEnrolledFactorsApp, +// getSessionApp, unenrollApp, enrollTotpApp, resolveSignInResolverId below, +// the TOTP block above, and the ivars in @implementation's leading braces. +// There are ~9 call sites for this method scattered through sign-in/link/ +// reauthenticate flows below; rather than guard every call site, this +// method keeps its one signature and gets a tvOS-only stub body so callers +// don't need to change. +#if TARGET_OS_TV +- (void)handleMultiFactorError:(AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion + withError:(NSError *_Nullable)error { + completion(nil, [FlutterError + errorWithCode:@"second-factor-required" + message:@"Multi-factor authentication is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} +#else +- (void)handleMultiFactorError:(AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion + withError:(NSError *_Nullable)error { + FIRMultiFactorResolver *resolver = + (FIRMultiFactorResolver *)error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; + + NSArray *hints = resolver.hints; + FIRMultiFactorSession *session = resolver.session; + + NSString *sessionId = [[NSUUID UUID] UUIDString]; + self->_multiFactorSessionMap[sessionId] = session; + + NSString *resolverId = [[NSUUID UUID] UUIDString]; + self->_multiFactorResolverMap[resolverId] = resolver; + + NSMutableArray *pigeonHints = [NSMutableArray array]; + + for (FIRMultiFactorInfo *multiFactorInfo in hints) { + NSString *phoneNumber; + if ([multiFactorInfo class] == [FIRPhoneMultiFactorInfo class]) { + FIRPhoneMultiFactorInfo *phoneFactorInfo = (FIRPhoneMultiFactorInfo *)multiFactorInfo; + phoneNumber = phoneFactorInfo.phoneNumber; + } + + InternalMultiFactorInfo *object = [InternalMultiFactorInfo + makeWithDisplayName:multiFactorInfo.displayName + enrollmentTimestamp:multiFactorInfo.enrollmentDate.timeIntervalSince1970 + factorId:multiFactorInfo.factorID + uid:multiFactorInfo.UID + phoneNumber:phoneNumber]; + + [pigeonHints addObject:object.toList]; + } + + NSDictionary *output = @{ + kAppName : app.appName, + kArgumentMultiFactorHints : pigeonHints, + kArgumentMultiFactorSessionId : sessionId, + kArgumentMultiFactorResolverId : resolverId, + }; + completion(nil, [FlutterError errorWithCode:@"second-factor-required" + message:error.description + details:output]); +} +#endif // !TARGET_OS_TV + +static void launchAppleSignInRequest(FLTFirebaseAuthPlugin *object, AuthPigeonFirebaseApp *app, + InternalSignInProvider *signInProvider, + void (^_Nonnull completion)(InternalUserCredential *_Nullable, + FlutterError *_Nullable)) { + if (@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)) { + if (object.appleSignInRequestInFlight) { + completion(nil, + [FlutterError errorWithCode:@"operation-not-allowed" + message:@"A Sign in with Apple request is already in progress." + details:nil]); + return; + } + + NSString *nonce = [object randomNonce:32]; + object.currentNonce = nonce; + object.appleCompletion = completion; + object.appleArguments = app; + object.appleSignInRequestInFlight = YES; + + ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init]; + + ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest]; + NSMutableArray *requestedScopes = [NSMutableArray arrayWithCapacity:2]; + if ([signInProvider.scopes containsObject:@"name"]) { + [requestedScopes addObject:ASAuthorizationScopeFullName]; + } + if ([signInProvider.scopes containsObject:@"email"]) { + [requestedScopes addObject:ASAuthorizationScopeEmail]; + } + request.requestedScopes = [requestedScopes copy]; + request.nonce = [object stringBySha256HashingString:nonce]; + + ASAuthorizationController *authorizationController = + [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[ request ]]; + authorizationController.delegate = object; + authorizationController.presentationContextProvider = object; + [authorizationController performRequests]; + } else { + NSLog(@"Sign in with Apple was introduced in iOS 13, update your Podfile with platform :ios, " + @"'13.0'"); + } +} + +static void handleAppleAuthResult(FLTFirebaseAuthPlugin *object, AuthPigeonFirebaseApp *app, + FIRAuth *auth, FIRAuthCredential *credentials, NSError *error, + void (^_Nonnull completion)(InternalUserCredential *_Nullable, + FlutterError *_Nullable)) { + if (error) { + if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + [object handleMultiFactorError:app completion:completion withError:error]; + } else { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + return; + } + if (credentials) { + [auth + signInWithCredential:credentials + completion:^(FIRAuthDataResult *authResult, NSError *error) { + if (error != nil) { + NSDictionary *userInfo = [error userInfo]; + NSError *underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey]; + + NSDictionary *firebaseDictionary = + underlyingError.userInfo[@"FIRAuthErrorUserInfoDes" + @"erializedResponseKey"]; + + NSString *errorCode = userInfo[@"FIRAuthErrorUserInfoNameKey"]; + + if (firebaseDictionary == nil && errorCode != nil) { + if ([errorCode isEqual:@"ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL"]) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + return; + } + + // Removing since it's not parsed and causing issue when sending back the + // object to Flutter + NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy]; + [mutableUserInfo + removeObjectForKey:@"FIRAuthErrorUserInfoUpdatedCredentialKey"]; + NSError *modifiedError = [NSError errorWithDomain:error.domain + code:error.code + userInfo:mutableUserInfo]; + + completion(nil, + [FlutterError errorWithCode:@"sign-in-failed" + message:userInfo[@"NSLocalizedDescription"] + details:modifiedError.userInfo]); + + } else if (firebaseDictionary != nil && + firebaseDictionary[@"message"] != nil) { + // error from firebase-ios-sdk is + // buried in underlying error. + completion(nil, + [FlutterError errorWithCode:@"sign-in-failed" + message:error.localizedDescription + details:firebaseDictionary[@"message"]]); + } else { + completion(nil, [FlutterError errorWithCode:@"sign-in-failed" + message:error.localizedDescription + details:error.userInfo]); + } + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; + } +} + +#pragma mark - Utilities + ++ (NSNumber *_Nullable)storeAuthCredentialIfPresent:(NSError *)error { + if ([error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey] != nil) { + FIRAuthCredential *authCredential = [error userInfo][FIRAuthErrorUserInfoUpdatedCredentialKey]; + // We temporarily store the non-serializable credential so the + // Dart API can consume these at a later time. + NSNumber *authCredentialHash = @([authCredential hash]); + credentialsMap[authCredentialHash] = authCredential; + return authCredentialHash; + } + return nil; +} + +- (FIRAuth *_Nullable)getFIRAuthFromAppNameFromPigeon:(AuthPigeonFirebaseApp *)pigeonApp { + FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:pigeonApp.appName]; + FIRAuth *auth = [FIRAuth authWithApp:app]; + + auth.tenantID = pigeonApp.tenantId; + auth.customAuthDomain = [FLTFirebaseCorePlugin getCustomDomain:app.name]; + // Auth's `customAuthDomain` supersedes value from `getCustomDomain` set by `initializeApp` + if (pigeonApp.customAuthDomain != nil) { + auth.customAuthDomain = pigeonApp.customAuthDomain; + } + + return auth; +} + +- (void)getFIRAuthCredentialFromArguments:(NSDictionary *)arguments + app:(AuthPigeonFirebaseApp *)app + completion:(void (^)(FIRAuthCredential *credential, + NSError *error))completion { + // If the credential dictionary contains a token, it means a native one has + // been stored for later usage, so we'll attempt to retrieve it here. + if (arguments[kArgumentToken] != nil && ![arguments[kArgumentToken] isEqual:[NSNull null]]) { + NSNumber *credentialHashCode = arguments[kArgumentToken]; + if (credentialsMap[credentialHashCode] != nil) { + completion(credentialsMap[credentialHashCode], nil); + return; + } + } + + NSString *signInMethod = arguments[kArgumentSignInMethod]; + + if ([signInMethod isEqualToString:kSignInMethodGameCenter]) { + // Game Center Games is different to other providers, it requires below callback to get a + // credential. This is why getFIRAuthCredentialFromArguments now requires a completion() + // callback + [FIRGameCenterAuthProvider + getCredentialWithCompletion:^(FIRAuthCredential *credential, NSError *error) { + if (error) { + completion(nil, error); + } else { + completion(credential, nil); + } + }]; + return; + } + + NSString *secret = arguments[kArgumentSecret] == [NSNull null] ? nil : arguments[kArgumentSecret]; + NSString *idToken = + arguments[kArgumentIdToken] == [NSNull null] ? nil : arguments[kArgumentIdToken]; + NSString *accessToken = + arguments[kArgumentAccessToken] == [NSNull null] ? nil : arguments[kArgumentAccessToken]; + NSString *rawNonce = + arguments[kArgumentRawNonce] == [NSNull null] ? nil : arguments[kArgumentRawNonce]; + + // Password Auth + if ([signInMethod isEqualToString:kSignInMethodPassword]) { + NSString *email = arguments[kArgumentEmail]; + completion([FIREmailAuthProvider credentialWithEmail:email password:secret], nil); + return; + } + + // Email Link Auth + if ([signInMethod isEqualToString:kSignInMethodEmailLink]) { + NSString *email = arguments[kArgumentEmail]; + NSString *emailLink = arguments[kArgumentEmailLink]; + completion([FIREmailAuthProvider credentialWithEmail:email link:emailLink], nil); + return; + } + + // Facebook Auth + if ([signInMethod isEqualToString:kSignInMethodFacebook]) { + completion([FIRFacebookAuthProvider credentialWithAccessToken:accessToken], nil); + return; + } + + // Google Auth + if ([signInMethod isEqualToString:kSignInMethodGoogle]) { + completion([FIRGoogleAuthProvider credentialWithIDToken:idToken accessToken:accessToken], nil); + return; + } + + // Twitter Auth + if ([signInMethod isEqualToString:kSignInMethodTwitter]) { + completion([FIRTwitterAuthProvider credentialWithToken:accessToken secret:secret], nil); + return; + } + + // GitHub Auth + if ([signInMethod isEqualToString:kSignInMethodGithub]) { + completion([FIRGitHubAuthProvider credentialWithToken:accessToken], nil); + return; + } + + // Phone Auth - Only supported on iOS (TARGET_OS_IPHONE also covers tvOS; + // FIRPhoneAuthProvider isn't exported by Firebase's tvOS Auth pod, see + // PORTING_REPORT.md). + if ([signInMethod isEqualToString:kSignInMethodPhone]) { +#if TARGET_OS_IPHONE && !TARGET_OS_TV + NSString *verificationId = arguments[kArgumentVerificationId]; + NSString *smsCode = arguments[kArgumentSmsCode]; + completion([[FIRPhoneAuthProvider providerWithAuth:[self getFIRAuthFromAppNameFromPigeon:app]] + credentialWithVerificationID:verificationId + verificationCode:smsCode], + nil); + return; +#else + NSLog(@"The Firebase Phone Authentication provider is not supported on this " + @"platform."); + completion(nil, nil); + return; +#endif + } + // Apple Auth + if ([signInMethod isEqualToString:kSignInMethodApple]) { + if (idToken && rawNonce) { + // Credential with idToken, rawNonce and fullName + NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init]; + fullName.givenName = + arguments[kArgumentGivenName] == [NSNull null] ? nil : arguments[kArgumentGivenName]; + fullName.familyName = + arguments[kArgumentFamilyName] == [NSNull null] ? nil : arguments[kArgumentFamilyName]; + fullName.nickname = + arguments[kArgumentNickname] == [NSNull null] ? nil : arguments[kArgumentNickname]; + fullName.namePrefix = + arguments[kArgumentNamePrefix] == [NSNull null] ? nil : arguments[kArgumentNamePrefix]; + fullName.nameSuffix = + arguments[kArgumentNameSuffix] == [NSNull null] ? nil : arguments[kArgumentNameSuffix]; + fullName.middleName = + arguments[kArgumentMiddleName] == [NSNull null] ? nil : arguments[kArgumentMiddleName]; + + completion([FIROAuthProvider appleCredentialWithIDToken:idToken + rawNonce:rawNonce + fullName:fullName], + nil); + return; + } + } + // OAuth + if ([signInMethod isEqualToString:kSignInMethodOAuth]) { + NSString *providerId = arguments[kArgumentProviderId]; + completion([FIROAuthProvider credentialWithProviderID:providerId + IDToken:idToken + rawNonce:rawNonce + accessToken:accessToken], + nil); + return; + } + + NSLog(@"Support for an auth provider with identifier '%@' is not implemented.", signInMethod); + completion(nil, nil); + return; +} + +- (void)ensureAPNSTokenSetting { +// FIRAuth.APNSToken / -setAPNSToken:type: feed phone-auth silent-push +// verification, which isn't exported by Firebase's tvOS Auth pod (see +// PORTING_REPORT.md). +#if !TARGET_OS_OSX && !TARGET_OS_TV + FIRApp *defaultApp = [FIRApp defaultApp]; + if (defaultApp) { + if ([FIRAuth auth].APNSToken == nil && _apnsToken != nil) { + [[FIRAuth auth] setAPNSToken:_apnsToken type:FIRAuthAPNSTokenTypeUnknown]; + _apnsToken = nil; + } + } +#endif +} + +// Multi-factor auth (FIRMultiFactor and everything that touches it below) +// is disabled on tvOS by hand — see the comment above handleMultiFactorError +// for why. The MultiFactorUserHostApi / MultiFactoResolverHostApi / +// MultiFactorTotpHostApi / MultiFactorTotpSecretHostApi Pigeon protocols +// this class declares conformance to (see the @interface) aren't +// statically enforced by Objective-C, and Pigeon's own dispatcher already +// returns a "doesn't respond to selector" FlutterError for any protocol +// method without an implementation — so the methods below are simply +// omitted on tvOS rather than given stub bodies. +#if !TARGET_OS_TV +- (FIRMultiFactor *)getAppMultiFactorFromPigeon:(nonnull AuthPigeonFirebaseApp *)app { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + return currentUser.multiFactor; +} +#endif // !TARGET_OS_TV + +- (nonnull ASPresentationAnchor)presentationAnchorForAuthorizationController: + (nonnull ASAuthorizationController *)controller API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) { +#if TARGET_OS_OSX + return [[NSApplication sharedApplication] keyWindow]; +#else + // UIApplication.keyWindow is deprecated in iOS 13+ with UIScene lifecycle. + // Walk the connected scenes to find the foreground active window. + if (@available(iOS 15.0, tvOS 15.0, *)) { + for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { + if (scene.activationState == UISceneActivationStateForegroundActive && + [scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + if (windowScene.keyWindow) { + return windowScene.keyWindow; + } + } + } + } else if (@available(iOS 13.0, tvOS 13.0, *)) { + for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { + if (scene.activationState == UISceneActivationStateForegroundActive && + [scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + for (UIWindow *window in windowScene.windows) { + if (window.isKeyWindow) { + return window; + } + } + } + } + } + return [[UIApplication sharedApplication] keyWindow]; +#endif +} + +- (void)enrollPhoneApp:(nonnull AuthPigeonFirebaseApp *)app + assertion:(nonnull InternalPhoneMultiFactorAssertion *)assertion + displayName:(nullable NSString *)displayName + completion:(nonnull void (^)(FlutterError *_Nullable))completion { +#if TARGET_OS_OSX + completion([FlutterError errorWithCode:@"unsupported-platform" + message:@"Phone authentication is not supported on macOS" + details:nil]); +#elif TARGET_OS_TV + completion([FlutterError errorWithCode:@"unsupported-platform" + message:@"Phone/multi-factor authentication is not supported by " + @"the Firebase SDK on tvOS" + details:nil]); +#else + + FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; + + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthProvider providerWithAuth:[self getFIRAuthFromAppNameFromPigeon:app]] + credentialWithVerificationID:[assertion verificationId] + verificationCode:[assertion verificationCode]]; + + FIRMultiFactorAssertion *multiFactorAssertion = + [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; + + [multiFactor enrollWithAssertion:multiFactorAssertion + displayName:displayName + completion:^(NSError *_Nullable error) { + if (error == nil) { + completion(nil); + } else { + completion([FlutterError errorWithCode:@"enroll-failed" + message:error.localizedDescription + details:nil]); + } + }]; +#endif +} + +#if !TARGET_OS_TV +- (void)getEnrolledFactorsApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; + + NSArray *enrolledFactors = [multiFactor enrolledFactors]; + + NSMutableArray *results = [NSMutableArray array]; + + for (FIRMultiFactorInfo *multiFactorInfo in enrolledFactors) { + NSString *phoneNumber; + if ([multiFactorInfo class] == [FIRPhoneMultiFactorInfo class]) { + FIRPhoneMultiFactorInfo *phoneFactorInfo = (FIRPhoneMultiFactorInfo *)multiFactorInfo; + phoneNumber = phoneFactorInfo.phoneNumber; + } + + [results addObject:[InternalMultiFactorInfo + makeWithDisplayName:multiFactorInfo.displayName + enrollmentTimestamp:multiFactorInfo.enrollmentDate.timeIntervalSince1970 + factorId:multiFactorInfo.factorID + uid:multiFactorInfo.UID + phoneNumber:phoneNumber]]; + } + + completion(results, nil); +} + +- (void)getSessionApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(InternalMultiFactorSession *_Nullable, + FlutterError *_Nullable))completion { + FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; + [multiFactor getSessionWithCompletion:^(FIRMultiFactorSession *_Nullable session, + NSError *_Nullable error) { + NSString *UUID = [[NSUUID UUID] UUIDString]; + self->_multiFactorSessionMap[UUID] = session; + + InternalMultiFactorSession *pigeonSession = [InternalMultiFactorSession makeWithId:UUID]; + completion(pigeonSession, nil); + }]; +} + +- (void)unenrollApp:(nonnull AuthPigeonFirebaseApp *)app + factorUid:(nonnull NSString *)factorUid + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; + [multiFactor unenrollWithFactorUID:factorUid + completion:^(NSError *_Nullable error) { + if (error == nil) { + completion(nil); + } else { + completion([FlutterError errorWithCode:@"unenroll-failed" + message:error.localizedDescription + details:nil]); + } + }]; +} + +- (void)enrollTotpApp:(nonnull AuthPigeonFirebaseApp *)app + assertionId:(nonnull NSString *)assertionId + displayName:(nullable NSString *)displayName + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; + + FIRMultiFactorAssertion *assertion = _multiFactorAssertionMap[assertionId]; + + [multiFactor enrollWithAssertion:assertion + displayName:displayName + completion:^(NSError *_Nullable error) { + if (error == nil) { + completion(nil); + } else { + completion([FlutterError errorWithCode:@"enroll-failed" + message:error.localizedDescription + details:nil]); + } + }]; +} + +- (void)resolveSignInResolverId:(nonnull NSString *)resolverId + assertion:(nullable InternalPhoneMultiFactorAssertion *)assertion + totpAssertionId:(nullable NSString *)totpAssertionId + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRMultiFactorResolver *resolver = _multiFactorResolverMap[resolverId]; + + FIRMultiFactorAssertion *multiFactorAssertion; + + if (assertion != nil) { +#if TARGET_OS_IPHONE + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthProvider provider] credentialWithVerificationID:[assertion verificationId] + verificationCode:[assertion verificationCode]]; + multiFactorAssertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; +#endif + } else if (totpAssertionId != nil) { + multiFactorAssertion = _multiFactorAssertionMap[totpAssertionId]; + } else { + completion(nil, + [FlutterError errorWithCode:@"resolve-signin-failed" + message:@"Neither assertion nor totpAssertionId were provided" + details:nil]); + return; + } + + [resolver + resolveSignInWithAssertion:multiFactorAssertion + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error == nil) { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } else { + completion(nil, [FlutterError errorWithCode:@"resolve-signin-failed" + message:error.localizedDescription + details:nil]); + } + }]; +} +#else // TARGET_OS_TV +// tvOS stubs for the MultiFactorUserHostApi / MultiFactoResolverHostApi +// selectors. MFA is unavailable in the Firebase tvOS SDK, but Pigeon's +// SetUpWithSuffix ASSERTS (`respondsToSelector`) that the plugin +// implements every protocol method at registration time — a missing method +// aborts the app in +registerWithRegistrar: before any Dart runs (this was +// the crash the runtime smoke test caught). So each method must exist and +// simply return an "unsupported on tvOS" FlutterError. +- (void)getEnrolledFactorsApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"Multi-factor authentication is not supported " + @"by the Firebase SDK on tvOS." + details:nil]); +} + +- (void)getSessionApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(InternalMultiFactorSession *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"Multi-factor authentication is not supported " + @"by the Firebase SDK on tvOS." + details:nil]); +} + +- (void)unenrollApp:(nonnull AuthPigeonFirebaseApp *)app + factorUid:(nonnull NSString *)factorUid + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + completion([FlutterError errorWithCode:@"unsupported-platform" + message:@"Multi-factor authentication is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)enrollTotpApp:(nonnull AuthPigeonFirebaseApp *)app + assertionId:(nonnull NSString *)assertionId + displayName:(nullable NSString *)displayName + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + completion([FlutterError errorWithCode:@"unsupported-platform" + message:@"Multi-factor authentication is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)resolveSignInResolverId:(nonnull NSString *)resolverId + assertion:(nullable InternalPhoneMultiFactorAssertion *)assertion + totpAssertionId:(nullable NSString *)totpAssertionId + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"Multi-factor authentication is not supported " + @"by the Firebase SDK on tvOS." + details:nil]); +} +#endif // !TARGET_OS_TV + +// TOTP multi-factor auth disabled on tvOS by hand (not caught by the +// porter): FIRTOTPSecret is a Swift class (TOTPSecret.swift, "available on +// iOS and macOS" per its own doc comment) that Firebase's CocoaPods +// FirebaseAuth/Auth subspec does not export an Objective-C interface for on +// tvOS — only a forward `@class` declaration is visible, so any property/ +// method access on it fails to compile. These five MultiFactorTotpHostApi / +// MultiFactorTotpSecretHostApi methods all funnel through FIRTOTPSecret; +// without an implementation here the Pigeon dispatcher in +// firebase_auth_messages.g.m returns "method not implemented" for them at +// runtime, which is the right behavior until/unless Firebase ships tvOS +// support for TOTP MFA. +#if !TARGET_OS_TV +- (void)generateSecretSessionId:(nonnull NSString *)sessionId + completion:(nonnull void (^)(InternalTotpSecret *_Nullable, + FlutterError *_Nullable))completion { + FIRMultiFactorSession *multiFactorSession = _multiFactorSessionMap[sessionId]; + + [FIRTOTPMultiFactorGenerator + generateSecretWithMultiFactorSession:multiFactorSession + completion:^(FIRTOTPSecret *_Nullable secret, + NSError *_Nullable error) { + if (error == nil) { + self->_multiFactorTotpSecretMap[secret.sharedSecretKey] = + secret; + completion([PigeonParser getPigeonTotpSecret:secret], nil); + } else { + completion( + nil, [FlutterError errorWithCode:@"generate-secret-failed" + message:error.localizedDescription + details:nil]); + } + }]; +} + +- (void)getAssertionForEnrollmentSecretKey:(nonnull NSString *)secretKey + oneTimePassword:(nonnull NSString *)oneTimePassword + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRTOTPSecret *totpSecret = _multiFactorTotpSecretMap[secretKey]; + + FIRTOTPMultiFactorAssertion *assertion = + [FIRTOTPMultiFactorGenerator assertionForEnrollmentWithSecret:totpSecret + oneTimePassword:oneTimePassword]; + + NSString *UUID = [[NSUUID UUID] UUIDString]; + self->_multiFactorAssertionMap[UUID] = assertion; + completion(UUID, nil); +} + +- (void)getAssertionForSignInEnrollmentId:(nonnull NSString *)enrollmentId + oneTimePassword:(nonnull NSString *)oneTimePassword + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRTOTPMultiFactorAssertion *assertion = + [FIRTOTPMultiFactorGenerator assertionForSignInWithEnrollmentID:enrollmentId + oneTimePassword:oneTimePassword]; + NSString *UUID = [[NSUUID UUID] UUIDString]; + self->_multiFactorAssertionMap[UUID] = assertion; + completion(UUID, nil); +} + +- (void)generateQrCodeUrlSecretKey:(nonnull NSString *)secretKey + accountName:(nullable NSString *)accountName + issuer:(nullable NSString *)issuer + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRTOTPSecret *totpSecret = _multiFactorTotpSecretMap[secretKey]; + completion([totpSecret generateQRCodeURLWithAccountName:accountName issuer:issuer], nil); +} + +- (void)openInOtpAppSecretKey:(nonnull NSString *)secretKey + qrCodeUrl:(nonnull NSString *)qrCodeUrl + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRTOTPSecret *totpSecret = _multiFactorTotpSecretMap[secretKey]; + [totpSecret openInOTPAppWithQRCodeURL:qrCodeUrl]; + completion(nil); +} +#else // TARGET_OS_TV +// tvOS stubs for MultiFactorTotpHostApi / MultiFactorTotpSecretHostApi — see +// the MFA stub comment above: Pigeon asserts every selector exists at +// registration, so these must be present and return an unsupported error. +- (void)generateSecretSessionId:(nonnull NSString *)sessionId + completion:(nonnull void (^)(InternalTotpSecret *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"TOTP multi-factor auth is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)getAssertionForEnrollmentSecretKey:(nonnull NSString *)secretKey + oneTimePassword:(nonnull NSString *)oneTimePassword + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"TOTP multi-factor auth is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)getAssertionForSignInEnrollmentId:(nonnull NSString *)enrollmentId + oneTimePassword:(nonnull NSString *)oneTimePassword + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"TOTP multi-factor auth is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)generateQrCodeUrlSecretKey:(nonnull NSString *)secretKey + accountName:(nullable NSString *)accountName + issuer:(nullable NSString *)issuer + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"TOTP multi-factor auth is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} + +- (void)openInOtpAppSecretKey:(nonnull NSString *)secretKey + qrCodeUrl:(nonnull NSString *)qrCodeUrl + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + completion([FlutterError errorWithCode:@"unsupported-platform" + message:@"TOTP multi-factor auth is not supported by the " + @"Firebase SDK on tvOS." + details:nil]); +} +#endif // !TARGET_OS_TV + +- (void)applyActionCodeApp:(nonnull AuthPigeonFirebaseApp *)app + code:(nonnull NSString *)code + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth applyActionCode:code + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)revokeTokenWithAuthorizationCodeApp:(nonnull AuthPigeonFirebaseApp *)app + authorizationCode:(nonnull NSString *)authorizationCode + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth revokeTokenWithAuthorizationCode:authorizationCode + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)revokeAccessTokenApp:(nonnull AuthPigeonFirebaseApp *)app + accessToken:(nonnull NSString *)accessToken + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + // `revokeAccessToken(_:)` is currently Android-only on the Firebase SDK. + // On Apple platforms use `revokeTokenWithAuthorizationCode:` instead. + completion([FlutterError errorWithCode:@"unsupported-platform-operation" + message:@"revokeAccessToken is not supported on iOS/macOS. " + @"Use revokeTokenWithAuthorizationCode instead." + details:nil]); +} + +- (void)checkActionCodeApp:(nonnull AuthPigeonFirebaseApp *)app + code:(nonnull NSString *)code + completion:(nonnull void (^)(InternalActionCodeInfo *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth checkActionCode:code + completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + InternalActionCodeInfo *result = [self parseActionCode:info]; + if (result.operation == ActionCodeInfoOperationUnknown) { + // Workaround: Firebase iOS SDK >=11.12.0 returns .unknown because + // actionCodeOperation(forRequestType:) only matches camelCase but the + // REST API returns SCREAMING_SNAKE_CASE (e.g. "VERIFY_EMAIL"). + // Re-fetch the raw requestType via REST to resolve the operation. + // See: https://github.com/firebase/flutterfire/issues/17452 + [self resolveActionCodeOperationForApp:app + code:code + fallbackInfo:result + completion:completion]; + } else { + completion(result, nil); + } + } + }]; +} + +- (InternalActionCodeInfo *_Nullable)parseActionCode:(nonnull FIRActionCodeInfo *)info { + InternalActionCodeInfoData *data = [InternalActionCodeInfoData makeWithEmail:info.email + previousEmail:info.previousEmail]; + + ActionCodeInfoOperation operation; + + if (info.operation == FIRActionCodeOperationPasswordReset) { + operation = ActionCodeInfoOperationPasswordReset; + } else if (info.operation == FIRActionCodeOperationVerifyEmail) { + operation = ActionCodeInfoOperationVerifyEmail; + } else if (info.operation == FIRActionCodeOperationRecoverEmail) { + operation = ActionCodeInfoOperationRecoverEmail; + } else if (info.operation == FIRActionCodeOperationEmailLink) { + operation = ActionCodeInfoOperationEmailSignIn; + } else if (info.operation == FIRActionCodeOperationVerifyAndChangeEmail) { + operation = ActionCodeInfoOperationVerifyAndChangeEmail; + } else if (info.operation == FIRActionCodeOperationRevertSecondFactorAddition) { + operation = ActionCodeInfoOperationRevertSecondFactorAddition; + } else { + operation = ActionCodeInfoOperationUnknown; + } + + return [InternalActionCodeInfo makeWithOperation:operation data:data]; +} + +/// Maps a raw requestType string (either camelCase or SCREAMING_SNAKE_CASE) to +/// the corresponding Pigeon enum value. ++ (ActionCodeInfoOperation)operationFromRequestType:(nullable NSString *)requestType { + static NSDictionary *mapping; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mapping = @{ + @"PASSWORD_RESET" : @(ActionCodeInfoOperationPasswordReset), + @"resetPassword" : @(ActionCodeInfoOperationPasswordReset), + @"VERIFY_EMAIL" : @(ActionCodeInfoOperationVerifyEmail), + @"verifyEmail" : @(ActionCodeInfoOperationVerifyEmail), + @"RECOVER_EMAIL" : @(ActionCodeInfoOperationRecoverEmail), + @"recoverEmail" : @(ActionCodeInfoOperationRecoverEmail), + @"EMAIL_SIGNIN" : @(ActionCodeInfoOperationEmailSignIn), + @"signIn" : @(ActionCodeInfoOperationEmailSignIn), + @"VERIFY_AND_CHANGE_EMAIL" : @(ActionCodeInfoOperationVerifyAndChangeEmail), + @"verifyAndChangeEmail" : @(ActionCodeInfoOperationVerifyAndChangeEmail), + @"REVERT_SECOND_FACTOR_ADDITION" : @(ActionCodeInfoOperationRevertSecondFactorAddition), + @"revertSecondFactorAddition" : @(ActionCodeInfoOperationRevertSecondFactorAddition), + }; + }); + + NSNumber *value = mapping[requestType]; + return value ? (ActionCodeInfoOperation)value.integerValue : ActionCodeInfoOperationUnknown; +} + +/// Calls the Identity Toolkit REST API directly to retrieve the raw requestType +/// string, which the iOS SDK fails to parse correctly. Falls back to the original +/// result if the REST call fails for any reason. +- (void)resolveActionCodeOperationForApp:(nonnull AuthPigeonFirebaseApp *)app + code:(nonnull NSString *)code + fallbackInfo:(nonnull InternalActionCodeInfo *)fallbackInfo + completion:(nonnull void (^)(InternalActionCodeInfo *_Nullable, + FlutterError *_Nullable))completion { + FIRApp *firebaseApp = [FLTFirebasePlugin firebaseAppNamed:app.appName]; + NSString *apiKey = firebaseApp.options.APIKey; + + NSString *baseURL; + NSDictionary *emulatorConfig = _emulatorConfigs[app.appName]; + if (emulatorConfig) { + baseURL = [NSString stringWithFormat:@"http://%@:%@/identitytoolkit.googleapis.com", + emulatorConfig[@"host"], emulatorConfig[@"port"]]; + } else { + baseURL = @"https://identitytoolkit.googleapis.com"; + } + + NSString *urlString = + [NSString stringWithFormat:@"%@/v1/accounts:resetPassword?key=%@", baseURL, apiKey]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.HTTPMethod = @"POST"; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + request.HTTPBody = [NSJSONSerialization dataWithJSONObject:@{@"oobCode" : code} + options:0 + error:nil]; + + NSURLSessionDataTask *task = [[NSURLSession sharedSession] + dataTaskWithRequest:request + completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, + NSError *_Nullable error) { + if (error || !data) { + completion(fallbackInfo, nil); + return; + } + + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + if (!json || json[@"error"]) { + completion(fallbackInfo, nil); + return; + } + + ActionCodeInfoOperation operation = + [FLTFirebaseAuthPlugin operationFromRequestType:json[@"requestType"]]; + + if (operation != ActionCodeInfoOperationUnknown) { + completion([InternalActionCodeInfo makeWithOperation:operation data:fallbackInfo.data], + nil); + } else { + completion(fallbackInfo, nil); + } + }]; + [task resume]; +} + +- (void)confirmPasswordResetApp:(nonnull AuthPigeonFirebaseApp *)app + code:(nonnull NSString *)code + newPassword:(nonnull NSString *)newPassword + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth confirmPasswordResetWithCode:code + newPassword:newPassword + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)createUserWithEmailAndPasswordApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + password:(nonnull NSString *)password + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth createUserWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; +} + +- (void)fetchSignInMethodsForEmailApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth fetchSignInMethodsForEmail:email + completion:^(NSArray *_Nullable providers, + NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + if (providers == nil) { + completion(@[], nil); + } else { + completion(providers, nil); + } + } + }]; +} + +- (void)registerAuthStateListenerApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + NSString *name = + [NSString stringWithFormat:@"%@/auth-state/%@", kFLTFirebaseAuthChannelName, auth.app.name]; + FlutterEventChannel *channel = [FlutterEventChannel eventChannelWithName:name + binaryMessenger:_binaryMessenger]; + + FLTAuthStateChannelStreamHandler *handler = + [[FLTAuthStateChannelStreamHandler alloc] initWithAuth:auth]; + [channel setStreamHandler:handler]; + + [_eventChannels setObject:channel forKey:name]; + [_streamHandlers setObject:handler forKey:name]; + + completion(name, nil); +} + +- (void)registerIdTokenListenerApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + NSString *name = + [NSString stringWithFormat:@"%@/id-token/%@", kFLTFirebaseAuthChannelName, auth.app.name]; + + FlutterEventChannel *channel = [FlutterEventChannel eventChannelWithName:name + binaryMessenger:_binaryMessenger]; + + FLTIdTokenChannelStreamHandler *handler = + [[FLTIdTokenChannelStreamHandler alloc] initWithAuth:auth]; + [channel setStreamHandler:handler]; + + [_eventChannels setObject:channel forKey:name]; + [_streamHandlers setObject:handler forKey:name]; + + completion(name, nil); +} + +- (void)sendPasswordResetEmailApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + if (actionCodeSettings != nil) { + FIRActionCodeSettings *settings = [PigeonParser parseActionCodeSettings:actionCodeSettings]; + [auth sendPasswordResetWithEmail:email + actionCodeSettings:settings + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; + } else { + [auth sendPasswordResetWithEmail:email + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; + } +} + +- (void)sendSignInLinkToEmailApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + actionCodeSettings:(nonnull InternalActionCodeSettings *)actionCodeSettings + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth sendSignInLinkToEmail:email + actionCodeSettings:[PigeonParser parseActionCodeSettings:actionCodeSettings] + completion:^(NSError *_Nullable error) { + if (error != nil) { + if (error.code == FIRAuthErrorCodeInternalError) { + [self + handleInternalError:^(InternalUserCredential *_Nullable creds, + FlutterError *_Nullable internalError) { + completion(internalError); + } + withError:error]; + } else { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + } else { + completion(nil); + } + }]; +} + +- (void)setLanguageCodeApp:(nonnull AuthPigeonFirebaseApp *)app + languageCode:(nullable NSString *)languageCode + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + if (languageCode != nil && ![languageCode isEqual:[NSNull null]]) { + auth.languageCode = languageCode; + } else { + [auth useAppLanguage]; + } + + completion(auth.languageCode, nil); +} + +- (void)setSettingsApp:(nonnull AuthPigeonFirebaseApp *)app + settings:(nonnull InternalFirebaseAuthSettings *)settings + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + if (settings.userAccessGroup != nil) { + BOOL useUserAccessGroupSuccessful; + NSError *useUserAccessGroupErrorPtr; + useUserAccessGroupSuccessful = [auth useUserAccessGroup:settings.userAccessGroup + error:&useUserAccessGroupErrorPtr]; + if (!useUserAccessGroupSuccessful) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:useUserAccessGroupErrorPtr]); + return; + } + } + +#if TARGET_OS_IPHONE + if (settings.appVerificationDisabledForTesting) { + auth.settings.appVerificationDisabledForTesting = settings.appVerificationDisabledForTesting; + } +#else + NSLog(@"FIRAuthSettings.appVerificationDisabledForTesting is not supported " + @"on MacOS."); +#endif + + completion(nil); +} + +- (void)signInAnonymouslyApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth signInAnonymouslyWithCompletion:^(FIRAuthDataResult *authResult, NSError *error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; +} + +- (void)signInWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app + input:(nonnull NSDictionary *)input + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [self + getFIRAuthCredentialFromArguments:input + app:app + completion:^(FIRAuthCredential *credential, NSError *error) { + if (credential == nil) { + completion(nil, + [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + return; + } + + if (error) { + completion(nil, + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + + [auth + signInWithCredential:credential + completion:^(FIRAuthDataResult *authResult, + NSError *error) { + if (error != nil) { + NSDictionary *userInfo = [error userInfo]; + NSError *underlyingError = + [userInfo objectForKey:NSUnderlyingErrorKey]; + + NSDictionary *firebaseDictionary = + underlyingError + .userInfo[@"FIRAuthErrorUserInfoDeserializ" + @"edResponseKey"]; + + if (firebaseDictionary != nil && + firebaseDictionary[@"message"] != nil) { + // error from firebase-ios-sdk is buried in + // underlying error. + if ([firebaseDictionary[@"code"] + isKindOfClass:[NSNumber class]]) { + [self handleInternalError:completion + withError:error]; + } else { + completion(nil, + [FlutterError + errorWithCode:firebaseDictionary + [@"code"] + message:firebaseDictionary + [@"message"] + details:nil]); + } + } else { + if (error.code == + FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app + completion:completion + withError:error]; + } else if (error.code == + FIRAuthErrorCodeInternalError) { + [self handleInternalError:completion + withError:error]; + } else { + completion(nil, + [FLTFirebaseAuthPlugin + convertToFlutterError:error]); + } + } + } else { + completion( + [PigeonParser + getPigeonUserCredentialFromAuthResult: + authResult + authorizationCode:nil], + nil); + } + }]; + }]; +} + +- (void)signInWithCustomTokenApp:(nonnull AuthPigeonFirebaseApp *)app + token:(nonnull NSString *)token + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + [auth signInWithCustomToken:token + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error != nil) { + if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app completion:completion withError:error]; + } else if (error.code == FIRAuthErrorCodeInternalError) { + [self handleInternalError:completion withError:error]; + } else { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; +} + +- (void)signInWithEmailAndPasswordApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + password:(nonnull NSString *)password + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth signInWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error != nil) { + if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app completion:completion withError:error]; + } else if (error.code == FIRAuthErrorCodeInternalError) { + [self handleInternalError:completion withError:error]; + } else { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; +} + +- (void)signInWithEmailLinkApp:(nonnull AuthPigeonFirebaseApp *)app + email:(nonnull NSString *)email + emailLink:(nonnull NSString *)emailLink + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth signInWithEmail:email + link:emailLink + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error != nil) { + if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app completion:completion withError:error]; + } else if (error.code == FIRAuthErrorCodeInternalError) { + [self handleInternalError:completion withError:error]; + } else { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + } else { + completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult + authorizationCode:nil], + nil); + } + }]; +} + +- (void)signInWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + if ([signInProvider.providerId isEqualToString:kSignInMethodGameCenter]) { + completion( + nil, + [FlutterError + errorWithCode:@"sign-in-failure" + message: + @"Game Center sign-in requires signing in with 'signInWithCredential()' API." + details:@{}]); + return; + } + + if ([signInProvider.providerId isEqualToString:kSignInMethodApple]) { + self.signInWithAppleAuth = auth; + launchAppleSignInRequest(self, app, signInProvider, completion); + return; + } +#if TARGET_OS_OSX + NSLog(@"signInWithProvider is not supported on the " + @"MacOS platform."); + completion(nil, nil); +#elif TARGET_OS_TV + // -[FIROAuthProvider getCredentialWithUIDelegate:completion:] drives a + // browser-based OAuth redirect (ASWebAuthenticationSession), which tvOS + // doesn't support (no WebKit) — every signInWithProvider() call other + // than Sign in with Apple (handled above via ASAuthorizationController, + // which doesn't need a browser) needs this on tvOS. + NSLog(@"signInWithProvider (other than Apple) is not supported on tvOS: it requires a " + @"browser-based OAuth redirect."); + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"signInWithProvider is only supported for Apple " + @"Sign In on tvOS" + details:nil]); +#else + self.authProvider = [FIROAuthProvider providerWithProviderID:signInProvider.providerId auth:auth]; + NSArray *scopes = signInProvider.scopes; + if (scopes != nil) { + [self.authProvider setScopes:scopes]; + } + NSDictionary *customParameters = signInProvider.customParameters; + if (customParameters != nil) { + [self.authProvider setCustomParameters:customParameters]; + } + + [self.authProvider + getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, + NSError *_Nullable error) { + handleAppleAuthResult(self, app, auth, credential, error, completion); + }]; +#endif +} + +- (void)signOutApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + if (auth.currentUser == nil) { + completion(nil); + return; + } + + NSError *signOutErrorPtr; + BOOL signOutSuccessful = [auth signOut:&signOutErrorPtr]; + + if (!signOutSuccessful) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:signOutErrorPtr]); + } else { + completion(nil); + } +} + +- (void)useEmulatorApp:(nonnull AuthPigeonFirebaseApp *)app + host:(nonnull NSString *)host + port:(long)port + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth useEmulatorWithHost:host port:port]; + _emulatorConfigs[app.appName] = @{@"host" : host, @"port" : @(port)}; + completion(nil); +} + +- (void)verifyPasswordResetCodeApp:(nonnull AuthPigeonFirebaseApp *)app + code:(nonnull NSString *)code + completion:(nonnull void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + [auth verifyPasswordResetCode:code + completion:^(NSString *_Nullable email, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(email, nil); + } + }]; +} + +- (void)verifyPhoneNumberApp:(nonnull AuthPigeonFirebaseApp *)app + request:(nonnull InternalVerifyPhoneNumberRequest *)request + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { +#if TARGET_OS_OSX || TARGET_OS_TV + NSLog(@"The Firebase Phone Authentication provider is not supported on this " + @"platform."); + completion(nil, nil); +#else + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + + NSString *name = [NSString + stringWithFormat:@"%@/phone/%@", kFLTFirebaseAuthChannelName, [NSUUID UUID].UUIDString]; + FlutterEventChannel *channel = [FlutterEventChannel eventChannelWithName:name + binaryMessenger:_binaryMessenger]; + + NSString *multiFactorSessionId = request.multiFactorSessionId; + FIRMultiFactorSession *multiFactorSession = nil; + + if (multiFactorSessionId != nil) { + multiFactorSession = _multiFactorSessionMap[multiFactorSessionId]; + } + + NSString *multiFactorInfoId = request.multiFactorInfoId; + + FIRPhoneMultiFactorInfo *multiFactorInfo = nil; + if (multiFactorInfoId != nil) { + for (NSString *resolverId in _multiFactorResolverMap) { + for (FIRMultiFactorInfo *info in _multiFactorResolverMap[resolverId].hints) { + if ([info.UID isEqualToString:multiFactorInfoId] && + [info class] == [FIRPhoneMultiFactorInfo class]) { + multiFactorInfo = (FIRPhoneMultiFactorInfo *)info; + break; + } + } + } + } + +#if TARGET_OS_OSX + FLTPhoneNumberVerificationStreamHandler *handler = + [[FLTPhoneNumberVerificationStreamHandler alloc] initWithAuth:auth]; +#else + FLTPhoneNumberVerificationStreamHandler *handler = + [[FLTPhoneNumberVerificationStreamHandler alloc] initWithAuth:auth + request:request + session:multiFactorSession + factorInfo:multiFactorInfo]; +#endif + + [channel setStreamHandler:handler]; + + [_eventChannels setObject:channel forKey:name]; + [_streamHandlers setObject:handler forKey:name]; + + completion(name, nil); +#endif +} + +- (void)deleteApp:(nonnull AuthPigeonFirebaseApp *)app + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion([FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser deleteWithCompletion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)getIdTokenApp:(nonnull AuthPigeonFirebaseApp *)app + forceRefresh:(BOOL)forceRefresh + completion:(nonnull void (^)(InternalIdTokenResult *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser + getIDTokenResultForcingRefresh:forceRefresh + completion:^(FIRAuthTokenResult *tokenResult, NSError *error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + return; + } + + completion([PigeonParser parseIdTokenResult:tokenResult], nil); + }]; +} + +- (void)linkWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app + input:(nonnull NSDictionary *)input + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [self + getFIRAuthCredentialFromArguments:input + app:app + completion:^(FIRAuthCredential *credential, NSError *error) { + if (credential == nil) { + completion(nil, + [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + return; + } + + if (error) { + completion(nil, + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + + [currentUser + linkWithCredential:credential + completion:^(FIRAuthDataResult *authResult, + NSError *error) { + if (error != nil) { + if (error.code == + FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app + completion:completion + withError:error]; + } else { + completion(nil, [FLTFirebaseAuthPlugin + convertToFlutterError:error]); + } + } else { + completion( + [PigeonParser + getPigeonUserCredentialFromAuthResult: + authResult + authorizationCode:nil], + nil); + } + }]; + }]; +} + +- (void)linkWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if ([signInProvider.providerId isEqualToString:kSignInMethodGameCenter]) { + completion( + nil, + [FlutterError + errorWithCode:@"provider-link-failure" + message:@"Game Center provider requires linking with 'linkWithCredential()' API." + details:@{}]); + return; + } + + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + if ([signInProvider.providerId isEqualToString:kSignInMethodApple]) { + self.linkWithAppleUser = currentUser; + launchAppleSignInRequest(self, app, signInProvider, completion); + return; + } +#if TARGET_OS_OSX + NSLog(@"linkWithProvider is not supported on the " + @"MacOS platform."); + completion(nil, nil); +#elif TARGET_OS_TV + // See signInWithProviderApp above: browser-based OAuth redirect isn't + // available on tvOS for any provider other than Apple (handled above). + NSLog(@"linkWithProvider (other than Apple) is not supported on tvOS: it requires a " + @"browser-based OAuth redirect."); + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"linkWithProvider is only supported for Apple " + @"Sign In on tvOS" + details:nil]); +#else + self.authProvider = [FIROAuthProvider providerWithProviderID:signInProvider.providerId]; + NSArray *scopes = signInProvider.scopes; + if (scopes != nil) { + [self.authProvider setScopes:scopes]; + } + NSDictionary *customParameters = signInProvider.customParameters; + if (customParameters != nil) { + [self.authProvider setCustomParameters:customParameters]; + } + + [currentUser + linkWithProvider:self.authProvider + UIDelegate:nil + completion:^(FIRAuthDataResult *authResult, NSError *error) { + handleAppleAuthResult(self, app, auth, authResult.credential, error, completion); + }]; +#endif +} + +- (void)reauthenticateWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app + input:(nonnull NSDictionary *)input + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [self + getFIRAuthCredentialFromArguments:input + app:app + completion:^(FIRAuthCredential *credential, NSError *error) { + if (credential == nil) { + completion(nil, + [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + return; + } + + if (error) { + completion(nil, + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + + [currentUser + reauthenticateWithCredential:credential + completion:^(FIRAuthDataResult *authResult, + NSError *error) { + if (error != nil) { + if (error.code == + FIRAuthErrorCodeSecondFactorRequired) { + [self handleMultiFactorError:app + completion:completion + withError:error]; + } else { + completion( + nil, + [FLTFirebaseAuthPlugin + convertToFlutterError:error]); + } + } else { + completion( + [PigeonParser + getPigeonUserCredentialFromAuthResult: + authResult + authorizationCode: + nil], + nil); + } + }]; + }]; +} + +- (void)reauthenticateWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + if ([signInProvider.providerId isEqualToString:kSignInMethodApple]) { + self.isReauthenticatingWithApple = YES; + launchAppleSignInRequest(self, app, signInProvider, completion); + return; + } +#if TARGET_OS_OSX + NSLog(@"reauthenticateWithProvider is not supported on the " + @"MacOS platform."); + completion(nil, nil); +#elif TARGET_OS_TV + // See signInWithProviderApp above: browser-based OAuth redirect isn't + // available on tvOS for any provider other than Apple (handled above). + NSLog(@"reauthenticateWithProvider (other than Apple) is not supported on tvOS: it requires " + @"a browser-based OAuth redirect."); + completion(nil, [FlutterError errorWithCode:@"unsupported-platform" + message:@"reauthenticateWithProvider is only supported " + @"for Apple Sign In on tvOS" + details:nil]); +#else + self.authProvider = [FIROAuthProvider providerWithProviderID:signInProvider.providerId]; + NSArray *scopes = signInProvider.scopes; + if (scopes != nil) { + [self.authProvider setScopes:scopes]; + } + NSDictionary *customParameters = signInProvider.customParameters; + if (customParameters != nil) { + [self.authProvider setCustomParameters:customParameters]; + } + + [currentUser reauthenticateWithProvider:self.authProvider + UIDelegate:nil + completion:^(FIRAuthDataResult *authResult, NSError *error) { + handleAppleAuthResult(self, app, auth, authResult.credential, + error, completion); + }]; +#endif +} + +- (void)reloadApp:(nonnull AuthPigeonFirebaseApp *)app + completion: + (nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser reloadWithCompletion:^(NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion([PigeonParser getPigeonDetails:currentUser], nil); + } + }]; +} + +- (void)sendEmailVerificationApp:(nonnull AuthPigeonFirebaseApp *)app + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion([FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser + sendEmailVerificationWithActionCodeSettings:[PigeonParser + parseActionCodeSettings:actionCodeSettings] + + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion( + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)unlinkApp:(nonnull AuthPigeonFirebaseApp *)app + providerId:(nonnull NSString *)providerId + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser unlinkFromProvider:providerId + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion([PigeonParser getPigeonUserCredentialFromFIRUser:user], nil); + } + }]; +} + +- (void)updateEmailApp:(nonnull AuthPigeonFirebaseApp *)app + newEmail:(nonnull NSString *)newEmail + completion:(nonnull void (^)(InternalUserDetails *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser updateEmail:newEmail + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + [currentUser reloadWithCompletion:^(NSError *_Nullable reloadError) { + if (reloadError != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:reloadError]); + } else { + completion([PigeonParser getPigeonDetails:currentUser], nil); + } + }]; + } + }]; +} + +- (void)updatePasswordApp:(nonnull AuthPigeonFirebaseApp *)app + newPassword:(nonnull NSString *)newPassword + completion:(nonnull void (^)(InternalUserDetails *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser + updatePassword:newPassword + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + [currentUser reloadWithCompletion:^(NSError *_Nullable reloadError) { + if (reloadError != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:reloadError]); + } else { + completion([PigeonParser getPigeonDetails:currentUser], nil); + } + }]; + } + }]; +} + +- (void)updatePhoneNumberApp:(nonnull AuthPigeonFirebaseApp *)app + input:(nonnull NSDictionary *)input + completion:(nonnull void (^)(InternalUserDetails *_Nullable, + FlutterError *_Nullable))completion { +#if TARGET_OS_IPHONE && !TARGET_OS_TV + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [self + getFIRAuthCredentialFromArguments:input + app:app + completion:^(FIRAuthCredential *credential, NSError *error) { + if (credential == nil) { + completion(nil, + [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + return; + } + + if (error) { + completion(nil, + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } + + [currentUser + updatePhoneNumberCredential:(FIRPhoneAuthCredential *)credential + completion:^(NSError *_Nullable error) { + if (error != nil) { + completion( + nil, [FLTFirebaseAuthPlugin + convertToFlutterError:error]); + } else { + [currentUser + reloadWithCompletion:^( + NSError *_Nullable reloadError) { + if (reloadError != nil) { + completion( + nil, [FLTFirebaseAuthPlugin + convertToFlutterError: + reloadError]); + } else { + completion( + [PigeonParser getPigeonDetails: + currentUser], + nil); + } + }]; + } + }]; + }]; +#else + NSLog(@"Updating a users phone number via Firebase Authentication is only " + @"supported on the iOS " + @"platform."); + completion(nil, nil); +#endif // TARGET_OS_IPHONE && !TARGET_OS_TV +} + +- (void)updateProfileApp:(nonnull AuthPigeonFirebaseApp *)app + profile:(nonnull InternalUserProfile *)profile + completion:(nonnull void (^)(InternalUserDetails *_Nullable, + FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + FIRUserProfileChangeRequest *changeRequest = [currentUser profileChangeRequest]; + + if (profile.displayNameChanged) { + changeRequest.displayName = profile.displayName; + } + + if (profile.photoUrlChanged) { + if (profile.photoUrl == nil) { + // We apparently cannot set photoURL to nil/NULL to remove it. + // Instead, setting it to empty string appears to work. + // When doing so, Dart will properly receive `null` anyway. + changeRequest.photoURL = [NSURL URLWithString:@""]; + } else { + changeRequest.photoURL = [NSURL URLWithString:profile.photoUrl]; + } + } + + [changeRequest commitChangesWithCompletion:^(NSError *error) { + if (error != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + [currentUser reloadWithCompletion:^(NSError *_Nullable reloadError) { + if (reloadError != nil) { + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:reloadError]); + } else { + completion([PigeonParser getPigeonDetails:currentUser], nil); + } + }]; + } + }]; +} + +- (void)verifyBeforeUpdateEmailApp:(nonnull AuthPigeonFirebaseApp *)app + newEmail:(nonnull NSString *)newEmail + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + FIRUser *currentUser = auth.currentUser; + if (currentUser == nil) { + completion([FlutterError errorWithCode:kErrCodeNoCurrentUser + message:kErrMsgNoCurrentUser + details:nil]); + return; + } + + [currentUser + sendEmailVerificationBeforeUpdatingEmail:newEmail + actionCodeSettings:[PigeonParser + parseActionCodeSettings:actionCodeSettings] + completion:^(NSError *error) { + if (error != nil) { + completion( + [FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +} + +- (void)initializeRecaptchaConfigApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion { +#if TARGET_OS_OSX || TARGET_OS_TV + // reCAPTCHA app-verification needs WebKit, which tvOS doesn't have. + NSLog(@"initializeRecaptchaConfigWithCompletion is not supported on this " + @"platform."); + completion(nil); +#else + FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; + [auth initializeRecaptchaConfigWithCompletion:^(NSError *_Nullable error) { + if (error != nil) { + completion([FLTFirebaseAuthPlugin convertToFlutterError:error]); + } else { + completion(nil); + } + }]; +#endif +} + +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/FLTIdTokenChannelStreamHandler.m b/packages/firebase_auth_tvos/tvos/Classes/FLTIdTokenChannelStreamHandler.m new file mode 100644 index 0000000..315bc5e --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/FLTIdTokenChannelStreamHandler.m @@ -0,0 +1,54 @@ +// 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 FirebaseAuth; +#import "include/Private/FLTIdTokenChannelStreamHandler.h" +#import +#import "include/Private/PigeonParser.h" +#import "include/Public/FLTFirebaseAuthPlugin.h" + +@implementation FLTIdTokenChannelStreamHandler { + FIRAuth *_auth; + FIRIDTokenDidChangeListenerHandle _listener; +} + +- (instancetype)initWithAuth:(FIRAuth *)auth { + self = [super init]; + if (self) { + _auth = auth; + } + return self; +} + +- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events { + bool __block initialAuthState = YES; + + _listener = [_auth addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, + FIRUser *_Nullable user) { + if (initialAuthState) { + initialAuthState = NO; + return; + } + + if (user) { + events(@{ + @"user" : [PigeonParser getManualList:[PigeonParser getPigeonDetails:[auth currentUser]]] + }); + } else { + events(@{@"user" : [NSNull null]}); + } + }]; + + return nil; +} + +- (FlutterError *)onCancelWithArguments:(id)arguments { + if (_listener) { + [_auth removeIDTokenDidChangeListener:_listener]; + } + _listener = nil; + + return nil; +} + +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/FLTPhoneNumberVerificationStreamHandler.m b/packages/firebase_auth_tvos/tvos/Classes/FLTPhoneNumberVerificationStreamHandler.m new file mode 100644 index 0000000..056a04a --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/FLTPhoneNumberVerificationStreamHandler.m @@ -0,0 +1,105 @@ +// 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 FirebaseAuth; + +#import "include/Private/FLTPhoneNumberVerificationStreamHandler.h" +#import "include/Public/FLTFirebaseAuthPlugin.h" + +@implementation FLTPhoneNumberVerificationStreamHandler { + FIRAuth *_auth; + NSString *_phoneNumber; +#if TARGET_OS_OSX +#else + FIRMultiFactorSession *_session; + FIRPhoneMultiFactorInfo *_factorInfo; +#endif +} + +#if TARGET_OS_OSX +- (instancetype)initWithAuth:(id)auth request:(InternalVerifyPhoneNumberRequest *)request { + self = [super init]; + if (self) { + _auth = auth; + _phoneNumber = request.phoneNumber; + } + return self; +} +#else +- (instancetype)initWithAuth:(id)auth + request:(InternalVerifyPhoneNumberRequest *)request + session:(FIRMultiFactorSession *)session + factorInfo:(FIRPhoneMultiFactorInfo *)factorInfo { + self = [super init]; + if (self) { + _auth = auth; + _phoneNumber = request.phoneNumber; + _session = session; + _factorInfo = factorInfo; + } + return self; +} +#endif + +- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events { +// Phone-number verification disabled on tvOS by hand: TARGET_OS_IPHONE is +// true on tvOS too (it covers all embedded Apple OSes, not just iOS), so +// this block compiled here unmodified — but Firebase's CocoaPods +// FirebaseAuth/Auth subspec doesn't export FIRPhoneAuthProvider's +// interface for tvOS, same situation as FIRTOTPSecret above. Phone/SMS +// auth has no tvOS UI path anyway (no SIM, no number entry affordance); +// the stream simply never emits an event on tvOS. +#if TARGET_OS_IPHONE && !TARGET_OS_TV + id completer = ^(NSString *verificationID, NSError *error) { + if (error != nil) { + FlutterError *errorDetails = [FLTFirebaseAuthPlugin convertToFlutterError:error]; + events(@{ + @"name" : @"Auth#phoneVerificationFailed", + @"error" : @{ + @"code" : errorDetails.code, + @"message" : errorDetails.message, + @"details" : errorDetails.details, + } + }); + } else { + events(@{ + @"name" : @"Auth#phoneCodeSent", + @"verificationId" : verificationID, + }); + } + }; + + // Try catch to capture 'missing URL scheme' error. + @try { + if (_factorInfo != nil) { + [[FIRPhoneAuthProvider providerWithAuth:_auth] + verifyPhoneNumberWithMultiFactorInfo:_factorInfo + UIDelegate:nil + multiFactorSession:_session + completion:completer]; + + } else { + [[FIRPhoneAuthProvider providerWithAuth:_auth] verifyPhoneNumber:_phoneNumber + UIDelegate:nil + multiFactorSession:_session + completion:completer]; + } + } @catch (NSException *exception) { + events(@{ + @"name" : @"Auth#phoneVerificationFailed", + @"error" : @{ + @"message" : exception.reason, + } + }); + } +#endif + + return nil; +} + +- (FlutterError *)onCancelWithArguments:(id)arguments { + return nil; +} + +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/PigeonParser.m b/packages/firebase_auth_tvos/tvos/Classes/PigeonParser.m new file mode 100644 index 0000000..8c5fd47 --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/PigeonParser.m @@ -0,0 +1,177 @@ +// Copyright 2023, 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. + +@import FirebaseAuth; + +#import "include/Private/PigeonParser.h" +#import +#import +#import "include/Public/CustomPigeonHeader.h" + +@implementation PigeonParser + ++ (InternalUserCredential *) + getPigeonUserCredentialFromAuthResult:(nonnull FIRAuthDataResult *)authResult + authorizationCode:(nullable NSString *)authorizationCode { + return [InternalUserCredential + makeWithUser:[self getPigeonDetails:authResult.user] + additionalUserInfo:[self getPigeonAdditionalUserInfo:authResult.additionalUserInfo + authorizationCode:authorizationCode] + credential:[self getPigeonAuthCredential:authResult.credential token:nil]]; +} + ++ (InternalUserCredential *)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user { + return [InternalUserCredential makeWithUser:[self getPigeonDetails:user] + additionalUserInfo:nil + credential:nil]; +} + ++ (InternalUserDetails *)getPigeonDetails:(nonnull FIRUser *)user { + return [InternalUserDetails makeWithUserInfo:[self getPigeonUserInfo:user] + providerData:[self getProviderData:user.providerData]]; +} + ++ (InternalUserInfo *)getPigeonUserInfo:(nonnull FIRUser *)user { + NSString *photoUrlString = user.photoURL.absoluteString; + return [InternalUserInfo + makeWithUid:user.uid + email:user.email + displayName:user.displayName + photoUrl:(photoUrlString.length > 0) ? photoUrlString : nil + phoneNumber:user.phoneNumber + isAnonymous:user.isAnonymous + isEmailVerified:user.emailVerified + providerId:user.providerID + tenantId:user.tenantID + refreshToken:user.refreshToken + creationTimestamp:@((long)([user.metadata.creationDate timeIntervalSince1970] * 1000)) + lastSignInTimestamp:@((long)([user.metadata.lastSignInDate timeIntervalSince1970] * 1000))]; +} + ++ (NSArray *> *)getProviderData: + (nonnull NSArray> *)providerData { + NSMutableArray *> *dataArray = + [NSMutableArray arrayWithCapacity:providerData.count]; + + for (id userInfo in providerData) { + NSString *photoUrlStr = userInfo.photoURL.absoluteString; + NSDictionary *dataDict = @{ + @"providerId" : userInfo.providerID, + // Can be null on emulator + @"uid" : userInfo.uid ?: @"", + @"displayName" : userInfo.displayName ?: [NSNull null], + @"email" : userInfo.email ?: [NSNull null], + @"phoneNumber" : userInfo.phoneNumber ?: [NSNull null], + @"photoURL" : photoUrlStr ?: [NSNull null], + // isAnonymous is always false on in a providerData object (the user is not anonymous) + @"isAnonymous" : @NO, + // isEmailVerified is always true on in a providerData object (the email is verified by the + // provider) + @"isEmailVerified" : @YES, + }; + [dataArray addObject:dataDict]; + } + return [dataArray copy]; +} + ++ (InternalAdditionalUserInfo *)getPigeonAdditionalUserInfo: + (nonnull FIRAdditionalUserInfo *)userInfo + authorizationCode:(nullable NSString *)authorizationCode { + return [InternalAdditionalUserInfo makeWithIsNewUser:userInfo.isNewUser + providerId:userInfo.providerID + username:userInfo.username + authorizationCode:authorizationCode + profile:userInfo.profile]; +} + +// Disabled on tvOS by hand — see FLTFirebaseAuthPlugin.m for why +// FIRTOTPSecret's interface isn't visible on tvOS (it's the only caller of +// this method, and is itself disabled). +#if !TARGET_OS_TV ++ (InternalTotpSecret *)getPigeonTotpSecret:(FIRTOTPSecret *)secret { + return [InternalTotpSecret makeWithCodeIntervalSeconds:nil + codeLength:nil + enrollmentCompletionDeadline:nil + hashingAlgorithm:nil + secretKey:secret.sharedSecretKey]; +} +#endif // !TARGET_OS_TV + ++ (InternalAuthCredential *)getPigeonAuthCredential:(FIRAuthCredential *)authCredential + token:(NSNumber *_Nullable)token { + if (authCredential == nil) { + return nil; + } + + NSString *accessToken = nil; + if ([authCredential isKindOfClass:[FIROAuthCredential class]]) { + if (((FIROAuthCredential *)authCredential).accessToken != nil) { + accessToken = ((FIROAuthCredential *)authCredential).accessToken; + } else if (((FIROAuthCredential *)authCredential).IDToken != nil) { + // For Sign In With Apple, the token is stored in IDToken + accessToken = ((FIROAuthCredential *)authCredential).IDToken; + } + } + + NSUInteger nativeId = + token != nil ? [token unsignedLongValue] : (NSUInteger)[authCredential hash]; + + return [InternalAuthCredential makeWithProviderId:authCredential.provider + signInMethod:authCredential.provider + nativeId:nativeId + accessToken:accessToken ?: nil]; +} + ++ (FIRActionCodeSettings *_Nullable)parseActionCodeSettings: + (nullable InternalActionCodeSettings *)settings { + if (settings == nil) { + return nil; + } + + FIRActionCodeSettings *codeSettings = [[FIRActionCodeSettings alloc] init]; + + if (settings.url != nil) { + codeSettings.URL = [NSURL URLWithString:settings.url]; + } + + if (settings.linkDomain != nil) { + codeSettings.linkDomain = settings.linkDomain; + } + + codeSettings.handleCodeInApp = settings.handleCodeInApp; + + if (settings.iOSBundleId != nil) { + codeSettings.iOSBundleID = settings.iOSBundleId; + } + + return codeSettings; +} + ++ (InternalIdTokenResult *)parseIdTokenResult:(FIRAuthTokenResult *)tokenResult { + long expirationTimestamp = (long)[tokenResult.expirationDate timeIntervalSince1970] * 1000; + long authTimestamp = (long)[tokenResult.authDate timeIntervalSince1970] * 1000; + long issuedAtTimestamp = (long)[tokenResult.issuedAtDate timeIntervalSince1970] * 1000; + + return [InternalIdTokenResult makeWithToken:tokenResult.token + expirationTimestamp:@(expirationTimestamp) + authTimestamp:@(authTimestamp) + issuedAtTimestamp:@(issuedAtTimestamp) + signInProvider:tokenResult.signInProvider + claims:tokenResult.claims + signInSecondFactor:tokenResult.signInSecondFactor]; +} + ++ (NSArray *_Nonnull)getManualList:(nonnull InternalUserDetails *)userDetails { + NSMutableArray *output = [NSMutableArray array]; + + id userInfoList = [[userDetails userInfo] toList]; + [output addObject:userInfoList]; + + id providerData = [userDetails providerData]; + [output addObject:providerData]; + + return [output copy]; +} + +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/firebase_auth_messages.g.m b/packages/firebase_auth_tvos/tvos/Classes/firebase_auth_messages.g.m new file mode 100644 index 0000000..82ae8cf --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/firebase_auth_messages.g.m @@ -0,0 +1,3005 @@ +// Copyright 2023, 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 "include/Public/firebase_auth_messages.g.h" + +#if TARGET_OS_OSX +@import FlutterMacOS; +#else +@import Flutter; +#endif + +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} + +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +/// The type of operation that generated the action code from calling +/// [checkActionCode]. +@implementation ActionCodeInfoOperationBox +- (instancetype)initWithValue:(ActionCodeInfoOperation)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@interface InternalMultiFactorSession () ++ (InternalMultiFactorSession *)fromList:(NSArray *)list; ++ (nullable InternalMultiFactorSession *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalPhoneMultiFactorAssertion () ++ (InternalPhoneMultiFactorAssertion *)fromList:(NSArray *)list; ++ (nullable InternalPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalMultiFactorInfo () ++ (InternalMultiFactorInfo *)fromList:(NSArray *)list; ++ (nullable InternalMultiFactorInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface AuthPigeonFirebaseApp () ++ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list; ++ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalActionCodeInfoData () ++ (InternalActionCodeInfoData *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeInfoData *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalActionCodeInfo () ++ (InternalActionCodeInfo *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalAdditionalUserInfo () ++ (InternalAdditionalUserInfo *)fromList:(NSArray *)list; ++ (nullable InternalAdditionalUserInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalAuthCredential () ++ (InternalAuthCredential *)fromList:(NSArray *)list; ++ (nullable InternalAuthCredential *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalUserInfo () ++ (InternalUserInfo *)fromList:(NSArray *)list; ++ (nullable InternalUserInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalUserDetails () ++ (InternalUserDetails *)fromList:(NSArray *)list; ++ (nullable InternalUserDetails *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalUserCredential () ++ (InternalUserCredential *)fromList:(NSArray *)list; ++ (nullable InternalUserCredential *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalAuthCredentialInput () ++ (InternalAuthCredentialInput *)fromList:(NSArray *)list; ++ (nullable InternalAuthCredentialInput *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalActionCodeSettings () ++ (InternalActionCodeSettings *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalFirebaseAuthSettings () ++ (InternalFirebaseAuthSettings *)fromList:(NSArray *)list; ++ (nullable InternalFirebaseAuthSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalSignInProvider () ++ (InternalSignInProvider *)fromList:(NSArray *)list; ++ (nullable InternalSignInProvider *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalVerifyPhoneNumberRequest () ++ (InternalVerifyPhoneNumberRequest *)fromList:(NSArray *)list; ++ (nullable InternalVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalIdTokenResult () ++ (InternalIdTokenResult *)fromList:(NSArray *)list; ++ (nullable InternalIdTokenResult *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalUserProfile () ++ (InternalUserProfile *)fromList:(NSArray *)list; ++ (nullable InternalUserProfile *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalTotpSecret () ++ (InternalTotpSecret *)fromList:(NSArray *)list; ++ (nullable InternalTotpSecret *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation InternalMultiFactorSession ++ (instancetype)makeWithId:(NSString *)id { + InternalMultiFactorSession *pigeonResult = [[InternalMultiFactorSession alloc] init]; + pigeonResult.id = id; + return pigeonResult; +} ++ (InternalMultiFactorSession *)fromList:(NSArray *)list { + InternalMultiFactorSession *pigeonResult = [[InternalMultiFactorSession alloc] init]; + pigeonResult.id = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable InternalMultiFactorSession *)nullableFromList:(NSArray *)list { + return (list) ? [InternalMultiFactorSession fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.id ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalMultiFactorSession *other = (InternalMultiFactorSession *)object; + return FLTPigeonDeepEquals(self.id, other.id); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.id); + return result; +} +@end + +@implementation InternalPhoneMultiFactorAssertion ++ (instancetype)makeWithVerificationId:(NSString *)verificationId + verificationCode:(NSString *)verificationCode { + InternalPhoneMultiFactorAssertion *pigeonResult = + [[InternalPhoneMultiFactorAssertion alloc] init]; + pigeonResult.verificationId = verificationId; + pigeonResult.verificationCode = verificationCode; + return pigeonResult; +} ++ (InternalPhoneMultiFactorAssertion *)fromList:(NSArray *)list { + InternalPhoneMultiFactorAssertion *pigeonResult = + [[InternalPhoneMultiFactorAssertion alloc] init]; + pigeonResult.verificationId = GetNullableObjectAtIndex(list, 0); + pigeonResult.verificationCode = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable InternalPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPhoneMultiFactorAssertion fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.verificationId ?: [NSNull null], + self.verificationCode ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPhoneMultiFactorAssertion *other = (InternalPhoneMultiFactorAssertion *)object; + return FLTPigeonDeepEquals(self.verificationId, other.verificationId) && + FLTPigeonDeepEquals(self.verificationCode, other.verificationCode); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.verificationId); + result = result * 31 + FLTPigeonDeepHash(self.verificationCode); + return result; +} +@end + +@implementation InternalMultiFactorInfo ++ (instancetype)makeWithDisplayName:(nullable NSString *)displayName + enrollmentTimestamp:(double)enrollmentTimestamp + factorId:(nullable NSString *)factorId + uid:(NSString *)uid + phoneNumber:(nullable NSString *)phoneNumber { + InternalMultiFactorInfo *pigeonResult = [[InternalMultiFactorInfo alloc] init]; + pigeonResult.displayName = displayName; + pigeonResult.enrollmentTimestamp = enrollmentTimestamp; + pigeonResult.factorId = factorId; + pigeonResult.uid = uid; + pigeonResult.phoneNumber = phoneNumber; + return pigeonResult; +} ++ (InternalMultiFactorInfo *)fromList:(NSArray *)list { + InternalMultiFactorInfo *pigeonResult = [[InternalMultiFactorInfo alloc] init]; + pigeonResult.displayName = GetNullableObjectAtIndex(list, 0); + pigeonResult.enrollmentTimestamp = [GetNullableObjectAtIndex(list, 1) doubleValue]; + pigeonResult.factorId = GetNullableObjectAtIndex(list, 2); + pigeonResult.uid = GetNullableObjectAtIndex(list, 3); + pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable InternalMultiFactorInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalMultiFactorInfo fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.displayName ?: [NSNull null], + @(self.enrollmentTimestamp), + self.factorId ?: [NSNull null], + self.uid ?: [NSNull null], + self.phoneNumber ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalMultiFactorInfo *other = (InternalMultiFactorInfo *)object; + return FLTPigeonDeepEquals(self.displayName, other.displayName) && + (self.enrollmentTimestamp == other.enrollmentTimestamp || + (isnan(self.enrollmentTimestamp) && isnan(other.enrollmentTimestamp))) && + FLTPigeonDeepEquals(self.factorId, other.factorId) && + FLTPigeonDeepEquals(self.uid, other.uid) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + (isnan(self.enrollmentTimestamp) ? (NSUInteger)0x7FF8000000000000 + : @(self.enrollmentTimestamp).hash); + result = result * 31 + FLTPigeonDeepHash(self.factorId); + result = result * 31 + FLTPigeonDeepHash(self.uid); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + return result; +} +@end + +@implementation AuthPigeonFirebaseApp ++ (instancetype)makeWithAppName:(NSString *)appName + tenantId:(nullable NSString *)tenantId + customAuthDomain:(nullable NSString *)customAuthDomain { + AuthPigeonFirebaseApp *pigeonResult = [[AuthPigeonFirebaseApp alloc] init]; + pigeonResult.appName = appName; + pigeonResult.tenantId = tenantId; + pigeonResult.customAuthDomain = customAuthDomain; + return pigeonResult; +} ++ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list { + AuthPigeonFirebaseApp *pigeonResult = [[AuthPigeonFirebaseApp alloc] init]; + pigeonResult.appName = GetNullableObjectAtIndex(list, 0); + pigeonResult.tenantId = GetNullableObjectAtIndex(list, 1); + pigeonResult.customAuthDomain = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list { + return (list) ? [AuthPigeonFirebaseApp fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.appName ?: [NSNull null], + self.tenantId ?: [NSNull null], + self.customAuthDomain ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AuthPigeonFirebaseApp *other = (AuthPigeonFirebaseApp *)object; + return FLTPigeonDeepEquals(self.appName, other.appName) && + FLTPigeonDeepEquals(self.tenantId, other.tenantId) && + FLTPigeonDeepEquals(self.customAuthDomain, other.customAuthDomain); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appName); + result = result * 31 + FLTPigeonDeepHash(self.tenantId); + result = result * 31 + FLTPigeonDeepHash(self.customAuthDomain); + return result; +} +@end + +@implementation InternalActionCodeInfoData ++ (instancetype)makeWithEmail:(nullable NSString *)email + previousEmail:(nullable NSString *)previousEmail { + InternalActionCodeInfoData *pigeonResult = [[InternalActionCodeInfoData alloc] init]; + pigeonResult.email = email; + pigeonResult.previousEmail = previousEmail; + return pigeonResult; +} ++ (InternalActionCodeInfoData *)fromList:(NSArray *)list { + InternalActionCodeInfoData *pigeonResult = [[InternalActionCodeInfoData alloc] init]; + pigeonResult.email = GetNullableObjectAtIndex(list, 0); + pigeonResult.previousEmail = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable InternalActionCodeInfoData *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeInfoData fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.email ?: [NSNull null], + self.previousEmail ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeInfoData *other = (InternalActionCodeInfoData *)object; + return FLTPigeonDeepEquals(self.email, other.email) && + FLTPigeonDeepEquals(self.previousEmail, other.previousEmail); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.email); + result = result * 31 + FLTPigeonDeepHash(self.previousEmail); + return result; +} +@end + +@implementation InternalActionCodeInfo ++ (instancetype)makeWithOperation:(ActionCodeInfoOperation)operation + data:(InternalActionCodeInfoData *)data { + InternalActionCodeInfo *pigeonResult = [[InternalActionCodeInfo alloc] init]; + pigeonResult.operation = operation; + pigeonResult.data = data; + return pigeonResult; +} ++ (InternalActionCodeInfo *)fromList:(NSArray *)list { + InternalActionCodeInfo *pigeonResult = [[InternalActionCodeInfo alloc] init]; + ActionCodeInfoOperationBox *boxedActionCodeInfoOperation = GetNullableObjectAtIndex(list, 0); + pigeonResult.operation = boxedActionCodeInfoOperation.value; + pigeonResult.data = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable InternalActionCodeInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeInfo fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[ActionCodeInfoOperationBox alloc] initWithValue:self.operation], + self.data ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeInfo *other = (InternalActionCodeInfo *)object; + return self.operation == other.operation && FLTPigeonDeepEquals(self.data, other.data); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.operation).hash; + result = result * 31 + FLTPigeonDeepHash(self.data); + return result; +} +@end + +@implementation InternalAdditionalUserInfo ++ (instancetype)makeWithIsNewUser:(BOOL)isNewUser + providerId:(nullable NSString *)providerId + username:(nullable NSString *)username + authorizationCode:(nullable NSString *)authorizationCode + profile:(nullable NSDictionary *)profile { + InternalAdditionalUserInfo *pigeonResult = [[InternalAdditionalUserInfo alloc] init]; + pigeonResult.isNewUser = isNewUser; + pigeonResult.providerId = providerId; + pigeonResult.username = username; + pigeonResult.authorizationCode = authorizationCode; + pigeonResult.profile = profile; + return pigeonResult; +} ++ (InternalAdditionalUserInfo *)fromList:(NSArray *)list { + InternalAdditionalUserInfo *pigeonResult = [[InternalAdditionalUserInfo alloc] init]; + pigeonResult.isNewUser = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 1); + pigeonResult.username = GetNullableObjectAtIndex(list, 2); + pigeonResult.authorizationCode = GetNullableObjectAtIndex(list, 3); + pigeonResult.profile = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable InternalAdditionalUserInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAdditionalUserInfo fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.isNewUser), + self.providerId ?: [NSNull null], + self.username ?: [NSNull null], + self.authorizationCode ?: [NSNull null], + self.profile ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAdditionalUserInfo *other = (InternalAdditionalUserInfo *)object; + return self.isNewUser == other.isNewUser && + FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.username, other.username) && + FLTPigeonDeepEquals(self.authorizationCode, other.authorizationCode) && + FLTPigeonDeepEquals(self.profile, other.profile); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.isNewUser).hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.username); + result = result * 31 + FLTPigeonDeepHash(self.authorizationCode); + result = result * 31 + FLTPigeonDeepHash(self.profile); + return result; +} +@end + +@implementation InternalAuthCredential ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + nativeId:(NSInteger)nativeId + accessToken:(nullable NSString *)accessToken { + InternalAuthCredential *pigeonResult = [[InternalAuthCredential alloc] init]; + pigeonResult.providerId = providerId; + pigeonResult.signInMethod = signInMethod; + pigeonResult.nativeId = nativeId; + pigeonResult.accessToken = accessToken; + return pigeonResult; +} ++ (InternalAuthCredential *)fromList:(NSArray *)list { + InternalAuthCredential *pigeonResult = [[InternalAuthCredential alloc] init]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); + pigeonResult.signInMethod = GetNullableObjectAtIndex(list, 1); + pigeonResult.nativeId = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.accessToken = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalAuthCredential *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAuthCredential fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.providerId ?: [NSNull null], + self.signInMethod ?: [NSNull null], + @(self.nativeId), + self.accessToken ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAuthCredential *other = (InternalAuthCredential *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.signInMethod, other.signInMethod) && + self.nativeId == other.nativeId && + FLTPigeonDeepEquals(self.accessToken, other.accessToken); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.signInMethod); + result = result * 31 + @(self.nativeId).hash; + result = result * 31 + FLTPigeonDeepHash(self.accessToken); + return result; +} +@end + +@implementation InternalUserInfo ++ (instancetype)makeWithUid:(NSString *)uid + email:(nullable NSString *)email + displayName:(nullable NSString *)displayName + photoUrl:(nullable NSString *)photoUrl + phoneNumber:(nullable NSString *)phoneNumber + isAnonymous:(BOOL)isAnonymous + isEmailVerified:(BOOL)isEmailVerified + providerId:(nullable NSString *)providerId + tenantId:(nullable NSString *)tenantId + refreshToken:(nullable NSString *)refreshToken + creationTimestamp:(nullable NSNumber *)creationTimestamp + lastSignInTimestamp:(nullable NSNumber *)lastSignInTimestamp { + InternalUserInfo *pigeonResult = [[InternalUserInfo alloc] init]; + pigeonResult.uid = uid; + pigeonResult.email = email; + pigeonResult.displayName = displayName; + pigeonResult.photoUrl = photoUrl; + pigeonResult.phoneNumber = phoneNumber; + pigeonResult.isAnonymous = isAnonymous; + pigeonResult.isEmailVerified = isEmailVerified; + pigeonResult.providerId = providerId; + pigeonResult.tenantId = tenantId; + pigeonResult.refreshToken = refreshToken; + pigeonResult.creationTimestamp = creationTimestamp; + pigeonResult.lastSignInTimestamp = lastSignInTimestamp; + return pigeonResult; +} ++ (InternalUserInfo *)fromList:(NSArray *)list { + InternalUserInfo *pigeonResult = [[InternalUserInfo alloc] init]; + pigeonResult.uid = GetNullableObjectAtIndex(list, 0); + pigeonResult.email = GetNullableObjectAtIndex(list, 1); + pigeonResult.displayName = GetNullableObjectAtIndex(list, 2); + pigeonResult.photoUrl = GetNullableObjectAtIndex(list, 3); + pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 4); + pigeonResult.isAnonymous = [GetNullableObjectAtIndex(list, 5) boolValue]; + pigeonResult.isEmailVerified = [GetNullableObjectAtIndex(list, 6) boolValue]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 7); + pigeonResult.tenantId = GetNullableObjectAtIndex(list, 8); + pigeonResult.refreshToken = GetNullableObjectAtIndex(list, 9); + pigeonResult.creationTimestamp = GetNullableObjectAtIndex(list, 10); + pigeonResult.lastSignInTimestamp = GetNullableObjectAtIndex(list, 11); + return pigeonResult; +} ++ (nullable InternalUserInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserInfo fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.uid ?: [NSNull null], + self.email ?: [NSNull null], + self.displayName ?: [NSNull null], + self.photoUrl ?: [NSNull null], + self.phoneNumber ?: [NSNull null], + @(self.isAnonymous), + @(self.isEmailVerified), + self.providerId ?: [NSNull null], + self.tenantId ?: [NSNull null], + self.refreshToken ?: [NSNull null], + self.creationTimestamp ?: [NSNull null], + self.lastSignInTimestamp ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserInfo *other = (InternalUserInfo *)object; + return FLTPigeonDeepEquals(self.uid, other.uid) && FLTPigeonDeepEquals(self.email, other.email) && + FLTPigeonDeepEquals(self.displayName, other.displayName) && + FLTPigeonDeepEquals(self.photoUrl, other.photoUrl) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + self.isAnonymous == other.isAnonymous && self.isEmailVerified == other.isEmailVerified && + FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.tenantId, other.tenantId) && + FLTPigeonDeepEquals(self.refreshToken, other.refreshToken) && + FLTPigeonDeepEquals(self.creationTimestamp, other.creationTimestamp) && + FLTPigeonDeepEquals(self.lastSignInTimestamp, other.lastSignInTimestamp); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.uid); + result = result * 31 + FLTPigeonDeepHash(self.email); + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + FLTPigeonDeepHash(self.photoUrl); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + @(self.isAnonymous).hash; + result = result * 31 + @(self.isEmailVerified).hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.tenantId); + result = result * 31 + FLTPigeonDeepHash(self.refreshToken); + result = result * 31 + FLTPigeonDeepHash(self.creationTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.lastSignInTimestamp); + return result; +} +@end + +@implementation InternalUserDetails ++ (instancetype)makeWithUserInfo:(InternalUserInfo *)userInfo + providerData:(NSArray *> *)providerData { + InternalUserDetails *pigeonResult = [[InternalUserDetails alloc] init]; + pigeonResult.userInfo = userInfo; + pigeonResult.providerData = providerData; + return pigeonResult; +} ++ (InternalUserDetails *)fromList:(NSArray *)list { + InternalUserDetails *pigeonResult = [[InternalUserDetails alloc] init]; + pigeonResult.userInfo = GetNullableObjectAtIndex(list, 0); + pigeonResult.providerData = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable InternalUserDetails *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserDetails fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.userInfo ?: [NSNull null], + self.providerData ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserDetails *other = (InternalUserDetails *)object; + return FLTPigeonDeepEquals(self.userInfo, other.userInfo) && + FLTPigeonDeepEquals(self.providerData, other.providerData); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.userInfo); + result = result * 31 + FLTPigeonDeepHash(self.providerData); + return result; +} +@end + +@implementation InternalUserCredential ++ (instancetype)makeWithUser:(nullable InternalUserDetails *)user + additionalUserInfo:(nullable InternalAdditionalUserInfo *)additionalUserInfo + credential:(nullable InternalAuthCredential *)credential { + InternalUserCredential *pigeonResult = [[InternalUserCredential alloc] init]; + pigeonResult.user = user; + pigeonResult.additionalUserInfo = additionalUserInfo; + pigeonResult.credential = credential; + return pigeonResult; +} ++ (InternalUserCredential *)fromList:(NSArray *)list { + InternalUserCredential *pigeonResult = [[InternalUserCredential alloc] init]; + pigeonResult.user = GetNullableObjectAtIndex(list, 0); + pigeonResult.additionalUserInfo = GetNullableObjectAtIndex(list, 1); + pigeonResult.credential = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable InternalUserCredential *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserCredential fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.user ?: [NSNull null], + self.additionalUserInfo ?: [NSNull null], + self.credential ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserCredential *other = (InternalUserCredential *)object; + return FLTPigeonDeepEquals(self.user, other.user) && + FLTPigeonDeepEquals(self.additionalUserInfo, other.additionalUserInfo) && + FLTPigeonDeepEquals(self.credential, other.credential); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.user); + result = result * 31 + FLTPigeonDeepHash(self.additionalUserInfo); + result = result * 31 + FLTPigeonDeepHash(self.credential); + return result; +} +@end + +@implementation InternalAuthCredentialInput ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + token:(nullable NSString *)token + accessToken:(nullable NSString *)accessToken { + InternalAuthCredentialInput *pigeonResult = [[InternalAuthCredentialInput alloc] init]; + pigeonResult.providerId = providerId; + pigeonResult.signInMethod = signInMethod; + pigeonResult.token = token; + pigeonResult.accessToken = accessToken; + return pigeonResult; +} ++ (InternalAuthCredentialInput *)fromList:(NSArray *)list { + InternalAuthCredentialInput *pigeonResult = [[InternalAuthCredentialInput alloc] init]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); + pigeonResult.signInMethod = GetNullableObjectAtIndex(list, 1); + pigeonResult.token = GetNullableObjectAtIndex(list, 2); + pigeonResult.accessToken = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalAuthCredentialInput *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAuthCredentialInput fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.providerId ?: [NSNull null], + self.signInMethod ?: [NSNull null], + self.token ?: [NSNull null], + self.accessToken ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAuthCredentialInput *other = (InternalAuthCredentialInput *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.signInMethod, other.signInMethod) && + FLTPigeonDeepEquals(self.token, other.token) && + FLTPigeonDeepEquals(self.accessToken, other.accessToken); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.signInMethod); + result = result * 31 + FLTPigeonDeepHash(self.token); + result = result * 31 + FLTPigeonDeepHash(self.accessToken); + return result; +} +@end + +@implementation InternalActionCodeSettings ++ (instancetype)makeWithUrl:(NSString *)url + dynamicLinkDomain:(nullable NSString *)dynamicLinkDomain + handleCodeInApp:(BOOL)handleCodeInApp + iOSBundleId:(nullable NSString *)iOSBundleId + androidPackageName:(nullable NSString *)androidPackageName + androidInstallApp:(BOOL)androidInstallApp + androidMinimumVersion:(nullable NSString *)androidMinimumVersion + linkDomain:(nullable NSString *)linkDomain { + InternalActionCodeSettings *pigeonResult = [[InternalActionCodeSettings alloc] init]; + pigeonResult.url = url; + pigeonResult.dynamicLinkDomain = dynamicLinkDomain; + pigeonResult.handleCodeInApp = handleCodeInApp; + pigeonResult.iOSBundleId = iOSBundleId; + pigeonResult.androidPackageName = androidPackageName; + pigeonResult.androidInstallApp = androidInstallApp; + pigeonResult.androidMinimumVersion = androidMinimumVersion; + pigeonResult.linkDomain = linkDomain; + return pigeonResult; +} ++ (InternalActionCodeSettings *)fromList:(NSArray *)list { + InternalActionCodeSettings *pigeonResult = [[InternalActionCodeSettings alloc] init]; + pigeonResult.url = GetNullableObjectAtIndex(list, 0); + pigeonResult.dynamicLinkDomain = GetNullableObjectAtIndex(list, 1); + pigeonResult.handleCodeInApp = [GetNullableObjectAtIndex(list, 2) boolValue]; + pigeonResult.iOSBundleId = GetNullableObjectAtIndex(list, 3); + pigeonResult.androidPackageName = GetNullableObjectAtIndex(list, 4); + pigeonResult.androidInstallApp = [GetNullableObjectAtIndex(list, 5) boolValue]; + pigeonResult.androidMinimumVersion = GetNullableObjectAtIndex(list, 6); + pigeonResult.linkDomain = GetNullableObjectAtIndex(list, 7); + return pigeonResult; +} ++ (nullable InternalActionCodeSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeSettings fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.url ?: [NSNull null], + self.dynamicLinkDomain ?: [NSNull null], + @(self.handleCodeInApp), + self.iOSBundleId ?: [NSNull null], + self.androidPackageName ?: [NSNull null], + @(self.androidInstallApp), + self.androidMinimumVersion ?: [NSNull null], + self.linkDomain ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeSettings *other = (InternalActionCodeSettings *)object; + return FLTPigeonDeepEquals(self.url, other.url) && + FLTPigeonDeepEquals(self.dynamicLinkDomain, other.dynamicLinkDomain) && + self.handleCodeInApp == other.handleCodeInApp && + FLTPigeonDeepEquals(self.iOSBundleId, other.iOSBundleId) && + FLTPigeonDeepEquals(self.androidPackageName, other.androidPackageName) && + self.androidInstallApp == other.androidInstallApp && + FLTPigeonDeepEquals(self.androidMinimumVersion, other.androidMinimumVersion) && + FLTPigeonDeepEquals(self.linkDomain, other.linkDomain); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.url); + result = result * 31 + FLTPigeonDeepHash(self.dynamicLinkDomain); + result = result * 31 + @(self.handleCodeInApp).hash; + result = result * 31 + FLTPigeonDeepHash(self.iOSBundleId); + result = result * 31 + FLTPigeonDeepHash(self.androidPackageName); + result = result * 31 + @(self.androidInstallApp).hash; + result = result * 31 + FLTPigeonDeepHash(self.androidMinimumVersion); + result = result * 31 + FLTPigeonDeepHash(self.linkDomain); + return result; +} +@end + +@implementation InternalFirebaseAuthSettings ++ (instancetype)makeWithAppVerificationDisabledForTesting:(BOOL)appVerificationDisabledForTesting + userAccessGroup:(nullable NSString *)userAccessGroup + phoneNumber:(nullable NSString *)phoneNumber + smsCode:(nullable NSString *)smsCode + forceRecaptchaFlow:(nullable NSNumber *)forceRecaptchaFlow { + InternalFirebaseAuthSettings *pigeonResult = [[InternalFirebaseAuthSettings alloc] init]; + pigeonResult.appVerificationDisabledForTesting = appVerificationDisabledForTesting; + pigeonResult.userAccessGroup = userAccessGroup; + pigeonResult.phoneNumber = phoneNumber; + pigeonResult.smsCode = smsCode; + pigeonResult.forceRecaptchaFlow = forceRecaptchaFlow; + return pigeonResult; +} ++ (InternalFirebaseAuthSettings *)fromList:(NSArray *)list { + InternalFirebaseAuthSettings *pigeonResult = [[InternalFirebaseAuthSettings alloc] init]; + pigeonResult.appVerificationDisabledForTesting = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.userAccessGroup = GetNullableObjectAtIndex(list, 1); + pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 2); + pigeonResult.smsCode = GetNullableObjectAtIndex(list, 3); + pigeonResult.forceRecaptchaFlow = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable InternalFirebaseAuthSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalFirebaseAuthSettings fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.appVerificationDisabledForTesting), + self.userAccessGroup ?: [NSNull null], + self.phoneNumber ?: [NSNull null], + self.smsCode ?: [NSNull null], + self.forceRecaptchaFlow ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalFirebaseAuthSettings *other = (InternalFirebaseAuthSettings *)object; + return self.appVerificationDisabledForTesting == other.appVerificationDisabledForTesting && + FLTPigeonDeepEquals(self.userAccessGroup, other.userAccessGroup) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + FLTPigeonDeepEquals(self.smsCode, other.smsCode) && + FLTPigeonDeepEquals(self.forceRecaptchaFlow, other.forceRecaptchaFlow); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.appVerificationDisabledForTesting).hash; + result = result * 31 + FLTPigeonDeepHash(self.userAccessGroup); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + FLTPigeonDeepHash(self.smsCode); + result = result * 31 + FLTPigeonDeepHash(self.forceRecaptchaFlow); + return result; +} +@end + +@implementation InternalSignInProvider ++ (instancetype)makeWithProviderId:(NSString *)providerId + scopes:(nullable NSArray *)scopes + customParameters: + (nullable NSDictionary *)customParameters { + InternalSignInProvider *pigeonResult = [[InternalSignInProvider alloc] init]; + pigeonResult.providerId = providerId; + pigeonResult.scopes = scopes; + pigeonResult.customParameters = customParameters; + return pigeonResult; +} ++ (InternalSignInProvider *)fromList:(NSArray *)list { + InternalSignInProvider *pigeonResult = [[InternalSignInProvider alloc] init]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); + pigeonResult.scopes = GetNullableObjectAtIndex(list, 1); + pigeonResult.customParameters = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable InternalSignInProvider *)nullableFromList:(NSArray *)list { + return (list) ? [InternalSignInProvider fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.providerId ?: [NSNull null], + self.scopes ?: [NSNull null], + self.customParameters ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalSignInProvider *other = (InternalSignInProvider *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.scopes, other.scopes) && + FLTPigeonDeepEquals(self.customParameters, other.customParameters); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.scopes); + result = result * 31 + FLTPigeonDeepHash(self.customParameters); + return result; +} +@end + +@implementation InternalVerifyPhoneNumberRequest ++ (instancetype)makeWithPhoneNumber:(nullable NSString *)phoneNumber + timeout:(NSInteger)timeout + forceResendingToken:(nullable NSNumber *)forceResendingToken + autoRetrievedSmsCodeForTesting:(nullable NSString *)autoRetrievedSmsCodeForTesting + multiFactorInfoId:(nullable NSString *)multiFactorInfoId + multiFactorSessionId:(nullable NSString *)multiFactorSessionId { + InternalVerifyPhoneNumberRequest *pigeonResult = [[InternalVerifyPhoneNumberRequest alloc] init]; + pigeonResult.phoneNumber = phoneNumber; + pigeonResult.timeout = timeout; + pigeonResult.forceResendingToken = forceResendingToken; + pigeonResult.autoRetrievedSmsCodeForTesting = autoRetrievedSmsCodeForTesting; + pigeonResult.multiFactorInfoId = multiFactorInfoId; + pigeonResult.multiFactorSessionId = multiFactorSessionId; + return pigeonResult; +} ++ (InternalVerifyPhoneNumberRequest *)fromList:(NSArray *)list { + InternalVerifyPhoneNumberRequest *pigeonResult = [[InternalVerifyPhoneNumberRequest alloc] init]; + pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 0); + pigeonResult.timeout = [GetNullableObjectAtIndex(list, 1) integerValue]; + pigeonResult.forceResendingToken = GetNullableObjectAtIndex(list, 2); + pigeonResult.autoRetrievedSmsCodeForTesting = GetNullableObjectAtIndex(list, 3); + pigeonResult.multiFactorInfoId = GetNullableObjectAtIndex(list, 4); + pigeonResult.multiFactorSessionId = GetNullableObjectAtIndex(list, 5); + return pigeonResult; +} ++ (nullable InternalVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list { + return (list) ? [InternalVerifyPhoneNumberRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.phoneNumber ?: [NSNull null], + @(self.timeout), + self.forceResendingToken ?: [NSNull null], + self.autoRetrievedSmsCodeForTesting ?: [NSNull null], + self.multiFactorInfoId ?: [NSNull null], + self.multiFactorSessionId ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalVerifyPhoneNumberRequest *other = (InternalVerifyPhoneNumberRequest *)object; + return FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + self.timeout == other.timeout && + FLTPigeonDeepEquals(self.forceResendingToken, other.forceResendingToken) && + FLTPigeonDeepEquals(self.autoRetrievedSmsCodeForTesting, + other.autoRetrievedSmsCodeForTesting) && + FLTPigeonDeepEquals(self.multiFactorInfoId, other.multiFactorInfoId) && + FLTPigeonDeepEquals(self.multiFactorSessionId, other.multiFactorSessionId); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + @(self.timeout).hash; + result = result * 31 + FLTPigeonDeepHash(self.forceResendingToken); + result = result * 31 + FLTPigeonDeepHash(self.autoRetrievedSmsCodeForTesting); + result = result * 31 + FLTPigeonDeepHash(self.multiFactorInfoId); + result = result * 31 + FLTPigeonDeepHash(self.multiFactorSessionId); + return result; +} +@end + +@implementation InternalIdTokenResult ++ (instancetype)makeWithToken:(nullable NSString *)token + expirationTimestamp:(nullable NSNumber *)expirationTimestamp + authTimestamp:(nullable NSNumber *)authTimestamp + issuedAtTimestamp:(nullable NSNumber *)issuedAtTimestamp + signInProvider:(nullable NSString *)signInProvider + claims:(nullable NSDictionary *)claims + signInSecondFactor:(nullable NSString *)signInSecondFactor { + InternalIdTokenResult *pigeonResult = [[InternalIdTokenResult alloc] init]; + pigeonResult.token = token; + pigeonResult.expirationTimestamp = expirationTimestamp; + pigeonResult.authTimestamp = authTimestamp; + pigeonResult.issuedAtTimestamp = issuedAtTimestamp; + pigeonResult.signInProvider = signInProvider; + pigeonResult.claims = claims; + pigeonResult.signInSecondFactor = signInSecondFactor; + return pigeonResult; +} ++ (InternalIdTokenResult *)fromList:(NSArray *)list { + InternalIdTokenResult *pigeonResult = [[InternalIdTokenResult alloc] init]; + pigeonResult.token = GetNullableObjectAtIndex(list, 0); + pigeonResult.expirationTimestamp = GetNullableObjectAtIndex(list, 1); + pigeonResult.authTimestamp = GetNullableObjectAtIndex(list, 2); + pigeonResult.issuedAtTimestamp = GetNullableObjectAtIndex(list, 3); + pigeonResult.signInProvider = GetNullableObjectAtIndex(list, 4); + pigeonResult.claims = GetNullableObjectAtIndex(list, 5); + pigeonResult.signInSecondFactor = GetNullableObjectAtIndex(list, 6); + return pigeonResult; +} ++ (nullable InternalIdTokenResult *)nullableFromList:(NSArray *)list { + return (list) ? [InternalIdTokenResult fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.token ?: [NSNull null], + self.expirationTimestamp ?: [NSNull null], + self.authTimestamp ?: [NSNull null], + self.issuedAtTimestamp ?: [NSNull null], + self.signInProvider ?: [NSNull null], + self.claims ?: [NSNull null], + self.signInSecondFactor ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalIdTokenResult *other = (InternalIdTokenResult *)object; + return FLTPigeonDeepEquals(self.token, other.token) && + FLTPigeonDeepEquals(self.expirationTimestamp, other.expirationTimestamp) && + FLTPigeonDeepEquals(self.authTimestamp, other.authTimestamp) && + FLTPigeonDeepEquals(self.issuedAtTimestamp, other.issuedAtTimestamp) && + FLTPigeonDeepEquals(self.signInProvider, other.signInProvider) && + FLTPigeonDeepEquals(self.claims, other.claims) && + FLTPigeonDeepEquals(self.signInSecondFactor, other.signInSecondFactor); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.token); + result = result * 31 + FLTPigeonDeepHash(self.expirationTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.authTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.issuedAtTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.signInProvider); + result = result * 31 + FLTPigeonDeepHash(self.claims); + result = result * 31 + FLTPigeonDeepHash(self.signInSecondFactor); + return result; +} +@end + +@implementation InternalUserProfile ++ (instancetype)makeWithDisplayName:(nullable NSString *)displayName + photoUrl:(nullable NSString *)photoUrl + displayNameChanged:(BOOL)displayNameChanged + photoUrlChanged:(BOOL)photoUrlChanged { + InternalUserProfile *pigeonResult = [[InternalUserProfile alloc] init]; + pigeonResult.displayName = displayName; + pigeonResult.photoUrl = photoUrl; + pigeonResult.displayNameChanged = displayNameChanged; + pigeonResult.photoUrlChanged = photoUrlChanged; + return pigeonResult; +} ++ (InternalUserProfile *)fromList:(NSArray *)list { + InternalUserProfile *pigeonResult = [[InternalUserProfile alloc] init]; + pigeonResult.displayName = GetNullableObjectAtIndex(list, 0); + pigeonResult.photoUrl = GetNullableObjectAtIndex(list, 1); + pigeonResult.displayNameChanged = [GetNullableObjectAtIndex(list, 2) boolValue]; + pigeonResult.photoUrlChanged = [GetNullableObjectAtIndex(list, 3) boolValue]; + return pigeonResult; +} ++ (nullable InternalUserProfile *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserProfile fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.displayName ?: [NSNull null], + self.photoUrl ?: [NSNull null], + @(self.displayNameChanged), + @(self.photoUrlChanged), + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserProfile *other = (InternalUserProfile *)object; + return FLTPigeonDeepEquals(self.displayName, other.displayName) && + FLTPigeonDeepEquals(self.photoUrl, other.photoUrl) && + self.displayNameChanged == other.displayNameChanged && + self.photoUrlChanged == other.photoUrlChanged; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + FLTPigeonDeepHash(self.photoUrl); + result = result * 31 + @(self.displayNameChanged).hash; + result = result * 31 + @(self.photoUrlChanged).hash; + return result; +} +@end + +@implementation InternalTotpSecret ++ (instancetype)makeWithCodeIntervalSeconds:(nullable NSNumber *)codeIntervalSeconds + codeLength:(nullable NSNumber *)codeLength + enrollmentCompletionDeadline:(nullable NSNumber *)enrollmentCompletionDeadline + hashingAlgorithm:(nullable NSString *)hashingAlgorithm + secretKey:(NSString *)secretKey { + InternalTotpSecret *pigeonResult = [[InternalTotpSecret alloc] init]; + pigeonResult.codeIntervalSeconds = codeIntervalSeconds; + pigeonResult.codeLength = codeLength; + pigeonResult.enrollmentCompletionDeadline = enrollmentCompletionDeadline; + pigeonResult.hashingAlgorithm = hashingAlgorithm; + pigeonResult.secretKey = secretKey; + return pigeonResult; +} ++ (InternalTotpSecret *)fromList:(NSArray *)list { + InternalTotpSecret *pigeonResult = [[InternalTotpSecret alloc] init]; + pigeonResult.codeIntervalSeconds = GetNullableObjectAtIndex(list, 0); + pigeonResult.codeLength = GetNullableObjectAtIndex(list, 1); + pigeonResult.enrollmentCompletionDeadline = GetNullableObjectAtIndex(list, 2); + pigeonResult.hashingAlgorithm = GetNullableObjectAtIndex(list, 3); + pigeonResult.secretKey = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable InternalTotpSecret *)nullableFromList:(NSArray *)list { + return (list) ? [InternalTotpSecret fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.codeIntervalSeconds ?: [NSNull null], + self.codeLength ?: [NSNull null], + self.enrollmentCompletionDeadline ?: [NSNull null], + self.hashingAlgorithm ?: [NSNull null], + self.secretKey ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalTotpSecret *other = (InternalTotpSecret *)object; + return FLTPigeonDeepEquals(self.codeIntervalSeconds, other.codeIntervalSeconds) && + FLTPigeonDeepEquals(self.codeLength, other.codeLength) && + FLTPigeonDeepEquals(self.enrollmentCompletionDeadline, + other.enrollmentCompletionDeadline) && + FLTPigeonDeepEquals(self.hashingAlgorithm, other.hashingAlgorithm) && + FLTPigeonDeepEquals(self.secretKey, other.secretKey); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.codeIntervalSeconds); + result = result * 31 + FLTPigeonDeepHash(self.codeLength); + result = result * 31 + FLTPigeonDeepHash(self.enrollmentCompletionDeadline); + result = result * 31 + FLTPigeonDeepHash(self.hashingAlgorithm); + result = result * 31 + FLTPigeonDeepHash(self.secretKey); + return result; +} +@end + +@interface nullFirebaseAuthMessagesPigeonCodecReader : FlutterStandardReader +@end +@implementation nullFirebaseAuthMessagesPigeonCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 129: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ActionCodeInfoOperationBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 130: + return [InternalMultiFactorSession fromList:[self readValue]]; + case 131: + return [InternalPhoneMultiFactorAssertion fromList:[self readValue]]; + case 132: + return [InternalMultiFactorInfo fromList:[self readValue]]; + case 133: + return [AuthPigeonFirebaseApp fromList:[self readValue]]; + case 134: + return [InternalActionCodeInfoData fromList:[self readValue]]; + case 135: + return [InternalActionCodeInfo fromList:[self readValue]]; + case 136: + return [InternalAdditionalUserInfo fromList:[self readValue]]; + case 137: + return [InternalAuthCredential fromList:[self readValue]]; + case 138: + return [InternalUserInfo fromList:[self readValue]]; + case 139: + return [InternalUserDetails fromList:[self readValue]]; + case 140: + return [InternalUserCredential fromList:[self readValue]]; + case 141: + return [InternalAuthCredentialInput fromList:[self readValue]]; + case 142: + return [InternalActionCodeSettings fromList:[self readValue]]; + case 143: + return [InternalFirebaseAuthSettings fromList:[self readValue]]; + case 144: + return [InternalSignInProvider fromList:[self readValue]]; + case 145: + return [InternalVerifyPhoneNumberRequest fromList:[self readValue]]; + case 146: + return [InternalIdTokenResult fromList:[self readValue]]; + case 147: + return [InternalUserProfile fromList:[self readValue]]; + case 148: + return [InternalTotpSecret fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface nullFirebaseAuthMessagesPigeonCodecWriter : FlutterStandardWriter +@end +@implementation nullFirebaseAuthMessagesPigeonCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[ActionCodeInfoOperationBox class]]) { + ActionCodeInfoOperationBox *box = (ActionCodeInfoOperationBox *)value; + [self writeByte:129]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalMultiFactorSession class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalPhoneMultiFactorAssertion class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalMultiFactorInfo class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[AuthPigeonFirebaseApp class]]) { + [self writeByte:133]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalActionCodeInfoData class]]) { + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalActionCodeInfo class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalAdditionalUserInfo class]]) { + [self writeByte:136]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalAuthCredential class]]) { + [self writeByte:137]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalUserInfo class]]) { + [self writeByte:138]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalUserDetails class]]) { + [self writeByte:139]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalUserCredential class]]) { + [self writeByte:140]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalAuthCredentialInput class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalActionCodeSettings class]]) { + [self writeByte:142]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalFirebaseAuthSettings class]]) { + [self writeByte:143]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalSignInProvider class]]) { + [self writeByte:144]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalVerifyPhoneNumberRequest class]]) { + [self writeByte:145]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalIdTokenResult class]]) { + [self writeByte:146]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalUserProfile class]]) { + [self writeByte:147]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalTotpSecret class]]) { + [self writeByte:148]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface nullFirebaseAuthMessagesPigeonCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation nullFirebaseAuthMessagesPigeonCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[nullFirebaseAuthMessagesPigeonCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[nullFirebaseAuthMessagesPigeonCodecReader alloc] initWithData:data]; +} +@end + +NSObject *nullGetFirebaseAuthMessagesCodec(void) { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + nullFirebaseAuthMessagesPigeonCodecReaderWriter *readerWriter = + [[nullFirebaseAuthMessagesPigeonCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} +void SetUpFirebaseAuthHostApi(id binaryMessenger, + NSObject *api) { + SetUpFirebaseAuthHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseAuthHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.registerIdTokenListener", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(registerIdTokenListenerApp:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(registerIdTokenListenerApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api registerIdTokenListenerApp:arg_app + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.registerAuthStateListener", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(registerAuthStateListenerApp:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(registerAuthStateListenerApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api registerAuthStateListenerApp:arg_app + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.useEmulator", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(useEmulatorApp:host:port:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(useEmulatorApp:host:port:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_host = GetNullableObjectAtIndex(args, 1); + NSInteger arg_port = [GetNullableObjectAtIndex(args, 2) integerValue]; + [api useEmulatorApp:arg_app + host:arg_host + port:arg_port + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.applyActionCode", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(applyActionCodeApp:code:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(applyActionCodeApp:code:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_code = GetNullableObjectAtIndex(args, 1); + [api applyActionCodeApp:arg_app + code:arg_code + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.checkActionCode", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(checkActionCodeApp:code:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(checkActionCodeApp:code:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_code = GetNullableObjectAtIndex(args, 1); + [api checkActionCodeApp:arg_app + code:arg_code + completion:^(InternalActionCodeInfo *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.confirmPasswordReset", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(confirmPasswordResetApp:code:newPassword:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(confirmPasswordResetApp:code:newPassword:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_code = GetNullableObjectAtIndex(args, 1); + NSString *arg_newPassword = GetNullableObjectAtIndex(args, 2); + [api confirmPasswordResetApp:arg_app + code:arg_code + newPassword:arg_newPassword + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.createUserWithEmailAndPassword", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api + respondsToSelector:@selector( + createUserWithEmailAndPasswordApp:email:password:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(createUserWithEmailAndPasswordApp:email:password:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + NSString *arg_password = GetNullableObjectAtIndex(args, 2); + [api createUserWithEmailAndPasswordApp:arg_app + email:arg_email + password:arg_password + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInAnonymously", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(signInAnonymouslyApp:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInAnonymouslyApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api signInAnonymouslyApp:arg_app + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInWithCredential", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(signInWithCredentialApp:input:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInWithCredentialApp:input:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); + [api signInWithCredentialApp:arg_app + input:arg_input + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInWithCustomToken", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(signInWithCustomTokenApp:token:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInWithCustomTokenApp:token:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_token = GetNullableObjectAtIndex(args, 1); + [api signInWithCustomTokenApp:arg_app + token:arg_token + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInWithEmailAndPassword", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + signInWithEmailAndPasswordApp:email:password:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInWithEmailAndPasswordApp:email:password:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + NSString *arg_password = GetNullableObjectAtIndex(args, 2); + [api signInWithEmailAndPasswordApp:arg_app + email:arg_email + password:arg_password + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInWithEmailLink", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(signInWithEmailLinkApp:email:emailLink:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInWithEmailLinkApp:email:emailLink:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + NSString *arg_emailLink = GetNullableObjectAtIndex(args, 2); + [api signInWithEmailLinkApp:arg_app + email:arg_email + emailLink:arg_emailLink + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.signInWithProvider", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(signInWithProviderApp:signInProvider:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(signInWithProviderApp:signInProvider:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + [api signInWithProviderApp:arg_app + signInProvider:arg_signInProvider + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.signOut", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(signOutApp:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to @selector(signOutApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api signOutApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.fetchSignInMethodsForEmail", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(fetchSignInMethodsForEmailApp:email:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(fetchSignInMethodsForEmailApp:email:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + [api fetchSignInMethodsForEmailApp:arg_app + email:arg_email + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.sendPasswordResetEmail", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector: + @selector(sendPasswordResetEmailApp:email:actionCodeSettings:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(sendPasswordResetEmailApp:email:actionCodeSettings:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + [api sendPasswordResetEmailApp:arg_app + email:arg_email + actionCodeSettings:arg_actionCodeSettings + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.sendSignInLinkToEmail", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector: + @selector(sendSignInLinkToEmailApp:email:actionCodeSettings:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(sendSignInLinkToEmailApp:email:actionCodeSettings:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_email = GetNullableObjectAtIndex(args, 1); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + [api sendSignInLinkToEmailApp:arg_app + email:arg_email + actionCodeSettings:arg_actionCodeSettings + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.setLanguageCode", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setLanguageCodeApp:languageCode:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(setLanguageCodeApp:languageCode:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_languageCode = GetNullableObjectAtIndex(args, 1); + [api setLanguageCodeApp:arg_app + languageCode:arg_languageCode + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthHostApi.setSettings", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setSettingsApp:settings:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(setSettingsApp:settings:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalFirebaseAuthSettings *arg_settings = GetNullableObjectAtIndex(args, 1); + [api setSettingsApp:arg_app + settings:arg_settings + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.verifyPasswordResetCode", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(verifyPasswordResetCodeApp:code:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(verifyPasswordResetCodeApp:code:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_code = GetNullableObjectAtIndex(args, 1); + [api verifyPasswordResetCodeApp:arg_app + code:arg_code + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.verifyPhoneNumber", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(verifyPhoneNumberApp:request:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(verifyPhoneNumberApp:request:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalVerifyPhoneNumberRequest *arg_request = GetNullableObjectAtIndex(args, 1); + [api verifyPhoneNumberApp:arg_app + request:arg_request + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.revokeTokenWithAuthorizationCode", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(revokeTokenWithAuthorizationCodeApp: + authorizationCode:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(revokeTokenWithAuthorizationCodeApp:authorizationCode:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_authorizationCode = GetNullableObjectAtIndex(args, 1); + [api revokeTokenWithAuthorizationCodeApp:arg_app + authorizationCode:arg_authorizationCode + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.revokeAccessToken", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(revokeAccessTokenApp:accessToken:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(revokeAccessTokenApp:accessToken:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_accessToken = GetNullableObjectAtIndex(args, 1); + [api revokeAccessTokenApp:arg_app + accessToken:arg_accessToken + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.initializeRecaptchaConfig", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(initializeRecaptchaConfigApp:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(initializeRecaptchaConfigApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api initializeRecaptchaConfigApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpFirebaseAuthUserHostApi(id binaryMessenger, + NSObject *api) { + SetUpFirebaseAuthUserHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthUserHostApi.delete", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(deleteApp:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to @selector(deleteApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api deleteApp:arg_app + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthUserHostApi.getIdToken", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getIdTokenApp:forceRefresh:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(getIdTokenApp:forceRefresh:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + BOOL arg_forceRefresh = [GetNullableObjectAtIndex(args, 1) boolValue]; + [api getIdTokenApp:arg_app + forceRefresh:arg_forceRefresh + completion:^(InternalIdTokenResult *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.linkWithCredential", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(linkWithCredentialApp:input:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(linkWithCredentialApp:input:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); + [api linkWithCredentialApp:arg_app + input:arg_input + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.linkWithProvider", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(linkWithProviderApp:signInProvider:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(linkWithProviderApp:signInProvider:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + [api linkWithProviderApp:arg_app + signInProvider:arg_signInProvider + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.reauthenticateWithCredential", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(reauthenticateWithCredentialApp:input:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(reauthenticateWithCredentialApp:input:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); + [api reauthenticateWithCredentialApp:arg_app + input:arg_input + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.reauthenticateWithProvider", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + reauthenticateWithProviderApp:signInProvider:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(reauthenticateWithProviderApp:signInProvider:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + [api reauthenticateWithProviderApp:arg_app + signInProvider:arg_signInProvider + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthUserHostApi.reload", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(reloadApp:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to @selector(reloadApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api reloadApp:arg_app + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.sendEmailVerification", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + sendEmailVerificationApp:actionCodeSettings:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(sendEmailVerificationApp:actionCodeSettings:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 1); + [api sendEmailVerificationApp:arg_app + actionCodeSettings:arg_actionCodeSettings + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthUserHostApi.unlink", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(unlinkApp:providerId:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(unlinkApp:providerId:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_providerId = GetNullableObjectAtIndex(args, 1); + [api unlinkApp:arg_app + providerId:arg_providerId + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.FirebaseAuthUserHostApi.updateEmail", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateEmailApp:newEmail:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(updateEmailApp:newEmail:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_newEmail = GetNullableObjectAtIndex(args, 1); + [api + updateEmailApp:arg_app + newEmail:arg_newEmail + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.updatePassword", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updatePasswordApp:newPassword:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(updatePasswordApp:newPassword:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_newPassword = GetNullableObjectAtIndex(args, 1); + [api updatePasswordApp:arg_app + newPassword:arg_newPassword + completion:^(InternalUserDetails *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.updatePhoneNumber", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updatePhoneNumberApp:input:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(updatePhoneNumberApp:input:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); + [api updatePhoneNumberApp:arg_app + input:arg_input + completion:^(InternalUserDetails *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.updateProfile", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateProfileApp:profile:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(updateProfileApp:profile:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalUserProfile *arg_profile = GetNullableObjectAtIndex(args, 1); + [api updateProfileApp:arg_app + profile:arg_profile + completion:^(InternalUserDetails *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthUserHostApi.verifyBeforeUpdateEmail", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(verifyBeforeUpdateEmailApp:newEmail: + actionCodeSettings:completion:)], + @"FirebaseAuthUserHostApi api (%@) doesn't respond to " + @"@selector(verifyBeforeUpdateEmailApp:newEmail:actionCodeSettings:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_newEmail = GetNullableObjectAtIndex(args, 1); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + [api verifyBeforeUpdateEmailApp:arg_app + newEmail:arg_newEmail + actionCodeSettings:arg_actionCodeSettings + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpMultiFactorUserHostApi(id binaryMessenger, + NSObject *api) { + SetUpMultiFactorUserHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpMultiFactorUserHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.MultiFactorUserHostApi.enrollPhone", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(enrollPhoneApp:assertion:displayName:completion:)], + @"MultiFactorUserHostApi api (%@) doesn't respond to " + @"@selector(enrollPhoneApp:assertion:displayName:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + InternalPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); + NSString *arg_displayName = GetNullableObjectAtIndex(args, 2); + [api enrollPhoneApp:arg_app + assertion:arg_assertion + displayName:arg_displayName + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.MultiFactorUserHostApi.enrollTotp", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(enrollTotpApp:assertionId:displayName:completion:)], + @"MultiFactorUserHostApi api (%@) doesn't respond to " + @"@selector(enrollTotpApp:assertionId:displayName:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_assertionId = GetNullableObjectAtIndex(args, 1); + NSString *arg_displayName = GetNullableObjectAtIndex(args, 2); + [api enrollTotpApp:arg_app + assertionId:arg_assertionId + displayName:arg_displayName + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.MultiFactorUserHostApi.getSession", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getSessionApp:completion:)], + @"MultiFactorUserHostApi api (%@) doesn't respond to " + @"@selector(getSessionApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api getSessionApp:arg_app + completion:^(InternalMultiFactorSession *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.MultiFactorUserHostApi.unenroll", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(unenrollApp:factorUid:completion:)], + @"MultiFactorUserHostApi api (%@) doesn't respond to " + @"@selector(unenrollApp:factorUid:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_factorUid = GetNullableObjectAtIndex(args, 1); + [api unenrollApp:arg_app + factorUid:arg_factorUid + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorUserHostApi.getEnrolledFactors", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getEnrolledFactorsApp:completion:)], + @"MultiFactorUserHostApi api (%@) doesn't respond to " + @"@selector(getEnrolledFactorsApp:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + [api getEnrolledFactorsApp:arg_app + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpMultiFactoResolverHostApi(id binaryMessenger, + NSObject *api) { + SetUpMultiFactoResolverHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpMultiFactoResolverHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactoResolverHostApi.resolveSignIn", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector: + @selector(resolveSignInResolverId:assertion:totpAssertionId:completion:)], + @"MultiFactoResolverHostApi api (%@) doesn't respond to " + @"@selector(resolveSignInResolverId:assertion:totpAssertionId:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_resolverId = GetNullableObjectAtIndex(args, 0); + InternalPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); + NSString *arg_totpAssertionId = GetNullableObjectAtIndex(args, 2); + [api resolveSignInResolverId:arg_resolverId + assertion:arg_assertion + totpAssertionId:arg_totpAssertionId + completion:^(InternalUserCredential *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpMultiFactorTotpHostApi(id binaryMessenger, + NSObject *api) { + SetUpMultiFactorTotpHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorTotpHostApi.generateSecret", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(generateSecretSessionId:completion:)], + @"MultiFactorTotpHostApi api (%@) doesn't respond to " + @"@selector(generateSecretSessionId:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_sessionId = GetNullableObjectAtIndex(args, 0); + [api generateSecretSessionId:arg_sessionId + completion:^(InternalTotpSecret *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorTotpHostApi.getAssertionForEnrollment", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector: + @selector(getAssertionForEnrollmentSecretKey:oneTimePassword:completion:)], + @"MultiFactorTotpHostApi api (%@) doesn't respond to " + @"@selector(getAssertionForEnrollmentSecretKey:oneTimePassword:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); + NSString *arg_oneTimePassword = GetNullableObjectAtIndex(args, 1); + [api getAssertionForEnrollmentSecretKey:arg_secretKey + oneTimePassword:arg_oneTimePassword + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorTotpHostApi.getAssertionForSignIn", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector: + @selector(getAssertionForSignInEnrollmentId:oneTimePassword:completion:)], + @"MultiFactorTotpHostApi api (%@) doesn't respond to " + @"@selector(getAssertionForSignInEnrollmentId:oneTimePassword:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_enrollmentId = GetNullableObjectAtIndex(args, 0); + NSString *arg_oneTimePassword = GetNullableObjectAtIndex(args, 1); + [api getAssertionForSignInEnrollmentId:arg_enrollmentId + oneTimePassword:arg_oneTimePassword + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpMultiFactorTotpSecretHostApi(id binaryMessenger, + NSObject *api) { + SetUpMultiFactorTotpSecretHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpMultiFactorTotpSecretHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorTotpSecretHostApi.generateQrCodeUrl", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector( + generateQrCodeUrlSecretKey:accountName:issuer:completion:)], + @"MultiFactorTotpSecretHostApi api (%@) doesn't respond to " + @"@selector(generateQrCodeUrlSecretKey:accountName:issuer:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); + NSString *arg_accountName = GetNullableObjectAtIndex(args, 1); + NSString *arg_issuer = GetNullableObjectAtIndex(args, 2); + [api generateQrCodeUrlSecretKey:arg_secretKey + accountName:arg_accountName + issuer:arg_issuer + completion:^(NSString *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"MultiFactorTotpSecretHostApi.openInOtpApp", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(openInOtpAppSecretKey:qrCodeUrl:completion:)], + @"MultiFactorTotpSecretHostApi api (%@) doesn't respond to " + @"@selector(openInOtpAppSecretKey:qrCodeUrl:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); + NSString *arg_qrCodeUrl = GetNullableObjectAtIndex(args, 1); + [api openInOtpAppSecretKey:arg_secretKey + qrCodeUrl:arg_qrCodeUrl + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpGenerateInterfaces(id binaryMessenger, + NSObject *api) { + SetUpGenerateInterfacesWithSuffix(binaryMessenger, api, @""); +} + +void SetUpGenerateInterfacesWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_" + @"interface.GenerateInterfaces.pigeonInterface", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(pigeonInterfaceInfo:error:)], + @"GenerateInterfaces api (%@) doesn't respond to @selector(pigeonInterfaceInfo:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + InternalMultiFactorInfo *arg_info = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api pigeonInterfaceInfo:arg_info error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTAuthStateChannelStreamHandler.h b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTAuthStateChannelStreamHandler.h new file mode 100644 index 0000000..1adb165 --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTAuthStateChannelStreamHandler.h @@ -0,0 +1,26 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import +#import "CustomPigeonHeader.h" + +@class FIRAuth; + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTAuthStateChannelStreamHandler : NSObject + +- (instancetype)initWithAuth:(FIRAuth *)auth; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTIdTokenChannelStreamHandler.h b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTIdTokenChannelStreamHandler.h new file mode 100644 index 0000000..0a44d0f --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTIdTokenChannelStreamHandler.h @@ -0,0 +1,27 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import "CustomPigeonHeader.h" + +#import + +@class FIRAuth; + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTIdTokenChannelStreamHandler : NSObject + +- (instancetype)initWithAuth:(FIRAuth *)auth; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTPhoneNumberVerificationStreamHandler.h b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTPhoneNumberVerificationStreamHandler.h new file mode 100644 index 0000000..48f8f01 --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Private/FLTPhoneNumberVerificationStreamHandler.h @@ -0,0 +1,36 @@ +// 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import "firebase_auth_messages.g.h" + +#import + +@class FIRAuth; +@class FIRMultiFactorSession; +@class FIRPhoneMultiFactorInfo; + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTPhoneNumberVerificationStreamHandler : NSObject + +#if TARGET_OS_OSX +- (instancetype)initWithAuth:(FIRAuth *)auth arguments:(NSDictionary *)arguments; +#else +- (instancetype)initWithAuth:(FIRAuth *)auth + request:(InternalVerifyPhoneNumberRequest *)request + session:(FIRMultiFactorSession *)session + factorInfo:(FIRPhoneMultiFactorInfo *)factorInfo; +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Private/PigeonParser.h b/packages/firebase_auth_tvos/tvos/Classes/include/Private/PigeonParser.h new file mode 100644 index 0000000..93f0b3b --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Private/PigeonParser.h @@ -0,0 +1,33 @@ +/* + * Copyright 2023, 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. + */ + +#import +#import "firebase_auth_messages.g.h" + +@class FIRAuthDataResult; +@class FIRUser; +@class FIRActionCodeSettings; +@class FIRAuthTokenResult; +@class FIRTOTPSecret; +@class FIRAuthCredential; + +@interface PigeonParser : NSObject + ++ (NSArray *_Nonnull)getManualList:(nonnull InternalUserDetails *)userDetails; ++ (InternalUserCredential *_Nullable) + getPigeonUserCredentialFromAuthResult:(nonnull FIRAuthDataResult *)authResult + authorizationCode:(nullable NSString *)authorizationCode; ++ (InternalUserDetails *_Nullable)getPigeonDetails:(nonnull FIRUser *)user; ++ (InternalUserInfo *_Nullable)getPigeonUserInfo:(nonnull FIRUser *)user; ++ (FIRActionCodeSettings *_Nullable)parseActionCodeSettings: + (nullable InternalActionCodeSettings *)settings; ++ (InternalUserCredential *_Nullable)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user; ++ (InternalIdTokenResult *_Nonnull)parseIdTokenResult:(nonnull FIRAuthTokenResult *)tokenResult; ++ (InternalTotpSecret *_Nonnull)getPigeonTotpSecret:(nonnull FIRTOTPSecret *)secret; ++ (InternalAuthCredential *_Nullable)getPigeonAuthCredential: + (FIRAuthCredential *_Nullable)authCredentialToken + token:(NSNumber *_Nullable)token; +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Public/CustomPigeonHeader.h b/packages/firebase_auth_tvos/tvos/Classes/include/Public/CustomPigeonHeader.h new file mode 100644 index 0000000..d32a6b4 --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Public/CustomPigeonHeader.h @@ -0,0 +1,16 @@ +// 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 "firebase_auth_messages.g.h" + +@interface InternalMultiFactorInfo (Map) +- (NSDictionary *)toList; +@end + +@interface InternalUserDetails (Map) +- (NSDictionary *)toList; +@end + +@interface InternalUserInfo (Map) +- (NSDictionary *)toList; +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Public/FLTFirebaseAuthPlugin.h b/packages/firebase_auth_tvos/tvos/Classes/include/Public/FLTFirebaseAuthPlugin.h new file mode 100644 index 0000000..c1b4a9c --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Public/FLTFirebaseAuthPlugin.h @@ -0,0 +1,50 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +// Restored by hand — see FLTFirebaseAuthPlugin.m for why the porter's +// removal of this import was wrong (ASAuthorizationController et al. are +// used by FLTFirebaseAuthPlugin below and are tvOS 13+ APIs). +#import +#import +// Points at our own firebase_core_tvos pod, not upstream's "firebase_core" +// pod (which has no tvOS platform declaration) — see PORTING_REPORT.md. +#if __has_include() +#import +#else +#import "FLTFirebasePlugin.h" +#endif +#import "firebase_auth_messages.g.h" + +#if !TARGET_OS_OSX +@protocol FlutterSceneLifeCycleDelegate; +#endif + +@interface FLTFirebaseAuthPlugin + : FLTFirebasePlugin ) + , + FlutterSceneLifeCycleDelegate +#endif +#endif + > + ++ (FlutterError *)convertToFlutterError:(NSError *)error; +@end diff --git a/packages/firebase_auth_tvos/tvos/Classes/include/Public/firebase_auth_messages.g.h b/packages/firebase_auth_tvos/tvos/Classes/include/Public/firebase_auth_messages.g.h new file mode 100644 index 0000000..f83da7e --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/Classes/include/Public/firebase_auth_messages.g.h @@ -0,0 +1,571 @@ +// Copyright 2023, 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; + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +/// The type of operation that generated the action code from calling +/// [checkActionCode]. +typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { + /// Unknown operation. + ActionCodeInfoOperationUnknown = 0, + /// Password reset code generated via [sendPasswordResetEmail]. + ActionCodeInfoOperationPasswordReset = 1, + /// Email verification code generated via [User.sendEmailVerification]. + ActionCodeInfoOperationVerifyEmail = 2, + /// Email change revocation code generated via [User.updateEmail]. + ActionCodeInfoOperationRecoverEmail = 3, + /// Email sign in code generated via [sendSignInLinkToEmail]. + ActionCodeInfoOperationEmailSignIn = 4, + /// Verify and change email code generated via [User.verifyBeforeUpdateEmail]. + ActionCodeInfoOperationVerifyAndChangeEmail = 5, + /// Action code for reverting second factor addition. + ActionCodeInfoOperationRevertSecondFactorAddition = 6, +}; + +/// Wrapper for ActionCodeInfoOperation to allow for nullability. +@interface ActionCodeInfoOperationBox : NSObject +@property(nonatomic, assign) ActionCodeInfoOperation value; +- (instancetype)initWithValue:(ActionCodeInfoOperation)value; +@end + +@class InternalMultiFactorSession; +@class InternalPhoneMultiFactorAssertion; +@class InternalMultiFactorInfo; +@class AuthPigeonFirebaseApp; +@class InternalActionCodeInfoData; +@class InternalActionCodeInfo; +@class InternalAdditionalUserInfo; +@class InternalAuthCredential; +@class InternalUserInfo; +@class InternalUserDetails; +@class InternalUserCredential; +@class InternalAuthCredentialInput; +@class InternalActionCodeSettings; +@class InternalFirebaseAuthSettings; +@class InternalSignInProvider; +@class InternalVerifyPhoneNumberRequest; +@class InternalIdTokenResult; +@class InternalUserProfile; +@class InternalTotpSecret; + +@interface InternalMultiFactorSession : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithId:(NSString *)id; +@property(nonatomic, copy) NSString *id; +@end + +@interface InternalPhoneMultiFactorAssertion : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithVerificationId:(NSString *)verificationId + verificationCode:(NSString *)verificationCode; +@property(nonatomic, copy) NSString *verificationId; +@property(nonatomic, copy) NSString *verificationCode; +@end + +@interface InternalMultiFactorInfo : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithDisplayName:(nullable NSString *)displayName + enrollmentTimestamp:(double)enrollmentTimestamp + factorId:(nullable NSString *)factorId + uid:(NSString *)uid + phoneNumber:(nullable NSString *)phoneNumber; +@property(nonatomic, copy, nullable) NSString *displayName; +@property(nonatomic, assign) double enrollmentTimestamp; +@property(nonatomic, copy, nullable) NSString *factorId; +@property(nonatomic, copy) NSString *uid; +@property(nonatomic, copy, nullable) NSString *phoneNumber; +@end + +@interface AuthPigeonFirebaseApp : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAppName:(NSString *)appName + tenantId:(nullable NSString *)tenantId + customAuthDomain:(nullable NSString *)customAuthDomain; +@property(nonatomic, copy) NSString *appName; +@property(nonatomic, copy, nullable) NSString *tenantId; +@property(nonatomic, copy, nullable) NSString *customAuthDomain; +@end + +@interface InternalActionCodeInfoData : NSObject ++ (instancetype)makeWithEmail:(nullable NSString *)email + previousEmail:(nullable NSString *)previousEmail; +@property(nonatomic, copy, nullable) NSString *email; +@property(nonatomic, copy, nullable) NSString *previousEmail; +@end + +@interface InternalActionCodeInfo : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithOperation:(ActionCodeInfoOperation)operation + data:(InternalActionCodeInfoData *)data; +@property(nonatomic, assign) ActionCodeInfoOperation operation; +@property(nonatomic, strong) InternalActionCodeInfoData *data; +@end + +@interface InternalAdditionalUserInfo : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithIsNewUser:(BOOL)isNewUser + providerId:(nullable NSString *)providerId + username:(nullable NSString *)username + authorizationCode:(nullable NSString *)authorizationCode + profile:(nullable NSDictionary *)profile; +@property(nonatomic, assign) BOOL isNewUser; +@property(nonatomic, copy, nullable) NSString *providerId; +@property(nonatomic, copy, nullable) NSString *username; +@property(nonatomic, copy, nullable) NSString *authorizationCode; +@property(nonatomic, copy, nullable) NSDictionary *profile; +@end + +@interface InternalAuthCredential : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + nativeId:(NSInteger)nativeId + accessToken:(nullable NSString *)accessToken; +@property(nonatomic, copy) NSString *providerId; +@property(nonatomic, copy) NSString *signInMethod; +@property(nonatomic, assign) NSInteger nativeId; +@property(nonatomic, copy, nullable) NSString *accessToken; +@end + +@interface InternalUserInfo : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUid:(NSString *)uid + email:(nullable NSString *)email + displayName:(nullable NSString *)displayName + photoUrl:(nullable NSString *)photoUrl + phoneNumber:(nullable NSString *)phoneNumber + isAnonymous:(BOOL)isAnonymous + isEmailVerified:(BOOL)isEmailVerified + providerId:(nullable NSString *)providerId + tenantId:(nullable NSString *)tenantId + refreshToken:(nullable NSString *)refreshToken + creationTimestamp:(nullable NSNumber *)creationTimestamp + lastSignInTimestamp:(nullable NSNumber *)lastSignInTimestamp; +@property(nonatomic, copy) NSString *uid; +@property(nonatomic, copy, nullable) NSString *email; +@property(nonatomic, copy, nullable) NSString *displayName; +@property(nonatomic, copy, nullable) NSString *photoUrl; +@property(nonatomic, copy, nullable) NSString *phoneNumber; +@property(nonatomic, assign) BOOL isAnonymous; +@property(nonatomic, assign) BOOL isEmailVerified; +@property(nonatomic, copy, nullable) NSString *providerId; +@property(nonatomic, copy, nullable) NSString *tenantId; +@property(nonatomic, copy, nullable) NSString *refreshToken; +@property(nonatomic, strong, nullable) NSNumber *creationTimestamp; +@property(nonatomic, strong, nullable) NSNumber *lastSignInTimestamp; +@end + +@interface InternalUserDetails : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUserInfo:(InternalUserInfo *)userInfo + providerData:(NSArray *> *)providerData; +@property(nonatomic, strong) InternalUserInfo *userInfo; +@property(nonatomic, copy) NSArray *> *providerData; +@end + +@interface InternalUserCredential : NSObject ++ (instancetype)makeWithUser:(nullable InternalUserDetails *)user + additionalUserInfo:(nullable InternalAdditionalUserInfo *)additionalUserInfo + credential:(nullable InternalAuthCredential *)credential; +@property(nonatomic, strong, nullable) InternalUserDetails *user; +@property(nonatomic, strong, nullable) InternalAdditionalUserInfo *additionalUserInfo; +@property(nonatomic, strong, nullable) InternalAuthCredential *credential; +@end + +@interface InternalAuthCredentialInput : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + token:(nullable NSString *)token + accessToken:(nullable NSString *)accessToken; +@property(nonatomic, copy) NSString *providerId; +@property(nonatomic, copy) NSString *signInMethod; +@property(nonatomic, copy, nullable) NSString *token; +@property(nonatomic, copy, nullable) NSString *accessToken; +@end + +@interface InternalActionCodeSettings : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUrl:(NSString *)url + dynamicLinkDomain:(nullable NSString *)dynamicLinkDomain + handleCodeInApp:(BOOL)handleCodeInApp + iOSBundleId:(nullable NSString *)iOSBundleId + androidPackageName:(nullable NSString *)androidPackageName + androidInstallApp:(BOOL)androidInstallApp + androidMinimumVersion:(nullable NSString *)androidMinimumVersion + linkDomain:(nullable NSString *)linkDomain; +@property(nonatomic, copy) NSString *url; +@property(nonatomic, copy, nullable) NSString *dynamicLinkDomain; +@property(nonatomic, assign) BOOL handleCodeInApp; +@property(nonatomic, copy, nullable) NSString *iOSBundleId; +@property(nonatomic, copy, nullable) NSString *androidPackageName; +@property(nonatomic, assign) BOOL androidInstallApp; +@property(nonatomic, copy, nullable) NSString *androidMinimumVersion; +@property(nonatomic, copy, nullable) NSString *linkDomain; +@end + +@interface InternalFirebaseAuthSettings : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAppVerificationDisabledForTesting:(BOOL)appVerificationDisabledForTesting + userAccessGroup:(nullable NSString *)userAccessGroup + phoneNumber:(nullable NSString *)phoneNumber + smsCode:(nullable NSString *)smsCode + forceRecaptchaFlow:(nullable NSNumber *)forceRecaptchaFlow; +@property(nonatomic, assign) BOOL appVerificationDisabledForTesting; +@property(nonatomic, copy, nullable) NSString *userAccessGroup; +@property(nonatomic, copy, nullable) NSString *phoneNumber; +@property(nonatomic, copy, nullable) NSString *smsCode; +@property(nonatomic, strong, nullable) NSNumber *forceRecaptchaFlow; +@end + +@interface InternalSignInProvider : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProviderId:(NSString *)providerId + scopes:(nullable NSArray *)scopes + customParameters: + (nullable NSDictionary *)customParameters; +@property(nonatomic, copy) NSString *providerId; +@property(nonatomic, copy, nullable) NSArray *scopes; +@property(nonatomic, copy, nullable) NSDictionary *customParameters; +@end + +@interface InternalVerifyPhoneNumberRequest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPhoneNumber:(nullable NSString *)phoneNumber + timeout:(NSInteger)timeout + forceResendingToken:(nullable NSNumber *)forceResendingToken + autoRetrievedSmsCodeForTesting:(nullable NSString *)autoRetrievedSmsCodeForTesting + multiFactorInfoId:(nullable NSString *)multiFactorInfoId + multiFactorSessionId:(nullable NSString *)multiFactorSessionId; +@property(nonatomic, copy, nullable) NSString *phoneNumber; +@property(nonatomic, assign) NSInteger timeout; +@property(nonatomic, strong, nullable) NSNumber *forceResendingToken; +@property(nonatomic, copy, nullable) NSString *autoRetrievedSmsCodeForTesting; +@property(nonatomic, copy, nullable) NSString *multiFactorInfoId; +@property(nonatomic, copy, nullable) NSString *multiFactorSessionId; +@end + +@interface InternalIdTokenResult : NSObject ++ (instancetype)makeWithToken:(nullable NSString *)token + expirationTimestamp:(nullable NSNumber *)expirationTimestamp + authTimestamp:(nullable NSNumber *)authTimestamp + issuedAtTimestamp:(nullable NSNumber *)issuedAtTimestamp + signInProvider:(nullable NSString *)signInProvider + claims:(nullable NSDictionary *)claims + signInSecondFactor:(nullable NSString *)signInSecondFactor; +@property(nonatomic, copy, nullable) NSString *token; +@property(nonatomic, strong, nullable) NSNumber *expirationTimestamp; +@property(nonatomic, strong, nullable) NSNumber *authTimestamp; +@property(nonatomic, strong, nullable) NSNumber *issuedAtTimestamp; +@property(nonatomic, copy, nullable) NSString *signInProvider; +@property(nonatomic, copy, nullable) NSDictionary *claims; +@property(nonatomic, copy, nullable) NSString *signInSecondFactor; +@end + +@interface InternalUserProfile : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithDisplayName:(nullable NSString *)displayName + photoUrl:(nullable NSString *)photoUrl + displayNameChanged:(BOOL)displayNameChanged + photoUrlChanged:(BOOL)photoUrlChanged; +@property(nonatomic, copy, nullable) NSString *displayName; +@property(nonatomic, copy, nullable) NSString *photoUrl; +@property(nonatomic, assign) BOOL displayNameChanged; +@property(nonatomic, assign) BOOL photoUrlChanged; +@end + +@interface InternalTotpSecret : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithCodeIntervalSeconds:(nullable NSNumber *)codeIntervalSeconds + codeLength:(nullable NSNumber *)codeLength + enrollmentCompletionDeadline:(nullable NSNumber *)enrollmentCompletionDeadline + hashingAlgorithm:(nullable NSString *)hashingAlgorithm + secretKey:(NSString *)secretKey; +@property(nonatomic, strong, nullable) NSNumber *codeIntervalSeconds; +@property(nonatomic, strong, nullable) NSNumber *codeLength; +@property(nonatomic, strong, nullable) NSNumber *enrollmentCompletionDeadline; +@property(nonatomic, copy, nullable) NSString *hashingAlgorithm; +@property(nonatomic, copy) NSString *secretKey; +@end + +/// The codec used by all APIs. +NSObject *nullGetFirebaseAuthMessagesCodec(void); + +@protocol FirebaseAuthHostApi +- (void)registerIdTokenListenerApp:(AuthPigeonFirebaseApp *)app + completion: + (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)registerAuthStateListenerApp:(AuthPigeonFirebaseApp *)app + completion: + (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)useEmulatorApp:(AuthPigeonFirebaseApp *)app + host:(NSString *)host + port:(NSInteger)port + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)applyActionCodeApp:(AuthPigeonFirebaseApp *)app + code:(NSString *)code + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)checkActionCodeApp:(AuthPigeonFirebaseApp *)app + code:(NSString *)code + completion:(void (^)(InternalActionCodeInfo *_Nullable, + FlutterError *_Nullable))completion; +- (void)confirmPasswordResetApp:(AuthPigeonFirebaseApp *)app + code:(NSString *)code + newPassword:(NSString *)newPassword + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)createUserWithEmailAndPasswordApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + password:(NSString *)password + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInAnonymouslyApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInWithCredentialApp:(AuthPigeonFirebaseApp *)app + input:(NSDictionary *)input + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInWithCustomTokenApp:(AuthPigeonFirebaseApp *)app + token:(NSString *)token + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInWithEmailAndPasswordApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + password:(NSString *)password + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInWithEmailLinkApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + emailLink:(NSString *)emailLink + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signInWithProviderApp:(AuthPigeonFirebaseApp *)app + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)signOutApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)fetchSignInMethodsForEmailApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)sendPasswordResetEmailApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)sendSignInLinkToEmailApp:(AuthPigeonFirebaseApp *)app + email:(NSString *)email + actionCodeSettings:(InternalActionCodeSettings *)actionCodeSettings + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)setLanguageCodeApp:(AuthPigeonFirebaseApp *)app + languageCode:(nullable NSString *)languageCode + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)setSettingsApp:(AuthPigeonFirebaseApp *)app + settings:(InternalFirebaseAuthSettings *)settings + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)verifyPasswordResetCodeApp:(AuthPigeonFirebaseApp *)app + code:(NSString *)code + completion: + (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)verifyPhoneNumberApp:(AuthPigeonFirebaseApp *)app + request:(InternalVerifyPhoneNumberRequest *)request + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)revokeTokenWithAuthorizationCodeApp:(AuthPigeonFirebaseApp *)app + authorizationCode:(NSString *)authorizationCode + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)revokeAccessTokenApp:(AuthPigeonFirebaseApp *)app + accessToken:(NSString *)accessToken + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)initializeRecaptchaConfigApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +@end + +extern void SetUpFirebaseAuthHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFirebaseAuthHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol FirebaseAuthUserHostApi +- (void)deleteApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)getIdTokenApp:(AuthPigeonFirebaseApp *)app + forceRefresh:(BOOL)forceRefresh + completion: + (void (^)(InternalIdTokenResult *_Nullable, FlutterError *_Nullable))completion; +- (void)linkWithCredentialApp:(AuthPigeonFirebaseApp *)app + input:(NSDictionary *)input + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)linkWithProviderApp:(AuthPigeonFirebaseApp *)app + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)reauthenticateWithCredentialApp:(AuthPigeonFirebaseApp *)app + input:(NSDictionary *)input + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)reauthenticateWithProviderApp:(AuthPigeonFirebaseApp *)app + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +- (void)reloadApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; +- (void)sendEmailVerificationApp:(AuthPigeonFirebaseApp *)app + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)unlinkApp:(AuthPigeonFirebaseApp *)app + providerId:(NSString *)providerId + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; +- (void)updateEmailApp:(AuthPigeonFirebaseApp *)app + newEmail:(NSString *)newEmail + completion: + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; +- (void)updatePasswordApp:(AuthPigeonFirebaseApp *)app + newPassword:(NSString *)newPassword + completion: + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; +- (void)updatePhoneNumberApp:(AuthPigeonFirebaseApp *)app + input:(NSDictionary *)input + completion: + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; +- (void)updateProfileApp:(AuthPigeonFirebaseApp *)app + profile:(InternalUserProfile *)profile + completion: + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; +- (void)verifyBeforeUpdateEmailApp:(AuthPigeonFirebaseApp *)app + newEmail:(NSString *)newEmail + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings + completion:(void (^)(FlutterError *_Nullable))completion; +@end + +extern void SetUpFirebaseAuthUserHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol MultiFactorUserHostApi +- (void)enrollPhoneApp:(AuthPigeonFirebaseApp *)app + assertion:(InternalPhoneMultiFactorAssertion *)assertion + displayName:(nullable NSString *)displayName + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)enrollTotpApp:(AuthPigeonFirebaseApp *)app + assertionId:(NSString *)assertionId + displayName:(nullable NSString *)displayName + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)getSessionApp:(AuthPigeonFirebaseApp *)app + completion: + (void (^)(InternalMultiFactorSession *_Nullable, FlutterError *_Nullable))completion; +- (void)unenrollApp:(AuthPigeonFirebaseApp *)app + factorUid:(NSString *)factorUid + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)getEnrolledFactorsApp:(AuthPigeonFirebaseApp *)app + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void SetUpMultiFactorUserHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpMultiFactorUserHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol MultiFactoResolverHostApi +- (void)resolveSignInResolverId:(NSString *)resolverId + assertion:(nullable InternalPhoneMultiFactorAssertion *)assertion + totpAssertionId:(nullable NSString *)totpAssertionId + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void SetUpMultiFactoResolverHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpMultiFactoResolverHostApiWithSuffix( + id binaryMessenger, NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol MultiFactorTotpHostApi +- (void)generateSecretSessionId:(NSString *)sessionId + completion:(void (^)(InternalTotpSecret *_Nullable, + FlutterError *_Nullable))completion; +- (void)getAssertionForEnrollmentSecretKey:(NSString *)secretKey + oneTimePassword:(NSString *)oneTimePassword + completion:(void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion; +- (void)getAssertionForSignInEnrollmentId:(NSString *)enrollmentId + oneTimePassword:(NSString *)oneTimePassword + completion:(void (^)(NSString *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void SetUpMultiFactorTotpHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol MultiFactorTotpSecretHostApi +- (void)generateQrCodeUrlSecretKey:(NSString *)secretKey + accountName:(nullable NSString *)accountName + issuer:(nullable NSString *)issuer + completion: + (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)openInOtpAppSecretKey:(NSString *)secretKey + qrCodeUrl:(NSString *)qrCodeUrl + completion:(void (^)(FlutterError *_Nullable))completion; +@end + +extern void SetUpMultiFactorTotpSecretHostApi( + id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpMultiFactorTotpSecretHostApiWithSuffix( + id binaryMessenger, + NSObject *_Nullable api, NSString *messageChannelSuffix); + +/// Only used to generate the object interface that are use outside of the Pigeon interface +@protocol GenerateInterfaces +- (void)pigeonInterfaceInfo:(InternalMultiFactorInfo *)info + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void SetUpGenerateInterfaces(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpGenerateInterfacesWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +NS_ASSUME_NONNULL_END diff --git a/packages/firebase_auth_tvos/tvos/firebase_auth_tvos.podspec b/packages/firebase_auth_tvos/tvos/firebase_auth_tvos.podspec new file mode 100644 index 0000000..48c8165 --- /dev/null +++ b/packages/firebase_auth_tvos/tvos/firebase_auth_tvos.podspec @@ -0,0 +1,53 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint firebase_auth_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'firebase_auth_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of firebase_auth.' + s.description = <<-DESC +tvOS implementation of firebase_auth, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/firebase_auth_tvos' + s.license = { :file => '../LICENSE' } + s.author = { 'fluttertv' => 'noreply@fluttertv.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*.{h,m,mm,swift}' + s.public_header_files = 'Classes/**/include/**/*.h' + # Firebase/Auth 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', + } + + # FLTFirebaseAuthPlugin.m reads @LIBRARY_NAME / @LIBRARY_VERSION as + # preprocessor token-pasted string literals (same as upstream + # firebase_auth's podspec) — without these the file fails to compile + # with "unexpected '@' in program". + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) LIBRARY_VERSION=\"0.0.1\" LIBRARY_NAME=\"flutter-fire-auth-tvos\"', + } + + # The ported Classes/ call into FIRAuth (Firebase/Auth) and into our own + # firebase_core_tvos pod (FLTFirebaseCorePlugin / FLTFirebasePlugin), 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/Auth', '~> 12.15.0' + s.dependency 'firebase_core_tvos' + s.static_framework = true +end diff --git a/packages/firebase_core_tvos/.gitignore b/packages/firebase_core_tvos/.gitignore new file mode 100644 index 0000000..fd30348 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/CHANGELOG.md b/packages/firebase_core_tvos/CHANGELOG.md new file mode 100644 index 0000000..7d9830f --- /dev/null +++ b/packages/firebase_core_tvos/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.0.1 + +* Initial tvOS scaffolding generated by `flutter-tvos plugin port` from + `firebase_core`. diff --git a/packages/firebase_core_tvos/LICENSE b/packages/firebase_core_tvos/LICENSE new file mode 100644 index 0000000..000b461 --- /dev/null +++ b/packages/firebase_core_tvos/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium 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. \ No newline at end of file diff --git a/packages/firebase_core_tvos/PORTING_REPORT.md b/packages/firebase_core_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..9ecba0b --- /dev/null +++ b/packages/firebase_core_tvos/PORTING_REPORT.md @@ -0,0 +1,51 @@ +# firebase_core_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-06-30. + +Source: `firebase_core` 4.11.0 (path: `/Users/aliustaoglu/.pub-cache/hosted/pub.dev/firebase_core-4.11.0`) +Base platform: ios (Objective-C) +Output: `./firebase_core_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 + +- [ ] Read every `✗ stubbed` method above and confirm returning `FlutterMethodNotImplemented` is acceptable on tvOS. +- [ ] Review every `⚠️ partial` method against a real Apple TV (behaviour differs from iOS). +- [ ] Confirm the removed imports were not load-bearing for still-supported code paths. +- [ ] `flutter-tvos build tvos --simulator --debug` from the plugin's example app compiles the generated registrant. +- [ ] Bump the version and update `CHANGELOG.md` before publishing. + +--- + +Manual review required. Read this report top-to-bottom before publishing `firebase_core_tvos`. diff --git a/packages/firebase_core_tvos/README.md b/packages/firebase_core_tvos/README.md new file mode 100644 index 0000000..c5b1b99 --- /dev/null +++ b/packages/firebase_core_tvos/README.md @@ -0,0 +1,48 @@ +# firebase_core_tvos + +The tvOS (Apple TV) implementation of [`firebase_core`](https://pub.dev/packages/firebase_core), +provided by the [flutter-tvos](https://github.com/fluttertv/flutter-tvos) toolchain. + +> Generated by [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `firebase_core`, then completed by hand. See `PORTING_REPORT.md` for the +> full list of what was changed. + +## Usage + +This is a federated plugin implementation. An app that already depends on +`firebase_core` and targets Apple TV only needs to add this package alongside +it: + +```yaml +dependencies: + firebase_core: ^4.11.0 + firebase_core_tvos: ^0.0.1 +``` + +The native plugin registers automatically through flutter-tvos' plugin +registrant — no extra imports or setup in app code. Use the normal +`firebase_core` API (`Firebase.initializeApp`, `Firebase.app`, …); it routes to +the Apple TV native side. + +## tvOS support + +Built on the Firebase Apple SDK, which has supported tvOS since 8.9.0. The full +`firebase_core` surface works: + +| Capability | tvOS | +|---|---| +| `Firebase.initializeApp` (default & named apps) | ✅ | +| `Firebase.app` / `Firebase.apps` | ✅ | +| `FirebaseApp.delete` / options | ✅ | + +There are no `firebase_core` features disabled on tvOS. Plugins built on top of +Firebase Core (auth, Firestore, storage, messaging) have their own tvOS support +matrices — see their respective `*_tvos` packages. + +## Requirements + +- Apple TV running tvOS 15.0 or later (the Firebase Apple SDK's minimum). + +## License + +fluttertv, under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/firebase_core_tvos/analysis_options.yaml b/packages/firebase_core_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/README.md b/packages/firebase_core_tvos/example/README.md new file mode 100644 index 0000000..abd7213 --- /dev/null +++ b/packages/firebase_core_tvos/example/README.md @@ -0,0 +1,16 @@ +# firebase_core_example + +Demonstrates how to use the firebase_core plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/firebase_core_tvos/example/lib/firebase_options.dart b/packages/firebase_core_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..5d7690e --- /dev/null +++ b/packages/firebase_core_tvos/example/lib/firebase_options.dart @@ -0,0 +1,92 @@ +// Copyright 2022, 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. + +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + // TODO(Lyokone): Remove when FlutterFire CLI updated + case TargetPlatform.windows: + return android; + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.tests', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.tests', + ); +} diff --git a/packages/firebase_core_tvos/example/lib/main.dart b/packages/firebase_core_tvos/example/lib/main.dart new file mode 100644 index 0000000..78a4243 --- /dev/null +++ b/packages/firebase_core_tvos/example/lib/main.dart @@ -0,0 +1,106 @@ +// Copyright 2019 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 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + String get name => 'foo'; + + Future initializeDefault() async { + FirebaseApp app = await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + print('Initialized default app $app'); + } + + Future initializeDefaultFromAndroidResource() async { + if (defaultTargetPlatform != TargetPlatform.android || kIsWeb) { + print('Not running on Android, skipping'); + return; + } + FirebaseApp app = await Firebase.initializeApp(); + print('Initialized default app $app from Android resource'); + } + + Future initializeSecondary() async { + FirebaseApp app = await Firebase.initializeApp( + name: name, + options: DefaultFirebaseOptions.currentPlatform, + ); + + print('Initialized $app'); + } + + void apps() { + final List apps = Firebase.apps; + print('Currently initialized apps: $apps'); + } + + void options() { + final FirebaseApp app = Firebase.app(); + final options = app.options; + print('Current options for app ${app.name}: $options'); + } + + Future delete() async { + final FirebaseApp app = Firebase.app(name); + await app.delete(); + print('App $name deleted'); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Firebase Core example app'), + ), + body: Padding( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ElevatedButton( + onPressed: initializeDefault, + child: const Text('Initialize default app'), + ), + if (defaultTargetPlatform == TargetPlatform.android && !kIsWeb) + ElevatedButton( + onPressed: initializeDefaultFromAndroidResource, + child: const Text( + 'Initialize default app from Android resources', + ), + ), + ElevatedButton( + onPressed: initializeSecondary, + child: const Text('Initialize secondary app'), + ), + ElevatedButton( + onPressed: apps, + child: const Text('List apps'), + ), + ElevatedButton( + onPressed: options, + child: const Text('List default options'), + ), + ElevatedButton( + onPressed: delete, + child: const Text('Delete secondary app'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/firebase_core_tvos/example/pubspec.yaml b/packages/firebase_core_tvos/example/pubspec.yaml new file mode 100644 index 0000000..8d1ca16 --- /dev/null +++ b/packages/firebase_core_tvos/example/pubspec.yaml @@ -0,0 +1,16 @@ +name: firebase_core_example +description: Demonstrates how to use the firebase_core plugin. + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + firebase_core: ^4.11.0 + firebase_core_tvos: + path: ../ + flutter: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/firebase_core_tvos/example/tvos/.gitignore b/packages/firebase_core_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/firebase_core_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Flutter/Release.xcconfig b/packages/firebase_core_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Podfile b/packages/firebase_core_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e8fee79 --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 91BBCE11D55C2A80EF143450 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2B073BA3654E130C136F85E /* 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 = ""; }; + 226191204CC0B46D2B549E32 /* 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 = ""; }; + 3B3967151E833CAB004F5970 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; 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 = ""; }; + A622703C6488A334857C400F /* 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 = ""; }; + AAA000000000000000000003 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + D7A34B253994C62C747FA266 /* 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 = ""; }; + F2B073BA3654E130C136F85E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000082B4168C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AAF20000000000000000F00D /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + 91BBCE11D55C2A80EF143450 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 97C146E51CF9000082B4168C = { + isa = PBXGroup; + children = ( + 97C146F01CF9000082B4168C /* Runner */, + 97C146F01CF9000082B4168E /* Flutter */, + 97C146F01CF9000082B4168F /* Frameworks */, + 97C146EF1CF9000082B41690 /* Products */, + B63184B04424A951A5CEFEA2 /* 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 */, + F2B073BA3654E130C136F85E /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B63184B04424A951A5CEFEA2 /* Pods */ = { + isa = PBXGroup; + children = ( + A622703C6488A334857C400F /* Pods-Runner.debug.xcconfig */, + 226191204CC0B46D2B549E32 /* Pods-Runner.release.xcconfig */, + D7A34B253994C62C747FA266 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000082B41690 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 7CAC66FEBBC753EAD1889FAB /* [CP] Check Pods Manifest.lock */, + 97C146EA1CF9000082B4168C /* Sources */, + 97C146EB1CF9000082B4168C /* Frameworks */, + 97C146EC1CF9000082B4168C /* Resources */, + AAF10000000000000000F00D /* Embed App.framework */, + 9740EEB31CF901A200538489 /* Copy flutter_assets */, + 5B037EF0724BCB5F3EABCD4C /* [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 */ + 5B037EF0724BCB5F3EABCD4C /* [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; + }; + 7CAC66FEBBC753EAD1889FAB /* [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; + }; + 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"; + }; +/* 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.firebaseCoreExample; + 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)"; + 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.firebaseCoreExample; + 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.firebaseCoreExample; + 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_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ee3561d --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner/AppDelegate.swift b/packages/firebase_core_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/firebase_core_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/firebase_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/firebase_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/firebase_core_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_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/firebase_core_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_core_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/firebase_core_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/firebase_core_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner/Info.plist b/packages/firebase_core_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..c1742ef --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Firebase_core_example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + firebase_core_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + + FLTAssetsPath + flutter_assets + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/packages/firebase_core_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/firebase_core_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/firebase_core_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_core_tvos/lib/firebase_core_tvos.dart b/packages/firebase_core_tvos/lib/firebase_core_tvos.dart new file mode 100644 index 0000000..d8ef983 --- /dev/null +++ b/packages/firebase_core_tvos/lib/firebase_core_tvos.dart @@ -0,0 +1,15 @@ +// Copyright 2020 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_core's public Dart API (Firebase, FirebaseApp, FirebaseOptions, …) +// talks to the platform side purely through MethodChannelFirebase / +// MethodChannelFirebaseApp in firebase_core_platform_interface — there is no +// per-platform Dart override even on iOS. So this package only needs to +// supply the native tvOS pluginClass (see tvos/Classes/); duplicating the +// Dart API here would create a second, incompatible FirebaseApp type that +// firebase_auth_tvos / cloud_firestore_tvos and friends (which import +// package:firebase_core/firebase_core.dart directly) wouldn't recognize. +// Apps depend on firebase_core (for the Dart API) *and* firebase_core_tvos +// (for native plugin discovery/registration) side by side — see example/. +export 'package:firebase_core/firebase_core.dart'; diff --git a/packages/firebase_core_tvos/pubspec.yaml b/packages/firebase_core_tvos/pubspec.yaml new file mode 100644 index 0000000..b21c1c5 --- /dev/null +++ b/packages/firebase_core_tvos/pubspec.yaml @@ -0,0 +1,37 @@ +name: firebase_core_tvos +description: >- + tvOS (Apple TV) implementation of the firebase_core Flutter plugin, enabling + Firebase initialization on Apple TV via the flutter-tvos toolchain. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/firebase_core_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`. See PORTING_REPORT.md. +# License holder: fluttertv + +# The example ships the standard FlutterFire demo-project GoogleService +# values (client-side Firebase identifiers, not secrets — Firebase API keys +# are safe to embed in client apps). Tell pub's secret scanner they are +# intentional, matching upstream firebase_core's own pubspec. +false_secrets: + - example/** + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + firebase_core: ^4.11.0 + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + +flutter: + plugin: + platforms: + tvos: + pluginClass: FLTFirebaseCorePlugin diff --git a/packages/firebase_core_tvos/test/firebase_core_tvos_test.dart b/packages/firebase_core_tvos/test/firebase_core_tvos_test.dart new file mode 100644 index 0000000..d37c681 --- /dev/null +++ b/packages/firebase_core_tvos/test/firebase_core_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-06-30 by `flutter-tvos plugin port`. +// Source plugin: firebase_core + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/firebase_core_tvos/tvos/Classes/FLTFirebaseCorePlugin.m b/packages/firebase_core_tvos/tvos/Classes/FLTFirebaseCorePlugin.m new file mode 100644 index 0000000..192f984 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/FLTFirebaseCorePlugin.m @@ -0,0 +1,259 @@ +// Copyright 2020 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. + +#if __has_include("include/firebase_core/FLTFirebaseCorePlugin.h") +#import "include/firebase_core/FLTFirebaseCorePlugin.h" +#else +#import "include/FLTFirebaseCorePlugin.h" +#endif + +#if __has_include("include/firebase_core/FLTFirebasePluginRegistry.h") +#import "include/firebase_core/FLTFirebasePluginRegistry.h" +#else +#import "include/FLTFirebasePluginRegistry.h" +#endif + +#if __has_include("include/firebase_core/messages.g.h") +#import "include/firebase_core/messages.g.h" +#else +#import "include/messages.g.h" +#endif + +@implementation FLTFirebaseCorePlugin { + BOOL _coreInitialized; +} + +#pragma mark - FlutterPlugin + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FLTFirebaseCorePlugin *sharedInstance = [self sharedInstance]; +#if TARGET_OS_OSX +#else + [registrar publish:sharedInstance]; +#endif + SetUpFirebaseCoreHostApi(registrar.messenger, sharedInstance); + SetUpFirebaseAppHostApi(registrar.messenger, sharedInstance); +} + +// Returns a singleton instance of the Firebase Core plugin. ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static FLTFirebaseCorePlugin *instance; + + dispatch_once(&onceToken, ^{ + instance = [[FLTFirebaseCorePlugin alloc] init]; + // Register with the Flutter Firebase plugin registry. + [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:instance]; + + // Initialize default Firebase app, but only if the plist file options + // exist. + // - If it is missing then there is no default app discovered in Dart and + // Dart throws an error. + // - Without this the iOS/MacOS app would crash immediately on calling + // [FIRApp configure] without + // providing helpful context about the crash to the user. + // + // Default app exists check is for backwards compatibility of legacy + // FlutterFire plugins that call [FIRApp configure]; themselves internally. + FIROptions *options = [FIROptions defaultOptions]; + if (options != nil && [FIRApp allApps][@"__FIRAPP_DEFAULT"] == nil) { + [FIRApp configureWithOptions:options]; + } + }); + + return instance; +} + +static NSMutableDictionary *customAuthDomains; + +// Initialize static properties + ++ (void)initialize { + if (self == [FLTFirebaseCorePlugin self]) { + customAuthDomains = [[NSMutableDictionary alloc] init]; + } +} + ++ (NSString *)getCustomDomain:(NSString *)appName { + return customAuthDomains[appName]; +} + +#pragma mark - Helpers + +- (CoreFirebaseOptions *)optionsFromFIROptions:(FIROptions *)options { + CoreFirebaseOptions *pigeonOptions = [CoreFirebaseOptions alloc]; + pigeonOptions.apiKey = (id)options.APIKey ?: [NSNull null]; + pigeonOptions.appId = (id)options.googleAppID ?: [NSNull null]; + pigeonOptions.messagingSenderId = (id)options.GCMSenderID ?: [NSNull null]; + pigeonOptions.projectId = (id)options.projectID ?: [NSNull null]; + pigeonOptions.databaseURL = (id)options.databaseURL ?: [NSNull null]; + pigeonOptions.storageBucket = (id)options.storageBucket ?: [NSNull null]; + pigeonOptions.deepLinkURLScheme = [NSNull null]; + pigeonOptions.iosBundleId = (id)options.bundleID ?: [NSNull null]; + pigeonOptions.iosClientId = (id)options.clientID ?: [NSNull null]; + pigeonOptions.appGroupId = (id)options.appGroupID ?: [NSNull null]; + // recaptchaSiteKey is currently only exposed by Firebase JS options. + pigeonOptions.recaptchaSiteKey = [NSNull null]; + return pigeonOptions; +} + +- (CoreInitializeResponse *)initializeResponseFromFIRApp:(FIRApp *)firebaseApp { + NSString *appNameDart = [FLTFirebasePlugin firebaseAppNameFromIosName:firebaseApp.name]; + CoreInitializeResponse *response = [CoreInitializeResponse alloc]; + response.name = appNameDart; + response.options = [self optionsFromFIROptions:firebaseApp.options]; + response.isAutomaticDataCollectionEnabled = @(firebaseApp.isDataCollectionDefaultEnabled); + response.pluginConstants = + [[FLTFirebasePluginRegistry sharedInstance] pluginConstantsForFIRApp:firebaseApp]; + + return response; +} + +#pragma mark - FLTFirebasePlugin + +- (void)didReinitializeFirebaseCore:(void (^)(void))completion { + completion(); +} + +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *)firebase_app { + return @{}; +} + +- (NSString *_Nonnull)firebaseLibraryName { + return @LIBRARY_NAME; +} + +- (NSString *_Nonnull)firebaseLibraryVersion { + return @LIBRARY_VERSION; +} + +- (NSString *_Nonnull)flutterChannelName { + // The pigeon channel depends on each function + return @"dev.flutter.pigeon.FirebaseCoreHostApi.initializeApp"; +} + +#pragma mark - API + +- (void)initializeAppAppName:(nonnull NSString *)appName + initializeAppRequest:(nonnull CoreFirebaseOptions *)initializeAppRequest + completion:(nonnull void (^)(CoreInitializeResponse *_Nullable, + FlutterError *_Nullable))completion { + NSString *appNameIos = [FLTFirebasePlugin firebaseAppNameFromDartName:appName]; + + if ([FLTFirebasePlugin firebaseAppNamed:appNameIos]) { + completion([self initializeResponseFromFIRApp:[FLTFirebasePlugin firebaseAppNamed:appNameIos]], + nil); + return; + } + + NSString *appId = initializeAppRequest.appId; + NSString *messagingSenderId = initializeAppRequest.messagingSenderId; + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:appId + GCMSenderID:messagingSenderId]; + + options.APIKey = initializeAppRequest.apiKey; + options.projectID = initializeAppRequest.projectId; + + // kFirebaseOptionsDatabaseUrl + if (![initializeAppRequest.databaseURL isEqual:[NSNull null]]) { + options.databaseURL = initializeAppRequest.databaseURL; + } + + // kFirebaseOptionsStorageBucket + if (![options.storageBucket isEqual:[NSNull null]]) { + options.storageBucket = initializeAppRequest.storageBucket; + } + + // kFirebaseOptionsIosBundleId + if (![initializeAppRequest.iosBundleId isEqual:[NSNull null]]) { + options.bundleID = initializeAppRequest.iosBundleId; + } + + // kFirebaseOptionsIosClientId + if (![initializeAppRequest.iosClientId isEqual:[NSNull null]]) { + options.clientID = initializeAppRequest.iosClientId; + } + + // kFirebaseOptionsAppGroupId + if (![initializeAppRequest.appGroupId isEqual:[NSNull null]]) { + options.appGroupID = initializeAppRequest.appGroupId; + } + + if (initializeAppRequest.authDomain != nil) { + customAuthDomains[appNameIos] = initializeAppRequest.authDomain; + } + + [FIRApp configureWithName:appNameIos options:options]; + + completion([self initializeResponseFromFIRApp:[FIRApp appNamed:appNameIos]], nil); +} + +- (void)initializeCoreWithCompletion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + void (^initializeCoreBlock)(void) = ^void() { + NSDictionary *firebaseApps = [FIRApp allApps]; + NSMutableArray *firebaseAppsArray = [NSMutableArray arrayWithCapacity:firebaseApps.count]; + + for (NSString *appName in firebaseApps) { + FIRApp *firebaseApp = firebaseApps[appName]; + [firebaseAppsArray addObject:[self initializeResponseFromFIRApp:firebaseApp]]; + } + + completion(firebaseAppsArray, nil); + }; + + if (!_coreInitialized) { + _coreInitialized = YES; + initializeCoreBlock(); + } else { + [[FLTFirebasePluginRegistry sharedInstance] didReinitializeFirebaseCore:initializeCoreBlock]; + } +} + +- (void)optionsFromResourceWithCompletion:(nonnull void (^)(CoreFirebaseOptions *_Nullable, + FlutterError *_Nullable))completion { + // Unsupported on iOS/MacOS. + completion(nil, nil); +} + +- (void)deleteAppName:(nonnull NSString *)appName + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + FIRApp *firebaseApp = [FLTFirebasePlugin firebaseAppNamed:appName]; + + if (firebaseApp) { + [firebaseApp deleteApp:^(BOOL success) { + if (success) { + completion(nil); + } else { + completion([FlutterError errorWithCode:@"delete-failed" + message:@"Failed to delete a Firebase app instance." + details:nil]); + } + }]; + } else { + completion(nil); + } +} + +- (void)setAutomaticDataCollectionEnabledAppName:(nonnull NSString *)appName + enabled:(BOOL)enabled + completion: + (nonnull void (^)(FlutterError *_Nullable))completion { + FIRApp *firebaseApp = [FLTFirebasePlugin firebaseAppNamed:appName]; + if (firebaseApp) { + [firebaseApp setDataCollectionDefaultEnabled:enabled]; + } + + completion(nil); +} + +- (void)setAutomaticResourceManagementEnabledAppName:(nonnull NSString *)appName + enabled:(BOOL)enabled + completion:(nonnull void (^)(FlutterError *_Nullable)) + completion { + // Unsupported on iOS/MacOS. + completion(nil); +} + +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePlugin.m b/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePlugin.m new file mode 100644 index 0000000..2b00388 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePlugin.m @@ -0,0 +1,63 @@ +// Copyright 2020 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. + +#if __has_include("include/firebase_core/FLTFirebasePlugin.h") +#import "include/firebase_core/FLTFirebasePlugin.h" +#else +#import "include/FLTFirebasePlugin.h" +#endif + +// Firebase default app name. +NSString *_Nonnull const kFIRDefaultAppNameIOS = @"__FIRAPP_DEFAULT"; +NSString *_Nonnull const kFIRDefaultAppNameDart = @"[DEFAULT]"; + +@interface FLTFirebaseMethodCallResult () +@property(readwrite, nonatomic) FLTFirebaseMethodCallErrorBlock error; +@property(readwrite, nonatomic) FLTFirebaseMethodCallSuccessBlock success; +@end +@implementation FLTFirebaseMethodCallResult + ++ (instancetype)createWithSuccess:(FLTFirebaseMethodCallSuccessBlock)successBlock + andErrorBlock:(FLTFirebaseMethodCallErrorBlock)errorBlock { + FLTFirebaseMethodCallResult *methodCallResult = [[FLTFirebaseMethodCallResult alloc] init]; + methodCallResult.error = errorBlock; + methodCallResult.success = successBlock; + return methodCallResult; +} + +@end + +@implementation FLTFirebasePlugin ++ (FlutterError *_Nonnull)createFlutterErrorFromCode:(NSString *_Nonnull)code + message:(NSString *_Nonnull)message + optionalDetails:(NSDictionary *_Nullable)details + andOptionalNSError:(NSError *_Nullable)error { + NSMutableDictionary *detailsDict = [NSMutableDictionary dictionaryWithDictionary:details ?: @{}]; + if (error != nil) { + detailsDict[@"nativeErrorCode"] = [@(error.code) stringValue]; + detailsDict[@"nativeErrorMessage"] = error.localizedDescription; + } + return [FlutterError errorWithCode:code message:message details:detailsDict]; +} + ++ (NSString *)firebaseAppNameFromDartName:(NSString *_Nonnull)appName { + NSString *appNameIOS = appName; + if ([kFIRDefaultAppNameDart isEqualToString:appName]) { + appNameIOS = kFIRDefaultAppNameIOS; + } + return appNameIOS; +} + ++ (NSString *_Nonnull)firebaseAppNameFromIosName:(NSString *_Nonnull)appName { + NSString *appNameDart = appName; + if ([kFIRDefaultAppNameIOS isEqualToString:appName]) { + appNameDart = kFIRDefaultAppNameDart; + } + return appNameDart; +} + ++ (FIRApp *_Nullable)firebaseAppNamed:(NSString *_Nonnull)appName { + return [FIRApp allApps][[self firebaseAppNameFromDartName:appName]]; +} +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePluginRegistry.m b/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePluginRegistry.m new file mode 100644 index 0000000..a6f2f56 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/FLTFirebasePluginRegistry.m @@ -0,0 +1,79 @@ +// Copyright 2020 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. + +#if __has_include("include/firebase_core/FLTFirebasePluginRegistry.h") +#import "include/firebase_core/FLTFirebasePluginRegistry.h" +#else +#import "include/FLTFirebasePluginRegistry.h" +#endif + +#if __has_include() +#import +#define REGISTER_LIB +#elif __has_include() +#import +#define REGISTER_LIB +#endif + +@implementation FLTFirebasePluginRegistry { + NSMutableDictionary> *registeredPlugins; +} + +- (instancetype)init { + self = [super init]; + if (self) { + registeredPlugins = [NSMutableDictionary dictionary]; + } + return self; +} + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static FLTFirebasePluginRegistry *instance; + + dispatch_once(&onceToken, ^{ + instance = [[FLTFirebasePluginRegistry alloc] init]; + }); + + return instance; +} + +- (void)registerFirebasePlugin:(id)firebasePlugin { + // Register the library with the Firebase backend. +#ifdef REGISTER_LIB + [FIRApp registerLibrary:[firebasePlugin firebaseLibraryName] + withVersion:[firebasePlugin firebaseLibraryVersion]]; +#endif + // Store the plugin delegate for later usage. + registeredPlugins[[firebasePlugin flutterChannelName]] = firebasePlugin; +} + +- (NSDictionary *)pluginConstantsForFIRApp:(FIRApp *)firebaseApp { + NSString *pluginFlutterChannelName; + NSMutableDictionary *pluginConstants = [NSMutableDictionary dictionary]; + + for (pluginFlutterChannelName in registeredPlugins) { + pluginConstants[pluginFlutterChannelName] = + [registeredPlugins[pluginFlutterChannelName] pluginConstantsForFIRApp:firebaseApp]; + } + + return pluginConstants; +} + +- (void)didReinitializeFirebaseCore:(void (^_Nonnull)(void))completion { + __block int pluginsCompleted = 0; + NSUInteger pluginsCount = [self->registeredPlugins allKeys].count; + void (^allPluginsCompletion)(void) = ^void() { + pluginsCompleted++; + if (pluginsCompleted == pluginsCount) { + completion(); + } + }; + + for (NSString *pluginFlutterChannelName in registeredPlugins) { + [registeredPlugins[pluginFlutterChannelName] didReinitializeFirebaseCore:allPluginsCompletion]; + } +} + +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/dummy.m b/packages/firebase_core_tvos/tvos/Classes/dummy.m new file mode 100644 index 0000000..b26e568 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/dummy.m @@ -0,0 +1,3 @@ +// Copyright 2023, 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. diff --git a/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebaseCorePlugin.h b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebaseCorePlugin.h new file mode 100644 index 0000000..accb377 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebaseCorePlugin.h @@ -0,0 +1,24 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import "FLTFirebasePlugin.h" +#if __has_include("include/messages.g.h") +#import "include/messages.g.h" +#else +#import "messages.g.h" +#endif + +@interface FLTFirebaseCorePlugin + : FLTFirebasePlugin + ++ (NSString *)getCustomDomain:(NSString *)appName; + +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePlugin.h b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePlugin.h new file mode 100644 index 0000000..63e4290 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePlugin.h @@ -0,0 +1,168 @@ +// Copyright 2020 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. + +// Note: Don't use umbrella header here - will cause a +// build +// failure on MacOS builds (Flutter MacOS uses Swift) when this file is +// included in other Flutter plugins like Firestore with an error of +// "Include of non-modular header inside framework module". +#import +#import +#import + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +/** + * Block that is capable of sending a success response to a method call + * operation. Use this for returning success data to a Method call. + */ +typedef void (^FLTFirebaseMethodCallSuccessBlock)(id _Nullable result); + +/** + * Block that is capable of sending an error response to a method call + * operation. Use this for returning error information to a Method call. + */ +typedef void (^FLTFirebaseMethodCallErrorBlock)(NSString *_Nullable code, + NSString *_Nullable message, + NSDictionary *_Nullable details, + NSError *_Nullable error); + +/** + * A protocol that all FlutterFire plugins should implement. + */ +@protocol FLTFirebasePlugin +/** + * FlutterFire plugins implementing FLTFirebasePlugin should provide this method + * to be notified when FirebaseCore#initializeCore was called again (first time + * is ignored). + * + * This can be used by plugins to know when they might need to cleanup previous + * resources between Hot Restarts as `initializeCore` can only be called once in + * Dart. + */ +@required +- (void)didReinitializeFirebaseCore:(void (^_Nonnull)(void))completion; + +/** + * FlutterFire plugins implementing FLTFirebasePlugin must provide this method + * to provide it's constants that are initialized during + * FirebaseCore.initializeApp in Dart. + * + * @param registrar A helper providing application context and methods for + * registering callbacks. + */ +@required +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *_Nonnull)firebaseApp; + +/** + * The Firebase library name of the plugin, used by + * [FIRApp registerLibrary:firebaseLibraryName withVersion:] to + * register this plugin with the Firebase backend. + * + * Usually this is provided by the 'LIBRARY_NAME' preprocessor definition + * defined in the plugins .podspec file. + */ +@required +- (NSString *_Nonnull)firebaseLibraryName; + +/** + * The Firebase library version of the plugin, used by + * FIRApp registerLibrary:withVersion:firebaseLibraryVersion] to + * register this plugin with the Firebase backend. + * + * Usually this is provided by the 'LIBRARY_VERSION' preprocessor definition + * defined in the plugins .podspec file. + */ +@required +- (NSString *_Nonnull)firebaseLibraryVersion; + +/** + * FlutterFire plugins implementing FLTFirebasePlugin must provide this method + * to provide its main method channel name, used by FirebaseCore.initializeApp + * in Dart to identify constants specific to a plugin. + */ +@required +- (NSString *_Nonnull)flutterChannelName; +@end + +/** + * An interface represent a returned result from a Flutter Method Call. + */ +@interface FLTFirebaseMethodCallResult : NSObject ++ (instancetype _Nonnull)createWithSuccess:(FLTFirebaseMethodCallSuccessBlock _Nonnull)successBlock + andErrorBlock:(FLTFirebaseMethodCallErrorBlock _Nonnull)errorBlock; + +/** + * Submit a result indicating a successful method call. + * + * E.g.: `result.success(nil);` + */ +@property(readonly, nonatomic) FLTFirebaseMethodCallSuccessBlock _Nonnull success; + +/** + * Submit a result indicating a failed method call. + * + * E.g.: `result.error(@"code", @"message", nil);` + */ +@property(readonly, nonatomic) FLTFirebaseMethodCallErrorBlock _Nonnull error; + +@end + +@interface FLTFirebasePlugin : NSObject +/** + * Creates a standardized instance of FlutterError using the values returned + * through FLTFirebaseMethodCallErrorBlock. + * + * @param code Error Code. + * @param message Error Message. + * @param details Optional dictionary of additional key/values to return to + * Dart. + * @param error Optional NSError that this error relates to. + * + * @return FlutterError + */ ++ (FlutterError *_Nonnull)createFlutterErrorFromCode:(NSString *_Nonnull)code + message:(NSString *_Nonnull)message + optionalDetails:(NSDictionary *_Nullable)details + andOptionalNSError:(NSError *_Nullable)error; + +/** + * Converts the '[DEFAULT]' app name used in dart and other SDKs to the + * '__FIRAPP_DEFAULT' iOS equivalent. + * + * If name is not '[DEFAULT]' then just returns the same name that was passed + * in. + * + * @param appName The name of the Firebase App. + * + * @return NSString + */ ++ (NSString *_Nonnull)firebaseAppNameFromDartName:(NSString *_Nonnull)appName; + +/** + * Converts the '__FIRAPP_DEFAULT' app name used in iOS to '[DEFAULT]' - used in + * Dart & other SDKs. + * + * If name is not '__FIRAPP_DEFAULT' then just returns the same name that was + * passed in. + * + * @param appName The name of the Firebase App. + * + * @return NSString + */ ++ (NSString *_Nonnull)firebaseAppNameFromIosName:(NSString *_Nonnull)appName; + +/** + * Retrieves a FIRApp instance based on the app name provided from Dart code. + * + * @param appName The name of the Firebase App. + * + * @return FIRApp - returns nil if Firebase app does not exist. + */ ++ (FIRApp *_Nullable)firebaseAppNamed:(NSString *_Nonnull)appName; +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePluginRegistry.h b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePluginRegistry.h new file mode 100644 index 0000000..17b454f --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/FLTFirebasePluginRegistry.h @@ -0,0 +1,48 @@ +// Copyright 2020 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 +#import "FLTFirebasePlugin.h" + +@interface FLTFirebasePluginRegistry : NSObject +/** + * Get the shared singleton instance of the plugin registry. + * + * @return FLTFirebasePluginRegistry + */ ++ (instancetype _Nonnull)sharedInstance; + +/** + * Register a FlutterFire plugin with the plugin registry. + * + * Plugins must conform to the FLTFirebasePlugin protocol. + * + * @param firebasePlugin id + */ +- (void)registerFirebasePlugin:(id _Nonnull)firebasePlugin; + +/** + * Each FlutterFire plugin implementing FLTFirebasePlugin provides this method, + * allowing it's constants to be initialized during FirebaseCore.initializeApp + * in Dart. Here we call this method on each of the registered plugins and + * gather their constants for use in Dart. + * + * Constants for specific plugins are stored using the Flutter plugins channel + * name as the key. + * + * @param firebaseApp FIRApp Firebase App instance these constants relate to. + * @return NSDictionary Dictionary of plugins and their constants. + */ +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *_Nonnull)firebaseApp; + +/** + * Each FlutterFire plugin implementing this method are notified that + * FirebaseCore#initializeCore was called again. + * + * This is used by plugins to know if they need to cleanup previous + * resources between Hot Restarts as `initializeCore` can only be called once in + * Dart. + */ +- (void)didReinitializeFirebaseCore:(void (^_Nonnull)(void))completion; +@end diff --git a/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/dummy.h b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/dummy.h new file mode 100644 index 0000000..b26e568 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/dummy.h @@ -0,0 +1,3 @@ +// Copyright 2023, 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. diff --git a/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/messages.g.h b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/messages.g.h new file mode 100644 index 0000000..561383c --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/include/firebase_core/messages.g.h @@ -0,0 +1,105 @@ +// Copyright 2023, 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; + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +@class CoreFirebaseOptions; +@class CoreInitializeResponse; + +@interface CoreFirebaseOptions : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithApiKey:(NSString *)apiKey + appId:(NSString *)appId + messagingSenderId:(NSString *)messagingSenderId + projectId:(NSString *)projectId + authDomain:(nullable NSString *)authDomain + databaseURL:(nullable NSString *)databaseURL + storageBucket:(nullable NSString *)storageBucket + measurementId:(nullable NSString *)measurementId + trackingId:(nullable NSString *)trackingId + deepLinkURLScheme:(nullable NSString *)deepLinkURLScheme + androidClientId:(nullable NSString *)androidClientId + iosClientId:(nullable NSString *)iosClientId + iosBundleId:(nullable NSString *)iosBundleId + appGroupId:(nullable NSString *)appGroupId + recaptchaSiteKey:(nullable NSString *)recaptchaSiteKey; +@property(nonatomic, copy) NSString *apiKey; +@property(nonatomic, copy) NSString *appId; +@property(nonatomic, copy) NSString *messagingSenderId; +@property(nonatomic, copy) NSString *projectId; +@property(nonatomic, copy, nullable) NSString *authDomain; +@property(nonatomic, copy, nullable) NSString *databaseURL; +@property(nonatomic, copy, nullable) NSString *storageBucket; +@property(nonatomic, copy, nullable) NSString *measurementId; +@property(nonatomic, copy, nullable) NSString *trackingId; +@property(nonatomic, copy, nullable) NSString *deepLinkURLScheme; +@property(nonatomic, copy, nullable) NSString *androidClientId; +@property(nonatomic, copy, nullable) NSString *iosClientId; +@property(nonatomic, copy, nullable) NSString *iosBundleId; +@property(nonatomic, copy, nullable) NSString *appGroupId; +@property(nonatomic, copy, nullable) NSString *recaptchaSiteKey; +@end + +@interface CoreInitializeResponse : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithName:(NSString *)name + options:(CoreFirebaseOptions *)options + isAutomaticDataCollectionEnabled:(nullable NSNumber *)isAutomaticDataCollectionEnabled + pluginConstants:(NSDictionary *)pluginConstants; +@property(nonatomic, copy) NSString *name; +@property(nonatomic, strong) CoreFirebaseOptions *options; +@property(nonatomic, strong, nullable) NSNumber *isAutomaticDataCollectionEnabled; +@property(nonatomic, copy) NSDictionary *pluginConstants; +@end + +/// The codec used by all APIs. +NSObject *nullGetMessagesCodec(void); + +@protocol FirebaseCoreHostApi +- (void)initializeAppAppName:(NSString *)appName + initializeAppRequest:(CoreFirebaseOptions *)initializeAppRequest + completion:(void (^)(CoreInitializeResponse *_Nullable, + FlutterError *_Nullable))completion; +- (void)initializeCoreWithCompletion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)optionsFromResourceWithCompletion:(void (^)(CoreFirebaseOptions *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void SetUpFirebaseCoreHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFirebaseCoreHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +@protocol FirebaseAppHostApi +- (void)setAutomaticDataCollectionEnabledAppName:(NSString *)appName + enabled:(BOOL)enabled + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)setAutomaticResourceManagementEnabledAppName:(NSString *)appName + enabled:(BOOL)enabled + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)deleteAppName:(NSString *)appName completion:(void (^)(FlutterError *_Nullable))completion; +@end + +extern void SetUpFirebaseAppHostApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFirebaseAppHostApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + +NS_ASSUME_NONNULL_END diff --git a/packages/firebase_core_tvos/tvos/Classes/messages.g.m b/packages/firebase_core_tvos/tvos/Classes/messages.g.m new file mode 100644 index 0000000..4499443 --- /dev/null +++ b/packages/firebase_core_tvos/tvos/Classes/messages.g.m @@ -0,0 +1,545 @@ +// Copyright 2023, 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 "include/firebase_core/messages.g.h" + +#if TARGET_OS_OSX +@import FlutterMacOS; +#else +@import Flutter; +#endif + +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} + +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface CoreFirebaseOptions () ++ (CoreFirebaseOptions *)fromList:(NSArray *)list; ++ (nullable CoreFirebaseOptions *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface CoreInitializeResponse () ++ (CoreInitializeResponse *)fromList:(NSArray *)list; ++ (nullable CoreInitializeResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation CoreFirebaseOptions ++ (instancetype)makeWithApiKey:(NSString *)apiKey + appId:(NSString *)appId + messagingSenderId:(NSString *)messagingSenderId + projectId:(NSString *)projectId + authDomain:(nullable NSString *)authDomain + databaseURL:(nullable NSString *)databaseURL + storageBucket:(nullable NSString *)storageBucket + measurementId:(nullable NSString *)measurementId + trackingId:(nullable NSString *)trackingId + deepLinkURLScheme:(nullable NSString *)deepLinkURLScheme + androidClientId:(nullable NSString *)androidClientId + iosClientId:(nullable NSString *)iosClientId + iosBundleId:(nullable NSString *)iosBundleId + appGroupId:(nullable NSString *)appGroupId + recaptchaSiteKey:(nullable NSString *)recaptchaSiteKey { + CoreFirebaseOptions *pigeonResult = [[CoreFirebaseOptions alloc] init]; + pigeonResult.apiKey = apiKey; + pigeonResult.appId = appId; + pigeonResult.messagingSenderId = messagingSenderId; + pigeonResult.projectId = projectId; + pigeonResult.authDomain = authDomain; + pigeonResult.databaseURL = databaseURL; + pigeonResult.storageBucket = storageBucket; + pigeonResult.measurementId = measurementId; + pigeonResult.trackingId = trackingId; + pigeonResult.deepLinkURLScheme = deepLinkURLScheme; + pigeonResult.androidClientId = androidClientId; + pigeonResult.iosClientId = iosClientId; + pigeonResult.iosBundleId = iosBundleId; + pigeonResult.appGroupId = appGroupId; + pigeonResult.recaptchaSiteKey = recaptchaSiteKey; + return pigeonResult; +} ++ (CoreFirebaseOptions *)fromList:(NSArray *)list { + CoreFirebaseOptions *pigeonResult = [[CoreFirebaseOptions alloc] init]; + pigeonResult.apiKey = GetNullableObjectAtIndex(list, 0); + pigeonResult.appId = GetNullableObjectAtIndex(list, 1); + pigeonResult.messagingSenderId = GetNullableObjectAtIndex(list, 2); + pigeonResult.projectId = GetNullableObjectAtIndex(list, 3); + pigeonResult.authDomain = GetNullableObjectAtIndex(list, 4); + pigeonResult.databaseURL = GetNullableObjectAtIndex(list, 5); + pigeonResult.storageBucket = GetNullableObjectAtIndex(list, 6); + pigeonResult.measurementId = GetNullableObjectAtIndex(list, 7); + pigeonResult.trackingId = GetNullableObjectAtIndex(list, 8); + pigeonResult.deepLinkURLScheme = GetNullableObjectAtIndex(list, 9); + pigeonResult.androidClientId = GetNullableObjectAtIndex(list, 10); + pigeonResult.iosClientId = GetNullableObjectAtIndex(list, 11); + pigeonResult.iosBundleId = GetNullableObjectAtIndex(list, 12); + pigeonResult.appGroupId = GetNullableObjectAtIndex(list, 13); + pigeonResult.recaptchaSiteKey = GetNullableObjectAtIndex(list, 14); + return pigeonResult; +} ++ (nullable CoreFirebaseOptions *)nullableFromList:(NSArray *)list { + return (list) ? [CoreFirebaseOptions fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.apiKey ?: [NSNull null], + self.appId ?: [NSNull null], + self.messagingSenderId ?: [NSNull null], + self.projectId ?: [NSNull null], + self.authDomain ?: [NSNull null], + self.databaseURL ?: [NSNull null], + self.storageBucket ?: [NSNull null], + self.measurementId ?: [NSNull null], + self.trackingId ?: [NSNull null], + self.deepLinkURLScheme ?: [NSNull null], + self.androidClientId ?: [NSNull null], + self.iosClientId ?: [NSNull null], + self.iosBundleId ?: [NSNull null], + self.appGroupId ?: [NSNull null], + self.recaptchaSiteKey ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + CoreFirebaseOptions *other = (CoreFirebaseOptions *)object; + return FLTPigeonDeepEquals(self.apiKey, other.apiKey) && + FLTPigeonDeepEquals(self.appId, other.appId) && + FLTPigeonDeepEquals(self.messagingSenderId, other.messagingSenderId) && + FLTPigeonDeepEquals(self.projectId, other.projectId) && + FLTPigeonDeepEquals(self.authDomain, other.authDomain) && + FLTPigeonDeepEquals(self.databaseURL, other.databaseURL) && + FLTPigeonDeepEquals(self.storageBucket, other.storageBucket) && + FLTPigeonDeepEquals(self.measurementId, other.measurementId) && + FLTPigeonDeepEquals(self.trackingId, other.trackingId) && + FLTPigeonDeepEquals(self.deepLinkURLScheme, other.deepLinkURLScheme) && + FLTPigeonDeepEquals(self.androidClientId, other.androidClientId) && + FLTPigeonDeepEquals(self.iosClientId, other.iosClientId) && + FLTPigeonDeepEquals(self.iosBundleId, other.iosBundleId) && + FLTPigeonDeepEquals(self.appGroupId, other.appGroupId) && + FLTPigeonDeepEquals(self.recaptchaSiteKey, other.recaptchaSiteKey); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.apiKey); + result = result * 31 + FLTPigeonDeepHash(self.appId); + result = result * 31 + FLTPigeonDeepHash(self.messagingSenderId); + result = result * 31 + FLTPigeonDeepHash(self.projectId); + result = result * 31 + FLTPigeonDeepHash(self.authDomain); + result = result * 31 + FLTPigeonDeepHash(self.databaseURL); + result = result * 31 + FLTPigeonDeepHash(self.storageBucket); + result = result * 31 + FLTPigeonDeepHash(self.measurementId); + result = result * 31 + FLTPigeonDeepHash(self.trackingId); + result = result * 31 + FLTPigeonDeepHash(self.deepLinkURLScheme); + result = result * 31 + FLTPigeonDeepHash(self.androidClientId); + result = result * 31 + FLTPigeonDeepHash(self.iosClientId); + result = result * 31 + FLTPigeonDeepHash(self.iosBundleId); + result = result * 31 + FLTPigeonDeepHash(self.appGroupId); + result = result * 31 + FLTPigeonDeepHash(self.recaptchaSiteKey); + return result; +} +@end + +@implementation CoreInitializeResponse ++ (instancetype)makeWithName:(NSString *)name + options:(CoreFirebaseOptions *)options + isAutomaticDataCollectionEnabled:(nullable NSNumber *)isAutomaticDataCollectionEnabled + pluginConstants:(NSDictionary *)pluginConstants { + CoreInitializeResponse *pigeonResult = [[CoreInitializeResponse alloc] init]; + pigeonResult.name = name; + pigeonResult.options = options; + pigeonResult.isAutomaticDataCollectionEnabled = isAutomaticDataCollectionEnabled; + pigeonResult.pluginConstants = pluginConstants; + return pigeonResult; +} ++ (CoreInitializeResponse *)fromList:(NSArray *)list { + CoreInitializeResponse *pigeonResult = [[CoreInitializeResponse alloc] init]; + pigeonResult.name = GetNullableObjectAtIndex(list, 0); + pigeonResult.options = GetNullableObjectAtIndex(list, 1); + pigeonResult.isAutomaticDataCollectionEnabled = GetNullableObjectAtIndex(list, 2); + pigeonResult.pluginConstants = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable CoreInitializeResponse *)nullableFromList:(NSArray *)list { + return (list) ? [CoreInitializeResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.name ?: [NSNull null], + self.options ?: [NSNull null], + self.isAutomaticDataCollectionEnabled ?: [NSNull null], + self.pluginConstants ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + CoreInitializeResponse *other = (CoreInitializeResponse *)object; + return FLTPigeonDeepEquals(self.name, other.name) && + FLTPigeonDeepEquals(self.options, other.options) && + FLTPigeonDeepEquals(self.isAutomaticDataCollectionEnabled, + other.isAutomaticDataCollectionEnabled) && + FLTPigeonDeepEquals(self.pluginConstants, other.pluginConstants); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.name); + result = result * 31 + FLTPigeonDeepHash(self.options); + result = result * 31 + FLTPigeonDeepHash(self.isAutomaticDataCollectionEnabled); + result = result * 31 + FLTPigeonDeepHash(self.pluginConstants); + return result; +} +@end + +@interface nullMessagesPigeonCodecReader : FlutterStandardReader +@end +@implementation nullMessagesPigeonCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 129: + return [CoreFirebaseOptions fromList:[self readValue]]; + case 130: + return [CoreInitializeResponse fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface nullMessagesPigeonCodecWriter : FlutterStandardWriter +@end +@implementation nullMessagesPigeonCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[CoreFirebaseOptions class]]) { + [self writeByte:129]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[CoreInitializeResponse class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface nullMessagesPigeonCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation nullMessagesPigeonCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[nullMessagesPigeonCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[nullMessagesPigeonCodecReader alloc] initWithData:data]; +} +@end + +NSObject *nullGetMessagesCodec(void) { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + nullMessagesPigeonCodecReaderWriter *readerWriter = + [[nullMessagesPigeonCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} +void SetUpFirebaseCoreHostApi(id binaryMessenger, + NSObject *api) { + SetUpFirebaseCoreHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseCoreHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_" + @"interface.FirebaseCoreHostApi.initializeApp", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(initializeAppAppName:initializeAppRequest:completion:)], + @"FirebaseCoreHostApi api (%@) doesn't respond to " + @"@selector(initializeAppAppName:initializeAppRequest:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_appName = GetNullableObjectAtIndex(args, 0); + CoreFirebaseOptions *arg_initializeAppRequest = GetNullableObjectAtIndex(args, 1); + [api initializeAppAppName:arg_appName + initializeAppRequest:arg_initializeAppRequest + completion:^(CoreInitializeResponse *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_" + @"interface.FirebaseCoreHostApi.initializeCore", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(initializeCoreWithCompletion:)], + @"FirebaseCoreHostApi api (%@) doesn't respond to " + @"@selector(initializeCoreWithCompletion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + [api initializeCoreWithCompletion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_interface." + @"FirebaseCoreHostApi.optionsFromResource", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(optionsFromResourceWithCompletion:)], + @"FirebaseCoreHostApi api (%@) doesn't respond to " + @"@selector(optionsFromResourceWithCompletion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + [api optionsFromResourceWithCompletion:^(CoreFirebaseOptions *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +void SetUpFirebaseAppHostApi(id binaryMessenger, + NSObject *api) { + SetUpFirebaseAppHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseAppHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_interface." + @"FirebaseAppHostApi.setAutomaticDataCollectionEnabled", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert( + [api + respondsToSelector:@selector( + setAutomaticDataCollectionEnabledAppName:enabled:completion:)], + @"FirebaseAppHostApi api (%@) doesn't respond to " + @"@selector(setAutomaticDataCollectionEnabledAppName:enabled:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_appName = GetNullableObjectAtIndex(args, 0); + BOOL arg_enabled = [GetNullableObjectAtIndex(args, 1) boolValue]; + [api setAutomaticDataCollectionEnabledAppName:arg_appName + enabled:arg_enabled + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_interface." + @"FirebaseAppHostApi.setAutomaticResourceManagementEnabled", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setAutomaticResourceManagementEnabledAppName: + enabled:completion:)], + @"FirebaseAppHostApi api (%@) doesn't respond to " + @"@selector(setAutomaticResourceManagementEnabledAppName:enabled:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_appName = GetNullableObjectAtIndex(args, 0); + BOOL arg_enabled = [GetNullableObjectAtIndex(args, 1) boolValue]; + [api setAutomaticResourceManagementEnabledAppName:arg_appName + enabled:arg_enabled + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_core_platform_" + @"interface.FirebaseAppHostApi.delete", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(deleteAppName:completion:)], + @"FirebaseAppHostApi api (%@) doesn't respond to @selector(deleteAppName:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_appName = GetNullableObjectAtIndex(args, 0); + [api deleteAppName:arg_appName + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/firebase_core_tvos/tvos/firebase_core_tvos.podspec b/packages/firebase_core_tvos/tvos/firebase_core_tvos.podspec new file mode 100644 index 0000000..d8766da --- /dev/null +++ b/packages/firebase_core_tvos/tvos/firebase_core_tvos.podspec @@ -0,0 +1,51 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint firebase_core_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'firebase_core_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of firebase_core.' + s.description = <<-DESC +tvOS implementation of firebase_core, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/firebase_core_tvos' + s.license = { :file => '../LICENSE' } + s.author = { 'fluttertv' => 'noreply@fluttertv.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*.{h,m,mm,swift}' + s.public_header_files = 'Classes/**/include/**/*.h' + # Firebase/CoreOnly 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', + } + # FLTFirebaseCorePlugin.m reads @LIBRARY_NAME / @LIBRARY_VERSION as + # preprocessor token-pasted string literals (same as upstream + # firebase_core's podspec) — without these the file fails to compile + # with "unexpected '@' in program". + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) LIBRARY_VERSION=\"0.0.1\" LIBRARY_NAME=\"flutter-fire-core-tvos\"', + } + + # The ported Classes/ call into FIRApp (FirebaseCore), same as upstream + # firebase_core's own podspec. The Firebase iOS SDK has shipped tvOS + # support since 8.9.0 (Firebase/CoreOnly works unmodified on tvOS), so no + # patching of the SDK pod itself is required — only this dependency line, + # which the generic porter has no way to infer from Dart/ObjC source. + s.dependency 'Firebase/CoreOnly', '~> 12.15.0' + s.static_framework = true +end diff --git a/packages/firebase_messaging_tvos/.gitignore b/packages/firebase_messaging_tvos/.gitignore new file mode 100644 index 0000000..c83dba5 --- /dev/null +++ b/packages/firebase_messaging_tvos/.gitignore @@ -0,0 +1,32 @@ +# 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 + +# Local-dev dependency override resolving firebase_core_tvos from the sibling +# package before it is published to pub.dev. Not committed, not published; +# remove it at release time (after firebase_core_tvos is live). See pubspec.yaml. +pubspec_overrides.yaml +example/pubspec_overrides.yaml diff --git a/packages/firebase_messaging_tvos/CHANGELOG.md b/packages/firebase_messaging_tvos/CHANGELOG.md new file mode 100644 index 0000000..c8e3dca --- /dev/null +++ b/packages/firebase_messaging_tvos/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.0.1 + +* Initial tvOS scaffolding generated by `flutter-tvos plugin port` from + `firebase_messaging`. diff --git a/packages/firebase_messaging_tvos/LICENSE b/packages/firebase_messaging_tvos/LICENSE new file mode 100644 index 0000000..000b461 --- /dev/null +++ b/packages/firebase_messaging_tvos/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium 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. \ No newline at end of file diff --git a/packages/firebase_messaging_tvos/PORTING_REPORT.md b/packages/firebase_messaging_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..8b5cd9b --- /dev/null +++ b/packages/firebase_messaging_tvos/PORTING_REPORT.md @@ -0,0 +1,196 @@ +# firebase_messaging_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-06-30. + +Source: `firebase_messaging` 16.4.1 (path: `/Users/aliustaoglu/.pub-cache/hosted/pub.dev/firebase_messaging-16.4.1`) +Base platform: ios (Objective-C) +Output: `./firebase_messaging_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 | 20 | +| Methods stubbed (iOS-only) | 0 | +| Native regions disabled on tvOS | 0 | +| tvOS build outlook | ✅ expected to compile | +| Manual review items | 0 | + +## Methods + +### `Messaging#deleteToken` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#getAPNSToken` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#getInitialMessage` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#getNotificationSettings` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#getToken` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#requestPermission` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#setAutoInitEnabled` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#startBackgroundIsolate` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#subscribeToTopic` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `Messaging#unsubscribeFromTopic` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `aps` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `collapse_key` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `fcm_options` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `from` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `gcm.message_id` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `google.c.a.ts` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `message_id` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `message_type` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `to` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `unknown` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +## 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 + +- [ ] Read every `✗ stubbed` method above and confirm returning `FlutterMethodNotImplemented` is acceptable on tvOS. +- [ ] Review every `⚠️ partial` method against a real Apple TV (behaviour differs from iOS). +- [ ] Confirm the removed imports were not load-bearing for still-supported code paths. +- [ ] `flutter-tvos build tvos --simulator --debug` from the plugin's example app compiles the generated registrant. +- [ ] Bump the version and update `CHANGELOG.md` before publishing. + +--- + +## Addendum: manual fixes beyond the automated port (2026-06-30) + +This package needed by far the most manual work in this batch — `firebase_core`/`firebase_auth`/ +`cloud_firestore`/`firebase_storage` either compiled cleanly or only needed isolated feature +disables (MFA, phone auth). Messaging's core problem: **`UNNotificationContent.userInfo` itself is +`API_UNAVAILABLE(tvos)`** in the system SDK, which breaks reading the FCM payload out of a +delivered notification at all on tvOS, foreground or background. Combined with most of +`UNNotificationSettings`'s introspection properties also being tvOS-unavailable, this means tvOS +push notifications work at the *delivery* level (a device gets a push, `getToken`/ +`requestPermission`/topic subscription all function) but not at the *rich content inspection* +level apps usually build UI around. + +- **The usual fixes** (same as every package in this batch): podspec gets `Firebase/Messaging` + + `firebase_core_tvos` dependencies (not upstream's `firebase_core`) + tvOS 15.0 floor + + `LIBRARY_NAME`/`LIBRARY_VERSION` defines; `firebase_core/` header imports repointed to + `firebase_core_tvos/`; `lib/firebase_messaging_tvos.dart` replaced with a one-line re-export of + `package:firebase_messaging/firebase_messaging.dart`. +- **`UIApplicationLaunchOptionsRemoteNotificationKey`** (app-launched-via-notification) — disabled + on tvOS, falls back to `nil` (no notification-driven launch path there). +- **`didReceiveNotificationResponse:`/`openSettingsForNotification:` delegate methods, and + `UISceneConnectionOptions.notificationResponse`** — all "user interacted with a notification" + APIs, disabled wholesale. tvOS has no tap/interaction model for notifications the way iOS/macOS do. +- **`willPresentNotification:` partially disabled**: the delegate method itself stays (it's used to + control presentation options, which *is* available), but the FCM-payload extraction inside it + (`notification.request.content.userInfo`, used to dedupe iOS 18's double-foreground-notification + bug and to fire `Messaging#onMessage`) is `#if !TARGET_OS_TV`'d out, since `userInfo` itself + can't be read on tvOS. Practical effect: foreground pushes can still control presentation, but + the app won't get an `onMessage` callback with the payload on tvOS. +- **`NSDictionaryFromUNNotification:`** (the helper that reads `.content.userInfo`) excluded + entirely on tvOS — its only caller is the now-disabled branch above. +- **`NSDictionaryFromUNNotificationSettings:` rewritten with a `#if TARGET_OS_TV` early branch** + that returns only `authorizationStatus` (the one property that *is* available) plus `@-1` + sentinels for everything else (`alertSetting`, `badgeSetting`, `soundSetting`, `carPlaySetting`, + `lockScreenSetting`, `notificationCenterSetting`, `timeSensitiveSetting`, `showPreviewsSetting`, + `criticalAlertSetting`, `providesAppNotificationSettings` — all `API_UNAVAILABLE(tvos)`). The + non-tvOS branch is the porter's original logic, untouched. + +**Open question for real-device verification**: whether `getInitialMessage`/`onMessageOpenedApp` +(which read `userInfo` from `UNNotificationResponse`, already disabled — see +`didReceiveNotificationResponse` above) means tapped-notification deep-linking flows don't work at +all on tvOS, or whether there's an alternate Siri Remote / Top Shelf interaction path Firebase +doesn't model that a tvOS app would need to wire up itself. Not something a simulator build can +answer — needs a real push to a real Apple TV. + +Verified: `flutter-tvos build tvos --simulator --debug` against the example (unmodified from the +porter's `--include-example` output, aside from the `firebase_core_tvos` dependency and deployment +target bumps — it still depends on `flutter_local_notifications`, which has no tvOS implementation +and would throw `MissingPluginException` if exercised, but compiled fine) completes with no +compiler errors. Not verified: an actual push delivery to a real or simulated Apple TV. + +## Addendum: cross-plugin build fix + runtime verification (2026-07-01) + +Building this plugin **together with `firebase_auth_tvos` in one app** (as any app using both +would) surfaced a build break that neither plugin hits alone: + +- **`didReceiveRemoteNotification:` calls `[[FIRAuth auth] canHandleNotification:userInfo]`** behind + `#if __has_include()` — FlutterFire's hook for handing silent pushes + to Auth's phone-verification flow. That `__has_include` is false when messaging is built alone + (no auth pod), so the branch was dead; add `firebase_auth_tvos` and it compiles, but + `-[FIRAuth canHandleNotification:]` is unavailable on tvOS → compile error. **Fix:** gated the + branch on `&& !TARGET_OS_TV` as well. + +Runtime smoke on Apple TV 4K simulator (tvOS 17.5): `messaging.getToken` executed and returned +`[firebase_messaging/apns-token-not-set]` — the plugin registers, runs, and surfaces the expected +simulator/APNs limitation to Dart (no crash, no `MissingPluginException`). A real token needs APNs, +which the simulator can't provide; verify on physical hardware with a push-enabled profile. + +Manual review required. Read this report top-to-bottom before publishing `firebase_messaging_tvos`. diff --git a/packages/firebase_messaging_tvos/README.md b/packages/firebase_messaging_tvos/README.md new file mode 100644 index 0000000..53ae5a1 --- /dev/null +++ b/packages/firebase_messaging_tvos/README.md @@ -0,0 +1,59 @@ +# firebase_messaging_tvos + +The tvOS (Apple TV) implementation of [`firebase_messaging`](https://pub.dev/packages/firebase_messaging), +provided by the [flutter-tvos](https://github.com/fluttertv/flutter-tvos) toolchain. + +> Generated by [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `firebase_messaging`, then completed by hand. See `PORTING_REPORT.md` for +> the full list of what was changed and disabled. + +## Usage + +This is a federated plugin implementation. An app that already depends on +`firebase_messaging` and targets Apple TV only needs to add this package +alongside it: + +```yaml +dependencies: + firebase_messaging: ^16.4.1 + firebase_messaging_tvos: ^0.0.1 +``` + +The native plugin registers automatically through flutter-tvos' plugin +registrant — no extra imports or setup in app code. + +## tvOS support + +> **Important:** tvOS has a deliberately limited notification model. The +> Firebase Apple SDK and Apple's `UserNotifications` framework expose push +> *registration* (token, permission, topic subscription) on tvOS, but **not** +> the rich-content and interaction APIs apps usually build UI around — +> `UNNotificationContent.userInfo` is itself unavailable on tvOS. Plan your +> Apple TV messaging UX accordingly. + +| Capability | tvOS | Notes | +|---|:---:|---| +| `getToken` / `deleteToken` / `onTokenRefresh` | ✅ | FCM registration token | +| `getAPNSToken` | ✅ | | +| `requestPermission` / authorization status | ✅ | Only `authorizationStatus` is meaningful; other settings report "unsupported" | +| `subscribeToTopic` / `unsubscribeFromTopic` | ✅ | | +| `setAutoInitEnabled` | ✅ | | +| Foreground presentation control (banner/badge/sound options) | ✅ | The delegate fires; you can still set presentation options | +| `onMessage` (foreground payload) | ❌ | Reading the FCM payload (`UNNotificationContent.userInfo`) is unavailable on tvOS | +| `onMessageOpenedApp` / `getInitialMessage` (tapped notification) | ❌ | tvOS has no interactive notification-response model | +| Notification settings introspection (alert/badge/sound/critical/preview/etc.) | ❌ | `API_UNAVAILABLE(tvos)` in the SDK; reported as "unsupported" sentinels | + +Push messages are still *delivered* to a registered Apple TV; what's +constrained is reading their payload and reacting to user interaction from Dart. +See `PORTING_REPORT.md` for the exact per-API behaviour and the open +verification questions. + +## Requirements + +- Apple TV running tvOS 15.0 or later (the Firebase Apple SDK's minimum). +- A push-enabled provisioning profile and APNs setup, as for any FCM app. +- `firebase_core_tvos` (pulled in automatically). + +## License + +fluttertv, under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/firebase_messaging_tvos/analysis_options.yaml b/packages/firebase_messaging_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/README.md b/packages/firebase_messaging_tvos/example/README.md new file mode 100644 index 0000000..c3d1880 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/README.md @@ -0,0 +1,8 @@ +# firebase_messaging_example + +Demonstrates how to use the firebase_messaging plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/firebase_messaging_tvos/example/bundled-service-worker/firebase-messaging-sw.ts b/packages/firebase_messaging_tvos/example/bundled-service-worker/firebase-messaging-sw.ts new file mode 100644 index 0000000..0e24f26 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/bundled-service-worker/firebase-messaging-sw.ts @@ -0,0 +1,63 @@ +import { initializeApp } from 'firebase/app'; +import { + experimentalSetDeliveryMetricsExportedToBigQueryEnabled, + getMessaging, + isSupported, + onBackgroundMessage +} from 'firebase/messaging/sw'; + +declare var self: ServiceWorkerGlobalScope; + +self.addEventListener('install', (event) => { + console.log(self); + console.log(event); +}); + +// Focus the existing app tab when a notification is clicked. +self.addEventListener('notificationclick', (event) => { + event.notification.close(); + event.waitUntil( + self.clients + .matchAll({ type: 'window', includeUncontrolled: true }) + .then((clientList) => { + for (const client of clientList) { + if (!client.focused) { + return client.focus(); + } + } + }) + ); +}); + +const app = initializeApp({ + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', +}); + +isSupported().then((isSupported) => { + if (isSupported) { + const messaging = getMessaging(app); + + experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true); + + onBackgroundMessage(messaging, ({ notification: notification }) => { + const { title, body, image } = notification ?? {}; + + if (!title) { + return; + } + + self.registration.showNotification(title, { + body, + icon: image || '/assets/icons/icon-72x72.png', + }); + }); + } +}); diff --git a/packages/firebase_messaging_tvos/example/bundled-service-worker/package.json b/packages/firebase_messaging_tvos/example/bundled-service-worker/package.json new file mode 100644 index 0000000..7905143 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/bundled-service-worker/package.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "firebase": "12" + }, + "devDependencies": { + "esbuild": "^0.28.1" + }, + "scripts": { + "build": "esbuild firebase-messaging-sw.ts --outdir=../web --bundle --sourcemap --minify --format=esm" + } +} diff --git a/packages/firebase_messaging_tvos/example/bundled-service-worker/tsconfig.json b/packages/firebase_messaging_tvos/example/bundled-service-worker/tsconfig.json new file mode 100644 index 0000000..814f3a7 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/bundled-service-worker/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "lib": [ + "webworker", + "es6" + ] + } +} \ No newline at end of file diff --git a/packages/firebase_messaging_tvos/example/bundled-service-worker/yarn.lock b/packages/firebase_messaging_tvos/example/bundled-service-worker/yarn.lock new file mode 100644 index 0000000..6a54b94 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/bundled-service-worker/yarn.lock @@ -0,0 +1,844 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@esbuild/aix-ppc64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz#7a01a8d2ec2fbb2dac78adad09b0fa781e4082be" + integrity sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ== + +"@esbuild/android-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz#b540a27d14e4afd058496a4dbec4d3f414db110a" + integrity sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg== + +"@esbuild/android-arm@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.28.1.tgz#704bd297de6d762de54eabbeafbf55f6756abe2f" + integrity sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ== + +"@esbuild/android-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.28.1.tgz#d1cb166d34b0fbf0fe8ab460a5594f24a378701e" + integrity sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng== + +"@esbuild/darwin-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz#1034b26457fc886368fe61bbd09f653f6afa8e54" + integrity sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q== + +"@esbuild/darwin-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz#65556a432a1e4d72032d8218c1932fcca1a49772" + integrity sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ== + +"@esbuild/freebsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz#2e61e0592f9030d7e3dae18ee25ebc535918aef6" + integrity sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw== + +"@esbuild/freebsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz#c95ec289959ef8079c4dca817a1e2c4be66b9bd3" + integrity sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ== + +"@esbuild/linux-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz#40b22175dda06182f3ee8141186c5ff304c4a717" + integrity sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g== + +"@esbuild/linux-arm@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz#c09a0f67917592ac0de892a9be4d3814debd2a6c" + integrity sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ== + +"@esbuild/linux-ia32@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz#a580f9c676797833891e519fc7a1337c8afd8db3" + integrity sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w== + +"@esbuild/linux-loong64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz#46452cf321dc7f9e91c2fa780a56bb56e79cd68b" + integrity sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg== + +"@esbuild/linux-mips64el@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz#4211b3184dd6608f53dcb22e39f5d34ee08852c8" + integrity sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ== + +"@esbuild/linux-ppc64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz#697857c2a61cb9b0b6bb6652e40c1dc5e1ca8e5d" + integrity sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ== + +"@esbuild/linux-riscv64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz#d192943eb146a40ac4c6497d0cf7be35b986bf08" + integrity sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ== + +"@esbuild/linux-s390x@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz#acea0356da0e0ebc08f97cf7b9c2e401e1e648dc" + integrity sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag== + +"@esbuild/linux-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz#6f0c3ce0cb64c534b70c4c45ecb2c16d34e35dfd" + integrity sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA== + +"@esbuild/netbsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz#8bcd77077a0dce3378b574fedb26d2a253b73d36" + integrity sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw== + +"@esbuild/netbsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz#e7fb2a01e99c830c94e6623cd9fefb4c8fb58347" + integrity sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg== + +"@esbuild/openbsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz#c52909372db8b86e2c55e05a8940033b5660a3b2" + integrity sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q== + +"@esbuild/openbsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz#c427b9be5a64c262ff9a7eb70b5fbbaadf446c6c" + integrity sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw== + +"@esbuild/openharmony-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz#dc9b147baca2e6c4b3c85571741ef4860a489097" + integrity sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg== + +"@esbuild/sunos-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz#ce866d12df13c15e4c99f073a3d466f6e0649b3a" + integrity sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ== + +"@esbuild/win32-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz#7468e3692d01d629d5941e5d83817bb80f9e39b4" + integrity sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA== + +"@esbuild/win32-ia32@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz#a5bc0063fb2bcab6d0ed63f2a1537958bc269ec6" + integrity sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg== + +"@esbuild/win32-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz#10064ee44f4347b90c9a02b446bbf80a91632b12" + integrity sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A== + +"@firebase/ai@2.9.0": + version "2.9.0" + resolved "https://registry.npmjs.org/@firebase/ai/-/ai-2.9.0.tgz#9e6f3546eb688e31488f3e081702773300d609f1" + integrity sha512-NPvBBuvdGo9x3esnABAucFYmqbBmXvyTMimBq2PCuLZbdANZoHzGlx7vfzbwNDaEtCBq4RGGNMliLIv6bZ+PtA== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/analytics-compat@0.2.26": + version "0.2.26" + resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.26.tgz#2ec74dc4d41d075d38fab7670c33464803214f2f" + integrity sha512-0j2ruLOoVSwwcXAF53AMoniJKnkwiTjGVfic5LDzqiRkR13vb5j6TXMeix787zbLeQtN/m1883Yv1TxI0gItbA== + dependencies: + "@firebase/analytics" "0.10.20" + "@firebase/analytics-types" "0.8.3" + "@firebase/component" "0.7.1" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/analytics-types@0.8.3": + version "0.8.3" + resolved "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz#d08cd39a6209693ca2039ba7a81570dfa6c1518f" + integrity sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg== + +"@firebase/analytics@0.10.20": + version "0.10.20" + resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.20.tgz#ec3aaacaa157b979b6e2c12ac5a30e6484b19ddf" + integrity sha512-adGTNVUWH5q66tI/OQuKLSN6mamPpfYhj0radlH2xt+3eL6NFPtXoOs+ulvs+UsmK27vNFx5FjRDfWk+TyduHg== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/installations" "0.6.20" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/app-check-compat@0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.4.1.tgz#2ff3f4b28fd4ee136e7ee12b99edac8cdc8cbbb1" + integrity sha512-yjSvSl5B1u4CirnxhzirN1uiTRCRfx+/qtfbyeyI+8Cx8Cw1RWAIO/OqytPSVwLYbJJ1vEC3EHfxazRaMoWKaA== + dependencies: + "@firebase/app-check" "0.11.1" + "@firebase/app-check-types" "0.5.3" + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/app-check-interop-types@0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz#ed9c4a4f48d1395ef378f007476db3940aa5351a" + integrity sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A== + +"@firebase/app-check-types@0.5.3": + version "0.5.3" + resolved "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz#38ba954acf4bffe451581a32fffa20337f11d8e5" + integrity sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng== + +"@firebase/app-check@0.11.1": + version "0.11.1" + resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.11.1.tgz#f327a2190b405eb566a93cd5c7eb8ebe7556032b" + integrity sha512-gmKfwQ2k8aUQlOyRshc+fOQLq0OwUmibIZvpuY1RDNu2ho0aTMlwxOuEiJeYOs7AxzhSx7gnXPFNsXCFbnvXUQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/app-compat@0.5.9": + version "0.5.9" + resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.9.tgz#464efce323951283c6812893d251dddee15d61da" + integrity sha512-e5LzqjO69/N2z7XcJeuMzIp4wWnW696dQeaHAUpQvGk89gIWHAIvG6W+mA3UotGW6jBoqdppEJ9DnuwbcBByug== + dependencies: + "@firebase/app" "0.14.9" + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/app-types@0.9.3": + version "0.9.3" + resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz#8408219eae9b1fb74f86c24e7150a148460414ad" + integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== + +"@firebase/app@0.14.9": + version "0.14.9" + resolved "https://registry.npmjs.org/@firebase/app/-/app-0.14.9.tgz#b7f740904deee2889a3d6115736b16fdbdc853c7" + integrity sha512-3gtUX0e584MYkKBQMgSECMvE1Dwzg+eONefDQ0wxVSe5YMBsZwdN5pL7UapwWBlV8+i8QCztF9TP947tEjZAGA== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/auth-compat@0.6.3": + version "0.6.3" + resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.6.3.tgz#8e085d98bd133081e7e7d37b7fb421b876694847" + integrity sha512-nHOkupcYuGVxI1AJJ/OBhLPaRokbP14Gq4nkkoVvf1yvuREEWqdnrYB/CdsSnPxHMAnn5wJIKngxBF9jNX7s/Q== + dependencies: + "@firebase/auth" "1.12.1" + "@firebase/auth-types" "0.13.0" + "@firebase/component" "0.7.1" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/auth-interop-types@0.2.4": + version "0.2.4" + resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz#176a08686b0685596ff03d7879b7e4115af53de0" + integrity sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA== + +"@firebase/auth-types@0.13.0": + version "0.13.0" + resolved "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz#ae6e0015e3bd4bfe18edd0942b48a0a118a098d9" + integrity sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg== + +"@firebase/auth@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.12.1.tgz#5eb1c3bf99dfbe7025578a5f1439cc073a4183f0" + integrity sha512-nXKj7d5bMBlnq6XpcQQpmnSVwEeHBkoVbY/+Wk0P1ebLSICoH4XPtvKOFlXKfIHmcS84mLQ99fk3njlDGKSDtw== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/component@0.7.1": + version "0.7.1" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.7.1.tgz#f16376146d77034ac5055834de25405e6c011491" + integrity sha512-mFzsm7CLHR60o08S23iLUY8m/i6kLpOK87wdEFPLhdlCahaxKmWOwSVGiWoENYSmFJJoDhrR3gKSCxz7ENdIww== + dependencies: + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/data-connect@0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.4.0.tgz#957d2e0ee602d7120b4c5dbcb8494f911b8a2e47" + integrity sha512-vLXM6WHNIR3VtEeYNUb/5GTsUOyl3Of4iWNZHBe1i9f88sYFnxybJNWVBjvJ7flhCyF8UdxGpzWcUnv6F5vGfg== + dependencies: + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/database-compat@2.1.1": + version "2.1.1" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.1.tgz#8ab656d2f6b53d1645b86fa846295db4734b9ac5" + integrity sha512-heAEVZ9Z8c8PnBUcmGh91JHX0cXcVa1yESW/xkLuwaX7idRFyLiN8sl73KXpR8ZArGoPXVQDanBnk6SQiekRCQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/database" "1.1.1" + "@firebase/database-types" "1.0.17" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/database-types@1.0.17": + version "1.0.17" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.17.tgz#6b7a14d81655e9ee5e87c26dc853c24d9737e4fe" + integrity sha512-4eWaM5fW3qEIHjGzfi3cf0Jpqi1xQsAdT6rSDE1RZPrWu8oGjgrq6ybMjobtyHQFgwGCykBm4YM89qDzc+uG/w== + dependencies: + "@firebase/app-types" "0.9.3" + "@firebase/util" "1.14.0" + +"@firebase/database@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@firebase/database/-/database-1.1.1.tgz#591610b5087ffc25cc56486ad03749b09c887759" + integrity sha512-LwIXe8+mVHY5LBPulWECOOIEXDiatyECp/BOlu0gOhe+WOcKjWHROaCbLlkFTgHMY7RHr5MOxkLP/tltWAH3dA== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + faye-websocket "0.11.4" + tslib "^2.1.0" + +"@firebase/firestore-compat@0.4.6": + version "0.4.6" + resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.4.6.tgz#30a20be30a72e80b0cfa32d5d693564daff6911a" + integrity sha512-NgVyR4hHHN2FvSNQOtbgBOuVsEdD/in30d9FKbEvvITiAChrBN2nBstmhfjI4EOTnHaP8zigwvkNYFI9yKGAkQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/firestore" "4.12.0" + "@firebase/firestore-types" "3.0.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/firestore-types@3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz#7d0c3dd8850c0193d8f5ee0cc8f11961407742c1" + integrity sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q== + +"@firebase/firestore@4.12.0": + version "4.12.0" + resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.12.0.tgz#3321155f66d70c749924c635bb1f0deb92254df3" + integrity sha512-PM47OyiiAAoAMB8kkq4Je14mTciaRoAPDd3ng3Ckqz9i2TX9D9LfxIRcNzP/OxzNV4uBKRq6lXoOggkJBQR3Gw== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + "@firebase/webchannel-wrapper" "1.0.5" + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.8" + tslib "^2.1.0" + +"@firebase/functions-compat@0.4.2": + version "0.4.2" + resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.4.2.tgz#5788b9d33a700164eefd0b4e455de87cd62d635c" + integrity sha512-YNxgnezvZDkqxqXa6cT7/oTeD4WXbxgIP7qZp4LFnathQv5o2omM6EoIhXiT9Ie5AoQDcIhG9Y3/dj+DFJGaGQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/functions" "0.13.2" + "@firebase/functions-types" "0.6.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/functions-types@0.6.3": + version "0.6.3" + resolved "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz#f5faf770248b13f45d256f614230da6a11bfb654" + integrity sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg== + +"@firebase/functions@0.13.2": + version "0.13.2" + resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.13.2.tgz#2e7936898afcdfa391e564e39049e0e908282420" + integrity sha512-tHduUD+DeokM3NB1QbHCvEMoL16e8Z8JSkmuVA4ROoJKPxHn8ibnecHPO2e3nVCJR1D9OjuKvxz4gksfq92/ZQ== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.7.1" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/installations-compat@0.2.20": + version "0.2.20" + resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.20.tgz#f17bcd7623f1283937ac3192c3293dd68037fcdc" + integrity sha512-9C9pL/DIEGucmoPj8PlZTnztbX3nhNj5RTYVpUM7wQq/UlHywaYv99969JU/WHLvi9ptzIogXYS9d1eZ6XFe9g== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/installations" "0.6.20" + "@firebase/installations-types" "0.5.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/installations-types@0.5.3": + version "0.5.3" + resolved "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz#cac8a14dd49f09174da9df8ae453f9b359c3ef2f" + integrity sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA== + +"@firebase/installations@0.6.20": + version "0.6.20" + resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.20.tgz#a019da0e71d5a0bb59b58e43a8edef0153368b94" + integrity sha512-LOzvR7XHPbhS0YB5ANXhqXB5qZlntPpwU/4KFwhSNpXNsGk/sBQ9g5hepi0y0/MfenJLe2v7t644iGOOElQaHQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/util" "1.14.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/logger@0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz#a9e55b1c669a0983dc67127fa4a5964ce8ed5e1b" + integrity sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g== + dependencies: + tslib "^2.1.0" + +"@firebase/messaging-compat@0.2.24": + version "0.2.24" + resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.24.tgz#9ea9bf0d88d605c382dd416e231203310da7b867" + integrity sha512-wXH8FrKbJvFuFe6v98TBhAtvgknxKIZtGM/wCVsfpOGmaAE80bD8tBxztl+uochjnFb9plihkd6mC4y7sZXSpA== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/messaging" "0.12.24" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/messaging-interop-types@0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz#e647c9cd1beecfe6a6e82018a6eec37555e4da3e" + integrity sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q== + +"@firebase/messaging@0.12.24": + version "0.12.24" + resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.24.tgz#ac586f68a038d8595ee8cbaea2a4b60e1886029a" + integrity sha512-UtKoubegAhHyehcB7iQjvQ8OVITThPbbWk3g2/2ze42PrQr6oe6OmCElYQkBrE5RDCeMTNucXejbdulrQ2XwVg== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/installations" "0.6.20" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.14.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/performance-compat@0.2.23": + version "0.2.23" + resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.23.tgz#e4e440878c5be1e11e01d5fe28e5e1fe73d36857" + integrity sha512-c7qOAGBUAOpIuUlHu1axWcrCVtIYKPMhH0lMnoCDWnPwn1HcPuPUBVTWETbC7UWw71RMJF8DpirfWXzMWJQfgA== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/performance" "0.7.10" + "@firebase/performance-types" "0.2.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/performance-types@0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz#5ce64e90fa20ab5561f8b62a305010cf9fab86fb" + integrity sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ== + +"@firebase/performance@0.7.10": + version "0.7.10" + resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.10.tgz#a282de63f064477a62cf0379c3374f3cc693ffa4" + integrity sha512-8nRFld+Ntzp5cLKzZuG9g+kBaSn8Ks9dmn87UQGNFDygbmR6ebd8WawauEXiJjMj1n70ypkvAOdE+lzeyfXtGA== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/installations" "0.6.20" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + web-vitals "^4.2.4" + +"@firebase/remote-config-compat@0.2.22": + version "0.2.22" + resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.22.tgz#5d34d4e856c8a9010e77be5fc2dc183657ade58c" + integrity sha512-uW/eNKKtRBot2gnCC5mnoy5Voo2wMzZuQ7dwqqGHU176fO9zFgMwKiRzk+aaC99NLrFk1KOmr0ZVheD+zdJmjQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/logger" "0.5.0" + "@firebase/remote-config" "0.8.1" + "@firebase/remote-config-types" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/remote-config-types@0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.5.0.tgz#f0f503b32edda3384f5252f9900cd9613adbb99c" + integrity sha512-vI3bqLoF14L/GchtgayMiFpZJF+Ao3uR8WCde0XpYNkSokDpAKca2DxvcfeZv7lZUqkUwQPL2wD83d3vQ4vvrg== + +"@firebase/remote-config@0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.8.1.tgz#47309f3e623d358652878935ac90c880b97ef118" + integrity sha512-L86TReBnPiiJOWd7k9iaiE9f7rHtMpjAoYN0fH2ey2ZRzsOChHV0s5sYf1+IIUYzplzsE46pjlmAUNkRRKwHSQ== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/installations" "0.6.20" + "@firebase/logger" "0.5.0" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/storage-compat@0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.4.1.tgz#94c105a416f949fd1552ced075d2df613e761faa" + integrity sha512-bgl3FHHfXAmBgzIK/Fps6Xyv2HiAQlSTov07CBL+RGGhrC5YIk4lruS8JVIC+UkujRdYvnf8cpQFGn2RCilJ/A== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/storage" "0.14.1" + "@firebase/storage-types" "0.8.3" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/storage-types@0.8.3": + version "0.8.3" + resolved "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz#2531ef593a3452fc12c59117195d6485c6632d3d" + integrity sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg== + +"@firebase/storage@0.14.1": + version "0.14.1" + resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.14.1.tgz#2cdc6523bac9fd85bdd369c77e02a785866d4c02" + integrity sha512-uIpYgBBsv1vIET+5xV20XT7wwqV+H4GFp6PBzfmLUcEgguS4SWNFof56Z3uOC2lNDh0KDda1UflYq2VwD9Nefw== + dependencies: + "@firebase/component" "0.7.1" + "@firebase/util" "1.14.0" + tslib "^2.1.0" + +"@firebase/util@1.14.0": + version "1.14.0" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.14.0.tgz#e0a5998fc30a065fe5cba8bd7546ae8f095f3d3e" + integrity sha512-/gnejm7MKkVIXnSJGpc9L2CvvvzJvtDPeAEq5jAwgVlf/PeNxot+THx/bpD20wQ8uL5sz0xqgXy1nisOYMU+mw== + dependencies: + tslib "^2.1.0" + +"@firebase/webchannel-wrapper@1.0.5": + version "1.0.5" + resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.5.tgz#39cf5a600450cb42f1f0b507cc385459bf103b27" + integrity sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw== + +"@grpc/grpc-js@~1.9.0": + version "1.9.16" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.16.tgz#614f85036ac8e3c957374c1bd1ebb05934a79a1c" + integrity sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" + integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.5.tgz#d9315ad7cf3f30aac70bda3c068443dc6f143659" + integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== + +"@protobufjs/eventemitter@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz#d512cb26c0ae026091ee2c1167f1be6faf5c842a" + integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== + +"@protobufjs/fetch@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.1.tgz#4d6fc00c8fb64016a5c81b469d549046350f1065" + integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.1.tgz#eaee5900122c110a3dbcb728c0597014a2621774" + integrity sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg== + +"@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "18.8.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.2.tgz#17d42c6322d917764dd3d2d3a10d7884925de067" + integrity sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +esbuild@^0.28.1: + version "0.28.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.28.1.tgz#ef45b4634c9c9d97a296aea4114a5f9840f95578" + integrity sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.28.1" + "@esbuild/android-arm" "0.28.1" + "@esbuild/android-arm64" "0.28.1" + "@esbuild/android-x64" "0.28.1" + "@esbuild/darwin-arm64" "0.28.1" + "@esbuild/darwin-x64" "0.28.1" + "@esbuild/freebsd-arm64" "0.28.1" + "@esbuild/freebsd-x64" "0.28.1" + "@esbuild/linux-arm" "0.28.1" + "@esbuild/linux-arm64" "0.28.1" + "@esbuild/linux-ia32" "0.28.1" + "@esbuild/linux-loong64" "0.28.1" + "@esbuild/linux-mips64el" "0.28.1" + "@esbuild/linux-ppc64" "0.28.1" + "@esbuild/linux-riscv64" "0.28.1" + "@esbuild/linux-s390x" "0.28.1" + "@esbuild/linux-x64" "0.28.1" + "@esbuild/netbsd-arm64" "0.28.1" + "@esbuild/netbsd-x64" "0.28.1" + "@esbuild/openbsd-arm64" "0.28.1" + "@esbuild/openbsd-x64" "0.28.1" + "@esbuild/openharmony-arm64" "0.28.1" + "@esbuild/sunos-x64" "0.28.1" + "@esbuild/win32-arm64" "0.28.1" + "@esbuild/win32-ia32" "0.28.1" + "@esbuild/win32-x64" "0.28.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +firebase@12: + version "12.10.0" + resolved "https://registry.npmjs.org/firebase/-/firebase-12.10.0.tgz#2c000e889e8b423ce37399b6a0497cadfba890fe" + integrity sha512-tAjHnEirksqWpa+NKDUSUMjulOnsTcsPC1X1rQ+gwPtjlhJS572na91CwaBXQJHXharIrfj7sw/okDkXOsphjA== + dependencies: + "@firebase/ai" "2.9.0" + "@firebase/analytics" "0.10.20" + "@firebase/analytics-compat" "0.2.26" + "@firebase/app" "0.14.9" + "@firebase/app-check" "0.11.1" + "@firebase/app-check-compat" "0.4.1" + "@firebase/app-compat" "0.5.9" + "@firebase/app-types" "0.9.3" + "@firebase/auth" "1.12.1" + "@firebase/auth-compat" "0.6.3" + "@firebase/data-connect" "0.4.0" + "@firebase/database" "1.1.1" + "@firebase/database-compat" "2.1.1" + "@firebase/firestore" "4.12.0" + "@firebase/firestore-compat" "0.4.6" + "@firebase/functions" "0.13.2" + "@firebase/functions-compat" "0.4.2" + "@firebase/installations" "0.6.20" + "@firebase/installations-compat" "0.2.20" + "@firebase/messaging" "0.12.24" + "@firebase/messaging-compat" "0.2.24" + "@firebase/performance" "0.7.10" + "@firebase/performance-compat" "0.2.23" + "@firebase/remote-config" "0.8.1" + "@firebase/remote-config-compat" "0.2.22" + "@firebase/storage" "0.14.1" + "@firebase/storage-compat" "0.4.1" + "@firebase/util" "1.14.0" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +idb@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +long@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" + integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== + +long@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + +protobufjs@^7.2.5: + version "7.6.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.6.4.tgz#8bb000300026efd63eb7951d26e5dbb38f5658f2" + integrity sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.5" + "@protobufjs/eventemitter" "^1.1.1" + "@protobufjs/fetch" "^1.1.1" + "@protobufjs/float" "^1.0.2" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.1" + "@types/node" ">=13.7.0" + long "^5.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +safe-buffer@>=5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +web-vitals@^4.2.4: + version "4.2.4" + resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7" + integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" diff --git a/packages/firebase_messaging_tvos/example/lib/firebase_options.dart b/packages/firebase_messaging_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..6458196 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/firebase_options.dart @@ -0,0 +1,101 @@ +// Copyright 2022, 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. + +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.messaging', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.messaging', + ); +} diff --git a/packages/firebase_messaging_tvos/example/lib/main.dart b/packages/firebase_messaging_tvos/example/lib/main.dart new file mode 100644 index 0000000..6175abd --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/main.dart @@ -0,0 +1,382 @@ +// Copyright 2019 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 'dart:async'; +import 'dart:convert'; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:http/http.dart' as http; + +import 'firebase_options.dart'; +import 'message.dart'; +import 'message_list.dart'; +import 'permissions.dart'; +import 'token_monitor.dart'; + +/// Working example of FirebaseMessaging. +/// Please use this in order to verify messages are working in foreground, background & terminated state. +/// Setup your app following this guide: +/// https://firebase.google.com/docs/cloud-messaging/flutter/client#platform-specific_setup_and_requirements): +/// +/// Once you've completed platform specific requirements, follow these instructions: +/// 1. Install melos tool by running `flutter pub global activate melos`. +/// 2. Run `melos bootstrap` in FlutterFire project. +/// 3. In your terminal, root to ./packages/firebase_messaging/firebase_messaging/example directory. +/// 4. Run `flutterfire configure` in the example/ directory to setup your app with your Firebase project. +/// 5. Open `token_monitor.dart` and change `vapidKey` to yours. +/// 6. Run the app on an actual device for iOS, android is fine to run on an emulator. +/// 7. Use the following script to send a message to your device: scripts/send-message.js. To run this script, +/// you will need nodejs installed on your computer. Then the following: +/// a. Download a service account key (JSON file) from your Firebase console, rename it to "google-services.json" and add to the example/scripts directory. +/// b. Ensure your device/emulator is running, and run the FirebaseMessaging example app using `flutter run`. +/// c. Copy the token that is printed in the console and paste it here: https://github.com/firebase/flutterfire/blob/01b4d357e1/packages/firebase_messaging/firebase_messaging/example/lib/main.dart#L32 +/// c. From your terminal, root to example/scripts directory & run `npm install`. +/// d. Run `npm run send-message` in the example/scripts directory and your app will receive messages in any state; foreground, background, terminated. +/// Note: Flutter API documentation for receiving messages: https://firebase.google.com/docs/cloud-messaging/flutter/receive +/// Note: If you find your messages have stopped arriving, it is extremely likely they are being throttled by the platform. iOS in particular +/// are aggressive with their throttling policy. +/// +/// To verify that your messages are being received, you ought to see a notification appearon your device/emulator via the flutter_local_notifications plugin. +/// Define a top-level named handler which background/terminated messages will +/// call. Be sure to annotate the handler with `@pragma('vm:entry-point')` above the function declaration. +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + await setupFlutterNotifications(); + showFlutterNotification(message); + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + print('Handling a background message ${message.messageId}'); +} + +/// Create a [AndroidNotificationChannel] for heads up notifications +late AndroidNotificationChannel channel; + +bool isFlutterLocalNotificationsInitialized = false; + +Future setupFlutterNotifications() async { + if (isFlutterLocalNotificationsInitialized) { + return; + } + channel = const AndroidNotificationChannel( + 'high_importance_channel', // id + 'High Importance Notifications', // title + description: + 'This channel is used for important notifications.', // description + importance: Importance.high, + ); + + flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + + /// Create an Android Notification Channel. + /// + /// We use this channel in the `AndroidManifest.xml` file to override the + /// default FCM channel to enable heads up notifications. + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + + /// Update the iOS foreground notification presentation options to allow + /// heads up notifications. + await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( + alert: true, + badge: true, + sound: true, + ); + isFlutterLocalNotificationsInitialized = true; +} + +void showFlutterNotification(RemoteMessage message) { + print('foreground message received: ${message.messageId}'); + RemoteNotification? notification = message.notification; + AndroidNotification? android = message.notification?.android; + if (notification != null && android != null && !kIsWeb) { + flutterLocalNotificationsPlugin.show( + id: notification.hashCode, + title: notification.title, + body: notification.body, + notificationDetails: NotificationDetails( + android: AndroidNotificationDetails( + channel.id, + channel.name, + channelDescription: channel.description, + // TODO add a proper drawable resource to android, for now using + // one that already exists in example app. + icon: 'launch_background', + ), + ), + ); + } +} + +/// Initialize the [FlutterLocalNotificationsPlugin] package. +late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + // Set the background messaging handler early on, as a named top-level function + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + + if (!kIsWeb) { + await setupFlutterNotifications(); + } + + runApp(MessagingExampleApp()); +} + +/// Entry point for the example application. +class MessagingExampleApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Messaging Example App', + theme: ThemeData.dark(), + routes: { + '/': (context) => Application(), + '/message': (context) => MessageView(), + }, + ); + } +} + +// Crude counter to make messages unique +int _messageCount = 0; + +/// The API endpoint here accepts a raw FCM payload for demonstration purposes. +String constructFCMPayload(String? token) { + _messageCount++; + return jsonEncode({ + 'token': token, + 'data': { + 'via': 'FlutterFire Cloud Messaging!!!', + 'count': _messageCount.toString(), + }, + 'notification': { + 'title': 'Hello FlutterFire!', + 'body': 'This notification (#$_messageCount) was created via FCM!', + }, + }); +} + +/// Renders the example application. +class Application extends StatefulWidget { + @override + State createState() => _Application(); +} + +class _Application extends State { + String? _token; + String? initialMessage; + bool _resolved = false; + + @override + void initState() { + super.initState(); + + // Delay getInitialMessage call by 3 seconds + Future.delayed(const Duration(seconds: 3), () { + FirebaseMessaging.instance.getInitialMessage().then( + (value) => setState( + () { + _resolved = true; + initialMessage = value?.data.toString(); + }, + ), + ); + }); + + FirebaseMessaging.onMessage.listen(showFlutterNotification); + + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { + print('A new onMessageOpenedApp event was published!'); + Navigator.pushNamed( + context, + '/message', + arguments: MessageArguments(message, true), + ); + }); + } + + Future sendPushMessage() async { + if (_token == null) { + print('Unable to send FCM message, no token exists.'); + return; + } + + try { + await http.post( + Uri.parse('https://api.rnfirebase.io/messaging/send'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: constructFCMPayload(_token), + ); + print('FCM request for device sent!'); + } catch (e) { + print(e); + } + } + + Future onActionSelected(String value) async { + switch (value) { + case 'subscribe': + { + print( + 'FlutterFire Messaging Example: Subscribing to topic "fcm_test".', + ); + await FirebaseMessaging.instance.subscribeToTopic('fcm_test'); + print( + 'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.', + ); + } + break; + case 'unsubscribe': + { + print( + 'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".', + ); + await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test'); + print( + 'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.', + ); + } + break; + case 'get_apns_token': + { + if (defaultTargetPlatform == TargetPlatform.iOS || + defaultTargetPlatform == TargetPlatform.macOS) { + print('FlutterFire Messaging Example: Getting APNs token...'); + String? token = await FirebaseMessaging.instance.getAPNSToken(); + print('FlutterFire Messaging Example: Got APNs token: $token'); + } else { + print( + 'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.', + ); + } + } + break; + default: + break; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Cloud Messaging'), + actions: [ + PopupMenuButton( + onSelected: onActionSelected, + itemBuilder: (BuildContext context) { + return [ + const PopupMenuItem( + value: 'subscribe', + child: Text('Subscribe to topic'), + ), + const PopupMenuItem( + value: 'unsubscribe', + child: Text('Unsubscribe to topic'), + ), + const PopupMenuItem( + value: 'get_apns_token', + child: Text('Get APNs token (Apple only)'), + ), + ]; + }, + ), + ], + ), + floatingActionButton: Builder( + builder: (context) => FloatingActionButton( + onPressed: sendPushMessage, + backgroundColor: Colors.white, + child: const Icon(Icons.send), + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + MetaCard('Permissions', Permissions()), + MetaCard( + 'Initial Message', + Column( + children: [ + Text(_resolved ? 'Resolved' : 'Resolving'), + Text(initialMessage ?? 'None'), + ], + ), + ), + MetaCard( + 'FCM Token', + TokenMonitor((token) { + _token = token; + return token == null + ? const CircularProgressIndicator() + : SelectableText( + token, + style: const TextStyle(fontSize: 12), + ); + }), + ), + ElevatedButton( + onPressed: () { + FirebaseMessaging.instance + .getInitialMessage() + .then((RemoteMessage? message) { + if (message != null) { + Navigator.pushNamed( + context, + '/message', + arguments: MessageArguments(message, true), + ); + } + }); + }, + child: const Text('getInitialMessage()'), + ), + MetaCard('Message Stream', MessageList()), + ], + ), + ), + ); + } +} + +/// UI Widget for displaying metadata. +class MetaCard extends StatelessWidget { + final String _title; + final Widget _children; + + // ignore: public_member_api_docs + MetaCard(this._title, this._children); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 8, right: 8, top: 8), + child: Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Container( + margin: const EdgeInsets.only(bottom: 16), + child: Text(_title, style: const TextStyle(fontSize: 18)), + ), + _children, + ], + ), + ), + ), + ); + } +} diff --git a/packages/firebase_messaging_tvos/example/lib/message.dart b/packages/firebase_messaging_tvos/example/lib/message.dart new file mode 100644 index 0000000..ce2a38c --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/message.dart @@ -0,0 +1,163 @@ +// Copyright 2022, 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. + +// ignore_for_file: require_trailing_commas + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; + +/// Message route arguments. +class MessageArguments { + /// The RemoteMessage + final RemoteMessage message; + + /// Whether this message caused the application to open. + final bool openedApplication; + + // ignore: public_member_api_docs + MessageArguments(this.message, this.openedApplication); +} + +/// Displays information about a [RemoteMessage]. +class MessageView extends StatelessWidget { + /// A single data row. + Widget row(String title, String? value) { + return Padding( + padding: const EdgeInsets.only(left: 8, right: 8, top: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('$title: '), + Expanded(child: Text(value ?? 'N/A')), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + final MessageArguments args = + ModalRoute.of(context)!.settings.arguments! as MessageArguments; + RemoteMessage message = args.message; + RemoteNotification? notification = message.notification; + + return Scaffold( + appBar: AppBar( + title: Text(message.messageId ?? 'N/A'), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + children: [ + row('Triggered application open', + args.openedApplication.toString()), + row('Message ID', message.messageId), + row('Sender ID', message.senderId), + row('Category', message.category), + row('Collapse Key', message.collapseKey), + row('Content Available', message.contentAvailable.toString()), + row('Data', message.data.toString()), + row('From', message.from), + row('Message ID', message.messageId), + row('Sent Time', message.sentTime?.toString()), + row('Thread ID', message.threadId), + row('Time to Live (TTL)', message.ttl?.toString()), + if (notification != null) ...[ + Padding( + padding: const EdgeInsets.only(top: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Remote Notification', + style: TextStyle(fontSize: 18), + ), + row( + 'Title', + notification.title, + ), + row( + 'Body', + notification.body, + ), + if (notification.android != null) ...[ + const SizedBox(height: 16), + const Text( + 'Android Properties', + style: TextStyle(fontSize: 18), + ), + row( + 'Channel ID', + notification.android!.channelId, + ), + row( + 'Click Action', + notification.android!.clickAction, + ), + row( + 'Color', + notification.android!.color, + ), + row( + 'Count', + notification.android!.count?.toString(), + ), + row( + 'Image URL', + notification.android!.imageUrl, + ), + row( + 'Link', + notification.android!.link, + ), + row( + 'Priority', + notification.android!.priority.toString(), + ), + row( + 'Small Icon', + notification.android!.smallIcon, + ), + row( + 'Sound', + notification.android!.sound, + ), + row( + 'Ticker', + notification.android!.ticker, + ), + row( + 'Visibility', + notification.android!.visibility.toString(), + ), + ], + if (notification.apple != null) ...[ + const Text( + 'Apple Properties', + style: TextStyle(fontSize: 18), + ), + row( + 'Subtitle', + notification.apple!.subtitle, + ), + row( + 'Badge', + notification.apple!.badge, + ), + row( + 'Sound', + notification.apple!.sound?.name, + ), + ] + ], + ), + ) + ] + ], + ), + )), + ); + } +} diff --git a/packages/firebase_messaging_tvos/example/lib/message_list.dart b/packages/firebase_messaging_tvos/example/lib/message_list.dart new file mode 100644 index 0000000..7411ba5 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/message_list.dart @@ -0,0 +1,53 @@ +// Copyright 2022, 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. + +// ignore_for_file: require_trailing_commas + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; + +import 'message.dart'; + +/// Listens for incoming foreground messages and displays them in a list. +class MessageList extends StatefulWidget { + @override + State createState() => _MessageList(); +} + +class _MessageList extends State { + List _messages = []; + + @override + void initState() { + super.initState(); + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + setState(() { + _messages = [..._messages, message]; + }); + }); + } + + @override + Widget build(BuildContext context) { + if (_messages.isEmpty) { + return const Text('No messages received'); + } + + return ListView.builder( + shrinkWrap: true, + itemCount: _messages.length, + itemBuilder: (context, index) { + RemoteMessage message = _messages[index]; + + return ListTile( + title: Text( + message.messageId ?? 'no RemoteMessage.messageId available'), + subtitle: + Text(message.sentTime?.toString() ?? DateTime.now().toString()), + onTap: () => Navigator.pushNamed(context, '/message', + arguments: MessageArguments(message, false)), + ); + }); + } +} diff --git a/packages/firebase_messaging_tvos/example/lib/permissions.dart b/packages/firebase_messaging_tvos/example/lib/permissions.dart new file mode 100644 index 0000000..2717a6a --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/permissions.dart @@ -0,0 +1,126 @@ +// Copyright 2022, 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. + +// ignore_for_file: require_trailing_commas + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Requests & displays the current user permissions for this device. +class Permissions extends StatefulWidget { + @override + State createState() => _Permissions(); +} + +class _Permissions extends State { + bool _requested = false; + bool _fetching = false; + late NotificationSettings _settings; + + Future requestPermissions() async { + setState(() { + _fetching = true; + }); + + NotificationSettings settings = + await FirebaseMessaging.instance.requestPermission( + announcement: true, + carPlay: true, + criticalAlert: true, + ); + + setState(() { + _requested = true; + _fetching = false; + _settings = settings; + }); + } + + Future checkPermissions() async { + setState(() { + _fetching = true; + }); + + NotificationSettings settings = + await FirebaseMessaging.instance.getNotificationSettings(); + + setState(() { + _requested = true; + _fetching = false; + _settings = settings; + }); + } + + Widget row(String title, String value) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Expanded( + child: Text( + '$title:', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + Text(value), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + if (_fetching) { + return const CircularProgressIndicator(); + } + + if (!_requested) { + return ElevatedButton( + onPressed: requestPermissions, + child: const Text('Request Permissions')); + } + + return Column(children: [ + row('Authorization Status', statusMap[_settings.authorizationStatus]!), + if (defaultTargetPlatform == TargetPlatform.iOS) ...[ + row('Alert', settingsMap[_settings.alert]!), + row('Announcement', settingsMap[_settings.announcement]!), + row('Badge', settingsMap[_settings.badge]!), + row('Car Play', settingsMap[_settings.carPlay]!), + row('Lock Screen', settingsMap[_settings.lockScreen]!), + row('Notification Center', settingsMap[_settings.notificationCenter]!), + row('Show Previews', previewMap[_settings.showPreviews]!), + row('Sound', settingsMap[_settings.sound]!), + row('Provides App Notification Settings', + settingsMap[_settings.providesAppNotificationSettings]!), + ], + ElevatedButton( + onPressed: checkPermissions, child: const Text('Reload Permissions')), + ]); + } +} + +/// Maps a [AuthorizationStatus] to a string value. +const statusMap = { + AuthorizationStatus.authorized: 'Authorized', + AuthorizationStatus.denied: 'Denied', + AuthorizationStatus.notDetermined: 'Not Determined', + AuthorizationStatus.provisional: 'Provisional', +}; + +/// Maps a [AppleNotificationSetting] to a string value. +const settingsMap = { + AppleNotificationSetting.disabled: 'Disabled', + AppleNotificationSetting.enabled: 'Enabled', + AppleNotificationSetting.notSupported: 'Not Supported', +}; + +/// Maps a [AppleShowPreviewSetting] to a string value. +const previewMap = { + AppleShowPreviewSetting.always: 'Always', + AppleShowPreviewSetting.never: 'Never', + AppleShowPreviewSetting.notSupported: 'Not Supported', + AppleShowPreviewSetting.whenAuthenticated: 'Only When Authenticated', +}; diff --git a/packages/firebase_messaging_tvos/example/lib/token_monitor.dart b/packages/firebase_messaging_tvos/example/lib/token_monitor.dart new file mode 100644 index 0000000..6fd6daf --- /dev/null +++ b/packages/firebase_messaging_tvos/example/lib/token_monitor.dart @@ -0,0 +1,50 @@ +// Copyright 2022, 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. + +// ignore_for_file: require_trailing_commas + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; + +/// Manages & returns the users FCM token. +/// +/// Also monitors token refreshes and updates state. +class TokenMonitor extends StatefulWidget { + // ignore: public_member_api_docs + TokenMonitor(this._builder); + + final Widget Function(String? token) _builder; + + @override + State createState() => _TokenMonitor(); +} + +class _TokenMonitor extends State { + String? _token; + late Stream _tokenStream; + + void setToken(String? token) { + print('FCM Token: $token'); + setState(() { + _token = token; + }); + } + + @override + void initState() { + super.initState(); + FirebaseMessaging.instance + .getToken( + vapidKey: + 'BNKkaUWxyP_yC_lki1kYazgca0TNhuzt2drsOrL6WrgGbqnMnr8ZMLzg_rSPDm6HKphABS0KzjPfSqCXHXEd06Y') + .then(setToken); + _tokenStream = FirebaseMessaging.instance.onTokenRefresh; + _tokenStream.listen(setToken); + } + + @override + Widget build(BuildContext context) { + return widget._builder(_token); + } +} diff --git a/packages/firebase_messaging_tvos/example/pubspec.yaml b/packages/firebase_messaging_tvos/example/pubspec.yaml new file mode 100644 index 0000000..8ce5f66 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/pubspec.yaml @@ -0,0 +1,21 @@ +name: firebase_messaging_example +description: Demonstrates how to use the firebase_messaging plugin. + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + firebase_messaging: ^16.4.1 + firebase_messaging_tvos: + path: ../ + firebase_core: ^4.11.0 + firebase_core_tvos: + path: ../../firebase_core_tvos + flutter: + sdk: flutter + flutter_local_notifications: ^21.0.0 + http: ^1.0.0 + +flutter: + uses-material-design: true diff --git a/packages/firebase_messaging_tvos/example/tvos/.gitignore b/packages/firebase_messaging_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/firebase_messaging_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Flutter/Release.xcconfig b/packages/firebase_messaging_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Podfile b/packages/firebase_messaging_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8d00eff --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 */; }; + B41E4899F3D725B77E6A8C3C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6950A401BA294DDC3404D3DA /* Pods_Runner.framework */; }; +/* 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 = ""; }; + 5B8F8CF0700F160ABCB8A7A5 /* 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 = ""; }; + 6950A401BA294DDC3404D3DA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 7ECAE9FEA8E0C047419654E7 /* 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 = ""; }; + 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 = ""; }; + F964A17321D6D60E2B889609 /* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000082B4168C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AAF20000000000000000F00D /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + B41E4899F3D725B77E6A8C3C /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 97C146E51CF9000082B4168C = { + isa = PBXGroup; + children = ( + 97C146F01CF9000082B4168C /* Runner */, + 97C146F01CF9000082B4168E /* Flutter */, + 97C146F01CF9000082B4168F /* Frameworks */, + 97C146EF1CF9000082B41690 /* Products */, + FEB91E483EA548FD809A5177 /* 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 */, + 6950A401BA294DDC3404D3DA /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + FEB91E483EA548FD809A5177 /* Pods */ = { + isa = PBXGroup; + children = ( + 7ECAE9FEA8E0C047419654E7 /* Pods-Runner.debug.xcconfig */, + 5B8F8CF0700F160ABCB8A7A5 /* Pods-Runner.release.xcconfig */, + F964A17321D6D60E2B889609 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000082B41690 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000082B4168C /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 6B0809AD79CEB1B1D3A628EC /* [CP] Check Pods Manifest.lock */, + 97C146EA1CF9000082B4168C /* Sources */, + 97C146EB1CF9000082B4168C /* Frameworks */, + 97C146EC1CF9000082B4168C /* Resources */, + AAF10000000000000000F00D /* Embed App.framework */, + 9740EEB31CF901A200538489 /* Copy flutter_assets */, + E5F3C5056185DD5DE58D1ECB /* [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 */ + 6B0809AD79CEB1B1D3A628EC /* [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; + }; + 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"; + }; + E5F3C5056185DD5DE58D1ECB /* [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; + }; +/* 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.firebaseMessagingExample; + 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)"; + 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.firebaseMessagingExample; + 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.firebaseMessagingExample; + 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_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ee3561d --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/AppDelegate.swift b/packages/firebase_messaging_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/firebase_messaging_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/firebase_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/firebase_messaging_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_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/firebase_messaging_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_messaging_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/firebase_messaging_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/Info.plist b/packages/firebase_messaging_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..90c136f --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Firebase_messaging_example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + firebase_messaging_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + + FLTAssetsPath + flutter_assets + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/packages/firebase_messaging_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/firebase_messaging_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/firebase_messaging_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_messaging_tvos/lib/firebase_messaging_tvos.dart b/packages/firebase_messaging_tvos/lib/firebase_messaging_tvos.dart new file mode 100644 index 0000000..93db3e7 --- /dev/null +++ b/packages/firebase_messaging_tvos/lib/firebase_messaging_tvos.dart @@ -0,0 +1,14 @@ +// Copyright 2020 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. + +// Same reasoning as firebase_core_tvos: firebase_messaging's public Dart +// API (FirebaseMessaging, RemoteMessage, …) has no per-platform Dart +// override — it talks to native through firebase_messaging_platform_interface's +// MethodChannel implementation regardless of platform. Duplicating it here +// would create incompatible types vs. apps that import +// package:firebase_messaging/firebase_messaging.dart directly. This package +// only supplies the native tvOS pluginClass (tvos/Classes/); apps depend on +// firebase_messaging (Dart API) and firebase_messaging_tvos (native +// registration) side by side — see example/. +export 'package:firebase_messaging/firebase_messaging.dart'; diff --git a/packages/firebase_messaging_tvos/pubspec.yaml b/packages/firebase_messaging_tvos/pubspec.yaml new file mode 100644 index 0000000..3550a69 --- /dev/null +++ b/packages/firebase_messaging_tvos/pubspec.yaml @@ -0,0 +1,44 @@ +name: firebase_messaging_tvos +description: >- + tvOS (Apple TV) implementation of the firebase_messaging Flutter plugin, + providing Firebase Cloud Messaging push notifications on Apple TV. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/firebase_messaging_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`. See PORTING_REPORT.md. +# License holder: fluttertv + +# The example ships the standard FlutterFire demo-project GoogleService +# values (client-side Firebase identifiers, not secrets). Tell pub's secret +# scanner they are intentional, matching upstream firebase_messaging's pubspec. +false_secrets: + - example/** + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + firebase_messaging: ^16.4.1 + # Transitive Dart dependency so the app's dependency graph includes + # firebase_core_tvos — our 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 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: FLTFirebaseMessagingPlugin diff --git a/packages/firebase_messaging_tvos/test/firebase_messaging_tvos_test.dart b/packages/firebase_messaging_tvos/test/firebase_messaging_tvos_test.dart new file mode 100644 index 0000000..73962c6 --- /dev/null +++ b/packages/firebase_messaging_tvos/test/firebase_messaging_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-06-30 by `flutter-tvos plugin port`. +// Source plugin: firebase_messaging + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/firebase_messaging_tvos/tvos/Classes/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging_tvos/tvos/Classes/FLTFirebaseMessagingPlugin.m new file mode 100644 index 0000000..84ce978 --- /dev/null +++ b/packages/firebase_messaging_tvos/tvos/Classes/FLTFirebaseMessagingPlugin.m @@ -0,0 +1,1323 @@ +// Copyright 2020 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 + +#import +#if __has_include() +#import +#else +#import "FLTFirebasePluginRegistry.h" +#endif +#import + +#import "FLTFirebaseMessagingPlugin.h" + +#if __has_include() +@import FirebaseAuth; +#endif + +NSString *const kFLTFirebaseMessagingChannelName = @"plugins.flutter.io/firebase_messaging"; + +NSString *const kMessagingArgumentCode = @"code"; +NSString *const kMessagingArgumentMessage = @"message"; +NSString *const kMessagingArgumentAdditionalData = @"additionalData"; +NSString *const kMessagingPresentationOptionsUserDefaults = + @"flutter_firebase_messaging_presentation_options"; + +@implementation FLTFirebaseMessagingPlugin { + FlutterMethodChannel *_channel; + NSObject *_registrar; + NSData *_apnsToken; + NSDictionary *_initialNotification; + + // Used to track if everything as been initialized before answering + // to the initialNotification request + BOOL _initialNotificationGathered; + FLTFirebaseMethodCallResult *_initialNotificationResult; + + NSString *_initialNotificationID; + NSString *_notificationOpenedAppID; + NSString *_foregroundUniqueIdentifier; + + // Track if scene delegate connected (for iOS 13+ scene delegate support) + BOOL _sceneDidConnect; + + // Guard against calling setupNotificationHandling twice + BOOL _notificationHandlingSetup; + +#ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM + API_AVAILABLE(ios(10), tvos(10), macosx(10.14)) + __weak id _originalNotificationCenterDelegate; + API_AVAILABLE(ios(10), tvos(10), macosx(10.14)) + struct { + unsigned int willPresentNotification : 1; + unsigned int didReceiveNotificationResponse : 1; + unsigned int openSettingsForNotification : 1; + } _originalNotificationCenterDelegateRespondsTo; +#endif +} + +#pragma mark - FlutterPlugin + +- (instancetype)initWithFlutterMethodChannel:(FlutterMethodChannel *)channel + andFlutterPluginRegistrar:(NSObject *)registrar { + self = [super init]; + if (self) { + _initialNotificationGathered = NO; + _sceneDidConnect = NO; + _notificationHandlingSetup = NO; + _channel = channel; + _registrar = registrar; + // Application + // Dart -> `getInitialNotification` + // ObjC -> Initialize other delegates & observers + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(application_onDidFinishLaunchingNotification:) +#if TARGET_OS_OSX + name:NSApplicationDidFinishLaunchingNotification +#else + name:UIApplicationDidFinishLaunchingNotification +#endif + object:nil]; + } + return self; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:kFLTFirebaseMessagingChannelName + binaryMessenger:[registrar messenger]]; + id instance = [[FLTFirebaseMessagingPlugin alloc] initWithFlutterMethodChannel:channel + andFlutterPluginRegistrar:registrar]; + // Register with internal FlutterFire plugin registry. + [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:instance]; + + [registrar addMethodCallDelegate:instance channel:channel]; +#if !TARGET_OS_OSX + [registrar publish:instance]; // iOS only supported + if (@available(iOS 13.0, tvOS 13.0, *)) { + if ([registrar respondsToSelector:@selector(addSceneDelegate:)]) { + [registrar performSelector:@selector(addSceneDelegate:) withObject:instance]; + } + } +#endif +} + +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutterResult { + FLTFirebaseMethodCallErrorBlock errorBlock = ^( + NSString *_Nullable code, NSString *_Nullable message, NSDictionary *_Nullable details, + NSError *_Nullable error) { + if (code == nil) { + NSDictionary *errorDetails = [self NSDictionaryForNSError:error]; + code = errorDetails[kMessagingArgumentCode]; + message = errorDetails[kMessagingArgumentMessage]; + details = errorDetails; + } else { + details = @{ + kMessagingArgumentCode : code, + kMessagingArgumentMessage : message, + }; + } + + if ([@"unknown" isEqualToString:code]) { + NSLog(@"FLTFirebaseMessaging: An error occurred while calling method %@, errorOrNil => %@", + call.method, [error userInfo]); + } + + flutterResult([FLTFirebasePlugin createFlutterErrorFromCode:code + message:message + optionalDetails:details + andOptionalNSError:error]); + }; + + FLTFirebaseMethodCallResult *methodCallResult = + [FLTFirebaseMethodCallResult createWithSuccess:flutterResult andErrorBlock:errorBlock]; + + [self ensureAPNSTokenSetting]; + + if ([@"Messaging#getInitialMessage" isEqualToString:call.method]) { + _initialNotificationResult = methodCallResult; + [self initialNotificationCallback]; + + } else if ([@"Messaging#deleteToken" isEqualToString:call.method]) { + [self messagingDeleteToken:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#getAPNSToken" isEqualToString:call.method]) { + [self messagingGetAPNSToken:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#setForegroundNotificationPresentationOptions" + isEqualToString:call.method]) { + [self messagingSetForegroundNotificationPresentationOptions:call.arguments + withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#getToken" isEqualToString:call.method]) { + [self messagingGetToken:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#getNotificationSettings" isEqualToString:call.method]) { + if (@available(iOS 10, tvOS 10, macOS 10.14, *)) { + [self messagingGetNotificationSettings:call.arguments withMethodCallResult:methodCallResult]; + } else { + // Defaults handled in Dart. + methodCallResult.success(@{}); + } + } else if ([@"Messaging#requestPermission" isEqualToString:call.method]) { + if (@available(iOS 10, tvOS 10, macOS 10.14, *)) { + [self messagingRequestPermission:call.arguments withMethodCallResult:methodCallResult]; + } else { + // Defaults handled in Dart. + methodCallResult.success(@{}); + } + } else if ([@"Messaging#setAutoInitEnabled" isEqualToString:call.method]) { + [self messagingSetAutoInitEnabled:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#subscribeToTopic" isEqualToString:call.method]) { + [self messagingSubscribeToTopic:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#unsubscribeFromTopic" isEqualToString:call.method]) { + [self messagingUnsubscribeFromTopic:call.arguments withMethodCallResult:methodCallResult]; + } else if ([@"Messaging#startBackgroundIsolate" isEqualToString:call.method]) { + methodCallResult.success(nil); + } else { + methodCallResult.success(FlutterMethodNotImplemented); + } +} +- (void)messagingSetForegroundNotificationPresentationOptions:(id)arguments + withMethodCallResult: + (FLTFirebaseMethodCallResult *)result { + NSMutableDictionary *persistedOptions = [NSMutableDictionary dictionary]; + if ([arguments[@"alert"] isEqual:@(YES)]) { + persistedOptions[@"alert"] = @YES; + } + if ([arguments[@"badge"] isEqual:@(YES)]) { + persistedOptions[@"badge"] = @YES; + } + if ([arguments[@"sound"] isEqual:@(YES)]) { + persistedOptions[@"sound"] = @YES; + } + + [[NSUserDefaults standardUserDefaults] setObject:persistedOptions + forKey:kMessagingPresentationOptionsUserDefaults]; + result.success(nil); +} + +#pragma mark - Firebase Messaging Delegate + +- (void)messaging:(nonnull FIRMessaging *)messaging + didReceiveRegistrationToken:(nullable NSString *)fcmToken { + // Don't crash if the token is reset. + if (fcmToken == nil) { + return; + } + + // Send to Dart. + [_channel invokeMethod:@"Messaging#onTokenRefresh" arguments:fcmToken]; + + // If the users AppDelegate implements messaging:didReceiveRegistrationToken: then call it as well + // so we don't break other libraries. + SEL messaging_didReceiveRegistrationTokenSelector = + NSSelectorFromString(@"messaging:didReceiveRegistrationToken:"); + if ([[GULAppDelegateSwizzler sharedApplication].delegate + respondsToSelector:messaging_didReceiveRegistrationTokenSelector]) { + void (*usersDidReceiveRegistrationTokenIMP)(id, SEL, FIRMessaging *, NSString *) = + (typeof(usersDidReceiveRegistrationTokenIMP))&objc_msgSend; + usersDidReceiveRegistrationTokenIMP([GULAppDelegateSwizzler sharedApplication].delegate, + messaging_didReceiveRegistrationTokenSelector, messaging, + fcmToken); + } +} + +#pragma mark - NSNotificationCenter Observers + +- (void)setupNotificationHandlingWithRemoteNotification: + (nullable NSDictionary *)remoteNotification { + [self setupNotificationHandlingWithRemoteNotification:remoteNotification actionIdentifier:nil]; +} + +- (void)setupNotificationHandlingWithRemoteNotification:(nullable NSDictionary *)remoteNotification + actionIdentifier:(nullable NSString *)actionIdentifier { + // If notification handling was already set up (e.g. from + // application_onDidFinishLaunchingNotification) and we're called again (e.g. from + // scene:willConnectToSession:), only process the notification but skip delegate/swizzler + // re-registration to avoid _originalNotificationCenterDelegate being set to self, which causes + // infinite recursion in didReceiveNotificationResponse. See #18037. + if (_notificationHandlingSetup) { + if (remoteNotification != nil) { + _initialNotification = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:actionIdentifier]; + _initialNotificationID = remoteNotification[@"gcm.message_id"]; + _initialNotificationGathered = YES; + [self initialNotificationCallback]; + } else if (_sceneDidConnect && !_initialNotificationGathered) { + // Scene connected with no notification — delay to allow didReceiveRemoteNotification + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (!self->_initialNotificationGathered) { + self->_initialNotificationGathered = YES; + [self initialNotificationCallback]; + } + }); + } + return; + } + _notificationHandlingSetup = YES; + + if (remoteNotification != nil) { + // If remoteNotification exists, it is the notification that opened the app. + _initialNotification = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:actionIdentifier]; + _initialNotificationID = remoteNotification[@"gcm.message_id"]; + _initialNotificationGathered = YES; + [self initialNotificationCallback]; + } else if (_sceneDidConnect) { + // For scene delegates, if no notification was found in connectionOptions, + // delay marking as gathered to allow didReceiveRemoteNotification to fire first + // for contentAvailable notifications that caused the app to launch + [self markInitialNotificationGatheredAfterDelay]; + } else { +#if !TARGET_OS_OSX + if (@available(iOS 13.0, tvOS 13.0, *)) { + // Scene delegate launch notification responses arrive after didFinishLaunching. + // Give scene:willConnectToSession:options: a chance to provide the tapped notification + // before resolving getInitialMessage() as nil. + [self markInitialNotificationGatheredAfterDelay]; + } else { +#endif + // For non-scene delegate apps, mark as gathered immediately + _initialNotificationGathered = YES; + [self initialNotificationCallback]; +#if !TARGET_OS_OSX + } +#endif + } + + [GULAppDelegateSwizzler registerAppDelegateInterceptor:self]; + [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; + + SEL didReceiveRemoteNotificationWithCompletionSEL = + NSSelectorFromString(@"application:didReceiveRemoteNotification:fetchCompletionHandler:"); + if ([[GULAppDelegateSwizzler sharedApplication].delegate + respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { + // noop - user has own implementation of this method in their AppDelegate, this + // means GULAppDelegateSwizzler will have already replaced it with a donor method + } else { + // add our own donor implementation of + // application:didReceiveRemoteNotification:fetchCompletionHandler: + Method donorMethod = class_getInstanceMethod(object_getClass(self), + didReceiveRemoteNotificationWithCompletionSEL); + class_addMethod(object_getClass([GULAppDelegateSwizzler sharedApplication].delegate), + didReceiveRemoteNotificationWithCompletionSEL, + method_getImplementation(donorMethod), method_getTypeEncoding(donorMethod)); + } +#if !TARGET_OS_OSX + // `[_registrar addApplicationDelegate:self];` alone doesn't work for notifications to be received + // without the above swizzling This commit: + // https://github.com/google/GoogleUtilities/pull/162/files#diff-6bb6d1c46632fc66405a524071cc4baca5fc6a1a6c0eefef81d8c3e2c89cbc13L520-L533 + // broke notifications which was released with firebase-ios-sdk v11.0.0 + [_registrar addApplicationDelegate:self]; +#endif + + // Set UNUserNotificationCenter but preserve original delegate if necessary. + if (@available(iOS 10.0, tvOS 10.0, macOS 10.14, *)) { + BOOL shouldReplaceDelegate = YES; + UNUserNotificationCenter *notificationCenter = + [UNUserNotificationCenter currentNotificationCenter]; + + if (notificationCenter.delegate != nil) { +#if !TARGET_OS_OSX + // If a UNUserNotificationCenterDelegate is set and it conforms to + // FlutterAppLifeCycleProvider then we don't want to replace it on iOS as the earlier + // call to `[_registrar addApplicationDelegate:self];` will automatically delegate calls + // to this plugin. If we replace it, it will cause a stack overflow as our original + // delegate forwarding handler below causes an infinite loop of forwarding. See + // https://github.com/firebasefire/issues/4026. + if ([notificationCenter.delegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) { + // Note this one only executes if Firebase swizzling is **enabled**. + shouldReplaceDelegate = NO; + } +#endif + + if (shouldReplaceDelegate) { + _originalNotificationCenterDelegate = notificationCenter.delegate; + _originalNotificationCenterDelegateRespondsTo.openSettingsForNotification = + (unsigned int)[_originalNotificationCenterDelegate + respondsToSelector:@selector(userNotificationCenter:openSettingsForNotification:)]; + _originalNotificationCenterDelegateRespondsTo.willPresentNotification = + (unsigned int)[_originalNotificationCenterDelegate + respondsToSelector:@selector(userNotificationCenter:willPresentNotification: + withCompletionHandler:)]; + _originalNotificationCenterDelegateRespondsTo.didReceiveNotificationResponse = + (unsigned int)[_originalNotificationCenterDelegate + respondsToSelector:@selector(userNotificationCenter:didReceiveNotificationResponse: + withCompletionHandler:)]; + } + } + + if (shouldReplaceDelegate) { + __strong FLTFirebasePlugin *strongSelf = self; + notificationCenter.delegate = strongSelf; + } + } + + // We automatically register for remote notifications as + // application:didReceiveRemoteNotification:fetchCompletionHandler: will not get called unless + // registerForRemoteNotifications is called early on during app initialization, calling this from + // Dart would be too late. +#if TARGET_OS_OSX + if (@available(macOS 10.14, *)) { + [[NSApplication sharedApplication] registerForRemoteNotifications]; + } +#else + [[UIApplication sharedApplication] registerForRemoteNotifications]; +#endif +} + +- (void)markInitialNotificationGatheredAfterDelay { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (!self->_initialNotificationGathered) { + self->_initialNotificationGathered = YES; + [self initialNotificationCallback]; + } + }); +} + +- (void)application_onDidFinishLaunchingNotification:(nonnull NSNotification *)notification { + // Setup UIApplicationDelegate. +#if TARGET_OS_OSX + NSDictionary *remoteNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey]; +#elif TARGET_OS_TV + // UIApplicationLaunchOptionsRemoteNotificationKey is unavailable on tvOS + // (no notification-driven app launch path there). + NSDictionary *remoteNotification = nil; +#else + NSDictionary *remoteNotification = + notification.userInfo[UIApplicationLaunchOptionsRemoteNotificationKey]; +#endif + [self setupNotificationHandlingWithRemoteNotification:remoteNotification]; +} + +#pragma mark - UNUserNotificationCenter Delegate Methods + +#ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM +// Called when a notification is received whilst the app is in the foreground. +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler + API_AVAILABLE(macos(10.14), ios(10.0), tvos(10.0)) { + // We only want to handle FCM notifications. + + // FIX - bug on iOS 18 which results in duplicate foreground notifications posted + // See this Apple issue: https://forums.developer.apple.com/forums/thread/761597 + // when it has been resolved, "_foregroundUniqueIdentifier" can be removed (i.e. the commit for + // this fix) + // + // UNNotificationContent.userInfo (and therefore the FCM payload / message + // ID it carries) is unavailable on tvOS, so the foreground-message + // forwarding below (Messaging#onMessage) can't run there — the delegate + // method itself still fires (used only to control presentation options). +#if !TARGET_OS_TV + NSDictionary *userInfo = notification.request.content.userInfo; + NSString *messageID = userInfo[@"gcm.message_id"]; + + BOOL shouldCheckForDuplicate = NO; +#if !TARGET_OS_OSX + if (@available(iOS 18.0, tvOS 18.0, *)) { + if (!@available(iOS 18.1, tvOS 18.1, *)) { + // Only iOS 18.0 specifically + shouldCheckForDuplicate = [messageID isEqualToString:_foregroundUniqueIdentifier]; + } + } +#endif + + if (messageID && !shouldCheckForDuplicate) { + NSDictionary *notificationDict = + [FLTFirebaseMessagingPlugin NSDictionaryFromUNNotification:notification]; + [_channel invokeMethod:@"Messaging#onMessage" arguments:notificationDict]; + } +#endif // !TARGET_OS_TV + + // Forward on to any other delegates amd allow them to control presentation behavior. + if (_originalNotificationCenterDelegate != nil && + _originalNotificationCenterDelegateRespondsTo.willPresentNotification) { + [_originalNotificationCenterDelegate userNotificationCenter:center + willPresentNotification:notification + withCompletionHandler:completionHandler]; + } else { + UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionNone; + NSDictionary *persistedOptions = [[NSUserDefaults standardUserDefaults] + dictionaryForKey:kMessagingPresentationOptionsUserDefaults]; + if (persistedOptions != nil) { + if ([persistedOptions[@"alert"] isEqual:@(YES)]) { + presentationOptions |= UNNotificationPresentationOptionAlert; + } + if ([persistedOptions[@"badge"] isEqual:@(YES)]) { + presentationOptions |= UNNotificationPresentationOptionBadge; + } + if ([persistedOptions[@"sound"] isEqual:@(YES)]) { + presentationOptions |= UNNotificationPresentationOptionSound; + } + } + completionHandler(presentationOptions); + } + + // Store notification identifier for iOS 18.0 duplicate detection + // (messageID is only defined in the !TARGET_OS_TV block above). +#if !TARGET_OS_OSX && !TARGET_OS_TV + if (@available(iOS 18.0, *)) { + if (!@available(iOS 18.1, *)) { + _foregroundUniqueIdentifier = messageID; + } + } +#endif +} + +// Called when a user interacts with a notification. +// Disabled on tvOS by hand: despite the porter widening this method's +// @available annotation to claim tvos(10.0), the UNUserNotificationCenter +// Delegate protocol itself marks +// userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: +// (and UNNotificationResponse / -userInfo on its content, used below) +// API_UNAVAILABLE(tvos) in the system headers — tvOS doesn't support +// interactive notification responses (no "tap" gesture model for +// notifications the way iOS/macOS have). +#if !TARGET_OS_TV +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void (^)(void))completionHandler + API_AVAILABLE(macos(10.14), ios(10.0)) { + NSDictionary *remoteNotification = response.notification.request.content.userInfo; + _notificationOpenedAppID = remoteNotification[@"gcm.message_id"]; + NSDictionary *notificationDict = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:response.actionIdentifier]; + + if (_initialNotification != nil && + [_initialNotificationID isEqualToString:_notificationOpenedAppID]) { + _initialNotification = notificationDict; + [self initialNotificationCallback]; + } + + // We only want to handle FCM notifications and stop firing `onMessageOpenedApp()` when app is + // coming from a terminated state. + if (_notificationOpenedAppID != nil && + ![_initialNotificationID isEqualToString:_notificationOpenedAppID]) { + [_channel invokeMethod:@"Messaging#onMessageOpenedApp" arguments:notificationDict]; + } + + // Forward on to any other delegates. + if (_originalNotificationCenterDelegate != nil && + _originalNotificationCenterDelegateRespondsTo.didReceiveNotificationResponse) { + [_originalNotificationCenterDelegate userNotificationCenter:center + didReceiveNotificationResponse:response + withCompletionHandler:completionHandler]; + } else { + completionHandler(); + } +} + +// We don't use this for FlutterFire, but for the purpose of forwarding to any original delegates we +// implement this. +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + openSettingsForNotification:(nullable UNNotification *)notification + API_AVAILABLE(macos(10.14), ios(10.0)) { + // Forward on to any other delegates. + if (_originalNotificationCenterDelegate != nil && + _originalNotificationCenterDelegateRespondsTo.openSettingsForNotification) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + [_originalNotificationCenterDelegate userNotificationCenter:center + openSettingsForNotification:notification]; +#pragma clang diagnostic pop + } +} +#endif // !TARGET_OS_TV + +#endif + +#pragma mark - AppDelegate Methods + +#if TARGET_OS_OSX +// Called when `registerForRemoteNotifications` completes successfully. +- (void)application:(NSApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { +#else +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { +#endif + if ([FIRMessaging messaging] == nil) { + _apnsToken = deviceToken; + } +#ifdef DEBUG + [[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeSandbox]; +#else + [[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeProd]; +#endif +} + +#if TARGET_OS_OSX +// Called when `registerForRemoteNotifications` fails to complete. +- (void)application:(NSApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { +#else +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { +#endif + NSLog(@"%@", error.localizedDescription); +} + +// Called when a remote notification is received via APNs. +#if TARGET_OS_OSX +- (void)application:(NSApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo { + // Only handle notifications from FCM. + if (userInfo[@"gcm.message_id"]) { + NSDictionary *notificationDict = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:userInfo]; + + if ([NSApplication sharedApplication].isActive) { + [_channel invokeMethod:@"Messaging#onMessage" arguments:notificationDict]; + } else { + [_channel invokeMethod:@"Messaging#onBackgroundMessage" arguments:notificationDict]; + } + } +} +#endif + +#if !TARGET_OS_OSX +// Called for silent messages (i.e. data only) in the foreground & background +- (BOOL)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { +// FlutterFire's messaging plugin hands silent pushes to Firebase Auth's +// phone-verification handler when firebase_auth is also present in the app. +// `-[FIRAuth canHandleNotification:]` is unavailable on tvOS (no phone auth), +// and this whole `__has_include()` branch only compiles when +// firebase_auth_tvos is in the same app — so it must be gated on !TARGET_OS_TV +// too, otherwise adding firebase_messaging_tvos + firebase_auth_tvos together +// breaks the build (each compiles fine alone). See PORTING_REPORT.md. +#if __has_include() && !TARGET_OS_TV + if ([FIRApp defaultApp] != nil && [[FIRAuth auth] canHandleNotification:userInfo]) { + completionHandler(UIBackgroundFetchResultNoData); + return YES; + } +#endif + NSDictionary *notificationDict = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:userInfo]; + // Only handle notifications from FCM. + if (userInfo[@"gcm.message_id"]) { + // For scene delegate apps: if this notification arrives during cold launch + // (before initial notification gathering is complete) and no notification was found + // in connectionOptions, this is the notification that caused the launch. + if (_sceneDidConnect && !_initialNotificationGathered && _initialNotification == nil) { + _initialNotification = notificationDict; + _initialNotificationID = userInfo[@"gcm.message_id"]; + _initialNotificationGathered = YES; + [self initialNotificationCallback]; + } + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + __block BOOL completed = NO; + + // If app is in background state, register background task to guarantee async queues aren't + // frozen. + UIBackgroundTaskIdentifier __block backgroundTaskId = + [application beginBackgroundTaskWithExpirationHandler:^{ + @synchronized(self) { + if (completed == NO) { + completed = YES; + completionHandler(UIBackgroundFetchResultNewData); + if (backgroundTaskId != UIBackgroundTaskInvalid) { + [application endBackgroundTask:backgroundTaskId]; + backgroundTaskId = UIBackgroundTaskInvalid; + } + } + } + }]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(25 * NSEC_PER_SEC)), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + @synchronized(self) { + if (completed == NO) { + completed = YES; + completionHandler(UIBackgroundFetchResultNewData); + if (backgroundTaskId != UIBackgroundTaskInvalid) { + [application endBackgroundTask:backgroundTaskId]; + backgroundTaskId = UIBackgroundTaskInvalid; + } + } + } + }); + + [_channel invokeMethod:@"Messaging#onBackgroundMessage" + arguments:notificationDict + result:^(id _Nullable result) { + @synchronized(self) { + if (completed == NO) { + completed = YES; + completionHandler(UIBackgroundFetchResultNewData); + if (backgroundTaskId != UIBackgroundTaskInvalid) { + [application endBackgroundTask:backgroundTaskId]; + backgroundTaskId = UIBackgroundTaskInvalid; + } + } + } + }]; + } else { + // If "alert" (i.e. notification) is present in userInfo, this will be called by the other + // "Messaging#onMessage" channel handler + if (userInfo[@"aps"] != nil && userInfo[@"aps"][@"alert"] == nil) { + [_channel invokeMethod:@"Messaging#onMessage" arguments:notificationDict]; + } + completionHandler(UIBackgroundFetchResultNoData); + } + + return YES; + } // if (userInfo[@"gcm.message_id"]) + return NO; +} // didReceiveRemoteNotification +#endif + +#pragma mark - SceneDelegate Methods + +#if !TARGET_OS_OSX +- (void)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(UISceneConnectionOptions *)connectionOptions { + // Handle launch notification if present + // With scene delegates, the notification can be in notificationResponse if user tapped it + _sceneDidConnect = YES; + + NSDictionary *remoteNotification = nil; + NSString *actionIdentifier = nil; +// UISceneConnectionOptions.notificationResponse is unavailable on tvOS +// (interactive notification responses don't exist there). +#if !TARGET_OS_TV + if (connectionOptions.notificationResponse != nil) { + // User tapped the notification. + remoteNotification = + connectionOptions.notificationResponse.notification.request.content.userInfo; + actionIdentifier = connectionOptions.notificationResponse.actionIdentifier; + _notificationOpenedAppID = remoteNotification[@"gcm.message_id"]; + } +#endif // !TARGET_OS_TV + + [self setupNotificationHandlingWithRemoteNotification:remoteNotification + actionIdentifier:actionIdentifier]; +} +#endif + +#pragma mark - Firebase Messaging API + +- (void)messagingUnsubscribeFromTopic:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRMessaging *messaging = [FIRMessaging messaging]; + NSString *topic = arguments[@"topic"]; + [messaging unsubscribeFromTopic:topic + completion:^(NSError *error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + result.success(nil); + } + }]; +} + +- (void)messagingSubscribeToTopic:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRMessaging *messaging = [FIRMessaging messaging]; + NSString *topic = arguments[@"topic"]; + [messaging subscribeToTopic:topic + completion:^(NSError *error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + result.success(nil); + } + }]; +} + +- (void)messagingSetAutoInitEnabled:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRMessaging *messaging = [FIRMessaging messaging]; + messaging.autoInitEnabled = [arguments[@"enabled"] boolValue]; + result.success(@{ + @"isAutoInitEnabled" : @(messaging.isAutoInitEnabled), + }); +} + +- (void)messagingRequestPermission:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result + API_AVAILABLE(ios(10), tvos(10), macosx(10.14)) { + NSDictionary *permissions = arguments[@"permissions"]; + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + + UNAuthorizationOptions options = UNAuthorizationOptionNone; + + if ([permissions[@"alert"] isEqual:@(YES)]) { + options |= UNAuthorizationOptionAlert; + } + + if ([permissions[@"badge"] isEqual:@(YES)]) { + options |= UNAuthorizationOptionBadge; + } + + if ([permissions[@"sound"] isEqual:@(YES)]) { + options |= UNAuthorizationOptionSound; + } + + if ([permissions[@"provisional"] isEqual:@(YES)]) { + if (@available(iOS 12.0, tvOS 12.0, *)) { + options |= UNAuthorizationOptionProvisional; + } + } + + if ([permissions[@"announcement"] isEqual:@(YES)]) { + if (@available(iOS 13.0, tvOS 13.0, *)) { + // TODO not available in iOS9 deployment target - enable once iOS10+ deployment target + // specified in podspec. options |= UNAuthorizationOptionAnnouncement; + } + } + + if ([permissions[@"carPlay"] isEqual:@(YES)]) { + options |= UNAuthorizationOptionCarPlay; + } + + if ([permissions[@"criticalAlert"] isEqual:@(YES)]) { + if (@available(iOS 12.0, tvOS 12.0, *)) { + options |= UNAuthorizationOptionCriticalAlert; + } + } + + if ([permissions[@"providesAppNotificationSettings"] isEqual:@(YES)]) { + if (@available(iOS 12.0, tvOS 12.0, *)) { + options |= UNAuthorizationOptionProvidesAppNotificationSettings; + } + } + + id handler = ^(BOOL granted, NSError *_Nullable error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + [center getNotificationSettingsWithCompletionHandler:^( + UNNotificationSettings *_Nonnull settings) { + result.success( + [FLTFirebaseMessagingPlugin NSDictionaryFromUNNotificationSettings:settings]); + }]; + } + }; + + [center requestAuthorizationWithOptions:options completionHandler:handler]; +} + +- (void)messagingGetNotificationSettings:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result + API_AVAILABLE(ios(10), tvos(10), macos(10.14)) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center getNotificationSettingsWithCompletionHandler:^( + UNNotificationSettings *_Nonnull settings) { + result.success([FLTFirebaseMessagingPlugin NSDictionaryFromUNNotificationSettings:settings]); + }]; +} + +- (void)messagingGetToken:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRMessaging *messaging = [FIRMessaging messaging]; + + // Keep behavior consistent with android platform, newly retrieved tokens are streamed via + // onTokenRefresh + bool refreshToken = messaging.FCMToken == nil ? YES : NO; + [messaging tokenWithCompletion:^(NSString *_Nullable token, NSError *_Nullable error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + if (refreshToken) { + [self->_channel invokeMethod:@"Messaging#onTokenRefresh" arguments:token]; + } + + result.success(@{@"token" : token}); + } + }]; +} + +- (void)messagingGetAPNSToken:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + NSData *apnsToken = [FIRMessaging messaging].APNSToken; + if (apnsToken) { + result.success(@{@"token" : [FLTFirebaseMessagingPlugin APNSTokenFromNSData:apnsToken]}); + } else { + result.success(@{@"token" : [NSNull null]}); + } +} + +- (void)messagingDeleteToken:(id)arguments + withMethodCallResult:(FLTFirebaseMethodCallResult *)result { + FIRMessaging *messaging = [FIRMessaging messaging]; + [messaging deleteTokenWithCompletion:^(NSError *_Nullable error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + result.success(nil); + } + }]; +} + +#pragma mark - FLTFirebasePlugin + +- (void)didReinitializeFirebaseCore:(void (^)(void))completion { + completion(); +} + +- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *)firebase_app { + return @{ + @"AUTO_INIT_ENABLED" : @([FIRMessaging messaging].isAutoInitEnabled), + }; +} + +- (NSString *_Nonnull)firebaseLibraryName { + return @LIBRARY_NAME; +} + +- (NSString *_Nonnull)firebaseLibraryVersion { + return @LIBRARY_VERSION; +} + +- (NSString *_Nonnull)flutterChannelName { + return kFLTFirebaseMessagingChannelName; +} + +#pragma mark - Utilities + ++ (NSDictionary *)NSDictionaryFromUNNotificationSettings:(UNNotificationSettings *_Nonnull)settings + API_AVAILABLE(ios(10), macos(10.14)) { + NSMutableDictionary *settingsDictionary = [NSMutableDictionary dictionary]; + + // authorizedStatus — the one UNNotificationSettings property that *is* + // available on tvOS (push permission grant/deny still applies there). + NSNumber *authorizedStatus = @-1; + if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { + authorizedStatus = @-1; + } else if (settings.authorizationStatus == UNAuthorizationStatusDenied) { + authorizedStatus = @0; + } else if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) { + authorizedStatus = @1; + } + if (@available(iOS 12.0, tvOS 12.0, *)) { + if (settings.authorizationStatus == UNAuthorizationStatusProvisional) { + authorizedStatus = @2; + } + } + settingsDictionary[@"authorizationStatus"] = authorizedStatus; + + // Every other UNNotificationSettings property below (alertSetting, + // badgeSetting, soundSetting, carPlaySetting, lockScreenSetting, + // notificationCenterSetting, timeSensitiveSetting, showPreviewsSetting, + // criticalAlertSetting, providesAppNotificationSettings) is + // API_UNAVAILABLE(tvos) in the system headers — tvOS has no + // banner/sound/badge/lock-screen/notification-center presentation UI to + // configure, only the underlying push permission grant captured above. + // All settle to the same "@-1 / unsupported" sentinel already used for + // pre-iOS-version fallbacks, so tvOS clients see a consistent shape. +#if TARGET_OS_TV + settingsDictionary[@"announcement"] = @-1; + settingsDictionary[@"criticalAlert"] = @-1; + settingsDictionary[@"showPreviews"] = @-1; + settingsDictionary[@"alert"] = @-1; + settingsDictionary[@"badge"] = @-1; + settingsDictionary[@"sound"] = @-1; + settingsDictionary[@"carPlay"] = @-1; + settingsDictionary[@"lockScreen"] = @-1; + settingsDictionary[@"notificationCenter"] = @-1; + settingsDictionary[@"timeSensitive"] = @-1; + settingsDictionary[@"providesAppNotificationSettings"] = @-1; +#else + NSNumber *timeSensitive = @-1; + if (@available(iOS 15.0, macOS 12.0, *)) { + if (settings.timeSensitiveSetting == UNNotificationSettingDisabled) { + timeSensitive = @0; + } + if (settings.timeSensitiveSetting == UNNotificationSettingEnabled) { + timeSensitive = @1; + } + } + + NSNumber *showPreviews = @-1; + if (@available(iOS 11.0, *)) { + if (settings.showPreviewsSetting == UNShowPreviewsSettingNever) { + showPreviews = @0; + } else if (settings.showPreviewsSetting == UNShowPreviewsSettingAlways) { + showPreviews = @1; + } else if (settings.showPreviewsSetting == UNShowPreviewsSettingWhenAuthenticated) { + showPreviews = @2; + } + } + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCSimplifyInspectionLegacy" + if (@available(iOS 13.0, *)) { + // TODO not available in iOS9 deployment target - enable once iOS10+ deployment target specified + // in podspec. settingsDictionary[@"announcement"] = + // [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.announcementSetting]; + settingsDictionary[@"announcement"] = @-1; + } else { + settingsDictionary[@"announcement"] = @-1; + } +#pragma clang diagnostic pop + + if (@available(iOS 12.0, *)) { + settingsDictionary[@"criticalAlert"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.criticalAlertSetting]; + } else { + settingsDictionary[@"criticalAlert"] = @-1; + } + + settingsDictionary[@"showPreviews"] = showPreviews; + settingsDictionary[@"alert"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.alertSetting]; + settingsDictionary[@"badge"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.badgeSetting]; + settingsDictionary[@"sound"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.soundSetting]; +#if TARGET_OS_OSX + settingsDictionary[@"carPlay"] = @-1; +#else + settingsDictionary[@"carPlay"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.carPlaySetting]; +#endif + settingsDictionary[@"lockScreen"] = + [FLTFirebaseMessagingPlugin NSNumberForUNNotificationSetting:settings.lockScreenSetting]; + settingsDictionary[@"notificationCenter"] = [FLTFirebaseMessagingPlugin + NSNumberForUNNotificationSetting:settings.notificationCenterSetting]; + settingsDictionary[@"timeSensitive"] = timeSensitive; + + if (@available(iOS 12.0, *)) { + if (settings.providesAppNotificationSettings) { + settingsDictionary[@"providesAppNotificationSettings"] = @1; + } else { + settingsDictionary[@"providesAppNotificationSettings"] = @0; + } + } else { + settingsDictionary[@"providesAppNotificationSettings"] = @-1; + } +#endif // TARGET_OS_TV + return settingsDictionary; +} + ++ (NSNumber *)NSNumberForUNNotificationSetting:(UNNotificationSetting)setting + API_AVAILABLE(ios(10), tvos(10), macos(10.14)) { + NSNumber *asNumber = @-1; + + if (setting == UNNotificationSettingNotSupported) { + asNumber = @-1; + } else if (setting == UNNotificationSettingDisabled) { + asNumber = @0; + } else if (setting == UNNotificationSettingEnabled) { + asNumber = @1; + } + return asNumber; +} + ++ (NSString *)APNSTokenFromNSData:(NSData *)tokenData { + const char *data = [tokenData bytes]; + + NSMutableString *token = [NSMutableString string]; + for (NSInteger i = 0; i < tokenData.length; i++) { + [token appendFormat:@"%02.2hhX", data[i]]; + } + + return [token copy]; +} + +// Excluded on tvOS: its only caller (willPresentNotification) is itself +// guarded out there, since UNNotificationContent.userInfo is unavailable +// on tvOS (see willPresentNotification above). +#if !TARGET_OS_TV +#if TARGET_OS_OSX ++ (NSDictionary *)NSDictionaryFromUNNotification:(UNNotification *)notification + API_AVAILABLE(macos(10.14)) { +#else ++ (NSDictionary *)NSDictionaryFromUNNotification:(UNNotification *)notification { +#endif + return [self remoteMessageUserInfoToDict:notification.request.content.userInfo]; +} +#endif // !TARGET_OS_TV + ++ (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo { + return [self remoteMessageUserInfoToDict:userInfo withActionIdentifier:nil]; +} + ++ (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo + withActionIdentifier:(nullable NSString *)actionIdentifier { + NSMutableDictionary *message = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *notification = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *notificationIOS = [[NSMutableDictionary alloc] init]; + + // message.actionIdentifier + if (actionIdentifier != nil) { + message[@"actionIdentifier"] = actionIdentifier; + } + + // message.data + for (id key in userInfo) { + // message.messageId + if ([key isEqualToString:@"gcm.message_id"] || [key isEqualToString:@"google.message_id"] || + [key isEqualToString:@"message_id"]) { + message[@"messageId"] = userInfo[key]; + continue; + } + + // message.messageType + if ([key isEqualToString:@"message_type"]) { + message[@"messageType"] = userInfo[key]; + continue; + } + + // message.collapseKey + if ([key isEqualToString:@"collapse_key"]) { + message[@"collapseKey"] = userInfo[key]; + continue; + } + + // message.from + if ([key isEqualToString:@"from"]) { + message[@"from"] = userInfo[key]; + continue; + } + + // message.sentTime + if ([key isEqualToString:@"google.c.a.ts"]) { + message[@"sentTime"] = userInfo[key]; + continue; + } + + // message.to + if ([key isEqualToString:@"to"] || [key isEqualToString:@"google.to"]) { + message[@"to"] = userInfo[key]; + continue; + } + + // build data dict from remaining keys but skip keys that shouldn't be included in data + if ([key isEqualToString:@"aps"] || [key hasPrefix:@"gcm."] || [key hasPrefix:@"google."]) { + continue; + } + + // message.apple.imageUrl + if ([key isEqualToString:@"fcm_options"]) { + if (userInfo[key] != nil && userInfo[key][@"image"] != nil) { + notificationIOS[@"imageUrl"] = userInfo[key][@"image"]; + } + continue; + } + + data[key] = userInfo[key]; + } + message[@"data"] = data; + + if (userInfo[@"aps"] != nil) { + NSDictionary *apsDict = userInfo[@"aps"]; + // message.category + if (apsDict[@"category"] != nil) { + message[@"category"] = apsDict[@"category"]; + } + + // message.threadId + if (apsDict[@"thread-id"] != nil) { + message[@"threadId"] = apsDict[@"thread-id"]; + } + + // message.contentAvailable + if (apsDict[@"content-available"] != nil) { + message[@"contentAvailable"] = @([apsDict[@"content-available"] boolValue]); + } + + // message.mutableContent + if (apsDict[@"mutable-content"] != nil && [apsDict[@"mutable-content"] intValue] == 1) { + message[@"mutableContent"] = @([apsDict[@"mutable-content"] boolValue]); + } + + // message.notification.* + if (apsDict[@"alert"] != nil) { + // can be a string or dictionary + if ([apsDict[@"alert"] isKindOfClass:[NSString class]]) { + // message.notification.title + notification[@"title"] = apsDict[@"alert"]; + } else if ([apsDict[@"alert"] isKindOfClass:[NSDictionary class]]) { + NSDictionary *apsAlertDict = apsDict[@"alert"]; + + // message.notification.title + if (apsAlertDict[@"title"] != nil) { + notification[@"title"] = apsAlertDict[@"title"]; + } + + // message.notification.titleLocKey + if (apsAlertDict[@"title-loc-key"] != nil) { + notification[@"titleLocKey"] = apsAlertDict[@"title-loc-key"]; + } + + // message.notification.titleLocArgs + if (apsAlertDict[@"title-loc-args"] != nil) { + notification[@"titleLocArgs"] = apsAlertDict[@"title-loc-args"]; + } + + // message.notification.body + if (apsAlertDict[@"body"] != nil) { + notification[@"body"] = apsAlertDict[@"body"]; + } + + // message.notification.bodyLocKey + if (apsAlertDict[@"loc-key"] != nil) { + notification[@"bodyLocKey"] = apsAlertDict[@"loc-key"]; + } + + // message.notification.bodyLocArgs + if (apsAlertDict[@"loc-args"] != nil) { + notification[@"bodyLocArgs"] = apsAlertDict[@"loc-args"]; + } + + // Apple only + // message.notification.apple.subtitle + if (apsAlertDict[@"subtitle"] != nil) { + notificationIOS[@"subtitle"] = apsAlertDict[@"subtitle"]; + } + + // Apple only + // message.notification.apple.subtitleLocKey + if (apsAlertDict[@"subtitle-loc-key"] != nil) { + notificationIOS[@"subtitleLocKey"] = apsAlertDict[@"subtitle-loc-key"]; + } + + // Apple only + // message.notification.apple.subtitleLocArgs + if (apsAlertDict[@"subtitle-loc-args"] != nil) { + notificationIOS[@"subtitleLocArgs"] = apsAlertDict[@"subtitle-loc-args"]; + } + + // Apple only + // message.notification.apple.badge + if (apsDict[@"badge"] != nil) { + notificationIOS[@"badge"] = [NSString stringWithFormat:@"%@", apsDict[@"badge"]]; + } + } + + notification[@"apple"] = notificationIOS; + message[@"notification"] = notification; + } + + // message.notification.apple.sound + if (apsDict[@"sound"] != nil) { + if ([apsDict[@"sound"] isKindOfClass:[NSString class]]) { + // message.notification.apple.sound + notificationIOS[@"sound"] = @{ + @"name" : apsDict[@"sound"], + @"critical" : @NO, + @"volume" : @1, + }; + } else if ([apsDict[@"sound"] isKindOfClass:[NSDictionary class]]) { + NSDictionary *apsSoundDict = apsDict[@"sound"]; + NSMutableDictionary *notificationIOSSound = [[NSMutableDictionary alloc] init]; + + // message.notification.apple.sound.name String + if (apsSoundDict[@"name"] != nil) { + notificationIOSSound[@"name"] = apsSoundDict[@"name"]; + } + + // message.notification.apple.sound.critical Boolean + if (apsSoundDict[@"critical"] != nil) { + notificationIOSSound[@"critical"] = @([apsSoundDict[@"critical"] boolValue]); + } + + // message.notification.apple.sound.volume Number + if (apsSoundDict[@"volume"] != nil) { + notificationIOSSound[@"volume"] = apsSoundDict[@"volume"]; + } + + // message.notification.apple.sound + notificationIOS[@"sound"] = notificationIOSSound; + } + + notification[@"apple"] = notificationIOS; + message[@"notification"] = notification; + } + } + + return message; +} + +- (void)ensureAPNSTokenSetting { + FIRMessaging *messaging = [FIRMessaging messaging]; + + if (messaging.APNSToken == nil && _apnsToken != nil) { +#ifdef DEBUG + [[FIRMessaging messaging] setAPNSToken:_apnsToken type:FIRMessagingAPNSTokenTypeSandbox]; +#else + [[FIRMessaging messaging] setAPNSToken:_apnsToken type:FIRMessagingAPNSTokenTypeProd]; +#endif + _apnsToken = nil; + } +} + +- (nullable NSDictionary *)copyInitialNotification { + @synchronized(self) { + // Only return if initial notification was sent when app is terminated. Also ensure that + // it was the initial notification that was tapped to open the app. + if (_initialNotification != nil && + [_initialNotificationID isEqualToString:_notificationOpenedAppID]) { + NSDictionary *initialNotificationCopy = [_initialNotification copy]; + _initialNotification = nil; + return initialNotificationCopy; + } + } + + return nil; +} + +- (void)initialNotificationCallback { + if (_initialNotificationGathered && _initialNotificationResult != nil) { + _initialNotificationResult.success([self copyInitialNotification]); + _initialNotificationResult = nil; + } +} + +- (NSDictionary *)NSDictionaryForNSError:(NSError *)error { + NSString *code = @"unknown"; + NSString *message = @"An unknown error has occurred."; + + if (error == nil) { + return @{ + kMessagingArgumentCode : code, + kMessagingArgumentMessage : message, + }; + } + + // code - codes from taken from NSError+FIRMessaging.h + if (error.code == 4) { + code = @"unavailable"; + } else if (error.code == 7) { + code = @"invalid-request"; + } else if (error.code == 8) { + code = @"invalid-argument"; + } else if (error.code == 501) { + code = @"missing-device-id"; + } else if (error.code == 1001) { + code = @"unavailable"; + } else if (error.code == 1003) { + code = @"invalid-argument"; + } else if (error.code == 1004) { + code = @"save-failed"; + } else if (error.code == 1005) { + code = @"invalid-argument"; + } else if (error.code == 2001) { + code = @"already-connected"; + } else if (error.code == 3005) { + code = @"pubsub-operation-cancelled"; + } + + // message + if ([error userInfo][NSLocalizedDescriptionKey] != nil) { + message = [error userInfo][NSLocalizedDescriptionKey]; + } + + return @{ + kMessagingArgumentCode : code, + kMessagingArgumentMessage : message, + }; +} + +@end diff --git a/packages/firebase_messaging_tvos/tvos/Classes/include/FLTFirebaseMessagingPlugin.h b/packages/firebase_messaging_tvos/tvos/Classes/include/FLTFirebaseMessagingPlugin.h new file mode 100644 index 0000000..3fc6214 --- /dev/null +++ b/packages/firebase_messaging_tvos/tvos/Classes/include/FLTFirebaseMessagingPlugin.h @@ -0,0 +1,73 @@ +// Copyright 2020 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 + +#if TARGET_OS_OSX +#import +#else +#import +#endif + +@import FirebaseMessaging; + +#import +#import + +#if __has_include() +#import +#else +#import "FLTFirebasePlugin.h" +#endif + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#define __FF_NOTIFICATIONS_SUPPORTED_PLATFORM +#elif defined(__MAC_10_14) +#define __FF_NOTIFICATIONS_SUPPORTED_PLATFORM +#endif + +// Suppress warning - use can add the Flutter plugin for Firebase Analytics. +#define FIREBASE_ANALYTICS_SUPPRESS_WARNING + +// Forward declaration for FlutterSceneLifeCycleDelegate if not available +#if !TARGET_OS_OSX +@protocol FlutterSceneLifeCycleDelegate; +#endif + +#if TARGET_OS_OSX +#ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM +@interface FLTFirebaseMessagingPlugin : FLTFirebasePlugin +#else +@interface FLTFirebaseMessagingPlugin : FLTFirebasePlugin +#endif +#else +#ifdef __FF_NOTIFICATIONS_SUPPORTED_PLATFORM +API_AVAILABLE(ios(10.0), tvos(10.0)) +@interface FLTFirebaseMessagingPlugin : FLTFirebasePlugin ) + , + FlutterSceneLifeCycleDelegate +#endif + > +#else +@interface FLTFirebaseMessagingPlugin : FLTFirebasePlugin ) + , + FlutterSceneLifeCycleDelegate +#endif + > +#endif +#endif +@end diff --git a/packages/firebase_messaging_tvos/tvos/Resources/PrivacyInfo.xcprivacy b/packages/firebase_messaging_tvos/tvos/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..4c51e8f --- /dev/null +++ b/packages/firebase_messaging_tvos/tvos/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,25 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + + NSPrivacyCollectedDataTypes + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/packages/firebase_messaging_tvos/tvos/firebase_messaging_tvos.podspec b/packages/firebase_messaging_tvos/tvos/firebase_messaging_tvos.podspec new file mode 100644 index 0000000..5c50234 --- /dev/null +++ b/packages/firebase_messaging_tvos/tvos/firebase_messaging_tvos.podspec @@ -0,0 +1,53 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint firebase_messaging_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'firebase_messaging_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of firebase_messaging.' + s.description = <<-DESC +tvOS implementation of firebase_messaging, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/firebase_messaging_tvos' + s.license = { :file => '../LICENSE' } + s.author = { 'fluttertv' => 'noreply@fluttertv.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*.{h,m,mm,swift}' + s.public_header_files = 'Classes/**/include/**/*.h' + # Firebase/Messaging 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', + } + + # FLTFirebaseMessagingPlugin.m reads @LIBRARY_NAME / @LIBRARY_VERSION as + # preprocessor token-pasted string literals (same as upstream + # firebase_messaging's podspec) — without these the file fails to compile + # with "unexpected '@' in program". + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) LIBRARY_VERSION=\"0.0.1\" LIBRARY_NAME=\"flutter-fire-fcm-tvos\"', + } + + # The ported Classes/ call into FIRMessaging (Firebase/Messaging) 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/Messaging', '~> 12.15.0' + s.dependency 'firebase_core_tvos' + s.static_framework = true +end diff --git a/packages/firebase_storage_tvos/.gitignore b/packages/firebase_storage_tvos/.gitignore new file mode 100644 index 0000000..c83dba5 --- /dev/null +++ b/packages/firebase_storage_tvos/.gitignore @@ -0,0 +1,32 @@ +# 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 + +# Local-dev dependency override resolving firebase_core_tvos from the sibling +# package before it is published to pub.dev. Not committed, not published; +# remove it at release time (after firebase_core_tvos is live). See pubspec.yaml. +pubspec_overrides.yaml +example/pubspec_overrides.yaml diff --git a/packages/firebase_storage_tvos/CHANGELOG.md b/packages/firebase_storage_tvos/CHANGELOG.md new file mode 100644 index 0000000..2c15bfc --- /dev/null +++ b/packages/firebase_storage_tvos/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.0.1 + +* Initial tvOS scaffolding generated by `flutter-tvos plugin port` from + `firebase_storage`. diff --git a/packages/firebase_storage_tvos/LICENSE b/packages/firebase_storage_tvos/LICENSE new file mode 100644 index 0000000..000b461 --- /dev/null +++ b/packages/firebase_storage_tvos/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium 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. \ No newline at end of file diff --git a/packages/firebase_storage_tvos/PORTING_REPORT.md b/packages/firebase_storage_tvos/PORTING_REPORT.md new file mode 100644 index 0000000..334e0b6 --- /dev/null +++ b/packages/firebase_storage_tvos/PORTING_REPORT.md @@ -0,0 +1,97 @@ +# firebase_storage_tvos — porting report + +Generated by `flutter-tvos plugin port` on 2026-06-30. + +Source: `firebase_storage` 13.4.3 (path: `/Users/aliustaoglu/.pub-cache/hosted/pub.dev/firebase_storage-13.4.3`) +Base platform: ios (Swift) +Output: `./firebase_storage_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 | 3 | +| Methods stubbed (iOS-only) | 0 | +| Native regions disabled on tvOS | 0 | +| tvOS build outlook | ✅ expected to compile | +| Manual review items | 0 | + +## Methods + +### `canceled` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `object-not-found` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +### `unauthorized` ✓ ported + +No tvOS-incompatible APIs detected in this handler. Copied across unchanged. + +## 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 + +- [ ] Read every `✗ stubbed` method above and confirm returning `FlutterMethodNotImplemented` is acceptable on tvOS. +- [ ] Review every `⚠️ partial` method against a real Apple TV (behaviour differs from iOS). +- [ ] Confirm the removed imports were not load-bearing for still-supported code paths. +- [ ] `flutter-tvos build tvos --simulator --debug` from the plugin's example app compiles the generated registrant. +- [ ] Bump the version and update `CHANGELOG.md` before publishing. + +--- + +## Addendum: manual fixes beyond the automated port (2026-06-30) + +This is the first Swift-sourced port in this batch (`firebase_auth`/`cloud_firestore`/`firebase_core` +are Objective-C); the porter generates a `tvos/Package.swift` alongside the podspec for Swift +sources, and that surfaced a real architectural problem: + +- **Deleted the generated `tvos/Package.swift`.** This monorepo's Podfile loop deliberately skips + CocoaPods resolution for any plugin shipping a `Package.swift` (`has_spm` check — see + `flutter-tvos/lib/tvos_plugins.dart` / the generated Podfile), routing it through Swift Package + Manager instead so it isn't linked twice. But the generated `Package.swift` declared no Firebase + SDK dependency and no dependency on `firebase_core_tvos`, so the build failed with "Unable to + find module dependency: 'FirebaseStorage'" / `'firebase_core_tvos'`. Firebase's SPM distribution + isn't wired into this porter, and `firebase_core_tvos` itself is CocoaPods-only (no + `Package.swift`), so making this package SPM-resolved would mean inventing a second dependency + path no other package in this batch uses. Deleting `Package.swift` makes the Podfile loop fall + through to the (already-fixed) podspec, matching `firebase_core_tvos` / `firebase_auth_tvos` / + `cloud_firestore_tvos`. **Anyone re-running the porter on a Swift-sourced Firebase plugin should + expect to do the same** — this isn't `firebase_storage`-specific. +- **`FLTFirebaseStoragePlugin.swift`**: `#if canImport(firebase_core) import firebase_core #else + import firebase_core_shared #endif` repointed to a plain `import firebase_core_tvos` — same + "point at our own pod, not upstream's" fix as every other package in this batch, just expressed + as a Swift module import instead of an Objective-C `__has_include`. +- **Podspec**: added `Firebase/Storage` and `firebase_core_tvos` dependencies, bumped `s.platform` + to tvOS 15.0. (No `LIBRARY_NAME`/`LIBRARY_VERSION` preprocessor-define fix was needed here — + unlike the Objective-C ports, this Swift plugin file doesn't reference those macros.) +- **`lib/firebase_storage_tvos.dart`**: replaced the porter's copied Dart classes (`Reference`, + `UploadTask`, …) with a one-line re-export of `package:firebase_storage/firebase_storage.dart` — + same reasoning as the other three packages in this batch. + +Verified: `flutter-tvos build tvos --simulator --debug` against the example (unmodified from the +porter's `--include-example` output, aside from the `firebase_core_tvos` dependency and deployment +target bumps — it still depends on `image_picker`/`image_picker_for_web`, which have no tvOS +implementation and will throw `MissingPluginException` at runtime if exercised, but compiled fine) +completes with no compiler errors. Not verified: a live `Storage` round-trip against a real +project, or behavior on a physical Apple TV. + +Manual review required. Read this report top-to-bottom before publishing `firebase_storage_tvos`. diff --git a/packages/firebase_storage_tvos/README.md b/packages/firebase_storage_tvos/README.md new file mode 100644 index 0000000..1e74c2a --- /dev/null +++ b/packages/firebase_storage_tvos/README.md @@ -0,0 +1,47 @@ +# firebase_storage_tvos + +The tvOS (Apple TV) implementation of [`firebase_storage`](https://pub.dev/packages/firebase_storage), +provided by the [flutter-tvos](https://github.com/fluttertv/flutter-tvos) toolchain. + +> Generated by [`flutter-tvos plugin port`](https://github.com/fluttertv/flutter-tvos) +> from `firebase_storage`, then completed by hand. See `PORTING_REPORT.md` for the +> full list of what was changed. + +## Usage + +This is a federated plugin implementation. An app that already depends on +`firebase_storage` and targets Apple TV only needs to add this package +alongside it: + +```yaml +dependencies: + firebase_storage: ^13.4.3 + firebase_storage_tvos: ^0.0.1 +``` + +The native plugin registers automatically through flutter-tvos' plugin +registrant — no extra imports or setup in app code. Use the normal +`firebase_storage` API; it routes to the Apple TV native side. + +## tvOS support + +The Firebase Apple SDK exposes the full Cloud Storage surface on tvOS, so no +Storage features are disabled here: + +| Capability | tvOS | +|---|:---:| +| Upload (`putData`, `putString`, `putFile`) | ✅ | +| Download (`getData`, `getDownloadURL`, `writeToFile`) | ✅ | +| Metadata get / update | ✅ | +| List / listAll | ✅ | +| Delete | ✅ | +| Pause / resume / cancel tasks & progress events | ✅ | + +## Requirements + +- Apple TV running tvOS 15.0 or later (the Firebase Apple SDK's minimum). +- `firebase_core_tvos` (pulled in automatically). + +## License + +fluttertv, under a BSD-3-Clause license. See `LICENSE` for the full text. diff --git a/packages/firebase_storage_tvos/analysis_options.yaml b/packages/firebase_storage_tvos/analysis_options.yaml new file mode 100644 index 0000000..b49c352 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/README.md b/packages/firebase_storage_tvos/example/README.md new file mode 100755 index 0000000..2b0ef7b --- /dev/null +++ b/packages/firebase_storage_tvos/example/README.md @@ -0,0 +1,8 @@ +# firebase_storage_example + +Demonstrates how to use the firebase_storage plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/firebase_storage_tvos/example/assets/hello.txt b/packages/firebase_storage_tvos/example/assets/hello.txt new file mode 100644 index 0000000..802992c --- /dev/null +++ b/packages/firebase_storage_tvos/example/assets/hello.txt @@ -0,0 +1 @@ +Hello world diff --git a/packages/firebase_storage_tvos/example/cors.json b/packages/firebase_storage_tvos/example/cors.json new file mode 100644 index 0000000..ee516e7 --- /dev/null +++ b/packages/firebase_storage_tvos/example/cors.json @@ -0,0 +1,8 @@ +[ + { + "origin": ["*"], + "method": ["GET"], + "maxAgeSeconds": 3600 + } +] + \ No newline at end of file diff --git a/packages/firebase_storage_tvos/example/lib/firebase_options.dart b/packages/firebase_storage_tvos/example/lib/firebase_options.dart new file mode 100644 index 0000000..e55428c --- /dev/null +++ b/packages/firebase_storage_tvos/example/lib/firebase_options.dart @@ -0,0 +1,94 @@ +// Copyright 2022, 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. + +// File generated by FlutterFire CLI. +// 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; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + // ignore: missing_enum_constant_in_switch + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return android; + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + authDomain: 'your-project-id.firebaseapp.com', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + measurementId: 'YOUR_MEASUREMENT_ID', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.storage.example', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'YOUR_API_KEY', + appId: 'YOUR_APP_ID', + messagingSenderId: 'YOUR_SENDER_ID', + projectId: 'your-project-id', + databaseURL: + 'https://your-project-id-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'your-project-id.appspot.com', + androidClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosClientId: + 'YOUR_CLIENT_ID.apps.googleusercontent.com', + iosBundleId: 'io.flutter.plugins.firebase.storage.example', + ); +} diff --git a/packages/firebase_storage_tvos/example/lib/main.dart b/packages/firebase_storage_tvos/example/lib/main.dart new file mode 100755 index 0000000..927e38f --- /dev/null +++ b/packages/firebase_storage_tvos/example/lib/main.dart @@ -0,0 +1,416 @@ +// Copyright 2022, 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. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io' as io; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker/image_picker.dart'; + +import 'firebase_options.dart'; +import 'save_as/save_as.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + + if (defaultTargetPlatform != TargetPlatform.windows) { + // window currently don't support storage emulator + final emulatorHost = + (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) + ? '10.0.2.2' + : 'localhost'; + + await FirebaseStorage.instance.useStorageEmulator(emulatorHost, 9199); + } + + runApp(StorageExampleApp()); +} + +/// Enum representing the upload task types the example app supports. +enum UploadType { + /// Uploads a randomly generated string (as a file) to Storage. + string, + + /// Uploads a file from the device. + file, + + /// Uploads a Uint8List to Storage. + uint8List, + + /// Clears any tasks from the list. + clear, +} + +/// The entry point of the application. +/// +/// Returns a [MaterialApp]. +class StorageExampleApp extends StatelessWidget { + StorageExampleApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Storage Example App', + theme: ThemeData.dark(), + // Disable the banner to make the "+" button more visible. + debugShowCheckedModeBanner: false, + home: Scaffold( + body: TaskManager(), + ), + ); + } +} + +/// A StatefulWidget which keeps track of the current uploaded files. +class TaskManager extends StatefulWidget { + // ignore: public_member_api_docs + TaskManager({Key? key}) : super(key: key); + + @override + State createState() { + return _TaskManager(); + } +} + +class _TaskManager extends State { + List _uploadTasks = []; + + /// The user selects a file, and the task is added to the list. + Future uploadFile(XFile? file) async { + if (file == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('No file was selected'), + ), + ); + + return null; + } + + UploadTask uploadTask; + + // Create a Reference to the file + Reference ref = FirebaseStorage.instance + .ref() + .child('flutter-tests') + .child('/some-image.jpg'); + + final metadata = SettableMetadata( + contentType: 'image/jpeg', + customMetadata: {'picked-file-path': file.path}, + ); + + if (kIsWeb) { + uploadTask = ref.putData(await file.readAsBytes(), metadata); + } else { + uploadTask = ref.putFile(io.File(file.path), metadata); + } + + return Future.value(uploadTask); + } + + /// A new string is uploaded to storage. + UploadTask uploadString() { + const String putStringText = + 'This upload has been generated using the putString method! Check the metadata too!'; + + // Create a Reference to the file + Reference ref = FirebaseStorage.instance + .ref() + .child('flutter-tests') + .child('/put-string-example.txt'); + + // Start upload of putString + return ref.putString( + putStringText, + metadata: SettableMetadata( + contentLanguage: 'en', + customMetadata: {'example': 'putString'}, + ), + ); + } + + Future uploadUint8List() async { + UploadTask uploadTask; + + // Create a Reference to the file + Reference ref = FirebaseStorage.instance + .ref() + .child('flutter-tests') + .child('/some-json.json'); + + const response = '{"key": "value", "number": 42}'; + final data = jsonDecode(response); + + uploadTask = ref.putData(Uint8List.fromList(utf8.encode(jsonEncode(data)))); + + return Future.value(uploadTask); + } + + /// Handles the user pressing the PopupMenuItem item. + Future handleUploadType(UploadType type) async { + switch (type) { + case UploadType.string: + setState(() { + _uploadTasks = [..._uploadTasks, uploadString()]; + }); + break; + case UploadType.file: + final file = await ImagePicker().pickImage(source: ImageSource.gallery); + UploadTask? task = await uploadFile(file); + + if (task != null) { + setState(() { + _uploadTasks = [..._uploadTasks, task]; + }); + } + break; + case UploadType.uint8List: + final task = await uploadUint8List(); + setState(() { + _uploadTasks = [..._uploadTasks, task]; + }); + break; + case UploadType.clear: + setState(() { + _uploadTasks = []; + }); + break; + } + } + + void _removeTaskAtIndex(int index) { + setState(() { + _uploadTasks = _uploadTasks..removeAt(index); + }); + } + + Future _downloadBytes(Reference ref) async { + final bytes = await ref.getData(); + // Download... + await saveAsBytes(bytes!, 'some-image.jpg'); + } + + Future _downloadLink(Reference ref) async { + final link = await ref.getDownloadURL(); + + await Clipboard.setData( + ClipboardData( + text: link, + ), + ); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Success!\n Copied download URL to Clipboard!', + ), + ), + ); + } + + Future _downloadFile(Reference ref) async { + final io.Directory systemTempDir = io.Directory.systemTemp; + final io.File tempFile = io.File('${systemTempDir.path}/temp-${ref.name}'); + if (tempFile.existsSync()) await tempFile.delete(); + + await ref.writeToFile(tempFile); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Success!\n Downloaded ${ref.name} \n from bucket: ${ref.bucket}\n ' + 'at path: ${ref.fullPath} \n' + 'Wrote "${ref.fullPath}" to tmp-${ref.name}', + ), + ), + ); + } + + Future _delete(Reference ref) async { + await ref.delete(); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Success!\n deleted ${ref.name} \n from bucket: ${ref.bucket}\n ' + 'at path: ${ref.fullPath} \n'), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Storage Example App'), + actions: [ + PopupMenuButton( + onSelected: handleUploadType, + icon: const Icon(Icons.add), + itemBuilder: (context) => [ + const PopupMenuItem( + // ignore: sort_child_properties_last + child: Text('Upload string'), + value: UploadType.string, + ), + const PopupMenuItem( + // ignore: sort_child_properties_last + child: Text('Upload local file'), + value: UploadType.file, + ), + const PopupMenuItem( + // ignore: sort_child_properties_last + child: Text('Upload Uint8List'), + value: UploadType.uint8List, + ), + if (_uploadTasks.isNotEmpty) + const PopupMenuItem( + // ignore: sort_child_properties_last + child: Text('Clear list'), + value: UploadType.clear, + ), + ], + ), + ], + ), + body: _uploadTasks.isEmpty + ? const Center(child: Text("Press the '+' button to add a new file.")) + : ListView.builder( + itemCount: _uploadTasks.length, + itemBuilder: (context, index) => UploadTaskListTile( + task: _uploadTasks[index], + onDismissed: () => _removeTaskAtIndex(index), + onDownloadLink: () async { + return _downloadLink(_uploadTasks[index].snapshot.ref); + }, + onDownload: () async { + if (kIsWeb) { + return _downloadBytes(_uploadTasks[index].snapshot.ref); + } else { + return _downloadFile(_uploadTasks[index].snapshot.ref); + } + }, + onDelete: () async { + return _delete(_uploadTasks[index].snapshot.ref); + }, + ), + ), + ); + } +} + +/// Displays the current state of a single UploadTask. +class UploadTaskListTile extends StatelessWidget { + // ignore: public_member_api_docs + const UploadTaskListTile({ + Key? key, + required this.task, + required this.onDismissed, + required this.onDownload, + required this.onDownloadLink, + required this.onDelete, + }) : super(key: key); + + /// The [UploadTask]. + final UploadTask /*!*/ task; + + /// Triggered when the user dismisses the task from the list. + final VoidCallback /*!*/ onDismissed; + + /// Triggered when the user presses the download button on a completed upload task. + final VoidCallback /*!*/ onDownload; + + /// Triggered when the user presses the "link" button on a completed upload task. + final VoidCallback /*!*/ onDownloadLink; + + /// Triggered when the user presses the "delete" button on a completed upload task. + final VoidCallback /*!*/ onDelete; + + /// Displays the current transferred bytes of the task. + String _bytesTransferred(TaskSnapshot snapshot) { + return '${snapshot.bytesTransferred}/${snapshot.totalBytes}'; + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: task.snapshotEvents, + builder: ( + BuildContext context, + AsyncSnapshot asyncSnapshot, + ) { + Widget subtitle = const Text('---'); + TaskSnapshot? snapshot = asyncSnapshot.data; + TaskState? state = snapshot?.state; + + if (asyncSnapshot.hasError) { + if (asyncSnapshot.error is FirebaseException && + // ignore: cast_nullable_to_non_nullable + (asyncSnapshot.error as FirebaseException).code == 'canceled') { + subtitle = const Text('Upload canceled.'); + } else { + // ignore: avoid_print + print(asyncSnapshot.error); + subtitle = const Text('Something went wrong.'); + } + } else if (snapshot != null) { + subtitle = Text('$state: ${_bytesTransferred(snapshot)} bytes sent'); + } + + return Dismissible( + key: Key(task.hashCode.toString()), + onDismissed: ($) => onDismissed(), + child: ListTile( + title: Text('Upload Task #${task.hashCode}'), + subtitle: subtitle, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (state == TaskState.running) + IconButton( + icon: const Icon(Icons.pause), + onPressed: task.pause, + ), + if (state == TaskState.running) + IconButton( + icon: const Icon(Icons.cancel), + onPressed: task.cancel, + ), + if (state == TaskState.paused) + IconButton( + icon: const Icon(Icons.file_upload), + onPressed: task.resume, + ), + if (state == TaskState.success) + IconButton( + icon: const Icon(Icons.file_download), + onPressed: onDownload, + ), + if (state == TaskState.success) + IconButton( + icon: const Icon(Icons.link), + onPressed: onDownloadLink, + ), + if (state == TaskState.success) + IconButton( + icon: const Icon(Icons.delete), + onPressed: onDelete, + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/packages/firebase_storage_tvos/example/lib/save_as/save_as.dart b/packages/firebase_storage_tvos/example/lib/save_as/save_as.dart new file mode 100644 index 0000000..c126e02 --- /dev/null +++ b/packages/firebase_storage_tvos/example/lib/save_as/save_as.dart @@ -0,0 +1,5 @@ +// Copyright 2022, 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. + +export 'save_as_interface.dart' if (dart.library.html) 'save_as_html.dart'; diff --git a/packages/firebase_storage_tvos/example/lib/save_as/save_as_html.dart b/packages/firebase_storage_tvos/example/lib/save_as/save_as_html.dart new file mode 100644 index 0000000..f9de349 --- /dev/null +++ b/packages/firebase_storage_tvos/example/lib/save_as/save_as_html.dart @@ -0,0 +1,63 @@ +// Copyright 2022, 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. + +import 'dart:js_interop'; +import 'dart:typed_data'; + +import 'package:web/web.dart' as web; + +/// Initializes a DOM container where we can host elements. +web.Element _ensureInitialized(String id) { + var target = web.document.querySelector('#$id'); + if (target == null) { + final web.Element targetElement = + web.document.createElement('flt-x-file') as web.HTMLElement; + targetElement.id = id; + + web.document.querySelector('body')?.children.add(targetElement); + target = targetElement; + } + return target; +} + +web.HTMLAnchorElement _createAnchorElement(String href, String suggestedName) { + final element = web.HTMLAnchorElement(); + element.href = href; + element.download = suggestedName; + return element; +} + +/// Add an element to a container and click it +void _addElementToContainerAndClick( + web.Element container, + web.Element element, +) { + // Add the element and click it + // All previous elements will be removed before adding the new one + container.children.add(element); + final event = web.MouseEvent('click'); + element.dispatchEvent(event); +} + +/// Present a dialog so the user can save as... a bunch of bytes. +Future saveAsBytes(Uint8List bytes, String suggestedName) async { + // Convert bytes to an ObjectUrl through Blob + final blob = web.Blob([bytes.toJS].toJS); + final path = web.URL.createObjectURL(blob); + + // Create a DOM container where we can host the anchor. + final target = _ensureInitialized('__x_file_dom_element'); + + // Create an tag with the appropriate download attributes and click it + // May be overridden with XFileTestOverrides + final web.HTMLAnchorElement element = + _createAnchorElement(path, suggestedName); + + // Clear the children in our container so we can add an element to click + do { + target.children.item(0)?.remove(); + } while (target.children.length > 0); + + _addElementToContainerAndClick(target, element); +} diff --git a/packages/firebase_storage_tvos/example/lib/save_as/save_as_interface.dart b/packages/firebase_storage_tvos/example/lib/save_as/save_as_interface.dart new file mode 100644 index 0000000..e4d8a5a --- /dev/null +++ b/packages/firebase_storage_tvos/example/lib/save_as/save_as_interface.dart @@ -0,0 +1,10 @@ +// Copyright 2022, 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. + +import 'dart:typed_data'; + +/// Present a dialog so the user can save as... a bunch of bytes. +Future saveAsBytes(Uint8List bytes, String suggestedName) async { + return; +} diff --git a/packages/firebase_storage_tvos/example/pubspec.yaml b/packages/firebase_storage_tvos/example/pubspec.yaml new file mode 100755 index 0000000..3d0cabc --- /dev/null +++ b/packages/firebase_storage_tvos/example/pubspec.yaml @@ -0,0 +1,24 @@ +name: firebase_storage_example +description: Demonstrates how to use the firebase_storage plugin. + +environment: + sdk: '^3.6.0' + flutter: '>=3.27.0' + +dependencies: + firebase_storage: ^13.4.3 + firebase_storage_tvos: + path: ../ + firebase_core: ^4.11.0 + firebase_core_tvos: + path: ../../firebase_core_tvos + flutter: + sdk: flutter + image_picker: ^1.1.2 + image_picker_for_web: ^3.0.5 + web: ^1.0.0 + +flutter: + uses-material-design: true + assets: + - assets/hello.txt diff --git a/packages/firebase_storage_tvos/example/tvos/.gitignore b/packages/firebase_storage_tvos/example/tvos/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Flutter/Debug.xcconfig b/packages/firebase_storage_tvos/example/tvos/Flutter/Debug.xcconfig new file mode 100644 index 0000000..f5ba6d4 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Flutter/Release.xcconfig b/packages/firebase_storage_tvos/example/tvos/Flutter/Release.xcconfig new file mode 100644 index 0000000..075d0bd --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Podfile b/packages/firebase_storage_tvos/example/tvos/Podfile new file mode 100644 index 0000000..2e1ee47 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner.xcodeproj/project.pbxproj b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c2ac442 --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + 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 */, + ); + name = Pods; + 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.firebaseStorageExample; + 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)"; + 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.firebaseStorageExample; + 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.firebaseStorageExample; + 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_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ee3561d --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner/AppDelegate.swift b/packages/firebase_storage_tvos/example/tvos/Runner/AppDelegate.swift new file mode 100644 index 0000000..e867cf0 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/firebase_storage_tvos/example/tvos/Runner/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/large_back.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/large_front.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/large_middle.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/small_back.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/small_front.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/small_middle.png b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json b/packages/firebase_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Contents.json new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/packages/firebase_storage_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_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/top_shelf.png differ diff --git a/packages/firebase_storage_tvos/example/tvos/Runner/Assets.xcassets/Contents.json b/packages/firebase_storage_tvos/example/tvos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard b/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..088a3ba --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/Main.storyboard b/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4e805a1 --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/firebase_storage_tvos/example/tvos/Runner/Info.plist b/packages/firebase_storage_tvos/example/tvos/Runner/Info.plist new file mode 100644 index 0000000..4e54d18 --- /dev/null +++ b/packages/firebase_storage_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_storage_tvos/example/tvos/Runner/Runner-Bridging-Header.h b/packages/firebase_storage_tvos/example/tvos/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/packages/firebase_storage_tvos/example/tvos/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/firebase_storage_tvos/lib/firebase_storage_tvos.dart b/packages/firebase_storage_tvos/lib/firebase_storage_tvos.dart new file mode 100644 index 0000000..449238c --- /dev/null +++ b/packages/firebase_storage_tvos/lib/firebase_storage_tvos.dart @@ -0,0 +1,14 @@ +// Copyright 2020 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. + +// Same reasoning as firebase_core_tvos: firebase_storage's public Dart API +// (FirebaseStorage, Reference, UploadTask, …) has no per-platform Dart +// override — it talks to native through firebase_storage_platform_interface's +// MethodChannel implementation regardless of platform. Duplicating it here +// would create incompatible types vs. apps that import +// package:firebase_storage/firebase_storage.dart directly. This package +// only supplies the native tvOS pluginClass (tvos/Classes/); apps depend on +// firebase_storage (Dart API) and firebase_storage_tvos (native +// registration) side by side — see example/. +export 'package:firebase_storage/firebase_storage.dart'; diff --git a/packages/firebase_storage_tvos/pubspec.yaml b/packages/firebase_storage_tvos/pubspec.yaml new file mode 100644 index 0000000..dd6962d --- /dev/null +++ b/packages/firebase_storage_tvos/pubspec.yaml @@ -0,0 +1,44 @@ +name: firebase_storage_tvos +description: >- + tvOS (Apple TV) implementation of the firebase_storage Flutter plugin, + bringing Cloud Storage for Firebase to Apple TV apps via flutter-tvos. +version: 0.0.1 +homepage: https://fluttertv.dev +repository: https://github.com/fluttertv/plugins/tree/main/packages/firebase_storage_tvos +issue_tracker: https://github.com/fluttertv/plugins/issues +# Generated by `flutter-tvos plugin port`. See PORTING_REPORT.md. +# License holder: fluttertv + +# The example ships the standard FlutterFire demo-project GoogleService +# values (client-side Firebase identifiers, not secrets). Tell pub's secret +# scanner they are intentional, matching upstream firebase_storage's pubspec. +false_secrets: + - example/** + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + firebase_storage: ^13.4.3 + # Transitive Dart dependency so the app's dependency graph includes + # firebase_core_tvos — our 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 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: FLTFirebaseStoragePlugin diff --git a/packages/firebase_storage_tvos/test/firebase_storage_tvos_test.dart b/packages/firebase_storage_tvos/test/firebase_storage_tvos_test.dart new file mode 100644 index 0000000..9fc6249 --- /dev/null +++ b/packages/firebase_storage_tvos/test/firebase_storage_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-06-30 by `flutter-tvos plugin port`. +// Source plugin: firebase_storage + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('test harness runs', () { + expect(1 + 1, 2); + }); +} diff --git a/packages/firebase_storage_tvos/tvos/Classes/FLTFirebaseStoragePlugin.swift b/packages/firebase_storage_tvos/tvos/Classes/FLTFirebaseStoragePlugin.swift new file mode 100644 index 0000000..e79fc91 --- /dev/null +++ b/packages/firebase_storage_tvos/tvos/Classes/FLTFirebaseStoragePlugin.swift @@ -0,0 +1,492 @@ +// Copyright 2025 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FirebaseStorage +import Foundation + +// Points at our own firebase_core_tvos pod, not upstream's "firebase_core" +// pod (which has no tvOS platform declaration) — see PORTING_REPORT.md. +import firebase_core_tvos + +#if (os(iOS) || os(tvOS)) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +extension FlutterError: Error {} + +public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseStorageHostApi { + private var channel: FlutterMethodChannel? + private var messenger: FlutterBinaryMessenger? + private var eventChannels: [String: FlutterEventChannel] = [:] + private var streamHandlers: [String: FlutterStreamHandler] = [:] + private var handleToTask: [Int64: AnyObject] = [:] + private var handleToPath: [Int64: String] = [:] + private var handleToIdentifier: [Int64: String] = [:] + /// Tracks which buckets have had the emulator set to avoid calling useEmulator more than once + /// per bucket (prevents crash on hot restart). See + /// https://github.com/firebase/flutterfire/pull/11862 + private var emulatorBooted: [String: Bool] = [:] + + /// Registry to help stream handler classify failure events as cancellations when initiated from + /// Dart + static var canceledIdentifiers = Set() + + @objc + public static func register(with registrar: FlutterPluginRegistrar) { + let channelName = "plugins.flutter.io/firebase_storage" + // Resolve platform-specific messenger API differences + #if (os(iOS) || os(tvOS)) + let resolvedMessenger: FlutterBinaryMessenger = registrar.messenger() + #else + let resolvedMessenger: FlutterBinaryMessenger = registrar.messenger + #endif + let channel = FlutterMethodChannel(name: channelName, binaryMessenger: resolvedMessenger) + let instance = FLTFirebaseStoragePlugin() + instance.channel = channel + instance.messenger = resolvedMessenger + registrar.addMethodCallDelegate(instance, channel: channel) + FirebaseStorageHostApiSetup.setUp(binaryMessenger: resolvedMessenger, api: instance) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + result(FlutterMethodNotImplemented) + } + + private func storage(app: InternalStorageFirebaseApp) -> Storage { + let base = "gs://" + app.bucket + let firApp = FLTFirebasePlugin.firebaseAppNamed(app.appName)! + return Storage.storage(app: firApp, url: base) + } + + private func ref( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference + ) -> StorageReference { + storage(app: app).reference(withPath: reference.fullPath) + } + + private func toPigeon(_ ref: StorageReference) -> InternalStorageReference { + InternalStorageReference(bucket: ref.bucket, fullPath: ref.fullPath, name: ref.name) + } + + func getReferencebyPath( + app: InternalStorageFirebaseApp, path: String, bucket: String?, + completion: @escaping (Result) -> Void + ) { + let r = storage(app: app).reference(withPath: path) + completion( + .success( + InternalStorageReference( + bucket: r.bucket, + fullPath: r.fullPath, + name: r.name + ) + ) + ) + } + + func setMaxOperationRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { + storage(app: app).maxOperationRetryTime = TimeInterval(Double(time) / 1000.0) + completion(.success(())) + } + + func setMaxUploadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { + storage(app: app).maxUploadRetryTime = TimeInterval(Double(time) / 1000.0) + completion(.success(())) + } + + func setMaxDownloadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { + storage(app: app).maxDownloadRetryTime = TimeInterval(Double(time) / 1000.0) + completion(.success(())) + } + + func useStorageEmulator( + app: InternalStorageFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void + ) { + guard emulatorBooted[app.bucket] == nil else { + completion(.success(())) + return + } + let s = storage(app: app) + s.useEmulator(withHost: host, port: Int(port)) + emulatorBooted[app.bucket] = true + completion(.success(())) + } + + func referenceDelete( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { + ref(app: app, reference: reference).delete { error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(())) + } + } + } + + func referenceGetDownloadURL( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { + ref(app: app, reference: reference).downloadURL { url, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion( + .success( + url!.absoluteString.replacingOccurrences( + of: ":443", + with: "" + ) + ) + ) + } + } + } + + func referenceGetMetaData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { + ref(app: app, reference: reference).getMetadata { md, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(InternalFullMetaData(metadata: self.metaToDict(md)))) + } + } + } + + func referenceList( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + options: InternalListOptions, + completion: @escaping (Result) -> Void + ) { + let r = ref(app: app, reference: reference) + let block: (StorageListResult?, Error?) -> Void = { list, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(self.listToPigeon(list!))) + } + } + if let token = options.pageToken { + r.list(maxResults: options.maxResults, pageToken: token, completion: block) + } else { + r.list(maxResults: options.maxResults, completion: block) + } + } + + func referenceListAll( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { + ref(app: app, reference: reference).listAll { list, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(self.listToPigeon(list!))) + } + } + } + + func referenceGetData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + maxSize: Int64, + completion: @escaping (Result) -> Void + ) { + ref(app: app, reference: reference).getData(maxSize: maxSize) { data, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else if let data { + completion(.success(FlutterStandardTypedData(bytes: data))) + } else { + completion(.success(nil)) + } + } + } + + func referencePutData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: FlutterStandardTypedData, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void + ) { + let r = ref(app: app, reference: reference) + let task = r.putData(data.data, metadata: toMeta(settableMetaData)) + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referencePutString( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: String, format: Int64, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void + ) { + let r = ref(app: app, reference: reference) + let d: Data + if format == 1 { + d = Data(base64Encoded: data) ?? Data() + } else if format == 2 { + d = + Data( + base64Encoded: data.replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + .padding(toLength: ((data.count + 3) / 4) * 4, withPad: "=", startingAt: 0) + ) ?? Data() + } else { + d = Data() + } + let task = r.putData(d, metadata: toMeta(settableMetaData)) + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referencePutFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, settableMetaData: InternalSettableMetadata?, + handle: Int64, + completion: @escaping (Result) -> Void + ) { + let r = ref(app: app, reference: reference) + let url = URL(fileURLWithPath: filePath) + let task: StorageUploadTask + if let md = settableMetaData { + task = r.putFile(from: url, metadata: toMeta(md)) + } else { + task = r.putFile(from: url) + } + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referenceDownloadFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, handle: Int64, + completion: @escaping (Result) -> Void + ) { + let r = ref(app: app, reference: reference) + let url = URL(fileURLWithPath: filePath) + let task = r.write(toFile: url) + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referenceUpdateMetadata( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + metadata: InternalSettableMetadata, + completion: + @escaping (Result) + -> Void + ) { + ref(app: app, reference: reference).updateMetadata(toMeta(metadata)) { md, error in + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(InternalFullMetaData(metadata: self.metaToDict(md)))) + } + } + } + + func taskPause( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { + if let task = handleToTask[handle] as? StorageUploadTask { + task.pause() + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else if let task = handleToTask[handle] as? StorageDownloadTask { + task.pause() + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else { + completion(.success(["status": false])) + } + } + + func taskResume( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { + if let task = handleToTask[handle] as? StorageUploadTask { + task.resume() + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else if let task = handleToTask[handle] as? StorageDownloadTask { + task.resume() + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else { + completion(.success(["status": false])) + } + } + + func taskCancel( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { + if let task = handleToTask[handle] as? StorageUploadTask { + task.cancel() + if let id = handleToIdentifier[handle] { + FLTFirebaseStoragePlugin.canceledIdentifiers.insert(id) + } + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else if let task = handleToTask[handle] as? StorageDownloadTask { + task.cancel() + if let id = handleToIdentifier[handle] { + FLTFirebaseStoragePlugin.canceledIdentifiers.insert(id) + } + completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) + } else { + completion(.success(["status": false])) + } + } + + private func toMeta(_ m: InternalSettableMetadata) -> StorageMetadata { + let md = StorageMetadata() + if let v = m.cacheControl { md.cacheControl = v } + if let v = m.contentType { md.contentType = v } + if let v = m.contentDisposition { md.contentDisposition = v } + if let v = m.contentEncoding { md.contentEncoding = v } + if let v = m.contentLanguage { md.contentLanguage = v } + if let v = m.customMetadata { md.customMetadata = v as? [String: String] } + return md + } + + private func metaToDict(_ md: StorageMetadata?) -> [String: Any]? { + guard let md else { return nil } + var out: [String: Any] = [:] + out["name"] = md.name + out["bucket"] = md.bucket + out["generation"] = String(md.generation) + out["metadataGeneration"] = String(md.metageneration) + out["fullPath"] = md.path + out["size"] = md.size + out["creationTimeMillis"] = Int((md.timeCreated?.timeIntervalSince1970 ?? 0) * 1000) + out["updatedTimeMillis"] = Int((md.updated?.timeIntervalSince1970 ?? 0) * 1000) + if let v = md.md5Hash { out["md5Hash"] = v } + if let v = md.cacheControl { out["cacheControl"] = v } + if let v = md.contentDisposition { out["contentDisposition"] = v } + if let v = md.contentEncoding { out["contentEncoding"] = v } + if let v = md.contentLanguage { out["contentLanguage"] = v } + if let v = md.contentType { out["contentType"] = v } + out["customMetadata"] = md.customMetadata ?? [:] + return out + } + + private func listToPigeon(_ list: StorageListResult) -> InternalListResult { + let items = list.items.map { toPigeon($0) } + let prefixes = list.prefixes.map { toPigeon($0) } + let itemsOpt: [InternalStorageReference?] = items.map { Optional($0) } + let prefixesOpt: [InternalStorageReference?] = prefixes.map { Optional($0) } + return InternalListResult(items: itemsOpt, pageToken: list.pageToken, prefixs: prefixesOpt) + } + + private func registerTask( + task: StorageObservableTask, appName: String, handle: Int64, + path: String + ) -> String { + let uuid = UUID().uuidString + let channelName = "plugins.flutter.io/firebase_storage/taskEvent/\(uuid)" + let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger!) + let storageInstance = Storage.storage(app: FLTFirebasePlugin.firebaseAppNamed(appName)!) + channel.setStreamHandler( + TaskStateChannelStreamHandler( + task: task, + storage: storageInstance, + identifier: channelName + ) + ) + eventChannels[channelName] = channel + handleToTask[handle] = task as AnyObject + handleToPath[handle] = path + handleToIdentifier[handle] = channelName + return uuid + } + + private func currentSnapshot(handle: Int64) -> [String: Any] { + [ + "path": handleToPath[handle] ?? "", + "bytesTransferred": 0, + "totalBytes": 0, + ] + } + + private func toFlutterError(_ error: Error) -> Error { + let ns = error as NSError + let code = mapStorageErrorCode(ns) + let message = standardMessage(for: code) ?? ns.localizedDescription + return FlutterError(code: code, message: message, details: [:]) + } + + private func mapStorageErrorCode(_ error: NSError) -> String { + if error.domain == StorageErrorDomain, let code = StorageErrorCode(rawValue: error.code) { + switch code { + case .objectNotFound: return "object-not-found" + case .bucketNotFound: return "bucket-not-found" + case .projectNotFound: return "project-not-found" + case .quotaExceeded: return "quota-exceeded" + case .unauthenticated: return "unauthenticated" + case .unauthorized: return "unauthorized" + case .retryLimitExceeded: return "retry-limit-exceeded" + case .cancelled: return "canceled" + case .downloadSizeExceeded: return "download-size-exceeded" + @unknown default: return "unknown" + } + } else if error.domain == NSURLErrorDomain, error.code == NSURLErrorCancelled { + return "canceled" + } + return "unknown" + } + + private func standardMessage(for code: String) -> String? { + switch code { + case "object-not-found": return "No object exists at the desired reference." + case "unauthorized": return "User is not authorized to perform the desired action." + default: return nil + } + } +} diff --git a/packages/firebase_storage_tvos/tvos/Classes/FirebaseStorageMessages.g.swift b/packages/firebase_storage_tvos/tvos/Classes/FirebaseStorageMessages.g.swift new file mode 100644 index 0000000..5114230 --- /dev/null +++ b/packages/firebase_storage_tvos/tvos/Classes/FirebaseStorageMessages.g.swift @@ -0,0 +1,1192 @@ +// Copyright 2023, 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 doubleEqualsFirebaseStorageMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseStorageMessages(_ 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 deepEqualsFirebaseStorageMessages(_ 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 !deepEqualsFirebaseStorageMessages(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 !doubleEqualsFirebaseStorageMessages(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 deepEqualsFirebaseStorageMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseStorageMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseStorageMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + + default: + return false + } +} + +func deepHashFirebaseStorageMessages(value: Any?, hasher: inout Hasher) { + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseStorageMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseStorageMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseStorageMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseStorageMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseStorageMessages(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) + } +} + +/// The type of operation that generated the action code from calling +/// [TaskState]. +enum InternalStorageTaskState: Int { + /// Indicates the task has been paused by the user. + case paused = 0 + /// Indicates the task is currently in-progress. + case running = 1 + /// Indicates the task has successfully completed. + case success = 2 + /// Indicates the task was canceled. + case canceled = 3 + /// Indicates the task failed with an error. + case error = 4 +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalStorageFirebaseApp: Hashable { + var appName: String + var tenantId: String? + var bucket: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageFirebaseApp? { + let appName = pigeonVar_list[0] as! String + let tenantId: String? = nilOrValue(pigeonVar_list[1]) + let bucket = pigeonVar_list[2] as! String + + return InternalStorageFirebaseApp( + appName: appName, + tenantId: tenantId, + bucket: bucket + ) + } + + func toList() -> [Any?] { + [ + appName, + tenantId, + bucket, + ] + } + + static func == (lhs: InternalStorageFirebaseApp, rhs: InternalStorageFirebaseApp) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.appName, rhs.appName) + && deepEqualsFirebaseStorageMessages( + lhs.tenantId, + rhs.tenantId + ) && deepEqualsFirebaseStorageMessages(lhs.bucket, rhs.bucket) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageFirebaseApp") + deepHashFirebaseStorageMessages(value: appName, hasher: &hasher) + deepHashFirebaseStorageMessages(value: tenantId, hasher: &hasher) + deepHashFirebaseStorageMessages(value: bucket, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalStorageReference: Hashable { + var bucket: String + var fullPath: String + var name: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageReference? { + let bucket = pigeonVar_list[0] as! String + let fullPath = pigeonVar_list[1] as! String + let name = pigeonVar_list[2] as! String + + return InternalStorageReference( + bucket: bucket, + fullPath: fullPath, + name: name + ) + } + + func toList() -> [Any?] { + [ + bucket, + fullPath, + name, + ] + } + + static func == (lhs: InternalStorageReference, rhs: InternalStorageReference) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.bucket, rhs.bucket) + && deepEqualsFirebaseStorageMessages( + lhs.fullPath, + rhs.fullPath + ) && deepEqualsFirebaseStorageMessages(lhs.name, rhs.name) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageReference") + deepHashFirebaseStorageMessages(value: bucket, hasher: &hasher) + deepHashFirebaseStorageMessages(value: fullPath, hasher: &hasher) + deepHashFirebaseStorageMessages(value: name, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalFullMetaData: Hashable { + var metadata: [String?: Any?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalFullMetaData? { + let metadata: [String?: Any?]? = nilOrValue(pigeonVar_list[0]) + + return InternalFullMetaData( + metadata: metadata + ) + } + + func toList() -> [Any?] { + [ + metadata + ] + } + + static func == (lhs: InternalFullMetaData, rhs: InternalFullMetaData) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.metadata, rhs.metadata) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalFullMetaData") + deepHashFirebaseStorageMessages(value: metadata, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalListOptions: Hashable { + /// If set, limits the total number of `prefixes` and `items` to return. + /// + /// The default and maximum maxResults is 1000. + var maxResults: Int64 + /// The nextPageToken from a previous call to list(). + /// + /// If provided, listing is resumed from the previous position. + var pageToken: String? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalListOptions? { + let maxResults = pigeonVar_list[0] as! Int64 + let pageToken: String? = nilOrValue(pigeonVar_list[1]) + + return InternalListOptions( + maxResults: maxResults, + pageToken: pageToken + ) + } + + func toList() -> [Any?] { + [ + maxResults, + pageToken, + ] + } + + static func == (lhs: InternalListOptions, rhs: InternalListOptions) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.maxResults, rhs.maxResults) + && deepEqualsFirebaseStorageMessages( + lhs.pageToken, + rhs.pageToken + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalListOptions") + deepHashFirebaseStorageMessages(value: maxResults, hasher: &hasher) + deepHashFirebaseStorageMessages(value: pageToken, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalSettableMetadata: Hashable { + /// Served as the 'Cache-Control' header on object download. + /// + /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control. + var cacheControl: String? + /// Served as the 'Content-Disposition' header on object download. + /// + /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition. + var contentDisposition: String? + /// Served as the 'Content-Encoding' header on object download. + /// + /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding. + var contentEncoding: String? + /// Served as the 'Content-Language' header on object download. + /// + /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language. + var contentLanguage: String? + /// Served as the 'Content-Type' header on object download. + /// + /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type. + var contentType: String? + /// Additional user-defined custom metadata. + var customMetadata: [String?: String?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalSettableMetadata? { + let cacheControl: String? = nilOrValue(pigeonVar_list[0]) + let contentDisposition: String? = nilOrValue(pigeonVar_list[1]) + let contentEncoding: String? = nilOrValue(pigeonVar_list[2]) + let contentLanguage: String? = nilOrValue(pigeonVar_list[3]) + let contentType: String? = nilOrValue(pigeonVar_list[4]) + let customMetadata: [String?: String?]? = nilOrValue(pigeonVar_list[5]) + + return InternalSettableMetadata( + cacheControl: cacheControl, + contentDisposition: contentDisposition, + contentEncoding: contentEncoding, + contentLanguage: contentLanguage, + contentType: contentType, + customMetadata: customMetadata + ) + } + + func toList() -> [Any?] { + [ + cacheControl, + contentDisposition, + contentEncoding, + contentLanguage, + contentType, + customMetadata, + ] + } + + static func == (lhs: InternalSettableMetadata, rhs: InternalSettableMetadata) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.cacheControl, rhs.cacheControl) + && deepEqualsFirebaseStorageMessages( + lhs.contentDisposition, + rhs.contentDisposition + ) && deepEqualsFirebaseStorageMessages(lhs.contentEncoding, rhs.contentEncoding) + && deepEqualsFirebaseStorageMessages( + lhs.contentLanguage, + rhs.contentLanguage + ) && deepEqualsFirebaseStorageMessages(lhs.contentType, rhs.contentType) + && deepEqualsFirebaseStorageMessages( + lhs.customMetadata, + rhs.customMetadata + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalSettableMetadata") + deepHashFirebaseStorageMessages(value: cacheControl, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentDisposition, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentEncoding, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentLanguage, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentType, hasher: &hasher) + deepHashFirebaseStorageMessages(value: customMetadata, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalStorageTaskSnapShot: Hashable { + var bytesTransferred: Int64 + var metadata: InternalFullMetaData? + var state: InternalStorageTaskState + var totalBytes: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageTaskSnapShot? { + let bytesTransferred = pigeonVar_list[0] as! Int64 + let metadata: InternalFullMetaData? = nilOrValue(pigeonVar_list[1]) + let state = pigeonVar_list[2] as! InternalStorageTaskState + let totalBytes = pigeonVar_list[3] as! Int64 + + return InternalStorageTaskSnapShot( + bytesTransferred: bytesTransferred, + metadata: metadata, + state: state, + totalBytes: totalBytes + ) + } + + func toList() -> [Any?] { + [ + bytesTransferred, + metadata, + state, + totalBytes, + ] + } + + static func == (lhs: InternalStorageTaskSnapShot, rhs: InternalStorageTaskSnapShot) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.bytesTransferred, rhs.bytesTransferred) + && deepEqualsFirebaseStorageMessages( + lhs.metadata, + rhs.metadata + ) && deepEqualsFirebaseStorageMessages(lhs.state, rhs.state) + && deepEqualsFirebaseStorageMessages( + lhs.totalBytes, + rhs.totalBytes + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageTaskSnapShot") + deepHashFirebaseStorageMessages(value: bytesTransferred, hasher: &hasher) + deepHashFirebaseStorageMessages(value: metadata, hasher: &hasher) + deepHashFirebaseStorageMessages(value: state, hasher: &hasher) + deepHashFirebaseStorageMessages(value: totalBytes, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalListResult: Hashable { + var items: [InternalStorageReference?] + var pageToken: String? + var prefixs: [InternalStorageReference?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalListResult? { + let items = pigeonVar_list[0] as! [InternalStorageReference?] + let pageToken: String? = nilOrValue(pigeonVar_list[1]) + let prefixs = pigeonVar_list[2] as! [InternalStorageReference?] + + return InternalListResult( + items: items, + pageToken: pageToken, + prefixs: prefixs + ) + } + + func toList() -> [Any?] { + [ + items, + pageToken, + prefixs, + ] + } + + static func == (lhs: InternalListResult, rhs: InternalListResult) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.items, rhs.items) + && deepEqualsFirebaseStorageMessages( + lhs.pageToken, + rhs.pageToken + ) && deepEqualsFirebaseStorageMessages(lhs.prefixs, rhs.prefixs) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalListResult") + deepHashFirebaseStorageMessages(value: items, hasher: &hasher) + deepHashFirebaseStorageMessages(value: pageToken, hasher: &hasher) + deepHashFirebaseStorageMessages(value: prefixs, hasher: &hasher) + } +} + +private class FirebaseStorageMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return InternalStorageTaskState(rawValue: enumResultAsInt) + } + return nil + case 130: + return InternalStorageFirebaseApp.fromList(readValue() as! [Any?]) + case 131: + return InternalStorageReference.fromList(readValue() as! [Any?]) + case 132: + return InternalFullMetaData.fromList(readValue() as! [Any?]) + case 133: + return InternalListOptions.fromList(readValue() as! [Any?]) + case 134: + return InternalSettableMetadata.fromList(readValue() as! [Any?]) + case 135: + return InternalStorageTaskSnapShot.fromList(readValue() as! [Any?]) + case 136: + return InternalListResult.fromList(readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class FirebaseStorageMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? InternalStorageTaskState { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? InternalStorageFirebaseApp { + super.writeByte(130) + super.writeValue(value.toList()) + } else if let value = value as? InternalStorageReference { + super.writeByte(131) + super.writeValue(value.toList()) + } else if let value = value as? InternalFullMetaData { + super.writeByte(132) + super.writeValue(value.toList()) + } else if let value = value as? InternalListOptions { + super.writeByte(133) + super.writeValue(value.toList()) + } else if let value = value as? InternalSettableMetadata { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? InternalStorageTaskSnapShot { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? InternalListResult { + super.writeByte(136) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class FirebaseStorageMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirebaseStorageMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirebaseStorageMessagesPigeonCodecWriter(data: data) + } +} + +class FirebaseStorageMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = + FirebaseStorageMessagesPigeonCodec( + readerWriter: FirebaseStorageMessagesPigeonCodecReaderWriter() + ) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseStorageHostApi { + func getReferencebyPath( + app: InternalStorageFirebaseApp, path: String, bucket: String?, + completion: @escaping (Result) -> Void) + func setMaxOperationRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func setMaxUploadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func setMaxDownloadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func useStorageEmulator( + app: InternalStorageFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void) + func referenceDelete( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetDownloadURL( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetMetaData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceList( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + options: InternalListOptions, + completion: @escaping (Result) -> Void) + func referenceListAll( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + maxSize: Int64, + completion: @escaping (Result) -> Void) + func referencePutData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: FlutterStandardTypedData, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void) + func referencePutString( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: String, format: Int64, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void) + func referencePutFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, settableMetaData: InternalSettableMetadata?, + handle: Int64, completion: @escaping (Result) -> Void) + func referenceDownloadFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, handle: Int64, + completion: @escaping (Result) -> Void) + func referenceUpdateMetadata( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + metadata: InternalSettableMetadata, + completion: @escaping (Result) -> Void) + func taskPause( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) + func taskResume( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) + func taskCancel( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseStorageHostApiSetup { + static var codec: FlutterStandardMessageCodec { + FirebaseStorageMessagesPigeonCodec.shared + } + + /// Sets up an instance of `FirebaseStorageHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseStorageHostApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let getReferencebyPathChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getReferencebyPathChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let pathArg = args[1] as! String + let bucketArg: String? = nilOrValue(args[2]) + api.getReferencebyPath(app: appArg, path: pathArg, bucket: bucketArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getReferencebyPathChannel.setMessageHandler(nil) + } + let setMaxOperationRetryTimeChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setMaxOperationRetryTimeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 + api.setMaxOperationRetryTime(app: appArg, time: timeArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setMaxOperationRetryTimeChannel.setMessageHandler(nil) + } + let setMaxUploadRetryTimeChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setMaxUploadRetryTimeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 + api.setMaxUploadRetryTime(app: appArg, time: timeArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setMaxUploadRetryTimeChannel.setMessageHandler(nil) + } + let setMaxDownloadRetryTimeChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setMaxDownloadRetryTimeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 + api.setMaxDownloadRetryTime(app: appArg, time: timeArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setMaxDownloadRetryTimeChannel.setMessageHandler(nil) + } + let useStorageEmulatorChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + useStorageEmulatorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let hostArg = args[1] as! String + let portArg = args[2] as! Int64 + api.useStorageEmulator(app: appArg, host: hostArg, port: portArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + useStorageEmulatorChannel.setMessageHandler(nil) + } + let referenceDeleteChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceDeleteChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + api.referenceDelete(app: appArg, reference: referenceArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceDeleteChannel.setMessageHandler(nil) + } + let referenceGetDownloadURLChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceGetDownloadURLChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + api.referenceGetDownloadURL(app: appArg, reference: referenceArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceGetDownloadURLChannel.setMessageHandler(nil) + } + let referenceGetMetaDataChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceGetMetaDataChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + api.referenceGetMetaData(app: appArg, reference: referenceArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceGetMetaDataChannel.setMessageHandler(nil) + } + let referenceListChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceListChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let optionsArg = args[2] as! InternalListOptions + api.referenceList(app: appArg, reference: referenceArg, options: optionsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceListChannel.setMessageHandler(nil) + } + let referenceListAllChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceListAllChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + api.referenceListAll(app: appArg, reference: referenceArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceListAllChannel.setMessageHandler(nil) + } + let referenceGetDataChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceGetDataChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let maxSizeArg = args[2] as! Int64 + api.referenceGetData(app: appArg, reference: referenceArg, maxSize: maxSizeArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceGetDataChannel.setMessageHandler(nil) + } + let referencePutDataChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referencePutDataChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let dataArg = args[2] as! FlutterStandardTypedData + let settableMetaDataArg = args[3] as! InternalSettableMetadata + let handleArg = args[4] as! Int64 + api.referencePutData( + app: appArg, + reference: referenceArg, + data: dataArg, + settableMetaData: settableMetaDataArg, + handle: handleArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referencePutDataChannel.setMessageHandler(nil) + } + let referencePutStringChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referencePutStringChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let dataArg = args[2] as! String + let formatArg = args[3] as! Int64 + let settableMetaDataArg = args[4] as! InternalSettableMetadata + let handleArg = args[5] as! Int64 + api.referencePutString( + app: appArg, + reference: referenceArg, + data: dataArg, + format: formatArg, + settableMetaData: settableMetaDataArg, + handle: handleArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referencePutStringChannel.setMessageHandler(nil) + } + let referencePutFileChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referencePutFileChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let filePathArg = args[2] as! String + let settableMetaDataArg: InternalSettableMetadata? = nilOrValue(args[3]) + let handleArg = args[4] as! Int64 + api.referencePutFile( + app: appArg, + reference: referenceArg, + filePath: filePathArg, + settableMetaData: settableMetaDataArg, + handle: handleArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referencePutFileChannel.setMessageHandler(nil) + } + let referenceDownloadFileChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceDownloadFileChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let filePathArg = args[2] as! String + let handleArg = args[3] as! Int64 + api.referenceDownloadFile( + app: appArg, + reference: referenceArg, + filePath: filePathArg, + handle: handleArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceDownloadFileChannel.setMessageHandler(nil) + } + let referenceUpdateMetadataChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + referenceUpdateMetadataChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let metadataArg = args[2] as! InternalSettableMetadata + api + .referenceUpdateMetadata( + app: appArg, reference: referenceArg, + metadata: metadataArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + referenceUpdateMetadataChannel.setMessageHandler(nil) + } + let taskPauseChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + taskPauseChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 + api.taskPause(app: appArg, handle: handleArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + taskPauseChannel.setMessageHandler(nil) + } + let taskResumeChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + taskResumeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 + api.taskResume(app: appArg, handle: handleArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + taskResumeChannel.setMessageHandler(nil) + } + let taskCancelChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + taskCancelChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 + api.taskCancel(app: appArg, handle: handleArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + taskCancelChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/firebase_storage_tvos/tvos/Classes/TaskStateChannelStreamHandler.swift b/packages/firebase_storage_tvos/tvos/Classes/TaskStateChannelStreamHandler.swift new file mode 100644 index 0000000..8c5c5d7 --- /dev/null +++ b/packages/firebase_storage_tvos/tvos/Classes/TaskStateChannelStreamHandler.swift @@ -0,0 +1,163 @@ +// Copyright 2025 The Chromium Authors. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FirebaseStorage +import Foundation + +#if (os(iOS) || os(tvOS)) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class TaskStateChannelStreamHandler: NSObject, FlutterStreamHandler { + private let task: StorageObservableTask + private let storage: Storage + private let identifier: String + + private var successHandle: String? + private var failureHandle: String? + private var pausedHandle: String? + private var progressHandle: String? + + init(task: StorageObservableTask, storage: Storage, identifier: String) { + self.task = task + self.storage = storage + self.identifier = identifier + } + + func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { + successHandle = task.observe(.success) { snapshot in + events([ + "taskState": 2, // success + "appName": self.storage.app.name, + "snapshot": self.parseTaskSnapshot(snapshot), + ]) + self.cleanupObservers() + } + failureHandle = task.observe(.failure) { snapshot in + let err = snapshot.error as NSError? + let errorDict: [String: Any] = self.errorDict(err) + events([ + "taskState": 4, // error (including cancellations as errors per platform contract) + "appName": self.storage.app.name, + "error": errorDict, + ]) + self.cleanupObservers() + } + pausedHandle = task.observe(.pause) { snapshot in + events([ + "taskState": 0, // paused + "appName": self.storage.app.name, + "snapshot": self.parseTaskSnapshot(snapshot), + ]) + } + progressHandle = task.observe(.progress) { snapshot in + events([ + "taskState": 1, // running + "appName": self.storage.app.name, + "snapshot": self.parseTaskSnapshot(snapshot), + ]) + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + cleanupObservers() + return nil + } + + private func cleanupObservers() { + if let h = successHandle { task.removeObserver(withHandle: h) } + if let h = failureHandle { task.removeObserver(withHandle: h) } + if let h = pausedHandle { task.removeObserver(withHandle: h) } + if let h = progressHandle { task.removeObserver(withHandle: h) } + successHandle = nil + failureHandle = nil + pausedHandle = nil + progressHandle = nil + } + + private func parseTaskSnapshot(_ snapshot: StorageTaskSnapshot) -> [String: Any] { + var out: [String: Any] = [:] + out["path"] = snapshot.reference.fullPath + if let md = snapshot.metadata { + out["metadata"] = metaToDict(md) + } + if let progress = snapshot.progress { + out["bytesTransferred"] = progress.completedUnitCount + out["totalBytes"] = progress.totalUnitCount + } else { + out["bytesTransferred"] = 0 + out["totalBytes"] = 0 + } + return out + } + + private func errorDict(_ error: NSError?) -> [String: Any] { + guard let error else { + return [ + "code": "unknown", + "message": "An unknown error occurred", + ] + } + let code: String + if error.domain == StorageErrorDomain, + let storageCode = StorageErrorCode(rawValue: error.code) + { + switch storageCode { + case .objectNotFound: code = "object-not-found" + case .bucketNotFound: code = "bucket-not-found" + case .projectNotFound: code = "project-not-found" + case .quotaExceeded: code = "quota-exceeded" + case .unauthenticated: code = "unauthenticated" + case .unauthorized: code = "unauthorized" + case .retryLimitExceeded: code = "retry-limit-exceeded" + case .cancelled: code = "canceled" + case .downloadSizeExceeded: code = "download-size-exceeded" + @unknown default: code = "unknown" + } + } else if error.domain == NSURLErrorDomain, error.code == NSURLErrorCancelled { + code = "canceled" + } else { + code = "unknown" + } + return [ + "code": code, + "message": standardMessage(for: code) ?? error.localizedDescription, + ] + } + + private func standardMessage(for code: String) -> String? { + switch code { + case "object-not-found": return "No object exists at the desired reference." + case "unauthorized": return "User is not authorized to perform the desired action." + case "canceled": return "The operation was canceled." + default: return nil + } + } + + private func metaToDict(_ md: StorageMetadata) -> [String: Any] { + var out: [String: Any] = [:] + out["name"] = md.name + out["bucket"] = md.bucket + out["generation"] = String(md.generation) + out["metadataGeneration"] = String(md.metageneration) + out["fullPath"] = md.path + out["size"] = md.size + out["creationTimeMillis"] = Int((md.timeCreated?.timeIntervalSince1970 ?? 0) * 1000) + out["updatedTimeMillis"] = Int((md.updated?.timeIntervalSince1970 ?? 0) * 1000) + if let v = md.md5Hash { out["md5Hash"] = v } + if let v = md.cacheControl { out["cacheControl"] = v } + if let v = md.contentDisposition { out["contentDisposition"] = v } + if let v = md.contentEncoding { out["contentEncoding"] = v } + if let v = md.contentLanguage { out["contentLanguage"] = v } + if let v = md.contentType { out["contentType"] = v } + out["customMetadata"] = md.customMetadata ?? [:] + return out + } +} diff --git a/packages/firebase_storage_tvos/tvos/firebase_storage_tvos.podspec b/packages/firebase_storage_tvos/tvos/firebase_storage_tvos.podspec new file mode 100644 index 0000000..6ca3478 --- /dev/null +++ b/packages/firebase_storage_tvos/tvos/firebase_storage_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_storage_tvos.podspec` to validate before publishing. +# +# Generated by `flutter-tvos plugin port`. License holder: fluttertv. +# +Pod::Spec.new do |s| + s.name = 'firebase_storage_tvos' + s.version = '0.0.1' + s.summary = 'tvOS implementation of firebase_storage.' + s.description = <<-DESC +tvOS implementation of firebase_storage, the federated platform +package that ships native code targeting Apple tvOS. + DESC + s.homepage = 'https://github.com/fluttertv/plugins/tree/main/packages/firebase_storage_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/Storage 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 FIRStorage (Firebase/Storage) 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/Storage', '~> 12.15.0' + s.dependency 'firebase_core_tvos' + s.static_framework = true +end