From 7bf12169d6dd85a742c7c0c4e1a8de646acc40c6 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 24 Aug 2026 12:56:30 +0000 Subject: [PATCH 1/4] refactor(firestore,apple): migrate iOS/macOS plugin implementation to Swift Replace the Objective-C Firestore plugin with a Swift implementation so Apple platforms can use Pigeon swiftOut and the typed pipeline Expression APIs. --- .../ios/cloud_firestore.podspec | 7 +- .../ios/cloud_firestore/Package.swift | 23 +- .../Sources/cloud_firestore/Constants.swift | 19 + .../DocumentSnapshotStreamHandler.swift | 77 + .../FLTDocumentSnapshotStreamHandler.m | 84 - .../FLTFirebaseFirestoreExtension.m | 27 - .../FLTFirebaseFirestorePlugin.m | 984 -------- .../FLTFirebaseFirestorePlugin.swift | 786 +++++++ .../FLTFirebaseFirestoreReader.m | 318 --- .../FLTFirebaseFirestoreUtils.m | 263 --- .../FLTFirebaseFirestoreWriter.m | 259 --- .../FLTLoadBundleStreamHandler.m | 80 - .../cloud_firestore/FLTPipelineParser.m | 1772 -------------- .../FLTQuerySnapshotStreamHandler.m | 102 - .../FLTSnapshotsInSyncStreamHandler.m | 44 - .../FLTTransactionStreamHandler.m | 167 -- .../FirebaseFirestoreExtension.swift | 16 + .../FirebaseFirestoreReader.swift | 298 +++ .../FirebaseFirestoreUtils.swift | 223 ++ .../FirebaseFirestoreWriter.swift | 219 ++ .../cloud_firestore/FirestoreMessages.g.m | 2033 ----------------- .../cloud_firestore/FirestoreMessages.g.swift | 1852 +++++++++++++++ .../cloud_firestore/FirestorePigeonParser.m | 311 --- .../LoadBundleStreamHandler.swift | 64 + .../cloud_firestore/PigeonParser.swift | 291 +++ .../cloud_firestore/PipelineParser.swift | 1431 ++++++++++++ .../QuerySnapshotStreamHandler.swift | 90 + .../SnapshotsInSyncStreamHandler.swift | 37 + .../TransactionStreamHandler.swift | 144 ++ .../FLTDocumentSnapshotStreamHandler.h | 37 - .../Private/FLTFirebaseFirestoreExtension.h | 15 - .../Private/FLTFirebaseFirestoreReader.h | 17 - .../Private/FLTFirebaseFirestoreUtils.h | 70 - .../Private/FLTFirebaseFirestoreWriter.h | 16 - .../Private/FLTLoadBundleStreamHandler.h | 40 - .../Private/FLTPipelineParser.h | 23 - .../Private/FLTQuerySnapshotStreamHandler.h | 31 - .../Private/FLTSnapshotsInSyncStreamHandler.h | 23 - .../Private/FLTTransactionStreamHandler.h | 42 - .../Private/FirestorePigeonParser.h | 58 - .../Public/CustomPigeonHeaderFirestore.h | 16 - .../Public/FLTFirebaseFirestorePlugin.h | 23 - .../Public/FirestoreMessages.g.h | 457 ---- .../FLTFirestoreClientLanguage.mm | 4 +- .../include/FLTFirestoreClientLanguage.h | 13 + .../macos/cloud_firestore.podspec | 8 +- .../macos/cloud_firestore/Package.swift | 23 +- .../Sources/cloud_firestore/Constants.swift | 1 + .../DocumentSnapshotStreamHandler.swift | 1 + .../FLTDocumentSnapshotStreamHandler.m | 1 - .../FLTFirebaseFirestoreExtension.m | 1 - .../FLTFirebaseFirestorePlugin.m | 1 - .../FLTFirebaseFirestorePlugin.swift | 1 + .../FLTFirebaseFirestoreReader.m | 1 - .../FLTFirebaseFirestoreUtils.m | 1 - .../FLTFirebaseFirestoreWriter.m | 1 - .../FLTLoadBundleStreamHandler.m | 1 - .../cloud_firestore/FLTPipelineParser.m | 1 - .../FLTQuerySnapshotStreamHandler.m | 1 - .../FLTSnapshotsInSyncStreamHandler.m | 1 - .../FLTTransactionStreamHandler.m | 1 - .../FirebaseFirestoreExtension.swift | 1 + .../FirebaseFirestoreReader.swift | 1 + .../FirebaseFirestoreUtils.swift | 1 + .../FirebaseFirestoreWriter.swift | 1 + .../cloud_firestore/FirestoreMessages.g.m | 1 - .../cloud_firestore/FirestoreMessages.g.swift | 1 + .../cloud_firestore/FirestorePigeonParser.m | 1 - .../LoadBundleStreamHandler.swift | 1 + .../cloud_firestore/PigeonParser.swift | 1 + .../cloud_firestore/PipelineParser.swift | 1 + .../QuerySnapshotStreamHandler.swift | 1 + .../cloud_firestore/Resources/.gitkeep | 1 + .../SnapshotsInSyncStreamHandler.swift | 1 + .../TransactionStreamHandler.swift | 1 + .../FLTDocumentSnapshotStreamHandler.h | 1 - .../Private/FLTFirebaseFirestoreExtension.h | 1 - .../Private/FLTFirebaseFirestoreReader.h | 1 - .../Private/FLTFirebaseFirestoreUtils.h | 1 - .../Private/FLTFirebaseFirestoreWriter.h | 1 - .../Private/FLTLoadBundleStreamHandler.h | 1 - .../Private/FLTPipelineParser.h | 1 - .../Private/FLTQuerySnapshotStreamHandler.h | 1 - .../Private/FLTSnapshotsInSyncStreamHandler.h | 1 - .../Private/FLTTransactionStreamHandler.h | 1 - .../Private/FirestorePigeonParser.h | 1 - .../Public/CustomPigeonHeaderFirestore.h | 1 - .../Public/FLTFirebaseFirestorePlugin.h | 1 - .../Public/FirestoreMessages.g.h | 1 - .../FLTFirestoreClientLanguage.mm | 1 + .../include/FLTFirestoreClientLanguage.h | 1 + .../cloud_firestore/windows/messages.g.h | 87 - .../pigeons/generate_pigeon.sh | 33 +- .../pigeons/messages.dart | 6 +- .../test/pigeon/test_api.dart | 398 ++-- scripts/generate_versions_spm.dart | 1 + 96 files changed, 5782 insertions(+), 7724 deletions(-) create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/Constants.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h delete mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h rename packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/{cloud_firestore => cloud_firestore_objc}/FLTFirestoreClientLanguage.mm (87%) create mode 100644 packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Constants.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift mode change 100644 => 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h delete mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm create mode 120000 packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore.podspec b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore.podspec index 5f6046a9d0df..53886b85e07e 100755 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore.podspec +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore.podspec @@ -24,10 +24,10 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'cloud_firestore/Sources/cloud_firestore/**/*.{h,m,mm}' - s.public_header_files = 'cloud_firestore/Sources/cloud_firestore/include/Public/**/*.h' - s.private_header_files = 'cloud_firestore/Sources/cloud_firestore/include/Private/**/*.h' + s.source_files = 'cloud_firestore/Sources/**/*.{swift,h,m,mm}' + s.public_header_files = 'cloud_firestore/Sources/cloud_firestore_objc/include/*.h' + s.swift_version = '5.0' s.ios.deployment_target = '15.0' s.dependency 'Flutter' @@ -36,7 +36,6 @@ Pod::Spec.new do |s| s.static_framework = true s.pod_target_xcconfig = { - 'GCC_PREPROCESSOR_DEFINITIONS' => "LIBRARY_VERSION=\\\"#{library_version}\\\" LIBRARY_NAME=\\\"flutter-fire-fst\\\"", 'DEFINES_MODULE' => 'YES' } end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift index 6061aa127c64..2b69d01b790f 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift @@ -7,7 +7,6 @@ import PackageDescription -let libraryVersion = "6.8.0" let firebaseSdkVersion: Version = "12.18.0" let package = Package( @@ -24,22 +23,30 @@ let package = Package( .package(name: "FlutterFramework", path: "../FlutterFramework"), ], targets: [ + // SPM does not allow mixing Swift and ObjC in a single target. + .target( + name: "cloud_firestore_objc", + dependencies: [ + .product(name: "FirebaseFirestore", package: "firebase-ios-sdk") + ], + path: "Sources/cloud_firestore_objc", + publicHeadersPath: "include", + cSettings: [ + .headerSearchPath("include") + ] + ), .target( name: "cloud_firestore", dependencies: [ + "cloud_firestore_objc", .product(name: "FirebaseFirestore", package: "firebase-ios-sdk"), .product(name: "firebase-core", package: "firebase_core"), .product(name: "FlutterFramework", package: "FlutterFramework"), ], + path: "Sources/cloud_firestore", resources: [ .process("Resources") - ], - cSettings: [ - .headerSearchPath("include/cloud_firestore/Private"), - .headerSearchPath("include/cloud_firestore/Public"), - .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), - .define("LIBRARY_NAME", to: "\"flutter-fire-fst\""), ] - ) + ), ] ) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/Constants.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/Constants.swift new file mode 100644 index 000000000000..23cc892df8a9 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/Constants.swift @@ -0,0 +1,19 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Auto-generated file. Do not edit. +public let versionNumber = "6.8.0" + +let kFLTFirebaseFirestoreChannelName = "plugins.flutter.io/firebase_firestore" +let kFLTFirebaseFirestoreQuerySnapshotEventChannelName = + "plugins.flutter.io/firebase_firestore/query" +let kFLTFirebaseFirestoreDocumentSnapshotEventChannelName = + "plugins.flutter.io/firebase_firestore/document" +let kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName = + "plugins.flutter.io/firebase_firestore/snapshotsInSync" +let kFLTFirebaseFirestoreTransactionChannelName = + "plugins.flutter.io/firebase_firestore/transaction" +let kFLTFirebaseFirestoreLoadBundleChannelName = + "plugins.flutter.io/firebase_firestore/loadBundle" +let kFirebaseFirestoreLibraryName = "flutter-fire-fst" diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift new file mode 100644 index 000000000000..2c55647ad283 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift @@ -0,0 +1,77 @@ +// 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 Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { + private let firestore: Firestore + private let reference: DocumentReference + private let includeMetadataChanges: Bool + private let serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior + private let source: FirebaseFirestore.ListenSource + private var listenerRegistration: ListenerRegistration? + + init(firestore: Firestore, + reference: DocumentReference, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource) { + self.firestore = firestore + self.reference = reference + self.includeMetadataChanges = includeMetadataChanges + self.serverTimestampBehavior = serverTimestampBehavior + self.source = source + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + let options = SnapshotListenOptions() + .withIncludeMetadataChanges(includeMetadataChanges) + .withSource(source) + + listenerRegistration = reference.addSnapshotListener(options: options) { snapshot, error in + if let error { + let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) + DispatchQueue.main.async { + events( + FLTFirebasePlugin.createFlutterError( + fromCode: code, + message: message, + optionalDetails: ["code": code, "message": message], + andOptionalNSError: error as NSError + ) + ) + } + } else if let snapshot { + DispatchQueue.main.async { + events( + PigeonParser.toPigeonDocumentSnapshot( + snapshot, serverTimestampBehavior: self.serverTimestampBehavior + ) + ) + } + } + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + listenerRegistration?.remove() + listenerRegistration = nil + return nil + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m deleted file mode 100644 index e8119c70d761..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m +++ /dev/null @@ -1,84 +0,0 @@ -// 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 -#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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m deleted file mode 100644 index 33fd92271fc4..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m +++ /dev/null @@ -1,27 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m deleted file mode 100644 index 667ce9c43ba0..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m +++ /dev/null @@ -1,984 +0,0 @@ -// 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 -#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; - @synchronized(self->_transactions) { - 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) { - // Called from Firestore's transaction worker queue; multiple - // in-flight transactions may hit this concurrently. - @synchronized(self->_transactions) { - self->_transactions[transactionId] = transaction; - } - } - ended:^{ - @synchronized(self->_transactions) { - [self->_transactions removeObjectForKey:transactionId]; - } - }]; - - _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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift new file mode 100644 index 000000000000..840c5130bd00 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift @@ -0,0 +1,786 @@ +// 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 FirebaseCore +import FirebaseFirestore +import Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if canImport(cloud_firestore_objc) + import cloud_firestore_objc +#endif + +#if os(iOS) + import Flutter + import UIKit +#elseif os(macOS) + import AppKit + import FlutterMacOS +#endif + +@objc(FLTFirebaseFirestorePlugin) +public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, + FirebaseFirestoreHostApi { + private var messenger: FlutterBinaryMessenger + private var transactions: [String: Transaction] = [:] + private var eventChannels: [String: FlutterEventChannel] = [:] + private var streamHandlers: [String: NSObject & FlutterStreamHandler] = [:] + private var transactionHandlers: [String: TransactionStreamHandler] = [:] + private let transactionLock = NSLock() + + static let serverTimestampMap = NSCache() + + private static let codec = FlutterStandardMethodCodec( + readerWriter: FirestoreMessagesPigeonCodecReaderWriter() + ) + + init(messenger: FlutterBinaryMessenger) { + self.messenger = messenger + super.init() + FLTFirebasePluginRegistry.sharedInstance().register(self) + } + + @objc + public static func register(with registrar: FlutterPluginRegistrar) { + #if os(macOS) + let binaryMessenger = registrar.messenger + #else + let binaryMessenger = registrar.messenger() + #endif + + let instance = FLTFirebaseFirestorePlugin(messenger: binaryMessenger) + #if os(iOS) + registrar.publish(instance) + FLTFirestoreClientLanguage.setClientLanguage("gl-dart/\(versionNumber)") + #endif + + FirebaseFirestoreHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) + } + + public func detachFromEngine(for registrar: FlutterPluginRegistrar) { + cleanupEventListeners() + } + + public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { + cleanupEventListeners() + cleanupFirestoreInstances(completion) + } + + public func pluginConstants(for firebaseApp: FirebaseApp) -> [AnyHashable: Any] { + [:] + } + + @objc public func firebaseLibraryName() -> String { + kFirebaseFirestoreLibraryName + } + + @objc public func firebaseLibraryVersion() -> String { + versionNumber + } + + @objc public func flutterChannelName() -> String { + kFLTFirebaseFirestoreChannelName + } + + private func cleanupEventListeners() { + for channel in eventChannels.values { + channel.setStreamHandler(nil) + } + eventChannels.removeAll() + for handler in streamHandlers.values { + _ = handler.onCancel(withArguments: nil) + } + streamHandlers.removeAll() + transactionLock.lock() + transactions.removeAll() + transactionLock.unlock() + } + + private func cleanupFirestoreInstances(_ completion: (() -> Void)?) { + if FirebaseFirestoreUtils.count > 0 { + FirebaseFirestoreUtils.cleanupFirestoreInstances(completion) + } else { + completion?() + } + } + + @discardableResult + private func registerEventChannel(prefix: String, + identifier: String = UUID().uuidString.lowercased(), + streamHandler: NSObject & FlutterStreamHandler) -> String { + let channelName = "\(prefix)/\(identifier)" + let channel = FlutterEventChannel( + name: channelName, + binaryMessenger: messenger, + codec: Self.codec + ) + channel.setStreamHandler(streamHandler) + eventChannels[identifier] = channel + streamHandlers[identifier] = streamHandler + return identifier + } + + private func firestore(from pigeonApp: FirestorePigeonFirebaseApp) -> Firestore { + objc_sync_enter(self) + defer { objc_sync_exit(self) } + + let app = FLTFirebasePlugin.firebaseAppNamed(pigeonApp.appName)! + if let cached = FirebaseFirestoreUtils.firestoreInstance( + appName: app.name, databaseURL: pigeonApp.databaseURL + ) { + return cached + } + + let settings = FirestoreSettings() + if let persistenceEnabled = pigeonApp.settings.persistenceEnabled { + var size = NSNumber(value: FirestoreCacheSizeUnlimited) + if let cacheSizeBytes = pigeonApp.settings.cacheSizeBytes, cacheSizeBytes != -1 { + size = NSNumber(value: cacheSizeBytes) + } + if persistenceEnabled { + settings.cacheSettings = PersistentCacheSettings(sizeBytes: size) + } else { + settings.cacheSettings = MemoryCacheSettings( + garbageCollectorSettings: MemoryLRUGCSettings() + ) + } + } + + if let host = pigeonApp.settings.host { + settings.host = host + if let sslEnabled = pigeonApp.settings.sslEnabled { + settings.isSSLEnabled = sslEnabled + } + } + + settings.dispatchQueue = FirebaseFirestoreReader.firestoreQueue + + let firestore = Firestore.firestore(app: app, database: pigeonApp.databaseURL) + firestore.settings = settings + FirebaseFirestoreUtils.setCachedInstance( + firestore, appName: app.name, databaseURL: pigeonApp.databaseURL + ) + return firestore + } + + private func completeOnError(_ error: Error, + _ completion: @escaping (Result) -> Void) { + completion(.failure(FirebaseFirestoreUtils.flutterError(from: error))) + } + + func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let identifier = registerEventChannel( + prefix: kFLTFirebaseFirestoreLoadBundleChannelName, + streamHandler: LoadBundleStreamHandler(firestore: firestore, bundle: bundle) + ) + completion(.success(identifier)) + } + + func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let source = PigeonParser.parseSource(options.source) + let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( + options.serverTimestampBehavior + ) + + firestore.getQuery(named: name) { query in + guard let query else { + completion( + .failure( + FlutterError( + code: "non-existent-named-query", + message: + "Named query has not been found. Please check it has been loaded properly via loadBundle().", + details: nil + ) + ) + ) + return + } + query.getDocuments(source: source) { snapshot, error in + if let error { + self.completeOnError(error, completion) + } else if let snapshot { + completion( + .success( + PigeonParser.toPigeonQuerySnapshot( + snapshot, serverTimestampBehavior: serverTimestampBehavior + ) + ) + ) + } + } + } + } + + func clearPersistence(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + firestore(from: app).clearPersistence { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func disableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + firestore(from: app).disableNetwork { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func enableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + firestore(from: app).enableNetwork { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func terminate(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + firestore.terminate { error in + if let error { + self.completeOnError(error, completion) + } else { + let extensionInstance = FirebaseFirestoreUtils.cachedInstance(for: firestore) + FirebaseFirestoreUtils.destroyCachedInstance( + appName: firestore.app.name, databaseURL: extensionInstance.databaseURL + ) + completion(.success(())) + } + } + } + + func waitForPendingWrites(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + firestore(from: app).waitForPendingWrites { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void) { + firestore(from: app).setIndexConfiguration(indexConfiguration) { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func setLoggingEnabled(loggingEnabled: Bool, + completion: @escaping (Result) -> Void) { + Firestore.enableLogging(loggingEnabled) + completion(.success(())) + } + + func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let identifier = registerEventChannel( + prefix: kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName, + streamHandler: SnapshotsInSyncStreamHandler(firestore: firestore) + ) + completion(.success(identifier)) + } + + func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let transactionId = UUID().uuidString.lowercased() + let handler = TransactionStreamHandler( + id: transactionId, + firestore: firestore, + timeout: Int(timeout), + maxAttempts: Int(maxAttempts), + started: { [weak self] transaction in + guard let self else { return } + self.transactionLock.lock() + self.transactions[transactionId] = transaction + self.transactionLock.unlock() + }, + ended: { [weak self] in + guard let self else { return } + self.transactionLock.lock() + self.transactions.removeValue(forKey: transactionId) + self.transactionLock.unlock() + } + ) + transactionHandlers[transactionId] = handler + let identifier = registerEventChannel( + prefix: kFLTFirebaseFirestoreTransactionChannelName, + identifier: transactionId, + streamHandler: handler + ) + completion(.success(identifier)) + } + + func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void) { + transactionHandlers[transactionId]?.receiveTransactionResponse(resultType, commands: commands) + completion(.success(())) + } + + func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void) { + DispatchQueue.global(qos: .default).async { + let firestore = self.firestore(from: app) + let document = firestore.document(path) + + self.transactionLock.lock() + let transaction = self.transactions[transactionId] + self.transactionLock.unlock() + + guard let transaction else { + completion( + .failure( + FlutterError( + code: "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 + } + + do { + let snapshot = try transaction.getDocument(document) + completion( + .success( + PigeonParser.toPigeonDocumentSnapshot( + snapshot, serverTimestampBehavior: .none + ) + ) + ) + } catch { + self.completeOnError(error, completion) + } + } + } + + func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { + let document = firestore(from: app).document(request.path) + let data = request.data as? [String: Any] ?? [:] + let finish: (Error?) -> Void = { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + + if request.option?.merge == true { + document.setData(data, merge: true, completion: finish) + } else if let mergeFields = request.option?.mergeFields { + document.setData( + data, mergeFields: PigeonParser.parseFieldPath(mergeFields), completion: finish + ) + } else { + document.setData(data, completion: finish) + } + } + + func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { + let document = firestore(from: app).document(request.path) + let data = request.data as? [AnyHashable: Any] ?? [:] + document.updateData(data) { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) + -> Void) { + let document = firestore(from: app).document(request.path) + let source = PigeonParser.parseSource(request.source ?? .serverAndCache) + let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( + request.serverTimestampBehavior ?? .none + ) + document.getDocument(source: source) { snapshot, error in + if let error { + self.completeOnError(error, completion) + } else if let snapshot { + completion( + .success( + PigeonParser.toPigeonDocumentSnapshot( + snapshot, serverTimestampBehavior: serverTimestampBehavior + ) + ) + ) + } + } + } + + func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { + firestore(from: app).document(request.path).delete { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + guard + let query = PigeonParser.parseQuery( + parameters: parameters, firestore: firestore, path: path, + isCollectionGroup: isCollectionGroup + ) + else { + completion( + .failure( + FlutterError( + code: "error-parsing", + message: + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + details: nil + ) + ) + ) + return + } + + let source = PigeonParser.parseSource(options.source) + let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( + options.serverTimestampBehavior + ) + query.getDocuments(source: source) { snapshot, error in + if let error { + self.completeOnError(error, completion) + } else if let snapshot { + completion( + .success( + PigeonParser.toPigeonQuerySnapshot( + snapshot, serverTimestampBehavior: serverTimestampBehavior + ) + ) + ) + } + } + } + + func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, + source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) { + let firestore = firestore(from: app) + guard + let query = PigeonParser.parseQuery( + parameters: parameters, firestore: firestore, path: path, + isCollectionGroup: isCollectionGroup + ) + else { + completion( + .failure( + FlutterError( + code: "error-parsing", + message: + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + details: nil + ) + ) + ) + return + } + + var aggregateFields: [AggregateField] = [] + for queryRequest in queries.compactMap({ $0 }) { + switch queryRequest.type { + case .count: + aggregateFields.append(AggregateField.count()) + case .sum: + if let field = queryRequest.field { + aggregateFields.append(AggregateField.sum(field)) + } + case .average: + if let field = queryRequest.field { + aggregateFields.append(AggregateField.average(field)) + } + } + } + + let firebaseAggregateQuery: FirebaseFirestore.AggregateQuery = query.aggregate( + aggregateFields + ) + firebaseAggregateQuery.getAggregation(source: .server) { snapshot, error in + if let error { + self.completeOnError(error, completion) + return + } + guard let snapshot else { + completion(.success([])) + return + } + + var responses: [AggregateQueryResponse?] = [] + for queryRequest in queries.compactMap({ $0 }) { + switch queryRequest.type { + case .count: + responses.append( + AggregateQueryResponse( + type: .count, field: nil, value: snapshot.count.doubleValue + ) + ) + case .sum: + let value = + snapshot.get(AggregateField.sum(queryRequest.field ?? "")) as? NSNumber + responses.append( + AggregateQueryResponse( + type: .sum, field: queryRequest.field, value: value?.doubleValue + ) + ) + case .average: + let value = + snapshot.get(AggregateField.average(queryRequest.field ?? "")) as? NSNumber + responses.append( + AggregateQueryResponse( + type: .average, field: queryRequest.field, value: value?.doubleValue + ) + ) + } + } + completion(.success(responses)) + } + } + + func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let batch = firestore.batch() + for write in writes.compactMap({ $0 }) { + let reference = firestore.document(write.path) + switch write.type { + case .get: + break + case .deleteType: + batch.deleteDocument(reference) + case .update: + if let data = write.data as? [AnyHashable: Any] { + batch.updateData(data, forDocument: reference) + } + case .set: + let data = write.data as? [String: Any] ?? [:] + if write.option?.merge == true { + batch.setData(data, forDocument: reference, merge: true) + } else if let mergeFields = write.option?.mergeFields { + batch.setData( + data, forDocument: reference, mergeFields: PigeonParser.parseFieldPath(mergeFields) + ) + } else { + batch.setData(data, forDocument: reference) + } + } + } + batch.commit { error in + if let error { + self.completeOnError(error, completion) + } else { + completion(.success(())) + } + } + } + + func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let query = PigeonParser.parseQuery( + parameters: parameters, firestore: firestore, path: path, isCollectionGroup: isCollectionGroup + ) + if query == nil { + completion( + .failure( + FlutterError( + code: "error-parsing", + message: + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + details: nil + ) + ) + ) + return + } + + let identifier = registerEventChannel( + prefix: kFLTFirebaseFirestoreQuerySnapshotEventChannelName, + streamHandler: QuerySnapshotStreamHandler( + firestore: firestore, + query: query, + includeMetadataChanges: includeMetadataChanges, + serverTimestampBehavior: PigeonParser.parseServerTimestampBehavior( + options.serverTimestampBehavior + ), + source: PigeonParser.parseListenSource(source) + ) + ) + completion(.success(identifier)) + } + + func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let document = firestore.document(parameters.path) + let identifier = registerEventChannel( + prefix: kFLTFirebaseFirestoreDocumentSnapshotEventChannelName, + streamHandler: DocumentSnapshotStreamHandler( + firestore: firestore, + reference: document, + includeMetadataChanges: includeMetadataChanges, + serverTimestampBehavior: PigeonParser.parseServerTimestampBehavior( + parameters.serverTimestampBehavior ?? .none + ), + source: PigeonParser.parseListenSource(source) + ) + ) + completion(.success(identifier)) + } + + func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void) { + if let manager = firestore(from: app).persistentCacheIndexManager { + switch request { + case .enableIndexAutoCreation: + manager.enableIndexAutoCreation() + case .disableIndexAutoCreation: + manager.disableIndexAutoCreation() + case .deleteAllIndexes: + manager.deleteAllIndexes() + } + } else { + NSLog("FLTFirebaseFirestore: `PersistentCacheIndexManager` is not available.") + } + completion(.success(())) + } + + func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void) { + let firestore = firestore(from: app) + let mappedStages: [[String: Any?]] = stages.compactMap { stage in + guard let stage else { return nil } + var mapped: [String: Any?] = [:] + for (key, value) in stage { + if let key { + mapped[key] = value + } + } + return mapped + } + var mappedOptions: [String: Any?]? + if let options { + var mapped: [String: Any?] = [:] + for (key, value) in options { + if let key { + mapped[key] = value + } + } + mappedOptions = mapped + } + + PipelineParser.executePipeline( + firestore: firestore, stages: mappedStages, options: mappedOptions + ) { snapshot, error in + if let error { + self.completeOnError(error, completion) + return + } + guard let snapshot else { + completion( + .failure( + FlutterError( + code: "error", + message: "Pipeline execution returned no result", + details: nil + ) + ) + ) + return + } + + func timestampToMs(_ value: Any?) -> Int64? { + if value == nil || value is NSNull { return nil } + if let number = value as? NSNumber { return number.int64Value } + if let timestamp = value as? Timestamp { + return timestamp.seconds * 1000 + Int64(timestamp.nanoseconds) / 1_000_000 + } + return nil + } + + var pigeonResults: [InternalPipelineResult?] = [] + if let results = (snapshot as AnyObject).value(forKey: "results") as? [Any] { + for result in results { + let object = result as AnyObject + let ref = object.value(forKey: "reference") as AnyObject? + let path = + (ref?.value(forKey: "path") as? String) + ?? (object.value(forKey: "documentID") as? String) + let data = object.value(forKey: "data") as? [String: Any] + let mappedData: [String?: Any?]? = data.map { + Dictionary(uniqueKeysWithValues: $0.map { ($0.key as String?, $0.value as Any?) }) + } + pigeonResults.append( + InternalPipelineResult( + documentPath: path, + createTime: timestampToMs(object.value(forKey: "create_time")), + updateTime: timestampToMs(object.value(forKey: "update_time")), + data: mappedData + ) + ) + } + } + + var executionTime = timestampToMs((snapshot as AnyObject).value(forKey: "execution_time")) + if executionTime == nil { + executionTime = Int64(Date().timeIntervalSince1970 * 1000) + } + completion( + .success( + InternalPipelineSnapshot(results: pigeonResults, executionTime: executionTime ?? 0) + ) + ) + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m deleted file mode 100644 index edfa4cba17ae..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m +++ /dev/null @@ -1,318 +0,0 @@ -// 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 -#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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m deleted file mode 100644 index c1e557790706..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m +++ /dev/null @@ -1,263 +0,0 @@ -// 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; - } - - if (error.localizedDescription.length > 0) { - message = error.localizedDescription; - } - - return @[ code, message ]; -} - -@end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m deleted file mode 100644 index cdcd84ae3b78..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m +++ /dev/null @@ -1,259 +0,0 @@ -// 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" - -static const UInt8 FLTStandardFieldList = 12; -static const UInt8 FLTStandardFieldMap = 13; - -@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]]) { - [self writeValue:[self FIRDocumentSnapshot:value]]; - } else if ([value isKindOfClass:[FIRLoadBundleTaskProgress class]]) { - [self writeValue:[self FIRLoadBundleTaskProgress:value]]; - } else if ([value isKindOfClass:[FIRQuerySnapshot class]]) { - [self writeValue:[self FIRQuerySnapshot:value]]; - } else if ([value isKindOfClass:[FIRDocumentChange class]]) { - [self writeValue:[self FIRDocumentChange:value]]; - } else if ([value isKindOfClass:[FIRSnapshotMetadata class]]) { - [self writeValue:[self FIRSnapshotMetadata:value]]; - } else if ([value isKindOfClass:[NSArray class]]) { - NSArray *list = value; - [self writeByte:FLTStandardFieldList]; - [self writeSize:(UInt32)list.count]; - for (id item in list) { - [self writeValue:item]; - } - } else if ([value isKindOfClass:[NSDictionary class]]) { - NSDictionary *map = value; - [self writeByte:FLTStandardFieldMap]; - [self writeSize:(UInt32)map.count]; - for (id key in map) { - [self writeValue:key]; - [self writeValue:map[key]]; - } - } 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m deleted file mode 100644 index cc8bd9be9ae2..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m +++ /dev/null @@ -1,80 +0,0 @@ -// 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 -#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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m deleted file mode 100644 index ed2838214342..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m +++ /dev/null @@ -1,1772 +0,0 @@ -/* - * 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, nor - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"and"] || [name isEqualToString:@"or"] || - [name isEqualToString:@"xor"] || [name isEqualToString:@"nor"]) { - 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 + find + replacement: string_replace_one / string_replace_all - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"string_replace_one"] || - [name isEqualToString:@"string_replace_all"]) { - 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( - [NSString stringWithFormat:@"%@ requires expression, find, and replacement", name]); - 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(name, @[ expr, find, replacement ]); - } - - // ------------------------------------------------------------------------- - // expression + search/repetitions: string_index_of / string_repeat - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"string_index_of"] || [name isEqualToString:@"string_repeat"]) { - id exprMap = args[@"expression"]; - NSString *argumentName = [name isEqualToString:@"string_index_of"] ? @"search" : @"repetitions"; - id argumentMap = args[argumentName]; - if (![exprMap isKindOfClass:[NSDictionary class]] || - ![argumentMap isKindOfClass:[NSDictionary class]]) { - if (error) - *error = parseError( - [NSString stringWithFormat:@"%@ requires expression and %@", name, argumentName]); - return nil; - } - FIRExprBridge *expr = [self parseExpression:exprMap error:error]; - FIRExprBridge *argument = [self parseExpression:argumentMap error:error]; - if (!expr || !argument) return nil; - return FLTNewFunctionExprBridge(name, @[ expr, argument ]); - } - - // ------------------------------------------------------------------------- - // expression + optional value: ltrim / rtrim - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"ltrim"] || [name isEqualToString:@"rtrim"]) { - 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; - - id valueMap = args[@"value"]; - if (![valueMap isKindOfClass:[NSDictionary class]]) { - return FLTNewFunctionExprBridge(name, @[ expr ]); - } - FIRExprBridge *value = [self parseExpression:valueMap error:error]; - if (!value) return nil; - return FLTNewFunctionExprBridge(name, @[ expr, value ]); - } - - // ------------------------------------------------------------------------- - // 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 ]); - } - - // ------------------------------------------------------------------------- - // map_keys, map_values (unary on map expression) - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"map_keys"] || [name isEqualToString:@"map_values"]) { - id exprMap = args[@"expression"]; - if (![exprMap isKindOfClass:[NSDictionary class]]) { - if (error) *error = parseError([NSString stringWithFormat:@"%@ requires expression", name]); - return nil; - } - FIRExprBridge *expr = [self parseExpression:(NSDictionary *)exprMap error:error]; - if (!expr) return nil; - return FLTNewFunctionExprBridge(name, @[ expr ]); - } - - // ------------------------------------------------------------------------- - // parent: doc_ref path or expression - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"parent"]) { - NSString *docPath = args[@"doc_ref"]; - if ([docPath isKindOfClass:[NSString class]] && docPath.length > 0) { - FIRDocumentReference *ref = [self.firestore documentWithPath:docPath]; - FIRExprBridge *refExpr = [[FIRConstantBridge alloc] init:ref]; - return FLTNewFunctionExprBridge(@"parent", @[ refExpr ]); - } - id exprMap = args[@"expression"]; - if (![exprMap isKindOfClass:[NSDictionary class]]) { - if (error) *error = parseError(@"parent requires expression or doc_ref"); - return nil; - } - FIRExprBridge *expr = [self parseExpression:(NSDictionary *)exprMap error:error]; - if (!expr) return nil; - return FLTNewFunctionExprBridge(@"parent", @[ expr ]); - } - - // ------------------------------------------------------------------------- - // timestamp_diff: end, start, unit (unit = string or expression map) - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"timestamp_diff"]) { - id endMap = args[@"end"]; - id startMap = args[@"start"]; - id unitObj = args[@"unit"]; - if (![endMap isKindOfClass:[NSDictionary class]] || - ![startMap isKindOfClass:[NSDictionary class]] || !unitObj) { - if (error) *error = parseError(@"timestamp_diff requires end, start, and unit"); - return nil; - } - FIRExprBridge *endExpr = [self parseExpression:(NSDictionary *)endMap error:error]; - FIRExprBridge *startExpr = [self parseExpression:(NSDictionary *)startMap error:error]; - if (!endExpr || !startExpr) return nil; - FIRExprBridge *unitExpr = nil; - if ([unitObj isKindOfClass:[NSString class]]) { - unitExpr = [[FIRConstantBridge alloc] init:unitObj]; - } else if ([unitObj isKindOfClass:[NSDictionary class]]) { - unitExpr = [self parseExpression:(NSDictionary *)unitObj error:error]; - } else { - if (error) *error = parseError(@"timestamp_diff unit must be string or expression"); - return nil; - } - if (!unitExpr) return nil; - return FLTNewFunctionExprBridge(@"timestamp_diff", @[ endExpr, startExpr, unitExpr ]); - } - - // ------------------------------------------------------------------------- - // timestamp_extract: timestamp, part; optional timezone (string or expression) - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"timestamp_extract"]) { - id timestampMap = args[@"timestamp"]; - id partMap = args[@"part"]; - if (![timestampMap isKindOfClass:[NSDictionary class]] || - ![partMap isKindOfClass:[NSDictionary class]]) { - if (error) *error = parseError(@"timestamp_extract requires timestamp and part"); - return nil; - } - FIRExprBridge *tsExpr = [self parseExpression:(NSDictionary *)timestampMap error:error]; - FIRExprBridge *partExpr = [self parseExpression:(NSDictionary *)partMap error:error]; - if (!tsExpr || !partExpr) return nil; - id tzRaw = args[@"timezone"]; - if (tzRaw == nil) { - return FLTNewFunctionExprBridge(@"timestamp_extract", @[ tsExpr, partExpr ]); - } - FIRExprBridge *tzExpr = nil; - if ([tzRaw isKindOfClass:[NSString class]]) { - tzExpr = [[FIRConstantBridge alloc] init:tzRaw]; - } else if ([tzRaw isKindOfClass:[NSDictionary class]]) { - tzExpr = [self parseExpression:(NSDictionary *)tzRaw error:error]; - } else { - if (error) *error = parseError(@"timestamp_extract timezone must be string or expression"); - return nil; - } - if (!tzExpr) return nil; - return FLTNewFunctionExprBridge(@"timestamp_extract", @[ tsExpr, partExpr, tzExpr ]); - } - - // ------------------------------------------------------------------------- - // if_null: expression + replacement - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"if_null"]) { - id exprMap = args[@"expression"]; - id replMap = args[@"replacement"]; - if (![exprMap isKindOfClass:[NSDictionary class]] || - ![replMap isKindOfClass:[NSDictionary class]]) { - if (error) *error = parseError(@"if_null requires expression and replacement"); - return nil; - } - FIRExprBridge *ifExpr = [self parseExpression:(NSDictionary *)exprMap error:error]; - FIRExprBridge *replExpr = [self parseExpression:(NSDictionary *)replMap error:error]; - if (!ifExpr || !replExpr) return nil; - return FLTNewFunctionExprBridge(@"if_null", @[ ifExpr, replExpr ]); - } - - // ------------------------------------------------------------------------- - // coalesce: expressions[] (>= 2) - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"coalesce"]) { - NSArray *exprMaps = args[@"expressions"]; - if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count < 2) { - if (error) *error = parseError(@"coalesce requires at least two expressions"); - return nil; - } - NSMutableArray *exprs = [NSMutableArray array]; - for (id em in exprMaps) { - if (![em isKindOfClass:[NSDictionary class]]) continue; - FIRExprBridge *e = [self parseExpression:(NSDictionary *)em error:error]; - if (!e) return nil; - [exprs addObject:e]; - } - if (exprs.count < 2) { - if (error) *error = parseError(@"coalesce requires at least two expressions"); - return nil; - } - return FLTNewFunctionExprBridge(@"coalesce", exprs); - } - - // ------------------------------------------------------------------------- - // switch_on: alternating condition (bool), result (expr), optional default - // ------------------------------------------------------------------------- - if ([name isEqualToString:@"switch_on"]) { - NSArray *exprMaps = args[@"expressions"]; - if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count < 2) { - if (error) *error = parseError(@"switch_on requires at least two expressions"); - return nil; - } - NSUInteger n = exprMaps.count; - FIRExprBridge *first = [self parseBooleanExpression:exprMaps[0] error:error]; - FIRExprBridge *second = [self parseExpression:exprMaps[1] error:error]; - if (!first || !second) return nil; - if (n == 2) { - return FLTNewFunctionExprBridge(@"switch_on", @[ first, second ]); - } - NSMutableArray *rest = [NSMutableArray array]; - for (NSUInteger i = 2; i < n; i++) { - FIRExprBridge *e = nil; - if ((n % 2 == 1) && (i == n - 1)) { - e = [self parseExpression:exprMaps[i] error:error]; - } else if (i % 2 == 0) { - e = [self parseBooleanExpression:exprMaps[i] error:error]; - } else { - e = [self parseExpression:exprMaps[i] error:error]; - } - if (!e) return nil; - [rest addObject:e]; - } - NSMutableArray *all = [NSMutableArray arrayWithObjects:first, second, nil]; - [all addObjectsFromArray:rest]; - return FLTNewFunctionExprBridge(@"switch_on", all); - } - - // ------------------------------------------------------------------------- - // 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m deleted file mode 100644 index a821cc35e94f..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m +++ /dev/null @@ -1,102 +0,0 @@ -// 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 -#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; -@property(nonatomic) dispatch_queue_t snapshotQueue; -@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; - _snapshotQueue = dispatch_queue_create("io.flutter.plugins.firebase.firestore.query_snapshot", - DISPATCH_QUEUE_SERIAL); - } - 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(self.snapshotQueue, ^{ - // 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`. - InternalQuerySnapshot *pigeonSnapshot = - [FirestorePigeonParser toPigeonQuerySnapshot:snapshot - serverTimestampBehavior:self.serverTimestampBehavior]; - dispatch_async(dispatch_get_main_queue(), ^{ - events(pigeonSnapshot); - }); - }); - } - }; - - 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m deleted file mode 100644 index 1003be610006..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m +++ /dev/null @@ -1,44 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m deleted file mode 100644 index 8cd347b0b269..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m +++ /dev/null @@ -1,167 +0,0 @@ -// 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 -#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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift new file mode 100644 index 000000000000..28820e1a2973 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift @@ -0,0 +1,16 @@ +// 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 Foundation + +final class FirebaseFirestoreExtension { + let instance: Firestore + let databaseURL: String + + init(firestoreInstance firestore: Firestore, databaseURL: String) { + instance = firestore + self.databaseURL = databaseURL + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift new file mode 100644 index 000000000000..b6216fbb9c09 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift @@ -0,0 +1,298 @@ +// 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 FirebaseCore +import FirebaseFirestore +import Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +class FirebaseFirestoreReader: FlutterStandardReader { + static let firestoreQueue = DispatchQueue(label: "dev.flutter.firebase.firestore") + + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case FirestoreDataType.dateTime.rawValue: + var value: Int64 = 0 + readBytes(&value, length: 8) + return Date(timeIntervalSince1970: Double(value) / 1000.0) + case FirestoreDataType.timestamp.rawValue: + var seconds: Int64 = 0 + var nanoseconds: Int32 = 0 + readBytes(&seconds, length: 8) + readBytes(&nanoseconds, length: 4) + return Timestamp(seconds: seconds, nanoseconds: nanoseconds) + case FirestoreDataType.geoPoint.rawValue: + var latitude: Double = 0 + var longitude: Double = 0 + readAlignment(8) + readBytes(&latitude, length: 8) + readBytes(&longitude, length: 8) + return GeoPoint(latitude: latitude, longitude: longitude) + case FirestoreDataType.vectorValue.rawValue: + return VectorValue((readValue() as? [NSNumber] ?? []).map(\.doubleValue)) + case FirestoreDataType.documentReference.rawValue: + let firestore = readValue() as! Firestore + let documentPath = readValue() as! String + return firestore.document(documentPath) + case FirestoreDataType.fieldPath.rawValue: + let length = readSize() + var array: [Any] = [] + array.reserveCapacity(Int(length)) + for _ in 0 ..< length { + let value = readValue() + array.append(value ?? NSNull()) + } + return FieldPath(array as! [String]) + case FirestoreDataType.blob.rawValue: + return readData(UInt(readSize())) + case FirestoreDataType.arrayUnion.rawValue: + return FieldValue.arrayUnion(readValue() as? [Any] ?? []) + case FirestoreDataType.arrayRemove.rawValue: + return FieldValue.arrayRemove(readValue() as? [Any] ?? []) + case FirestoreDataType.delete.rawValue: + return FieldValue.delete() + case FirestoreDataType.serverTimestamp.rawValue: + return FieldValue.serverTimestamp() + case FirestoreDataType.incrementDouble.rawValue: + return FieldValue.increment((readValue() as! NSNumber).doubleValue) + case FirestoreDataType.incrementInteger.rawValue: + return FieldValue.increment(Int64((readValue() as! NSNumber).intValue)) + case FirestoreDataType.documentId.rawValue: + return FieldPath.documentID() + case FirestoreDataType.firestoreInstance.rawValue: + return readFirestore() + case FirestoreDataType.firestoreQuery.rawValue: + return readQuery() + case FirestoreDataType.firestoreSettings.rawValue: + return readFirestoreSettings() + case FirestoreDataType.nan.rawValue: + return Double.nan + case FirestoreDataType.infinity.rawValue: + return Double.infinity + case FirestoreDataType.negativeInfinity.rawValue: + return -Double.infinity + default: + return super.readValue(ofType: type) + } + } + + private func readFirestoreSettings() -> FirestoreSettings { + let values = readValue() as! [String: Any] + let settings = FirestoreSettings() + + if let persistenceEnabled = values["persistenceEnabled"], !(persistenceEnabled is NSNull) { + let persistEnabled = (persistenceEnabled as! NSNumber).boolValue + var size = NSNumber(value: FirestoreCacheSizeUnlimited) + if let cacheSizeBytes = values["cacheSizeBytes"], !(cacheSizeBytes is NSNull) { + let cacheSize = cacheSizeBytes as! NSNumber + if cacheSize.intValue != -1 { + size = cacheSize + } + } + if persistEnabled { + settings.cacheSettings = PersistentCacheSettings(sizeBytes: size) + } else { + settings.cacheSettings = MemoryCacheSettings( + garbageCollectorSettings: MemoryLRUGCSettings() + ) + } + } + + if let host = values["host"] as? String { + settings.host = host + if let sslEnabled = values["sslEnabled"], !(sslEnabled is NSNull) { + settings.isSSLEnabled = (sslEnabled as! NSNumber).boolValue + } + } + + settings.dispatchQueue = FirebaseFirestoreReader.firestoreQueue + return settings + } + + private func filterFromJson(_ map: [String: Any]?) -> Filter { + guard let map else { + NSException( + name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil + ) + .raise() + fatalError("Invalid operator") + } + + if map["fieldPath"] != nil { + let op = map["op"] as! String + let fieldPath = map["fieldPath"] as! FieldPath + let value = map["value"] as Any + switch op { + case "==": + return Filter.whereField(fieldPath, isEqualTo: value) + case "!=": + return Filter.whereField(fieldPath, isNotEqualTo: value) + case "<": + return Filter.whereField(fieldPath, isLessThan: value) + case "<=": + return Filter.whereField(fieldPath, isLessThanOrEqualTo: value) + case ">": + return Filter.whereField(fieldPath, isGreaterThan: value) + case ">=": + return Filter.whereField(fieldPath, isGreaterOrEqualTo: value) + case "array-contains": + return Filter.whereField(fieldPath, arrayContains: value) + case "array-contains-any": + return Filter.whereField(fieldPath, arrayContainsAny: value as? [Any] ?? []) + case "in": + return Filter.whereField(fieldPath, in: value as? [Any] ?? []) + case "not-in": + return Filter.whereField(fieldPath, notIn: value as? [Any] ?? []) + default: + NSException( + name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil + ) + .raise() + fatalError("Invalid operator") + } + } + + let op = map["op"] as! String + let queries = map["queries"] as! [[String: Any]] + let parsedFilters = queries.map { filterFromJson($0) } + + if op == "OR" { + return Filter.orFilter(parsedFilters) + } + if op == "AND" { + return Filter.andFilter(parsedFilters) + } + + NSException(name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil) + .raise() + fatalError("Invalid operator") + } + + private func readQuery() -> Query? { + do { + let values = readValue() as! [String: Any] + let firestore = values["firestore"] as! Firestore + let parameters = values["parameters"] as! [String: Any] + let whereConditions = parameters["where"] as? [Any] ?? [] + let isCollectionGroup = (values["isCollectionGroup"] as! NSNumber).boolValue + let path = values["path"] as! String + + var query: Query + if isCollectionGroup { + query = firestore.collectionGroup(path) + } else { + query = firestore.collection(path) + } + + if let filters = parameters["filters"] as? [String: Any] { + query = query.whereFilter(filterFromJson(filters)) + } + + for item in whereConditions { + let condition = item as! [Any] + let fieldPath = condition[0] as! FieldPath + let op = condition[1] as! String + let value = condition[2] + switch op { + case "==": + query = query.whereField(fieldPath, isEqualTo: value as Any) + case "!=": + query = query.whereField(fieldPath, isNotEqualTo: value as Any) + case "<": + query = query.whereField(fieldPath, isLessThan: value as Any) + case "<=": + query = query.whereField(fieldPath, isLessThanOrEqualTo: value as Any) + case ">": + query = query.whereField(fieldPath, isGreaterThan: value as Any) + case ">=": + query = query.whereField(fieldPath, isGreaterThanOrEqualTo: value as Any) + case "array-contains": + query = query.whereField(fieldPath, arrayContains: value as Any) + case "array-contains-any": + query = query.whereField(fieldPath, arrayContainsAny: value as? [Any] ?? []) + case "in": + query = query.whereField(fieldPath, in: value as? [Any] ?? []) + case "not-in": + query = query.whereField(fieldPath, notIn: value as? [Any] ?? []) + default: + NSLog( + "FLTFirebaseFirestore: An invalid query operator %@ was received but not handled.", op + ) + } + } + + if let limit = parameters["limit"], !(limit is NSNull) { + query = query.limit(to: (limit as! NSNumber).intValue) + } + if let limitToLast = parameters["limitToLast"], !(limitToLast is NSNull) { + query = query.limit(toLast: (limitToLast as! NSNumber).intValue) + } + + let orderBy = parameters["orderBy"] + if orderBy is NSNull || orderBy == nil { + return query + } + + for orderByParameters in orderBy as! [[Any]] { + let fieldPath = orderByParameters[0] as! FieldPath + let descending = orderByParameters[1] as! NSNumber + query = query.order(by: fieldPath, descending: descending.boolValue) + } + + if let startAt = parameters["startAt"], !(startAt is NSNull) { + query = query.start(at: startAt as! [Any]) + } + if let startAfter = parameters["startAfter"], !(startAfter is NSNull) { + query = query.start(after: startAfter as! [Any]) + } + if let endAt = parameters["endAt"], !(endAt is NSNull) { + query = query.end(at: endAt as! [Any]) + } + if let endBefore = parameters["endBefore"], !(endBefore is NSNull) { + query = query.end(before: endBefore as! [Any]) + } + + return query + } catch { + NSLog( + "An error occurred while parsing query arguments, this is most likely an error with this SDK. %@" + ) + return nil + } + } + + private func readFirestore() -> Firestore { + objc_sync_enter(self) + defer { objc_sync_exit(self) } + + let appNameDart = readValue() as! String + let databaseUrl = readValue() as! String + let settings = readValue() as! FirestoreSettings + let app = FLTFirebasePlugin.firebaseAppNamed(appNameDart)! + + if let cached = FirebaseFirestoreUtils.firestoreInstance( + appName: app.name, databaseURL: databaseUrl + ) { + return cached + } + + let firestore = Firestore.firestore(app: app, database: databaseUrl) + firestore.settings = settings + FirebaseFirestoreUtils.setCachedInstance( + firestore, appName: app.name, databaseURL: databaseUrl + ) + return firestore + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift new file mode 100644 index 000000000000..649ac75143f2 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift @@ -0,0 +1,223 @@ +// 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 Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +enum FirestoreDataType: UInt8 { + case dateTime = 180 + case geoPoint = 181 + case documentReference = 182 + case blob = 183 + case arrayUnion = 184 + case arrayRemove = 185 + case delete = 186 + case serverTimestamp = 187 + case timestamp = 188 + case incrementDouble = 189 + case incrementInteger = 190 + case documentId = 191 + case fieldPath = 192 + case nan = 193 + case infinity = 194 + case negativeInfinity = 195 + case firestoreInstance = 196 + case firestoreQuery = 197 + case firestoreSettings = 198 + case vectorValue = 199 +} + +enum FirebaseFirestoreUtils { + static let pipelineParseErrorCode: Int = -1 + static let errorDomain = "FLTFirebaseFirestore" + + private static let cacheLock = NSLock() + private static var firestoreInstanceCache: [String: FirebaseFirestoreExtension] = [:] + + private static func key(appName: String, databaseURL: String) -> String { + "\(appName)|\(databaseURL)" + } + + static func cachedInstance(appName: String, databaseURL: String) -> FirebaseFirestoreExtension? { + cacheLock.lock() + defer { cacheLock.unlock() } + return firestoreInstanceCache[key(appName: appName, databaseURL: databaseURL)] + } + + static func setCachedInstance(_ firestore: Firestore, appName: String, databaseURL: String) { + cacheLock.lock() + defer { cacheLock.unlock() } + firestoreInstanceCache[key(appName: appName, databaseURL: databaseURL)] = + FirebaseFirestoreExtension(firestoreInstance: firestore, databaseURL: databaseURL) + } + + static func destroyCachedInstance(appName: String, databaseURL: String) { + cacheLock.lock() + defer { cacheLock.unlock() } + firestoreInstanceCache.removeValue(forKey: key(appName: appName, databaseURL: databaseURL)) + } + + static func firestoreInstance(appName: String, databaseURL: String) -> Firestore? { + cachedInstance(appName: appName, databaseURL: databaseURL)?.instance + } + + static var count: Int { + cacheLock.lock() + defer { cacheLock.unlock() } + return firestoreInstanceCache.count + } + + static func cachedInstance(for firestore: Firestore) -> FirebaseFirestoreExtension { + cacheLock.lock() + defer { cacheLock.unlock() } + if let match = firestoreInstanceCache.values.first(where: { $0.instance === firestore }) { + return match + } + NSException( + name: NSExceptionName("NoCachedInstance"), + reason: "No cached instance of Firestore", + userInfo: nil + ).raise() + fatalError("No cached instance of Firestore") + } + + static func cleanupFirestoreInstances(_ completion: (() -> Void)?) { + cacheLock.lock() + let entries = Array(firestoreInstanceCache.values) + cacheLock.unlock() + + let numberOfInstances = entries.count + if numberOfInstances == 0 { + completion?() + return + } + + var instancesTerminated = 0 + for extensionInstance in entries { + let firestore = extensionInstance.instance + DispatchQueue.global(qos: .userInitiated).async { + firestore.terminate { _ in + destroyCachedInstance( + appName: firestore.app.name, databaseURL: extensionInstance.databaseURL + ) + instancesTerminated += 1 + if instancesTerminated == numberOfInstances { + completion?() + } + } + } + } + } + + static func errorCodeAndMessage(from error: Error?) -> (String, String) { + var code = "unknown" + var message = "An unknown error has occurred." + + guard let error = error as NSError? else { + return (code, message) + } + + switch error.code { + case FirestoreErrorCode.aborted.rawValue: + code = "aborted" + message = + "The operation was aborted, typically due to a concurrency issue like transaction aborts, etc." + case FirestoreErrorCode.alreadyExists.rawValue: + code = "already-exists" + message = "Some document that we attempted to create already exists." + case FirestoreErrorCode.cancelled.rawValue: + code = "cancelled" + message = "The operation was cancelled (typically by the caller)." + case FirestoreErrorCode.dataLoss.rawValue: + code = "data-loss" + message = "Unrecoverable data loss or corruption." + case FirestoreErrorCode.deadlineExceeded.rawValue: + 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." + case FirestoreErrorCode.failedPrecondition.rawValue: + code = "failed-precondition" + if error.localizedDescription.contains("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." + } + case FirestoreErrorCode.internal.rawValue: + 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." + case FirestoreErrorCode.invalidArgument.rawValue: + 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)." + case FirestoreErrorCode.notFound.rawValue: + code = "not-found" + message = "Some requested document was not found." + case FirestoreErrorCode.outOfRange.rawValue: + code = "out-of-range" + message = "Operation was attempted past the valid range." + case FirestoreErrorCode.permissionDenied.rawValue: + code = "permission-denied" + message = "The caller does not have permission to execute the specified operation." + case FirestoreErrorCode.resourceExhausted.rawValue: + code = "resource-exhausted" + message = + "Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space." + case FirestoreErrorCode.unauthenticated.rawValue: + code = "unauthenticated" + message = "The request does not have valid authentication credentials for the operation." + case FirestoreErrorCode.unavailable.rawValue: + 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." + case FirestoreErrorCode.unimplemented.rawValue: + code = "unimplemented" + message = "Operation is not implemented or not supported/enabled." + case FirestoreErrorCode.unknown.rawValue: + code = "unknown" + message = "Unknown error or an error from a different error domain." + case pipelineParseErrorCode: + code = "parse-error" + message = + error.localizedDescription.isEmpty + ? "An unknown error occurred." : error.localizedDescription + default: + code = "unknown" + message = "An unknown error occurred." + } + + if !error.localizedDescription.isEmpty { + message = error.localizedDescription + } + + return (code, message) + } + + static func flutterError(from error: Error) -> FlutterError { + let (code, message) = errorCodeAndMessage(from: error) + return FlutterError( + code: code, + message: message, + details: [ + "code": code, + "message": message, + ] + ) + } + + static func parseError(_ message: String) -> NSError { + NSError( + domain: errorDomain, + code: pipelineParseErrorCode, + userInfo: [NSLocalizedDescriptionKey: message] + ) + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift new file mode 100644 index 000000000000..abe238489472 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift @@ -0,0 +1,219 @@ +// 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 Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +private let kStandardFieldList: UInt8 = 12 +private let kStandardFieldMap: UInt8 = 13 + +class FirebaseFirestoreWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let date = value as? Date { + writeByte(FirestoreDataType.dateTime.rawValue) + var ms = Int64(date.timeIntervalSince1970 * 1000.0) + writeBytes(&ms, length: 8) + } else if let timestamp = value as? Timestamp { + var seconds = timestamp.seconds + var nanoseconds = timestamp.nanoseconds + writeByte(FirestoreDataType.timestamp.rawValue) + writeBytes(&seconds, length: 8) + writeBytes(&nanoseconds, length: 4) + } else if let geoPoint = value as? GeoPoint { + var latitude = geoPoint.latitude + var longitude = geoPoint.longitude + writeByte(FirestoreDataType.geoPoint.rawValue) + writeAlignment(8) + writeBytes(&latitude, length: 8) + writeBytes(&longitude, length: 8) + } else if let vector = value as? VectorValue { + writeByte(FirestoreDataType.vectorValue.rawValue) + writeValue(vector.array) + } else if let document = value as? DocumentReference { + writeByte(FirestoreDataType.documentReference.rawValue) + writeValue(FLTFirebasePlugin.firebaseAppName(fromIosName: document.firestore.app.name)) + writeValue(document.path) + let extensionInstance = FirebaseFirestoreUtils.cachedInstance(for: document.firestore) + writeValue(extensionInstance.databaseURL) + } else if let snapshot = value as? DocumentSnapshot { + writeValue(documentSnapshotMap(snapshot)) + } else if let progress = value as? LoadBundleTaskProgress { + writeValue(loadBundleTaskProgressMap(progress)) + } else if let snapshot = value as? QuerySnapshot { + writeValue(querySnapshotMap(snapshot)) + } else if let change = value as? DocumentChange { + writeValue(documentChangeMap(change)) + } else if let metadata = value as? SnapshotMetadata { + writeValue(snapshotMetadataMap(metadata)) + } else if let list = value as? [Any] { + writeByte(kStandardFieldList) + writeSize(UInt32(list.count)) + for item in list { + writeValue(item) + } + } else if let map = value as? [AnyHashable: Any] { + writeByte(kStandardFieldMap) + writeSize(UInt32(map.count)) + for (key, item) in map { + writeValue(key) + writeValue(item) + } + } else if let number = value as? NSNumber { + if number == NSNumber(value: Double.infinity) { + writeByte(FirestoreDataType.infinity.rawValue) + return + } + if number == NSNumber(value: -Double.infinity) { + writeByte(FirestoreDataType.negativeInfinity.rawValue) + return + } + if number.description.lowercased() == "nan" { + writeByte(FirestoreDataType.nan.rawValue) + return + } + super.writeValue(value) + } else if let blob = value as? Data { + writeByte(FirestoreDataType.blob.rawValue) + writeSize(UInt32(blob.count)) + write(blob) + } else { + super.writeValue(value) + } + } + + private func snapshotMetadataMap(_ snapshotMetadata: SnapshotMetadata) -> [String: Any] { + [ + "hasPendingWrites": snapshotMetadata.hasPendingWrites, + "isFromCache": snapshotMetadata.isFromCache, + ] + } + + private func documentChangeMap(_ documentChange: DocumentChange) -> [String: Any] { + let type: String + switch documentChange.type { + case .added: + type = "DocumentChangeType.added" + case .modified: + type = "DocumentChangeType.modified" + case .removed: + type = "DocumentChangeType.removed" + @unknown default: + type = "DocumentChangeType.modified" + } + + let maxVal = NSNotFound + let newIndex: Int + if documentChange.newIndex == NSNotFound || documentChange.newIndex == 4_294_967_295 + || documentChange.newIndex == maxVal { + newIndex = -1 + } else { + newIndex = Int(documentChange.newIndex) + } + + let oldIndex: Int + if documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4_294_967_295 + || documentChange.oldIndex == maxVal { + oldIndex = -1 + } else { + oldIndex = Int(documentChange.oldIndex) + } + + return [ + "type": type, + "data": documentChange.document.data(), + "path": documentChange.document.reference.path, + "oldIndex": oldIndex, + "newIndex": newIndex, + "metadata": documentChange.document.metadata, + ] + } + + private func serverTimestampBehavior(from string: String?) + -> FirebaseFirestore + .ServerTimestampBehavior { + switch string { + case "estimate": + return .estimate + case "previous": + return .previous + default: + return .none + } + } + + private func documentSnapshotMap(_ documentSnapshot: DocumentSnapshot) -> [String: Any]? { + let hash = NSNumber(value: documentSnapshot.hash) + let timestampBehaviorString = + FLTFirebaseFirestorePlugin.serverTimestampMap.object(forKey: hash) as String? + let behavior = serverTimestampBehavior(from: timestampBehaviorString) + FLTFirebaseFirestorePlugin.serverTimestampMap.removeObject(forKey: hash) + + let data: Any = + documentSnapshot.exists + ? documentSnapshot.data(with: behavior) as Any : NSNull() + return [ + "path": documentSnapshot.reference.path, + "data": data, + "metadata": documentSnapshot.metadata, + ] + } + + private func loadBundleTaskProgressMap(_ progress: LoadBundleTaskProgress) -> [String: Any] { + let state: String + switch progress.state { + case .error: + state = "error" + case .success: + state = "success" + case .inProgress: + state = "running" + @unknown default: + state = "running" + } + return [ + "bytesLoaded": progress.bytesLoaded, + "documentsLoaded": progress.documentsLoaded, + "totalBytes": progress.totalBytes, + "totalDocuments": progress.totalDocuments, + "taskState": state, + ] + } + + private func querySnapshotMap(_ querySnapshot: QuerySnapshot) -> [String: Any]? { + let hash = NSNumber(value: querySnapshot.hash) + let timestampBehaviorString = + FLTFirebaseFirestorePlugin.serverTimestampMap.object(forKey: hash) as String? + let behavior = serverTimestampBehavior(from: timestampBehaviorString) + FLTFirebaseFirestorePlugin.serverTimestampMap.removeObject(forKey: hash) + + var paths: [String] = [] + var documents: [[String: Any]] = [] + var metadatas: [SnapshotMetadata] = [] + for document in querySnapshot.documents { + paths.append(document.reference.path) + documents.append(document.data(with: behavior) ?? [:]) + metadatas.append(document.metadata) + } + + return [ + "paths": paths, + "documentChanges": querySnapshot.documentChanges, + "documents": documents, + "metadatas": metadatas, + "metadata": querySnapshot.metadata, + ] + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m deleted file mode 100644 index f9594c6e943f..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m +++ /dev/null @@ -1,2033 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift new file mode 100644 index 000000000000..4fcaa93a1e29 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift @@ -0,0 +1,1852 @@ +// 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) + 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 doubleEqualsFirestoreMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirestoreMessages(_ 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 deepEqualsFirestoreMessages(_ 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, let rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirestoreMessages(element, rhsArray[index]) { + return false + } + } + return true + + case (let lhsArray, let rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirestoreMessages(element, rhsArray[index]) { + return false + } + } + return true + + case (let lhsDictionary, let 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 deepEqualsFirestoreMessages(lhsKey, rhsKey) { + if deepEqualsFirestoreMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirestoreMessages(lhs, rhs) + + case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + + default: + return false + } +} + +func deepHashFirestoreMessages(value: Any?, hasher: inout Hasher) { + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirestoreMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirestoreMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirestoreMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirestoreMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirestoreMessages(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) + } +} + +/// An enumeration of document change types. +enum DocumentChangeType: Int { + /// Indicates a new document was added to the set of documents matching the + /// query. + case added = 0 + /// Indicates a document within the query was modified. + case modified = 1 + /// Indicates a document within the query was removed (either deleted or no + /// longer matches the query. + case removed = 2 +} + +/// An enumeration of firestore source types. +enum Source: Int { + /// 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. + case serverAndCache = 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). + case server = 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. + case cache = 2 +} + +/// 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. +enum ListenSource: Int { + /// 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. + case defaultSource = 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. + case cache = 1 +} + +enum ServerTimestampBehavior: Int { + /// Return null for [FieldValue.serverTimestamp()] values that have not yet + case none = 0 + /// Return local estimates for [FieldValue.serverTimestamp()] values that have not yet been set to + /// their final value. + case estimate = 1 + /// Return the previous value for [FieldValue.serverTimestamp()] values that have not yet been set + /// to their final value. + case previous = 2 +} + +/// [AggregateSource] represents the source of data for an [AggregateQuery]. +enum AggregateSource: Int { + /// Indicates that the data should be retrieved from the server. + case server = 0 +} + +/// [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache +/// index manager. +enum PersistenceCacheIndexManagerRequest: Int { + case enableIndexAutoCreation = 0 + case disableIndexAutoCreation = 1 + case deleteAllIndexes = 2 +} + +enum InternalTransactionResult: Int { + case success = 0 + case failure = 1 +} + +enum InternalTransactionType: Int { + case get = 0 + case update = 1 + case set = 2 + case deleteType = 3 +} + +enum AggregateType: Int { + case count = 0 + case sum = 1 + case average = 2 +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalFirebaseSettings: Hashable { + var persistenceEnabled: Bool? + var host: String? + var sslEnabled: Bool? + var cacheSizeBytes: Int64? + var ignoreUndefinedProperties: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalFirebaseSettings? { + let persistenceEnabled: Bool? = nilOrValue(pigeonVar_list[0]) + let host: String? = nilOrValue(pigeonVar_list[1]) + let sslEnabled: Bool? = nilOrValue(pigeonVar_list[2]) + let cacheSizeBytes: Int64? = nilOrValue(pigeonVar_list[3]) + let ignoreUndefinedProperties = pigeonVar_list[4] as! Bool + + return InternalFirebaseSettings( + persistenceEnabled: persistenceEnabled, + host: host, + sslEnabled: sslEnabled, + cacheSizeBytes: cacheSizeBytes, + ignoreUndefinedProperties: ignoreUndefinedProperties + ) + } + + func toList() -> [Any?] { + [ + persistenceEnabled, + host, + sslEnabled, + cacheSizeBytes, + ignoreUndefinedProperties, + ] + } + + static func == (lhs: InternalFirebaseSettings, rhs: InternalFirebaseSettings) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.persistenceEnabled, rhs.persistenceEnabled) && + deepEqualsFirestoreMessages( + lhs.host, + rhs.host + ) && deepEqualsFirestoreMessages(lhs.sslEnabled, rhs.sslEnabled) && + deepEqualsFirestoreMessages( + lhs.cacheSizeBytes, + rhs.cacheSizeBytes + ) && deepEqualsFirestoreMessages( + lhs.ignoreUndefinedProperties, + rhs.ignoreUndefinedProperties + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalFirebaseSettings") + deepHashFirestoreMessages(value: persistenceEnabled, hasher: &hasher) + deepHashFirestoreMessages(value: host, hasher: &hasher) + deepHashFirestoreMessages(value: sslEnabled, hasher: &hasher) + deepHashFirestoreMessages(value: cacheSizeBytes, hasher: &hasher) + deepHashFirestoreMessages(value: ignoreUndefinedProperties, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct FirestorePigeonFirebaseApp: Hashable { + var appName: String + var settings: InternalFirebaseSettings + var databaseURL: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> FirestorePigeonFirebaseApp? { + let appName = pigeonVar_list[0] as! String + let settings = pigeonVar_list[1] as! InternalFirebaseSettings + let databaseURL = pigeonVar_list[2] as! String + + return FirestorePigeonFirebaseApp( + appName: appName, + settings: settings, + databaseURL: databaseURL + ) + } + + func toList() -> [Any?] { + [ + appName, + settings, + databaseURL, + ] + } + + static func == (lhs: FirestorePigeonFirebaseApp, rhs: FirestorePigeonFirebaseApp) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.appName, rhs.appName) && deepEqualsFirestoreMessages( + lhs.settings, + rhs.settings + ) && deepEqualsFirestoreMessages(lhs.databaseURL, rhs.databaseURL) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("FirestorePigeonFirebaseApp") + deepHashFirestoreMessages(value: appName, hasher: &hasher) + deepHashFirestoreMessages(value: settings, hasher: &hasher) + deepHashFirestoreMessages(value: databaseURL, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalSnapshotMetadata: Hashable { + var hasPendingWrites: Bool + var isFromCache: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalSnapshotMetadata? { + let hasPendingWrites = pigeonVar_list[0] as! Bool + let isFromCache = pigeonVar_list[1] as! Bool + + return InternalSnapshotMetadata( + hasPendingWrites: hasPendingWrites, + isFromCache: isFromCache + ) + } + + func toList() -> [Any?] { + [ + hasPendingWrites, + isFromCache, + ] + } + + static func == (lhs: InternalSnapshotMetadata, rhs: InternalSnapshotMetadata) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.hasPendingWrites, rhs.hasPendingWrites) && + deepEqualsFirestoreMessages( + lhs.isFromCache, + rhs.isFromCache + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalSnapshotMetadata") + deepHashFirestoreMessages(value: hasPendingWrites, hasher: &hasher) + deepHashFirestoreMessages(value: isFromCache, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalDocumentSnapshot: Hashable { + var path: String + var data: [String?: Any?]? + var metadata: InternalSnapshotMetadata + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalDocumentSnapshot? { + let path = pigeonVar_list[0] as! String + let data: [String?: Any?]? = nilOrValue(pigeonVar_list[1]) + let metadata = pigeonVar_list[2] as! InternalSnapshotMetadata + + return InternalDocumentSnapshot( + path: path, + data: data, + metadata: metadata + ) + } + + func toList() -> [Any?] { + [ + path, + data, + metadata, + ] + } + + static func == (lhs: InternalDocumentSnapshot, rhs: InternalDocumentSnapshot) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.path, rhs.path) && deepEqualsFirestoreMessages( + lhs.data, + rhs.data + ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalDocumentSnapshot") + deepHashFirestoreMessages(value: path, hasher: &hasher) + deepHashFirestoreMessages(value: data, hasher: &hasher) + deepHashFirestoreMessages(value: metadata, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalDocumentChange: Hashable { + var type: DocumentChangeType + var document: InternalDocumentSnapshot + var oldIndex: Int64 + var newIndex: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalDocumentChange? { + let type = pigeonVar_list[0] as! DocumentChangeType + let document = pigeonVar_list[1] as! InternalDocumentSnapshot + let oldIndex = pigeonVar_list[2] as! Int64 + let newIndex = pigeonVar_list[3] as! Int64 + + return InternalDocumentChange( + type: type, + document: document, + oldIndex: oldIndex, + newIndex: newIndex + ) + } + + func toList() -> [Any?] { + [ + type, + document, + oldIndex, + newIndex, + ] + } + + static func == (lhs: InternalDocumentChange, rhs: InternalDocumentChange) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( + lhs.document, + rhs.document + ) && deepEqualsFirestoreMessages(lhs.oldIndex, rhs.oldIndex) && deepEqualsFirestoreMessages( + lhs.newIndex, + rhs.newIndex + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalDocumentChange") + deepHashFirestoreMessages(value: type, hasher: &hasher) + deepHashFirestoreMessages(value: document, hasher: &hasher) + deepHashFirestoreMessages(value: oldIndex, hasher: &hasher) + deepHashFirestoreMessages(value: newIndex, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalQuerySnapshot: Hashable { + var documents: [InternalDocumentSnapshot?] + var documentChanges: [InternalDocumentChange?] + var metadata: InternalSnapshotMetadata + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalQuerySnapshot? { + let documents = pigeonVar_list[0] as! [InternalDocumentSnapshot?] + let documentChanges = pigeonVar_list[1] as! [InternalDocumentChange?] + let metadata = pigeonVar_list[2] as! InternalSnapshotMetadata + + return InternalQuerySnapshot( + documents: documents, + documentChanges: documentChanges, + metadata: metadata + ) + } + + func toList() -> [Any?] { + [ + documents, + documentChanges, + metadata, + ] + } + + static func == (lhs: InternalQuerySnapshot, rhs: InternalQuerySnapshot) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.documents, rhs.documents) && deepEqualsFirestoreMessages( + lhs.documentChanges, + rhs.documentChanges + ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalQuerySnapshot") + deepHashFirestoreMessages(value: documents, hasher: &hasher) + deepHashFirestoreMessages(value: documentChanges, hasher: &hasher) + deepHashFirestoreMessages(value: metadata, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalPipelineResult: Hashable { + var documentPath: String? + var createTime: Int64? + var updateTime: Int64? + /// All fields in the result (from PipelineResult.data() on Android). + var data: [String?: Any?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalPipelineResult? { + let documentPath: String? = nilOrValue(pigeonVar_list[0]) + let createTime: Int64? = nilOrValue(pigeonVar_list[1]) + let updateTime: Int64? = nilOrValue(pigeonVar_list[2]) + let data: [String?: Any?]? = nilOrValue(pigeonVar_list[3]) + + return InternalPipelineResult( + documentPath: documentPath, + createTime: createTime, + updateTime: updateTime, + data: data + ) + } + + func toList() -> [Any?] { + [ + documentPath, + createTime, + updateTime, + data, + ] + } + + static func == (lhs: InternalPipelineResult, rhs: InternalPipelineResult) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.documentPath, rhs.documentPath) && + deepEqualsFirestoreMessages( + lhs.createTime, + rhs.createTime + ) && deepEqualsFirestoreMessages(lhs.updateTime, rhs.updateTime) && + deepEqualsFirestoreMessages( + lhs.data, + rhs.data + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalPipelineResult") + deepHashFirestoreMessages(value: documentPath, hasher: &hasher) + deepHashFirestoreMessages(value: createTime, hasher: &hasher) + deepHashFirestoreMessages(value: updateTime, hasher: &hasher) + deepHashFirestoreMessages(value: data, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalPipelineSnapshot: Hashable { + var results: [InternalPipelineResult?] + var executionTime: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalPipelineSnapshot? { + let results = pigeonVar_list[0] as! [InternalPipelineResult?] + let executionTime = pigeonVar_list[1] as! Int64 + + return InternalPipelineSnapshot( + results: results, + executionTime: executionTime + ) + } + + func toList() -> [Any?] { + [ + results, + executionTime, + ] + } + + static func == (lhs: InternalPipelineSnapshot, rhs: InternalPipelineSnapshot) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.results, rhs.results) && deepEqualsFirestoreMessages( + lhs.executionTime, + rhs.executionTime + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalPipelineSnapshot") + deepHashFirestoreMessages(value: results, hasher: &hasher) + deepHashFirestoreMessages(value: executionTime, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalGetOptions: Hashable { + var source: Source + var serverTimestampBehavior: ServerTimestampBehavior + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalGetOptions? { + let source = pigeonVar_list[0] as! Source + let serverTimestampBehavior = pigeonVar_list[1] as! ServerTimestampBehavior + + return InternalGetOptions( + source: source, + serverTimestampBehavior: serverTimestampBehavior + ) + } + + func toList() -> [Any?] { + [ + source, + serverTimestampBehavior, + ] + } + + static func == (lhs: InternalGetOptions, rhs: InternalGetOptions) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.source, rhs.source) && deepEqualsFirestoreMessages( + lhs.serverTimestampBehavior, + rhs.serverTimestampBehavior + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalGetOptions") + deepHashFirestoreMessages(value: source, hasher: &hasher) + deepHashFirestoreMessages(value: serverTimestampBehavior, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalDocumentOption: Hashable { + var merge: Bool? + var mergeFields: [[String?]?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalDocumentOption? { + let merge: Bool? = nilOrValue(pigeonVar_list[0]) + let mergeFields: [[String?]?]? = nilOrValue(pigeonVar_list[1]) + + return InternalDocumentOption( + merge: merge, + mergeFields: mergeFields + ) + } + + func toList() -> [Any?] { + [ + merge, + mergeFields, + ] + } + + static func == (lhs: InternalDocumentOption, rhs: InternalDocumentOption) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.merge, rhs.merge) && deepEqualsFirestoreMessages( + lhs.mergeFields, + rhs.mergeFields + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalDocumentOption") + deepHashFirestoreMessages(value: merge, hasher: &hasher) + deepHashFirestoreMessages(value: mergeFields, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalTransactionCommand: Hashable { + var type: InternalTransactionType + var path: String + var data: [AnyHashable?: Any?]? + var option: InternalDocumentOption? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalTransactionCommand? { + let type = pigeonVar_list[0] as! InternalTransactionType + let path = pigeonVar_list[1] as! String + let data: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[2]) + let option: InternalDocumentOption? = nilOrValue(pigeonVar_list[3]) + + return InternalTransactionCommand( + type: type, + path: path, + data: data, + option: option + ) + } + + func toList() -> [Any?] { + [ + type, + path, + data, + option, + ] + } + + static func == (lhs: InternalTransactionCommand, rhs: InternalTransactionCommand) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( + lhs.path, + rhs.path + ) && deepEqualsFirestoreMessages(lhs.data, rhs.data) && deepEqualsFirestoreMessages( + lhs.option, + rhs.option + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalTransactionCommand") + deepHashFirestoreMessages(value: type, hasher: &hasher) + deepHashFirestoreMessages(value: path, hasher: &hasher) + deepHashFirestoreMessages(value: data, hasher: &hasher) + deepHashFirestoreMessages(value: option, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DocumentReferenceRequest: Hashable { + var path: String + var data: [AnyHashable?: Any?]? + var option: InternalDocumentOption? + var source: Source? + var serverTimestampBehavior: ServerTimestampBehavior? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DocumentReferenceRequest? { + let path = pigeonVar_list[0] as! String + let data: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[1]) + let option: InternalDocumentOption? = nilOrValue(pigeonVar_list[2]) + let source: Source? = nilOrValue(pigeonVar_list[3]) + let serverTimestampBehavior: ServerTimestampBehavior? = nilOrValue(pigeonVar_list[4]) + + return DocumentReferenceRequest( + path: path, + data: data, + option: option, + source: source, + serverTimestampBehavior: serverTimestampBehavior + ) + } + + func toList() -> [Any?] { + [ + path, + data, + option, + source, + serverTimestampBehavior, + ] + } + + static func == (lhs: DocumentReferenceRequest, rhs: DocumentReferenceRequest) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.path, rhs.path) && deepEqualsFirestoreMessages( + lhs.data, + rhs.data + ) && deepEqualsFirestoreMessages(lhs.option, rhs.option) && deepEqualsFirestoreMessages( + lhs.source, + rhs.source + ) && deepEqualsFirestoreMessages(lhs.serverTimestampBehavior, rhs.serverTimestampBehavior) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("DocumentReferenceRequest") + deepHashFirestoreMessages(value: path, hasher: &hasher) + deepHashFirestoreMessages(value: data, hasher: &hasher) + deepHashFirestoreMessages(value: option, hasher: &hasher) + deepHashFirestoreMessages(value: source, hasher: &hasher) + deepHashFirestoreMessages(value: serverTimestampBehavior, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalQueryParameters: Hashable { + var `where`: [[Any?]?]? + var orderBy: [[Any?]?]? + var limit: Int64? + var limitToLast: Int64? + var startAt: [Any?]? + var startAfter: [Any?]? + var endAt: [Any?]? + var endBefore: [Any?]? + var filters: [String?: Any?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalQueryParameters? { + let `where`: [[Any?]?]? = nilOrValue(pigeonVar_list[0]) + let orderBy: [[Any?]?]? = nilOrValue(pigeonVar_list[1]) + let limit: Int64? = nilOrValue(pigeonVar_list[2]) + let limitToLast: Int64? = nilOrValue(pigeonVar_list[3]) + let startAt: [Any?]? = nilOrValue(pigeonVar_list[4]) + let startAfter: [Any?]? = nilOrValue(pigeonVar_list[5]) + let endAt: [Any?]? = nilOrValue(pigeonVar_list[6]) + let endBefore: [Any?]? = nilOrValue(pigeonVar_list[7]) + let filters: [String?: Any?]? = nilOrValue(pigeonVar_list[8]) + + return InternalQueryParameters( + where: `where`, + orderBy: orderBy, + limit: limit, + limitToLast: limitToLast, + startAt: startAt, + startAfter: startAfter, + endAt: endAt, + endBefore: endBefore, + filters: filters + ) + } + + func toList() -> [Any?] { + [ + `where`, + orderBy, + limit, + limitToLast, + startAt, + startAfter, + endAt, + endBefore, + filters, + ] + } + + static func == (lhs: InternalQueryParameters, rhs: InternalQueryParameters) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.where, rhs.where) && deepEqualsFirestoreMessages( + lhs.orderBy, + rhs.orderBy + ) && deepEqualsFirestoreMessages(lhs.limit, rhs.limit) && deepEqualsFirestoreMessages( + lhs.limitToLast, + rhs.limitToLast + ) && deepEqualsFirestoreMessages(lhs.startAt, rhs.startAt) && deepEqualsFirestoreMessages( + lhs.startAfter, + rhs.startAfter + ) && deepEqualsFirestoreMessages(lhs.endAt, rhs.endAt) && deepEqualsFirestoreMessages( + lhs.endBefore, + rhs.endBefore + ) && deepEqualsFirestoreMessages(lhs.filters, rhs.filters) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalQueryParameters") + deepHashFirestoreMessages(value: `where`, hasher: &hasher) + deepHashFirestoreMessages(value: orderBy, hasher: &hasher) + deepHashFirestoreMessages(value: limit, hasher: &hasher) + deepHashFirestoreMessages(value: limitToLast, hasher: &hasher) + deepHashFirestoreMessages(value: startAt, hasher: &hasher) + deepHashFirestoreMessages(value: startAfter, hasher: &hasher) + deepHashFirestoreMessages(value: endAt, hasher: &hasher) + deepHashFirestoreMessages(value: endBefore, hasher: &hasher) + deepHashFirestoreMessages(value: filters, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct AggregateQuery: Hashable { + var type: AggregateType + var field: String? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> AggregateQuery? { + let type = pigeonVar_list[0] as! AggregateType + let field: String? = nilOrValue(pigeonVar_list[1]) + + return AggregateQuery( + type: type, + field: field + ) + } + + func toList() -> [Any?] { + [ + type, + field, + ] + } + + static func == (lhs: AggregateQuery, rhs: AggregateQuery) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( + lhs.field, + rhs.field + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("AggregateQuery") + deepHashFirestoreMessages(value: type, hasher: &hasher) + deepHashFirestoreMessages(value: field, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct AggregateQueryResponse: Hashable { + var type: AggregateType + var field: String? + var value: Double? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> AggregateQueryResponse? { + let type = pigeonVar_list[0] as! AggregateType + let field: String? = nilOrValue(pigeonVar_list[1]) + let value: Double? = nilOrValue(pigeonVar_list[2]) + + return AggregateQueryResponse( + type: type, + field: field, + value: value + ) + } + + func toList() -> [Any?] { + [ + type, + field, + value, + ] + } + + static func == (lhs: AggregateQueryResponse, rhs: AggregateQueryResponse) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( + lhs.field, + rhs.field + ) && deepEqualsFirestoreMessages(lhs.value, rhs.value) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("AggregateQueryResponse") + deepHashFirestoreMessages(value: type, hasher: &hasher) + deepHashFirestoreMessages(value: field, hasher: &hasher) + deepHashFirestoreMessages(value: value, hasher: &hasher) + } +} + +class FirestoreMessagesPigeonCodecReader: FirebaseFirestoreReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return DocumentChangeType(rawValue: enumResultAsInt) + } + return nil + case 130: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return Source(rawValue: enumResultAsInt) + } + return nil + case 131: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return ListenSource(rawValue: enumResultAsInt) + } + return nil + case 132: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return ServerTimestampBehavior(rawValue: enumResultAsInt) + } + return nil + case 133: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return AggregateSource(rawValue: enumResultAsInt) + } + return nil + case 134: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return PersistenceCacheIndexManagerRequest(rawValue: enumResultAsInt) + } + return nil + case 135: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return InternalTransactionResult(rawValue: enumResultAsInt) + } + return nil + case 136: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return InternalTransactionType(rawValue: enumResultAsInt) + } + return nil + case 137: + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return AggregateType(rawValue: enumResultAsInt) + } + return nil + case 138: + return InternalFirebaseSettings.fromList(readValue() as! [Any?]) + case 139: + return FirestorePigeonFirebaseApp.fromList(readValue() as! [Any?]) + case 140: + return InternalSnapshotMetadata.fromList(readValue() as! [Any?]) + case 141: + return InternalDocumentSnapshot.fromList(readValue() as! [Any?]) + case 142: + return InternalDocumentChange.fromList(readValue() as! [Any?]) + case 143: + return InternalQuerySnapshot.fromList(readValue() as! [Any?]) + case 144: + return InternalPipelineResult.fromList(readValue() as! [Any?]) + case 145: + return InternalPipelineSnapshot.fromList(readValue() as! [Any?]) + case 146: + return InternalGetOptions.fromList(readValue() as! [Any?]) + case 147: + return InternalDocumentOption.fromList(readValue() as! [Any?]) + case 148: + return InternalTransactionCommand.fromList(readValue() as! [Any?]) + case 149: + return DocumentReferenceRequest.fromList(readValue() as! [Any?]) + case 150: + return InternalQueryParameters.fromList(readValue() as! [Any?]) + case 151: + return AggregateQuery.fromList(readValue() as! [Any?]) + case 152: + return AggregateQueryResponse.fromList(readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +class FirestoreMessagesPigeonCodecWriter: FirebaseFirestoreWriter { + override func writeValue(_ value: Any) { + if let value = value as? DocumentChangeType { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? Source { + super.writeByte(130) + super.writeValue(value.rawValue) + } else if let value = value as? ListenSource { + super.writeByte(131) + super.writeValue(value.rawValue) + } else if let value = value as? ServerTimestampBehavior { + super.writeByte(132) + super.writeValue(value.rawValue) + } else if let value = value as? AggregateSource { + super.writeByte(133) + super.writeValue(value.rawValue) + } else if let value = value as? PersistenceCacheIndexManagerRequest { + super.writeByte(134) + super.writeValue(value.rawValue) + } else if let value = value as? InternalTransactionResult { + super.writeByte(135) + super.writeValue(value.rawValue) + } else if let value = value as? InternalTransactionType { + super.writeByte(136) + super.writeValue(value.rawValue) + } else if let value = value as? AggregateType { + super.writeByte(137) + super.writeValue(value.rawValue) + } else if let value = value as? InternalFirebaseSettings { + super.writeByte(138) + super.writeValue(value.toList()) + } else if let value = value as? FirestorePigeonFirebaseApp { + super.writeByte(139) + super.writeValue(value.toList()) + } else if let value = value as? InternalSnapshotMetadata { + super.writeByte(140) + super.writeValue(value.toList()) + } else if let value = value as? InternalDocumentSnapshot { + super.writeByte(141) + super.writeValue(value.toList()) + } else if let value = value as? InternalDocumentChange { + super.writeByte(142) + super.writeValue(value.toList()) + } else if let value = value as? InternalQuerySnapshot { + super.writeByte(143) + super.writeValue(value.toList()) + } else if let value = value as? InternalPipelineResult { + super.writeByte(144) + super.writeValue(value.toList()) + } else if let value = value as? InternalPipelineSnapshot { + super.writeByte(145) + super.writeValue(value.toList()) + } else if let value = value as? InternalGetOptions { + super.writeByte(146) + super.writeValue(value.toList()) + } else if let value = value as? InternalDocumentOption { + super.writeByte(147) + super.writeValue(value.toList()) + } else if let value = value as? InternalTransactionCommand { + super.writeByte(148) + super.writeValue(value.toList()) + } else if let value = value as? DocumentReferenceRequest { + super.writeByte(149) + super.writeValue(value.toList()) + } else if let value = value as? InternalQueryParameters { + super.writeByte(150) + super.writeValue(value.toList()) + } else if let value = value as? AggregateQuery { + super.writeByte(151) + super.writeValue(value.toList()) + } else if let value = value as? AggregateQueryResponse { + super.writeByte(152) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +class FirestoreMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirestoreMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirestoreMessagesPigeonCodecWriter(data: data) + } +} + +class FirestoreMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = + FirestoreMessagesPigeonCodec(readerWriter: FirestoreMessagesPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseFirestoreHostApi { + func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void) + func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void) + func clearPersistence(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func disableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func enableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func terminate(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func waitForPendingWrites(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void) + func setLoggingEnabled(loggingEnabled: Bool, completion: @escaping (Result) -> Void) + func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void) + func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void) + func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void) + func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void) + func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, source: AggregateSource, + queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) + func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void) + func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) + func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, includeMetadataChanges: Bool, + source: ListenSource, + completion: @escaping (Result) -> Void) + func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void) + func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseFirestoreHostApiSetup { + static var codec: FlutterStandardMessageCodec { + FirestoreMessagesPigeonCodec.shared + } + + /// Sets up an instance of `FirebaseFirestoreHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseFirestoreHostApi?, + messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let loadBundleChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + loadBundleChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let bundleArg = args[1] as! FlutterStandardTypedData + api.loadBundle(app: appArg, bundle: bundleArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + loadBundleChannel.setMessageHandler(nil) + } + let namedQueryGetChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + namedQueryGetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let nameArg = args[1] as! String + let optionsArg = args[2] as! InternalGetOptions + api.namedQueryGet(app: appArg, name: nameArg, options: optionsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + namedQueryGetChannel.setMessageHandler(nil) + } + let clearPersistenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + clearPersistenceChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.clearPersistence(app: appArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + clearPersistenceChannel.setMessageHandler(nil) + } + let disableNetworkChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + disableNetworkChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.disableNetwork(app: appArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + disableNetworkChannel.setMessageHandler(nil) + } + let enableNetworkChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + enableNetworkChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.enableNetwork(app: appArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + enableNetworkChannel.setMessageHandler(nil) + } + let terminateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + terminateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.terminate(app: appArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + terminateChannel.setMessageHandler(nil) + } + let waitForPendingWritesChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + waitForPendingWritesChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.waitForPendingWrites(app: appArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + waitForPendingWritesChannel.setMessageHandler(nil) + } + let setIndexConfigurationChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setIndexConfigurationChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let indexConfigurationArg = args[1] as! String + api + .setIndexConfiguration(app: appArg, indexConfiguration: indexConfigurationArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setIndexConfigurationChannel.setMessageHandler(nil) + } + let setLoggingEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setLoggingEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let loggingEnabledArg = args[0] as! Bool + api.setLoggingEnabled(loggingEnabled: loggingEnabledArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setLoggingEnabledChannel.setMessageHandler(nil) + } + let snapshotsInSyncSetupChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + snapshotsInSyncSetupChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + api.snapshotsInSyncSetup(app: appArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + snapshotsInSyncSetupChannel.setMessageHandler(nil) + } + let transactionCreateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + transactionCreateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let timeoutArg = args[1] as! Int64 + let maxAttemptsArg = args[2] as! Int64 + api + .transactionCreate(app: appArg, timeout: timeoutArg, + maxAttempts: maxAttemptsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + transactionCreateChannel.setMessageHandler(nil) + } + let transactionStoreResultChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + transactionStoreResultChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let transactionIdArg = args[0] as! String + let resultTypeArg = args[1] as! InternalTransactionResult + let commandsArg: [InternalTransactionCommand?]? = nilOrValue(args[2]) + api.transactionStoreResult( + transactionId: transactionIdArg, + resultType: resultTypeArg, + commands: commandsArg + ) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + transactionStoreResultChannel.setMessageHandler(nil) + } + let transactionGetChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + transactionGetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let transactionIdArg = args[1] as! String + let pathArg = args[2] as! String + api.transactionGet(app: appArg, transactionId: transactionIdArg, path: pathArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + transactionGetChannel.setMessageHandler(nil) + } + let documentReferenceSetChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + documentReferenceSetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let requestArg = args[1] as! DocumentReferenceRequest + api.documentReferenceSet(app: appArg, request: requestArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + documentReferenceSetChannel.setMessageHandler(nil) + } + let documentReferenceUpdateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + documentReferenceUpdateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let requestArg = args[1] as! DocumentReferenceRequest + api.documentReferenceUpdate(app: appArg, request: requestArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + documentReferenceUpdateChannel.setMessageHandler(nil) + } + let documentReferenceGetChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + documentReferenceGetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let requestArg = args[1] as! DocumentReferenceRequest + api.documentReferenceGet(app: appArg, request: requestArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + documentReferenceGetChannel.setMessageHandler(nil) + } + let documentReferenceDeleteChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + documentReferenceDeleteChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let requestArg = args[1] as! DocumentReferenceRequest + api.documentReferenceDelete(app: appArg, request: requestArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + documentReferenceDeleteChannel.setMessageHandler(nil) + } + let queryGetChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + queryGetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let pathArg = args[1] as! String + let isCollectionGroupArg = args[2] as! Bool + let parametersArg = args[3] as! InternalQueryParameters + let optionsArg = args[4] as! InternalGetOptions + api.queryGet( + app: appArg, + path: pathArg, + isCollectionGroup: isCollectionGroupArg, + parameters: parametersArg, + options: optionsArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + queryGetChannel.setMessageHandler(nil) + } + let aggregateQueryChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + aggregateQueryChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let pathArg = args[1] as! String + let parametersArg = args[2] as! InternalQueryParameters + let sourceArg = args[3] as! AggregateSource + let queriesArg = args[4] as! [AggregateQuery?] + let isCollectionGroupArg = args[5] as! Bool + api.aggregateQuery( + app: appArg, + path: pathArg, + parameters: parametersArg, + source: sourceArg, + queries: queriesArg, + isCollectionGroup: isCollectionGroupArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + aggregateQueryChannel.setMessageHandler(nil) + } + let writeBatchCommitChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + writeBatchCommitChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let writesArg = args[1] as! [InternalTransactionCommand?] + api.writeBatchCommit(app: appArg, writes: writesArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + writeBatchCommitChannel.setMessageHandler(nil) + } + let querySnapshotChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + querySnapshotChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let pathArg = args[1] as! String + let isCollectionGroupArg = args[2] as! Bool + let parametersArg = args[3] as! InternalQueryParameters + let optionsArg = args[4] as! InternalGetOptions + let includeMetadataChangesArg = args[5] as! Bool + let sourceArg = args[6] as! ListenSource + api.querySnapshot( + app: appArg, + path: pathArg, + isCollectionGroup: isCollectionGroupArg, + parameters: parametersArg, + options: optionsArg, + includeMetadataChanges: includeMetadataChangesArg, + source: sourceArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + querySnapshotChannel.setMessageHandler(nil) + } + let documentReferenceSnapshotChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + documentReferenceSnapshotChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let parametersArg = args[1] as! DocumentReferenceRequest + let includeMetadataChangesArg = args[2] as! Bool + let sourceArg = args[3] as! ListenSource + api.documentReferenceSnapshot( + app: appArg, + parameters: parametersArg, + includeMetadataChanges: includeMetadataChangesArg, + source: sourceArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + documentReferenceSnapshotChannel.setMessageHandler(nil) + } + let persistenceCacheIndexManagerRequestChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + persistenceCacheIndexManagerRequestChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let requestArg = args[1] as! PersistenceCacheIndexManagerRequest + api.persistenceCacheIndexManagerRequest(app: appArg, request: requestArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + persistenceCacheIndexManagerRequestChannel.setMessageHandler(nil) + } + let executePipelineChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + executePipelineChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appArg = args[0] as! FirestorePigeonFirebaseApp + let stagesArg = args[1] as! [[String?: Any?]?] + let optionsArg: [String?: Any?]? = nilOrValue(args[2]) + api.executePipeline(app: appArg, stages: stagesArg, options: optionsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + executePipelineChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m deleted file mode 100644 index 0178847b7426..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m +++ /dev/null @@ -1,311 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift new file mode 100644 index 000000000000..cadeca5616a8 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift @@ -0,0 +1,64 @@ +// 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 FirebaseFirestore +import Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class LoadBundleStreamHandler: NSObject, FlutterStreamHandler { + private let firestore: Firestore + private let bundle: FlutterStandardTypedData + private var task: LoadBundleTask? + + init(firestore: Firestore, bundle: FlutterStandardTypedData) { + self.firestore = firestore + self.bundle = bundle + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + task = firestore.loadBundle(bundle.data) { _, error in + if let error { + let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) + DispatchQueue.main.async { + events( + FLTFirebasePlugin.createFlutterError( + fromCode: code, + message: message, + optionalDetails: ["code": code, "message": message], + andOptionalNSError: error as NSError + ) + ) + } + } + } + + task?.addObserver { progress in + DispatchQueue.main.async { + if progress.state != .error { + events(progress) + } + } + } + + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + task?.removeAllObservers() + task = nil + return nil + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift new file mode 100644 index 000000000000..6915bb1c03ae --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift @@ -0,0 +1,291 @@ +// 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 FirebaseFirestore +import Foundation + +enum PigeonParser { + static func filterFromJson(_ map: [String: Any]?) -> Filter { + guard let map else { + NSException( + name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil + ) + .raise() + fatalError("Invalid operator") + } + + if map["fieldPath"] != nil { + let op = map["op"] as! String + let fieldPath = map["fieldPath"] as! FieldPath + let value = map["value"] as Any + switch op { + case "==": + return Filter.whereField(fieldPath, isEqualTo: value) + case "!=": + return Filter.whereField(fieldPath, isNotEqualTo: value) + case "<": + return Filter.whereField(fieldPath, isLessThan: value) + case "<=": + return Filter.whereField(fieldPath, isLessThanOrEqualTo: value) + case ">": + return Filter.whereField(fieldPath, isGreaterThan: value) + case ">=": + return Filter.whereField(fieldPath, isGreaterOrEqualTo: value) + case "array-contains": + return Filter.whereField(fieldPath, arrayContains: value) + case "array-contains-any": + return Filter.whereField(fieldPath, arrayContainsAny: value as? [Any] ?? []) + case "in": + return Filter.whereField(fieldPath, in: value as? [Any] ?? []) + case "not-in": + return Filter.whereField(fieldPath, notIn: value as? [Any] ?? []) + default: + NSException( + name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil + ) + .raise() + fatalError("Invalid operator") + } + } + + let op = map["op"] as! String + let queries = map["queries"] as! [[String: Any]] + let parsedFilters = queries.map { filterFromJson($0) } + if op == "OR" { + return Filter.orFilter(parsedFilters) + } + if op == "AND" { + return Filter.andFilter(parsedFilters) + } + NSException(name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil) + .raise() + fatalError("Invalid operator") + } + + static func parseQuery(parameters: InternalQueryParameters, + firestore: Firestore, + path: String, + isCollectionGroup: Bool) -> Query? { + do { + var query: Query + if isCollectionGroup { + query = firestore.collectionGroup(path) + } else { + query = firestore.collection(path) + } + + if let filters = parameters.filters as? [String: Any] { + query = query.whereFilter(filterFromJson(filters)) + } + + if let whereConditions = parameters.where { + for item in whereConditions { + guard let condition = item, condition.count >= 3 else { continue } + let fieldPath = condition[0] as! FieldPath + let op = condition[1] as! String + let value = condition[2] + switch op { + case "==": + query = query.whereField(fieldPath, isEqualTo: value as Any) + case "!=": + query = query.whereField(fieldPath, isNotEqualTo: value as Any) + case "<": + query = query.whereField(fieldPath, isLessThan: value as Any) + case "<=": + query = query.whereField(fieldPath, isLessThanOrEqualTo: value as Any) + case ">": + query = query.whereField(fieldPath, isGreaterThan: value as Any) + case ">=": + query = query.whereField(fieldPath, isGreaterThanOrEqualTo: value as Any) + case "array-contains": + query = query.whereField(fieldPath, arrayContains: value as Any) + case "array-contains-any": + query = query.whereField(fieldPath, arrayContainsAny: value as? [Any] ?? []) + case "in": + query = query.whereField(fieldPath, in: value as? [Any] ?? []) + case "not-in": + query = query.whereField(fieldPath, notIn: value as? [Any] ?? []) + default: + NSLog( + "FLTFirebaseFirestore: An invalid query operator %@ was received but not handled.", + op + ) + } + } + } + + if let limit = parameters.limit { + query = query.limit(to: Int(limit)) + } + if let limitToLast = parameters.limitToLast { + query = query.limit(toLast: Int(limitToLast)) + } + + guard let orderBy = parameters.orderBy else { + return query + } + + for orderByParameters in orderBy { + guard let orderByParameters, orderByParameters.count >= 2 else { continue } + let fieldPath = orderByParameters[0] as! FieldPath + let descending = orderByParameters[1] as! NSNumber + query = query.order(by: fieldPath, descending: descending.boolValue) + } + + if let startAt = parameters.startAt { + query = query.start(at: startAt as [Any]) + } + if let startAfter = parameters.startAfter { + query = query.start(after: startAfter as [Any]) + } + if let endAt = parameters.endAt { + query = query.end(at: endAt as [Any]) + } + if let endBefore = parameters.endBefore { + query = query.end(before: endBefore as [Any]) + } + + return query + } catch { + NSLog( + "An error occurred while parsing query arguments, this is most likely an error with this SDK." + ) + return nil + } + } + + static func parseSource(_ source: Source) -> FirestoreSource { + switch source { + case .serverAndCache: + return .default + case .server: + return .server + case .cache: + return .cache + } + } + + static func parseFieldPath(_ fieldPaths: [[String?]?]) -> [FieldPath] { + fieldPaths.compactMap { components in + guard let components else { return nil } + return FieldPath(components.compactMap { $0 }) + } + } + + static func parseServerTimestampBehavior(_ behavior: ServerTimestampBehavior) + -> FirebaseFirestore.ServerTimestampBehavior { + switch behavior { + case .none: + return .none + case .estimate: + return .estimate + case .previous: + return .previous + } + } + + static func parseListenSource(_ source: ListenSource) -> FirebaseFirestore.ListenSource { + switch source { + case .defaultSource: + return .default + case .cache: + return .cache + } + } + + static func toPigeonSnapshotMetadata(_ snapshotMetadata: SnapshotMetadata) + -> InternalSnapshotMetadata { + InternalSnapshotMetadata( + hasPendingWrites: snapshotMetadata.hasPendingWrites, + isFromCache: snapshotMetadata.isFromCache + ) + } + + static func toPigeonDocumentSnapshot(_ documentSnapshot: DocumentSnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalDocumentSnapshot { + let data = documentSnapshot.data(with: serverTimestampBehavior) + let mapped: [String?: Any?]? = data.map { original in + Dictionary(uniqueKeysWithValues: original.map { ($0.key as String?, $0.value as Any?) }) + } + return InternalDocumentSnapshot( + path: documentSnapshot.reference.path, + data: mapped, + metadata: toPigeonSnapshotMetadata(documentSnapshot.metadata) + ) + } + + static func toPigeonDocumentChangeType(_ documentChangeType: DocumentChangeType) + -> DocumentChangeType { + documentChangeType + } + + static func toPigeonDocumentChange(_ documentChange: DocumentChange, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalDocumentChange { + let maxVal = NSNotFound + let newIndex: Int64 + if documentChange.newIndex == NSNotFound || documentChange.newIndex == 4_294_967_295 + || documentChange.newIndex == maxVal { + newIndex = -1 + } else { + newIndex = Int64(documentChange.newIndex) + } + + let oldIndex: Int64 + if documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4_294_967_295 + || documentChange.oldIndex == maxVal { + oldIndex = -1 + } else { + oldIndex = Int64(documentChange.oldIndex) + } + + let type: DocumentChangeType + switch documentChange.type { + case .added: + type = .added + case .modified: + type = .modified + case .removed: + type = .removed + @unknown default: + type = .modified + } + + return InternalDocumentChange( + type: type, + document: toPigeonDocumentSnapshot( + documentChange.document, serverTimestampBehavior: serverTimestampBehavior + ), + oldIndex: oldIndex, + newIndex: newIndex + ) + } + + static func toPigeonDocumentChanges(_ documentChanges: [DocumentChange], + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> [ + InternalDocumentChange? + ] { + documentChanges.map { + toPigeonDocumentChange($0, serverTimestampBehavior: serverTimestampBehavior) + } + } + + static func toPigeonQuerySnapshot(_ querySnapshot: QuerySnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalQuerySnapshot { + let documents = querySnapshot.documents.map { + toPigeonDocumentSnapshot($0, serverTimestampBehavior: serverTimestampBehavior) + as InternalDocumentSnapshot? + } + return InternalQuerySnapshot( + documents: documents, + documentChanges: toPigeonDocumentChanges( + querySnapshot.documentChanges, serverTimestampBehavior: serverTimestampBehavior + ), + metadata: toPigeonSnapshotMetadata(querySnapshot.metadata) + ) + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift new file mode 100644 index 000000000000..1bec34828a42 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift @@ -0,0 +1,1431 @@ +// 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 FirebaseFirestore +import Foundation +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +private let kPipelineNotAvailable = + "Pipeline API is not available. Firestore Pipelines require Firebase iOS SDK with pipeline support." +private let kPipelineErrorDomain = "FLTFirebaseFirestore" +private let kPipelineParseErrorCode = -1 + +private let kBinaryNames: [String] = [ + "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", +] + +private let kFilterComparisonKeys: [String] = [ + "isEqualTo", "isNotEqualTo", "isGreaterThan", "isGreaterThanOrEqualTo", "isLessThan", + "isLessThanOrEqualTo", "arrayContains", "arrayContainsAny", "whereIn", "whereNotIn", + "isNull", "isNotNull", +] + +private func pipelineUnavailableError() -> NSError { + NSError( + domain: kPipelineErrorDomain, + code: kPipelineParseErrorCode, + userInfo: [NSLocalizedDescriptionKey: kPipelineNotAvailable] + ) +} + +private func parseError(_ message: String) -> NSError { + NSError( + domain: kPipelineErrorDomain, + code: kPipelineParseErrorCode, + userInfo: [NSLocalizedDescriptionKey: message] + ) +} + +private func asMap(_ value: Any?) -> [String: Any]? { + guard let value, !(value is NSNull) else { return nil } + if let dict = value as? [String: Any] { + return dict + } + if let dict = value as? [String: Any?] { + var result: [String: Any] = [:] + for (key, nested) in dict { + result[key] = nested ?? NSNull() + } + return result + } + if let dict = value as? [AnyHashable: Any] { + var result: [String: Any] = [:] + for (key, nested) in dict { + if let key = key as? String { + result[key] = nested + } + } + return result + } + return nil +} + +private func asArray(_ value: Any?) -> [Any]? { + guard let value, !(value is NSNull) else { return nil } + if let array = value as? [Any] { + return array + } + if let array = value as? NSArray { + return array.map { $0 as Any } + } + return nil +} + +private func dictionaryFromOptionalKeyed(_ map: [String: Any?]) -> [String: Any] { + var result: [String: Any] = [:] + for (key, value) in map { + result[key] = value ?? NSNull() + } + return result +} + +private func nestedValue(_ map: [String: Any], keyPath: String) -> Any? { + let parts = keyPath.split(separator: ".").map(String.init) + var current: Any? = map + for part in parts { + guard let currentMap = asMap(current) else { return nil } + current = currentMap[part] + } + return current +} + +private func toExprBridge(_ expression: any FirebaseFirestore.Expression) throws -> ExprBridge { + if let bridge = exprBridge(from: expression) { + return bridge + } + throw parseError("Could not convert pipeline expression into a native bridge") +} + +/// `BridgeWrapper.bridge` is internal to FirebaseFirestore, so typed expressions are lowered +/// through public `.bridge` members where available and Mirror otherwise. +private func exprBridge(from value: Any) -> ExprBridge? { + if let bridge = value as? ExprBridge { + return bridge + } + if let score = value as? Score { + return score.bridge + } + if let matches = value as? DocumentMatches { + return matches.bridge + } + + var mirror: Mirror? = Mirror(reflecting: value) + while let current = mirror { + for child in current.children { + if child.label == "bridge", let bridge = child.value as? ExprBridge { + return bridge + } + } + mirror = current.superclassMirror + } + + for child in Mirror(reflecting: value).children { + if child.label == "expr" || child.label == "constant" || child.label == "field", + let nested = exprBridge(from: child.value) { + return nested + } + } + return nil +} + +private func sendableExpressions(_ expressions: [any FirebaseFirestore.Expression]) + -> [any Sendable] { + expressions.map { $0 as any Sendable } +} + +private func constantExpression(from value: Any) throws -> any FirebaseFirestore.Expression { + if value is NSNull { + return Constant.nil + } + if let number = value as? NSNumber { + if CFGetTypeID(number) == CFBooleanGetTypeID() { + return Constant(number.boolValue) + } + let doubleValue = number.doubleValue + if doubleValue.isFinite, doubleValue.rounded() == doubleValue, + doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) { + return Constant(number.intValue) + } + return Constant(number.doubleValue) + } + if let stringValue = value as? String { + return Constant(stringValue) + } + if let boolValue = value as? Bool { + return Constant(boolValue) + } + if let intValue = value as? Int { + return Constant(intValue) + } + if let doubleValue = value as? Double { + return Constant(doubleValue) + } + if let dateValue = value as? Date { + return Constant(dateValue) + } + if let timestampValue = value as? Timestamp { + return Constant(timestampValue) + } + if let geoPointValue = value as? GeoPoint { + return Constant(geoPointValue) + } + if let referenceValue = value as? DocumentReference { + return Constant(referenceValue) + } + if let vectorValue = value as? VectorValue { + return Constant(vectorValue) + } + if let data = value as? Data { + return Constant(data) + } + if let typedData = value as? FlutterStandardTypedData { + return Constant(typedData.data) + } + throw parseError("Unsupported constant value: \(type(of: value))") +} + +private func functionExpression(name: String, + args: [any FirebaseFirestore.Expression]) -> FunctionExpression { + FunctionExpression(functionName: name, args: args) +} + +private final class PipelineExpressionParser { + let firestore: Firestore + + init(firestore: Firestore) { + self.firestore = firestore + } + + func parseExpression(_ map: [String: Any]) throws -> ExprBridge { + try toExprBridge(parseTypedExpression(map)) + } + + func parseBooleanExpression(_ map: [String: Any]) throws -> ExprBridge { + try toExprBridge(parseBooleanTypedExpression(map)) + } + + private func parseBooleanTypedExpression(_ map: [String: Any]) throws + -> any FirebaseFirestore.BooleanExpression { + let expression = try parseTypedExpression(map) + if let booleanExpression = expression as? any FirebaseFirestore.BooleanExpression { + return booleanExpression + } + return expression.asBoolean() + } + + private func parseTypedExpressions(_ maps: [Any], + errorMessage: String) throws + -> [any FirebaseFirestore.Expression] { + var expressions: [any FirebaseFirestore.Expression] = [] + for value in maps { + guard let map = asMap(value) else { continue } + try expressions.append(parseTypedExpression(map)) + } + if expressions.isEmpty { + throw parseError(errorMessage) + } + return expressions + } + + private func parseTypedExpression(_ map: [String: Any]) throws -> any FirebaseFirestore + .Expression { + let name = map["name"] as? String + if name == nil { + if let args = asMap(map["args"]), let field = args["field"] as? String { + return Field(field) + } + throw parseError("Expression must have a 'name' field") + } + + let resolvedName = name! + let args = asMap(map["args"]) ?? [:] + + if resolvedName == "field" { + guard let field = args["field"] as? String else { + throw parseError("Field expression requires 'field' argument") + } + return Field(field) + } + + if resolvedName == "constant" { + guard let value = args["value"] else { + throw parseError("Constant requires 'value' argument") + } + if let valueMap = asMap(value), let path = valueMap["path"] as? String { + return Constant(firestore.document(path)) + } + return try constantExpression(from: value) + } + + if resolvedName == "alias" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("Alias requires 'expression'") + } + return try parseTypedExpression(exprMap) + } + + if resolvedName == "null" { + return Constant.nil + } + + if resolvedName == "score" { + return Score() + } + + if resolvedName == "document_id_from_ref" { + guard let path = args["doc_ref"] as? String, !path.isEmpty else { + throw parseError("document_id_from_ref requires doc_ref path") + } + return Constant(firestore.document(path)).documentId() + } + + if resolvedName == "as_boolean" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("as_boolean requires expression") + } + return try parseBooleanTypedExpression(exprMap) + } + + if resolvedName == "document_matches" { + guard let query = args["query"] as? String else { + throw parseError("document_matches requires query") + } + return DocumentMatches(query) + } + + var sdkName = resolvedName + if resolvedName == "bit_xor" { sdkName = "xor" } + if resolvedName == "modulo" { sdkName = "mod" } + + if kBinaryNames.contains(sdkName) || resolvedName == "bit_xor" { + guard let leftMap = asMap(args["left"]), let rightMap = asMap(args["right"]) else { + throw parseError("\(resolvedName) requires left and right expressions") + } + let left = try parseTypedExpression(leftMap) + let right = try parseTypedExpression(rightMap) + switch sdkName { + case "equal": + return left.equal(right) + case "not_equal": + return left.notEqual(right) + case "greater_than": + return left.greaterThan(right) + case "greater_than_or_equal": + return left.greaterThanOrEqual(right) + case "less_than": + return left.lessThan(right) + case "less_than_or_equal": + return left.lessThanOrEqual(right) + case "add": + return left.add(right) + case "subtract": + return left.subtract(right) + case "multiply": + return left.multiply(right) + case "divide": + return left.divide(right) + case "mod": + return left.mod(right) + default: + return functionExpression(name: sdkName, args: [left, right]) + } + } + + if resolvedName == "exists" || resolvedName == "is_error" || resolvedName == "is_absent" + || resolvedName == "not" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("\(resolvedName) requires expression") + } + if resolvedName == "not" { + return try !parseBooleanTypedExpression(exprMap) + } + let expr = try parseTypedExpression(exprMap) + switch resolvedName { + case "exists": + return expr.exists() + case "is_error": + return expr.isError() + default: + return expr.isAbsent() + } + } + + if [ + "length", "to_lower_case", "to_upper_case", "trim", "abs", "array_length", + "array_reverse", "bit_not", "document_id", "collection_id", + ].contains(resolvedName) { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("\(resolvedName) requires expression") + } + let expr = try parseTypedExpression(exprMap) + switch resolvedName { + case "length": + return expr.length() + case "to_lower_case": + return expr.toLower() + case "to_upper_case": + return expr.toUpper() + case "trim": + return expr.trim() + case "abs": + return expr.abs() + case "array_length": + return expr.arrayLength() + case "array_reverse": + return expr.arrayReverse() + case "document_id": + return expr.documentId() + case "collection_id": + return expr.collectionId() + default: + return functionExpression(name: resolvedName, args: [expr]) + } + } + + if resolvedName == "and" || resolvedName == "or" || resolvedName == "xor" + || resolvedName == "nor" { + guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { + throw parseError("\(resolvedName) requires at least one expression") + } + var booleanExprs: [any FirebaseFirestore.BooleanExpression] = [] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + try booleanExprs.append(parseBooleanTypedExpression(emMap)) + } + guard let first = booleanExprs.first else { + throw parseError("\(resolvedName) requires at least one expression") + } + if resolvedName == "and" { + return booleanExprs.dropFirst().reduce(first) { $0 && $1 } + } + if resolvedName == "or" { + return booleanExprs.dropFirst().reduce(first) { $0 || $1 } + } + if resolvedName == "xor" { + return booleanExprs.dropFirst().reduce(first) { $0 ^ $1 } + } + return !(booleanExprs.dropFirst().reduce(first) { $0 || $1 }) + } + + if resolvedName == "equal_any" || resolvedName == "not_equal_any" { + let valuesMaps = asArray(args["values"]) + guard let valueMap = asMap(args["value"]), + let valuesMaps, !valuesMaps.isEmpty + else { + throw parseError("\(resolvedName) requires value and non-empty values") + } + let valueExpr = try parseTypedExpression(valueMap) + let valueExprs = try parseTypedExpressions( + valuesMaps, + errorMessage: "\(resolvedName) requires at least one value" + ) + if resolvedName == "equal_any" { + return valueExpr.equalAny(valueExprs) + } + return valueExpr.notEqualAny(valueExprs) + } + + if resolvedName == "array_contains" { + guard let arrayMap = asMap(args["array"]), let elementMap = asMap(args["element"]) else { + throw parseError("array_contains requires array and element") + } + return try parseTypedExpression(arrayMap) + .arrayContains(parseTypedExpression(elementMap)) + } + + if resolvedName == "array_contains_all" || resolvedName == "array_contains_any" { + guard let arrayMap = asMap(args["array"]) else { + throw parseError("\(resolvedName) requires array") + } + let arrayExpr = try parseTypedExpression(arrayMap) + + var valuesMaps = asArray(args["values"]) + if valuesMaps == nil { + valuesMaps = asArray(args["elements"]) + } + + if let valuesMaps, !valuesMaps.isEmpty { + let valueExprs = try parseTypedExpressions( + valuesMaps, + errorMessage: "\(resolvedName) requires at least one value" + ) + if resolvedName == "array_contains_all" { + return arrayExpr.arrayContainsAll(valueExprs) + } + return arrayExpr.arrayContainsAny(valueExprs) + } + + if resolvedName == "array_contains_all", + let arrayExpressionMap = asMap(args["array_expression"]) { + return try arrayExpr.arrayContainsAll(parseTypedExpression(arrayExpressionMap)) + } + + throw parseError( + "\(resolvedName) requires array and values/elements, or array_contains_all with array_expression" + ) + } + + if resolvedName == "concat" { + guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { + throw parseError("concat requires non-empty expressions") + } + let expressions = try parseTypedExpressions( + exprMaps, + errorMessage: "concat requires at least one expression" + ) + if expressions.count == 1 { + return expressions[0] + } + return expressions[0].concat(sendableExpressions(Array(expressions.dropFirst()))) + } + + if resolvedName == "substring" { + guard let exprMap = asMap(args["expression"]), + let startMap = asMap(args["start"]), + let endMap = asMap(args["end"]) + else { + throw parseError("substring requires expression, start, and end") + } + return try parseTypedExpression(exprMap).substring( + position: parseTypedExpression(startMap), + length: parseTypedExpression(endMap) + ) + } + + if resolvedName == "replace" || resolvedName == "string_replace_all" { + guard let exprMap = asMap(args["expression"]), + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) + else { + throw parseError("\(resolvedName) requires expression, find, and replacement") + } + return try parseTypedExpression(exprMap).stringReplaceAll( + parseTypedExpression(findMap), + with: parseTypedExpression(replacementMap) + ) + } + + if resolvedName == "string_replace_one" { + guard let exprMap = asMap(args["expression"]), + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) + else { + throw parseError("string_replace_one requires expression, find, and replacement") + } + return try parseTypedExpression(exprMap).stringReplaceOne( + parseTypedExpression(findMap), + with: parseTypedExpression(replacementMap) + ) + } + + if resolvedName == "string_index_of" || resolvedName == "string_repeat" { + let argumentName = resolvedName == "string_index_of" ? "search" : "repetitions" + guard let exprMap = asMap(args["expression"]), + let argumentMap = asMap(args[argumentName]) + else { + throw parseError("\(resolvedName) requires expression and \(argumentName)") + } + let expr = try parseTypedExpression(exprMap) + let argument = try parseTypedExpression(argumentMap) + if resolvedName == "string_index_of" { + return expr.stringIndexOf(argument) + } + return expr.stringRepeat(argument) + } + + if resolvedName == "ltrim" || resolvedName == "rtrim" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("\(resolvedName) requires expression") + } + let expr = try parseTypedExpression(exprMap) + guard let valueMap = asMap(args["value"]) else { + return resolvedName == "ltrim" ? expr.ltrim() : expr.rtrim() + } + let value = try parseTypedExpression(valueMap) + return resolvedName == "ltrim" ? expr.ltrim(value) : expr.rtrim(value) + } + + if resolvedName == "split" || resolvedName == "join" { + guard let exprMap = asMap(args["expression"]), + let delimiterMap = asMap(args["delimiter"]) + else { + throw parseError("\(resolvedName) requires expression and delimiter") + } + let expr = try parseTypedExpression(exprMap) + let delimiter = try parseTypedExpression(delimiterMap) + if resolvedName == "split" { + return expr.split(delimiter: delimiter) + } + if let delimiterMap = asMap(args["delimiter"]), + (delimiterMap["name"] as? String) == "constant", + let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String { + return expr.join(delimiter: delimiterValue) + } + return functionExpression(name: "join", args: [expr, delimiter]) + } + + if resolvedName == "array_concat" { + guard let firstMap = asMap(args["first"]), let secondMap = asMap(args["second"]) else { + throw parseError("array_concat requires first and second") + } + return try parseTypedExpression(firstMap) + .arrayConcat([parseTypedExpression(secondMap)]) + } + + if resolvedName == "array_concat_multiple" { + guard let arraysMaps = asArray(args["arrays"]), !arraysMaps.isEmpty else { + throw parseError("array_concat_multiple requires non-empty arrays") + } + let expressions = try parseTypedExpressions( + arraysMaps, + errorMessage: "array_concat_multiple requires at least one array" + ) + if expressions.count == 1 { + return expressions[0] + } + return expressions[0].arrayConcat(Array(expressions.dropFirst())) + } + + if resolvedName == "array_slice" { + guard let exprMap = asMap(args["expression"]), let offsetMap = asMap(args["offset"]) else { + throw parseError("array_slice requires expression and offset") + } + let expr = try parseTypedExpression(exprMap) + let offset = try parseTypedExpression(offsetMap) + if let lengthMap = asMap(args["length"]) { + return try expr.arraySlice(offset: offset, length: parseTypedExpression(lengthMap)) + } + return expr.arraySlice(offset: offset) + } + + if resolvedName == "array_filter" { + guard let exprMap = asMap(args["expression"]), + let alias = args["alias"] as? String, + let filterMap = asMap(args["filter"]) + else { + throw parseError("array_filter requires expression, alias, and filter") + } + return try parseTypedExpression(exprMap).arrayFilter( + alias: alias, + filter: parseBooleanTypedExpression(filterMap) + ) + } + + if resolvedName == "array_transform" { + guard let exprMap = asMap(args["expression"]), + let elementAlias = args["element_alias"] as? String, + let transformMap = asMap(args["transform"]) + else { + throw parseError("array_transform requires expression, element_alias, and transform") + } + return try parseTypedExpression(exprMap).arrayTransform( + elementAlias: elementAlias, + transform: parseTypedExpression(transformMap) + ) + } + + if resolvedName == "array_transform_with_index" { + guard let exprMap = asMap(args["expression"]), + let elementAlias = args["element_alias"] as? String, + let indexAlias = args["index_alias"] as? String, + let transformMap = asMap(args["transform"]) + else { + throw parseError( + "array_transform_with_index requires expression, element_alias, index_alias, and transform" + ) + } + return try parseTypedExpression(exprMap).arrayTransformWithIndex( + elementAlias: elementAlias, + indexAlias: indexAlias, + transform: parseTypedExpression(transformMap) + ) + } + + if resolvedName == "array" { + guard let elementsMaps = asArray(args["elements"]), !elementsMaps.isEmpty else { + throw parseError("array requires non-empty elements") + } + return try ArrayExpression( + sendableExpressions( + parseTypedExpressions( + elementsMaps, + errorMessage: "array requires at least one element" + ) + ) + ) + } + + if resolvedName == "map" { + guard let dataMap = asMap(args["data"]), !dataMap.isEmpty else { + throw parseError("map requires non-empty data") + } + var elements: [String: any Sendable] = [:] + for (key, rawValue) in dataMap { + guard let valueMap = asMap(rawValue) else { continue } + elements[key] = try parseTypedExpression(valueMap) + } + if elements.isEmpty { + throw parseError("map requires at least one key-value pair") + } + return MapExpression(elements) + } + + if resolvedName == "map_get" { + guard let mapMap = asMap(args["map"]), let keyMap = asMap(args["key"]) else { + throw parseError("map_get requires map and key") + } + let mapExpr = try parseTypedExpression(mapMap) + let keyExpr = try parseTypedExpression(keyMap) + return mapExpr.getField(keyExpr) + } + + if resolvedName == "if_absent" { + guard let exprMap = asMap(args["expression"]), let elseMap = asMap(args["else"]) else { + throw parseError("if_absent requires expression and else") + } + return try parseTypedExpression(exprMap).ifAbsent(parseTypedExpression(elseMap)) + } + + if resolvedName == "if_error" { + guard let exprMap = asMap(args["expression"]), let catchMap = asMap(args["catch"]) else { + throw parseError("if_error requires expression and catch") + } + return try parseTypedExpression(exprMap).ifError(parseTypedExpression(catchMap)) + } + + if resolvedName == "conditional" { + guard let conditionMap = asMap(args["condition"]), + let thenMap = asMap(args["then"]), + let elseMap = asMap(args["else"]) + else { + throw parseError("conditional requires condition, then, and else") + } + return try ConditionalExpression( + parseBooleanTypedExpression(conditionMap), + then: parseTypedExpression(thenMap), + else: parseTypedExpression(elseMap) + ) + } + + if resolvedName == "timestamp_add" || resolvedName == "timestamp_subtract" { + let unitVal = args["unit"] + guard let timestampMap = asMap(args["timestamp"]), + unitVal != nil, + let amountMap = asMap(args["amount"]) + else { + throw parseError("\(resolvedName) requires timestamp, unit, and amount") + } + let timestampExpr = try parseTypedExpression(timestampMap) + let amountExpr = try parseTypedExpression(amountMap) + if resolvedName == "timestamp_add" { + if let unit = unitVal as? String { + return timestampExpr.timestampAdd(amount: amountExpr, unit: unit) + } + return try timestampExpr.timestampAdd( + amount: amountExpr, + unit: parseTypedExpression(asMap(unitVal) ?? [:]) + ) + } + if let unit = unitVal as? String { + return timestampExpr.timestampSubtract(amount: amountExpr, unit: unit) + } + return try timestampExpr.timestampSubtract( + amount: amountExpr, + unit: parseTypedExpression(asMap(unitVal) ?? [:]) + ) + } + + if resolvedName == "current_timestamp" { + return CurrentTimestamp() + } + + if resolvedName == "timestamp_truncate" { + let unitVal = args["unit"] + guard let timestampMap = asMap(args["timestamp"]), unitVal != nil else { + throw parseError("timestamp_truncate requires timestamp and unit") + } + let timestampExpr = try parseTypedExpression(timestampMap) + if let unit = unitVal as? String { + return timestampExpr.timestampTruncate(granularity: unit) + } + return try timestampExpr.timestampTruncate( + granularity: parseTypedExpression(asMap(unitVal) ?? [:]) + ) + } + + if resolvedName == "map_keys" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("map_keys requires expression") + } + return try parseTypedExpression(exprMap).mapKeys() + } + + if resolvedName == "map_values" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("map_values requires expression") + } + return try parseTypedExpression(exprMap).mapValues() + } + + if resolvedName == "parent" { + if let docPath = args["doc_ref"] as? String, !docPath.isEmpty { + return Constant(firestore.document(docPath)).parent() + } + guard let exprMap = asMap(args["expression"]) else { + throw parseError("parent requires expression or doc_ref") + } + return try parseTypedExpression(exprMap).parent() + } + + if resolvedName == "timestamp_diff" { + let unitObj = args["unit"] + guard let endMap = asMap(args["end"]), + let startMap = asMap(args["start"]), + unitObj != nil + else { + throw parseError("timestamp_diff requires end, start, and unit") + } + let endExpr = try parseTypedExpression(endMap) + let startExpr = try parseTypedExpression(startMap) + if let unit = unitObj as? String { + return endExpr.timestampDiff(startExpr, unit) + } + guard let unitMap = asMap(unitObj) else { + throw parseError("timestamp_diff unit must be string or expression") + } + return try endExpr.timestampDiff(startExpr, parseTypedExpression(unitMap)) + } + + if resolvedName == "timestamp_extract" { + guard let timestampMap = asMap(args["timestamp"]), let partMap = asMap(args["part"]) else { + throw parseError("timestamp_extract requires timestamp and part") + } + let tsExpr = try parseTypedExpression(timestampMap) + let partExpr = try parseTypedExpression(partMap) + let tzRaw = args["timezone"] + if tzRaw == nil { + return tsExpr.timestampExtract(part: partExpr) + } + if let tzRaw = tzRaw as? String { + return tsExpr.timestampExtract(part: partExpr, timezone: tzRaw) + } + guard let tzMap = asMap(tzRaw) else { + throw parseError("timestamp_extract timezone must be string or expression") + } + return try tsExpr.timestampExtract(part: partExpr, timezone: parseTypedExpression(tzMap)) + } + + if resolvedName == "if_null" { + guard let exprMap = asMap(args["expression"]), + let replMap = asMap(args["replacement"]) + else { + throw parseError("if_null requires expression and replacement") + } + return try parseTypedExpression(exprMap).ifNull(parseTypedExpression(replMap)) + } + + if resolvedName == "coalesce" { + guard let exprMaps = asArray(args["expressions"]), exprMaps.count >= 2 else { + throw parseError("coalesce requires at least two expressions") + } + let exprs = try parseTypedExpressions( + exprMaps, + errorMessage: "coalesce requires at least two expressions" + ) + guard exprs.count >= 2 else { + throw parseError("coalesce requires at least two expressions") + } + return exprs[0].coalesce(Array(exprs.dropFirst())) + } + + if resolvedName == "switch_on" { + guard let exprMaps = asArray(args["expressions"]), exprMaps.count >= 2 else { + throw parseError("switch_on requires at least two expressions") + } + var switchArgs: [any FirebaseFirestore.Expression] = [] + for i in 0 ..< exprMaps.count { + guard let emMap = asMap(exprMaps[i]) else { + throw parseError("switch_on requires at least two expressions") + } + let isCondition = i % 2 == 0 && i + 1 < exprMaps.count + if isCondition { + try switchArgs.append(parseBooleanTypedExpression(emMap)) + } else { + try switchArgs.append(parseTypedExpression(emMap)) + } + } + return FunctionExpression(functionName: "switch_on", args: switchArgs) + } + + if resolvedName == "filter" { + return try parseFilterTypedExpression(args: args) + } + + throw parseError("Unsupported expression: \(resolvedName)") + } + + private func rightTypedExpression(from value: Any?) throws -> any FirebaseFirestore.Expression { + if let map = asMap(value) { + return try parseTypedExpression(map) + } + return try constantExpression(from: value as Any) + } + + private func parseFilterTypedExpression(args: [String: Any]) throws + -> any FirebaseFirestore.Expression { + let op = args["operator"] as? String + let exprMaps = asArray(args["expressions"]) + if let op, let exprMaps { + if exprMaps.isEmpty { + throw parseError("filter with operator requires at least one expression") + } + var booleanExprs: [any FirebaseFirestore.BooleanExpression] = [] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + try booleanExprs.append(parseBooleanTypedExpression(emMap)) + } + guard let first = booleanExprs.first else { + throw parseError("filter with operator requires at least one expression") + } + if booleanExprs.count == 1 { + return first + } + if op == "or" || op == "OR" { + return booleanExprs.dropFirst().reduce(first) { $0 || $1 } + } + return booleanExprs.dropFirst().reduce(first) { $0 && $1 } + } + + guard let fieldName = args["field"] as? String else { + throw parseError("filter requires operator+expressions or field") + } + let fieldExpr = Field(fieldName) + + for key in kFilterComparisonKeys { + let value = args[key] + if value == nil { continue } + + switch key { + case "isEqualTo": + return try fieldExpr.equal(rightTypedExpression(from: value)) + case "isNotEqualTo": + return try fieldExpr.notEqual(rightTypedExpression(from: value)) + case "isGreaterThan": + return try fieldExpr.greaterThan(rightTypedExpression(from: value)) + case "isGreaterThanOrEqualTo": + return try fieldExpr.greaterThanOrEqual(rightTypedExpression(from: value)) + case "isLessThan": + return try fieldExpr.lessThan(rightTypedExpression(from: value)) + case "isLessThanOrEqualTo": + return try fieldExpr.lessThanOrEqual(rightTypedExpression(from: value)) + case "arrayContains": + return try fieldExpr.arrayContains(rightTypedExpression(from: value)) + case "arrayContainsAny": + let valuesList = asArray(value) ?? [] + let valueExprs = try valuesList.map { try rightTypedExpression(from: $0) } + if valueExprs.isEmpty { + throw parseError("arrayContainsAny requires non-empty list") + } + return fieldExpr.arrayContainsAny(valueExprs) + case "whereIn": + let valuesList = asArray(value) ?? [] + let valueExprs = try valuesList.map { try rightTypedExpression(from: $0) } + if valueExprs.isEmpty { + throw parseError("whereIn requires non-empty list") + } + return fieldExpr.equalAny(valueExprs) + case "whereNotIn": + let valuesList = asArray(value) ?? [] + let valueExprs = try valuesList.map { try rightTypedExpression(from: $0) } + if valueExprs.isEmpty { + throw parseError("whereNotIn requires non-empty list") + } + return fieldExpr.notEqualAny(valueExprs) + case "isNull": + return fieldExpr.equal(Constant.nil) + case "isNotNull": + return fieldExpr.notEqual(Constant.nil) + default: + continue + } + } + + throw parseError( + "filter requires at least one comparison (isEqualTo, isGreaterThan, etc.)" + ) + } +} + +enum PipelineParser { + static func executePipeline(firestore: Firestore, + stages: [[String: Any?]], + options: [String: Any?]?, + completion: @escaping (Any?, Error?) -> Void) { + _ = options + if NSClassFromString("FIRPipelineBridge") == nil { + completion(nil, pipelineUnavailableError()) + return + } + + if stages.isEmpty { + completion(nil, parseError("Pipeline requires at least one stage")) + return + } + + let stageMaps = stages.map { dictionaryFromOptionalKeyed($0) } + do { + let stageBridges = try parseStages(firestore: firestore, stages: stageMaps) + let pipeline = PipelineBridge(stages: stageBridges, db: firestore) + pipeline.execute { snapshot, execError in + if let execError { + completion(nil, execError) + return + } + completion(snapshot, nil) + } + } catch { + completion(nil, error) + } + } + + private static func keyForExpressionMap(_ em: [String: Any]) throws -> String { + if let alias = nestedValue(em, keyPath: "args.alias") as? String, !alias.isEmpty { + return alias + } + if (em["name"] as? String) == "field" { + if let field = nestedValue(em, keyPath: "args.field") as? String { + return field + } + throw parseError("field expression must have args.field") + } + throw parseError("expression must have alias or be a field reference") + } + + private static func parseSearchFields(expressionMaps exprMaps: [Any], + exprParser: PipelineExpressionParser) throws -> [ + String: ExprBridge + ] { + var fields: [String: ExprBridge] = [:] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + let expr = try exprParser.parseExpression(emMap) + let key = try keyForExpressionMap(emMap) + if key.isEmpty { + throw parseError("expression must have alias or be a field reference") + } + fields[key] = expr + } + return fields + } + + private static func parseSearchStage(args: [String: Any], + exprParser: PipelineExpressionParser) throws -> StageBridge { + let queryType = args["query_type"] as? String + let query = args["query"] + var options: [String: ExprBridge] = [:] + + if queryType == "string" { + guard let query = query as? String else { + throw parseError("search query_type 'string' requires string query") + } + options["query"] = DocumentMatches(query).bridge + } else if queryType == "expression" { + guard let queryMap = asMap(query) else { + throw parseError("search query_type 'expression' requires expression query") + } + options["query"] = try exprParser.parseBooleanExpression(queryMap) + } else { + throw parseError("search requires query_type to be 'string' or 'expression'") + } + + if let limit = args["limit"] as? NSNumber { + options["limit"] = ConstantBridge(limit) + } + if let offset = args["offset"] as? NSNumber { + options["offset"] = ConstantBridge(offset) + } + if let retrievalDepth = args["retrieval_depth"] as? NSNumber { + options["retrieval_depth"] = ConstantBridge(retrievalDepth) + } + if let languageCode = args["language_code"] as? String { + options["language_code"] = ConstantBridge(languageCode) + } + + var sort: [OrderingBridge] = [] + if let orderingMaps = asArray(args["sort"]) { + for om in orderingMaps { + guard let omMap = asMap(om), let exprMap = asMap(omMap["expression"]) else { continue } + let dir = omMap["order_direction"] as? String + let expr = try exprParser.parseExpression(exprMap) + let direction = dir == "asc" ? "ascending" : "descending" + sort.append(OrderingBridge(expr: expr, direction: direction)) + } + } + + var addFields: [String: ExprBridge] = [:] + if let addFieldMaps = asArray(args["add_fields"]), !addFieldMaps.isEmpty { + addFields = try parseSearchFields(expressionMaps: addFieldMaps, exprParser: exprParser) + } + + return SearchStageBridge( + options: options, + addFields: addFields, + select: [:], + sort: sort + ) + } + + private static func parseStages(firestore: Firestore, + stages: [[String: Any]]) throws -> [StageBridge] { + let exprParser = PipelineExpressionParser(firestore: firestore) + var stageBridges: [StageBridge] = [] + + for i in 0 ..< stages.count { + let stageMap = stages[i] + guard let stageName = stageMap["stage"] as? String else { + throw parseError("Stage must have a 'stage' field") + } + let argsObj = stageMap["args"] + let args = asMap(argsObj) ?? [:] + let argsArray = asArray(argsObj) + + var stage: StageBridge? + + if i == 0 { + if stageName == "collection" { + guard let path = args["path"] as? String else { + throw parseError("collection requires 'path'") + } + let ref = firestore.collection(path) + stage = CollectionSourceStageBridge(ref: ref, firestore: firestore, forceIndex: nil) + } else if stageName == "collection_group" { + guard let path = args["path"] as? String else { + throw parseError("collection_group requires 'path'") + } + stage = CollectionGroupSourceStageBridge(collectionId: path, forceIndex: nil) + } else if stageName == "database" { + stage = DatabaseSourceStageBridge() + } else if stageName == "documents" { + guard let docMaps = argsArray, !docMaps.isEmpty else { + throw parseError("documents requires array of document refs") + } + var refs: [DocumentReference] = [] + for docMap in docMaps { + guard let docMap = asMap(docMap) else { continue } + if let path = docMap["path"] as? String { + refs.append(firestore.document(path)) + } + } + stage = DocumentsSourceStageBridge(documents: refs, firestore: firestore) + } else { + throw parseError( + "First stage must be collection, collection_group, documents, or database. Got: \(stageName)" + ) + } + } else { + if stageName == "where" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("where requires expression") + } + let expr = try exprParser.parseBooleanExpression(exprMap) + stage = WhereStageBridge(expr: expr) + } else if stageName == "search" { + stage = try parseSearchStage(args: args, exprParser: exprParser) + } else if stageName == "limit" { + guard let limit = args["limit"] as? NSNumber else { + throw parseError("limit requires numeric limit") + } + stage = LimitStageBridge(limit: Int(limit.intValue)) + } else if stageName == "offset" { + guard let offset = args["offset"] as? NSNumber else { + throw parseError("offset requires numeric offset") + } + stage = OffsetStageBridge(offset: Int(offset.intValue)) + } else if stageName == "sort" { + guard let orderingMaps = asArray(args["orderings"]), !orderingMaps.isEmpty else { + throw parseError("sort requires at least one ordering") + } + var orderings: [OrderingBridge] = [] + for om in orderingMaps { + guard let omMap = asMap(om), let exprMap = asMap(omMap["expression"]) else { continue } + let dir = omMap["order_direction"] as? String + let expr = try exprParser.parseExpression(exprMap) + let direction = dir == "asc" ? "ascending" : "descending" + orderings.append(OrderingBridge(expr: expr, direction: direction)) + } + if orderings.isEmpty { + throw parseError("sort requires at least one ordering") + } + stage = SortStageBridge(orderings: orderings) + } else if stageName == "select" { + guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { + throw parseError("select requires at least one expression") + } + var fields: [String: ExprBridge] = [:] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + let expr = try exprParser.parseExpression(emMap) + let key = try keyForExpressionMap(emMap) + fields[key] = expr + } + stage = SelectStageBridge(selections: fields) + } else if stageName == "add_fields" { + guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { + throw parseError("add_fields requires at least one expression") + } + var fields: [String: ExprBridge] = [:] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + let expr = try exprParser.parseExpression(emMap) + guard let alias = nestedValue(emMap, keyPath: "args.alias") else { + throw parseError("add_fields expressions must have alias") + } + fields["\(alias)"] = expr + } + stage = AddFieldsStageBridge(fields: fields) + } else if stageName == "remove_fields" { + guard let paths = asArray(args["field_paths"]), !paths.isEmpty else { + throw parseError("remove_fields requires field_paths") + } + let fieldPaths = paths.compactMap { $0 as? String } + stage = RemoveFieldsStageBridge(fields: fieldPaths) + } else if stageName == "distinct" { + guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { + throw parseError("distinct requires at least one expression") + } + var fields: [String: ExprBridge] = [:] + for em in exprMaps { + guard let emMap = asMap(em) else { continue } + let expr = try exprParser.parseExpression(emMap) + let key = try keyForExpressionMap(emMap) + fields[key] = expr + } + stage = DistinctStageBridge(groups: fields) + } else if stageName == "replace_with" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("replace_with requires expression") + } + let expr = try exprParser.parseExpression(exprMap) + stage = ReplaceWithStageBridge(expr: expr) + } else if stageName == "union" { + guard let nestedStagesRaw = asArray(args["pipeline"]), !nestedStagesRaw.isEmpty else { + throw parseError("union requires non-empty pipeline") + } + var nestedStages: [[String: Any]] = [] + for nested in nestedStagesRaw { + guard let nestedMap = asMap(nested) else { + throw parseError("Stage must be a map") + } + nestedStages.append(nestedMap) + } + let otherPipeline = try buildPipeline(firestore: firestore, stages: nestedStages) + stage = UnionStageBridge(other: otherPipeline) + } else if stageName == "sample" { + let type = args["type"] as? String + let val = args["value"] + if type == "percentage" { + let v = (val as? NSNumber)?.doubleValue ?? 0 + stage = SampleStageBridge(percentage: v) + } else { + let v = (val as? NSNumber)?.intValue ?? 0 + stage = SampleStageBridge(count: Int64(v)) + } + } else if stageName == "aggregate" { + stage = try? parseAggregateStage(args: args, exprParser: exprParser) + } else if stageName == "aggregate_with_options" { + stage = try? parseAggregateStageWithOptions(args: args, exprParser: exprParser) + } else if stageName == "unnest" { + guard let exprMap = asMap(args["expression"]) else { + throw parseError("unnest requires expression") + } + var fieldExpr: ExprBridge? + var aliasStr: String? + if (exprMap["name"] as? String) == "alias" { + let aliasArgs = asMap(exprMap["args"]) + if let aliasArgs, aliasArgs["expression"] != nil, + let innerExpr = asMap(aliasArgs["expression"]) { + fieldExpr = try exprParser.parseExpression(innerExpr) + aliasStr = aliasArgs["alias"] as? String + } + } + if fieldExpr == nil { + fieldExpr = try exprParser.parseExpression(exprMap) + if aliasStr == nil, (exprMap["name"] as? String) == "field" { + let fieldArgs = asMap(exprMap["args"]) + aliasStr = (fieldArgs?["field"] as? String) ?? "_" + } + } + if aliasStr == nil { aliasStr = "_" } + let aliasExpr = FieldBridge(name: aliasStr!) + let indexFieldStr = args["index_field"] as? String + let indexFieldExpr: ExprBridge? = + (indexFieldStr?.isEmpty == false) ? FieldBridge(name: indexFieldStr!) : nil + stage = UnnestStageBridge( + field: fieldExpr!, + alias: aliasExpr, + indexField: indexFieldExpr + ) + } else if stageName == "find_nearest" { + let vectorFieldName = args["vector_field"] as? String + let vectorValueArray = asArray(args["vector_value"]) + let distanceMeasure = args["distance_measure"] as? String + let limit = args["limit"] as? NSNumber + let distanceField = args["distance_field"] as? String + guard let vectorFieldName, !vectorFieldName.isEmpty else { + throw parseError("find_nearest requires 'vector_field'") + } + guard let vectorValueArray, !vectorValueArray.isEmpty else { + throw parseError("find_nearest requires non-empty 'vector_value'") + } + guard let distanceMeasure, !distanceMeasure.isEmpty else { + throw parseError("find_nearest requires 'distance_measure'") + } + let embeddingField = FieldBridge(name: vectorFieldName) + var numbers: [NSNumber] = [] + numbers.reserveCapacity(vectorValueArray.count) + for v in vectorValueArray { + if let n = v as? NSNumber { + numbers.append(n) + } + } + if numbers.count != vectorValueArray.count { + throw parseError("find_nearest vector_value must be an array of numbers") + } + let queryVector = VectorValue(__array: numbers) + let distanceFieldExpr: ExprBridge? = distanceField.map { FieldBridge(name: $0) } + stage = FindNearestStageBridge( + field: embeddingField, + vectorValue: queryVector, + distanceMeasure: distanceMeasure, + limit: limit, + distanceField: distanceFieldExpr + ) + } else { + throw parseError("Unknown pipeline stage: \(stageName)") + } + } + + if let stage { + stageBridges.append(stage) + } + } + + if stageBridges.isEmpty { + throw parseError("No valid stages") + } + + return stageBridges + } + + private static func aggregateFunction(from funcMap: [String: Any], + exprParser: PipelineExpressionParser) throws + -> AggregateFunctionBridge { + guard let name = funcMap["name"] as? String else { + throw parseError("Aggregate function must have a 'name'") + } + var iosName = name + if name == "count_all" { + iosName = "count" + } else if name == "minimum" { + iosName = "min" + } else if name == "maximum" { + iosName = "max" + } + var argsArray: [ExprBridge] = [] + if let argsDict = asMap(funcMap["args"]), let exprMap = asMap(argsDict["expression"]) { + let expr = try exprParser.parseExpression(exprMap) + argsArray.append(expr) + } + return AggregateFunctionBridge(name: iosName, args: argsArray) + } + + private static func parseAggregateStage(args: [String: Any], + exprParser: PipelineExpressionParser) throws + -> StageBridge { + guard let accumulatorMaps = asArray(args["aggregate_functions"]), !accumulatorMaps.isEmpty + else { + throw parseError("aggregate requires aggregate_functions") + } + return try parseAggregateStage( + accumulatorMaps: accumulatorMaps, + groupMaps: nil, + exprParser: exprParser + ) + } + + private static func parseAggregateStageWithOptions(args: [String: Any], + exprParser: PipelineExpressionParser) throws + -> StageBridge { + guard let stageMap = asMap(args["aggregate_stage"]) else { + throw parseError("aggregate_with_options requires aggregate_stage") + } + var accumulatorMaps = asArray(stageMap["accumulators"]) + if accumulatorMaps == nil || accumulatorMaps?.isEmpty == true { + accumulatorMaps = asArray(stageMap["aggregate_functions"]) + } + guard let accumulatorMaps, !accumulatorMaps.isEmpty else { + throw parseError("aggregate_stage requires accumulators or aggregate_functions") + } + let groupMaps = asArray(stageMap["groups"]) + return try parseAggregateStage( + accumulatorMaps: accumulatorMaps, + groupMaps: groupMaps, + exprParser: exprParser + ) + } + + private static func parseAggregateStage(accumulatorMaps: [Any], + groupMaps: [Any]?, + exprParser: PipelineExpressionParser) throws + -> StageBridge { + var accumulators: [String: AggregateFunctionBridge] = [:] + for accMap in accumulatorMaps { + guard let accMap = asMap(accMap) else { continue } + var alias: String? + var funcMap: [String: Any]? + if (accMap["name"] as? String) == "alias" { + guard let accArgs = asMap(accMap["args"]) else { continue } + alias = accArgs["alias"] as? String + funcMap = asMap(accArgs["aggregate_function"]) + } + guard let alias, let funcMap else { + throw parseError("Each accumulator must have alias and aggregate_function") + } + let funcBridge = try aggregateFunction(from: funcMap, exprParser: exprParser) + accumulators[alias] = funcBridge + } + if accumulators.isEmpty { + throw parseError("aggregate requires at least one valid accumulator") + } + + var groups: [String: ExprBridge] = [:] + if let groupMaps, !groupMaps.isEmpty { + for gm in groupMaps { + guard let gmMap = asMap(gm) else { continue } + guard let expr = try? exprParser.parseExpression(gmMap) else { continue } + let groupKey = try keyForExpressionMap(gmMap) + if groupKey.isEmpty { + throw parseError( + "aggregate group expression must be a field reference or have an alias" + ) + } + groups[groupKey] = expr + } + } + + return AggregateStageBridge(accumulators: accumulators, groups: groups) + } + + private static func buildPipeline(firestore: Firestore, + stages: [[String: Any]]) throws -> PipelineBridge { + let stageBridges = try parseStages(firestore: firestore, stages: stages) + return PipelineBridge(stages: stageBridges, db: firestore) + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift new file mode 100644 index 000000000000..4f38f0912e02 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift @@ -0,0 +1,90 @@ +// 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 Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { + private let firestore: Firestore + private let query: Query? + private let includeMetadataChanges: Bool + private let serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior + private let source: FirebaseFirestore.ListenSource + private var listenerRegistration: ListenerRegistration? + private let snapshotQueue = DispatchQueue( + label: "io.flutter.plugins.firebase.firestore.query_snapshot" + ) + + init(firestore: Firestore, + query: Query?, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource) { + self.firestore = firestore + self.query = query + self.includeMetadataChanges = includeMetadataChanges + self.serverTimestampBehavior = serverTimestampBehavior + self.source = source + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + guard let query else { + return FlutterError( + code: "sdk-error", + message: + "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + details: nil + ) + } + + let options = SnapshotListenOptions() + .withIncludeMetadataChanges(includeMetadataChanges) + .withSource(source) + + listenerRegistration = query.addSnapshotListener(options: options) { snapshot, error in + if let error { + let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) + DispatchQueue.main.async { + events( + FLTFirebasePlugin.createFlutterError( + fromCode: code, + message: message, + optionalDetails: ["code": code, "message": message], + andOptionalNSError: error as NSError + ) + ) + } + } else if let snapshot { + self.snapshotQueue.async { + let pigeonSnapshot = PigeonParser.toPigeonQuerySnapshot( + snapshot, serverTimestampBehavior: self.serverTimestampBehavior + ) + DispatchQueue.main.async { + events(pigeonSnapshot) + } + } + } + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + listenerRegistration?.remove() + listenerRegistration = nil + return nil + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift new file mode 100644 index 000000000000..91baad7ab672 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift @@ -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 FirebaseFirestore +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class SnapshotsInSyncStreamHandler: NSObject, FlutterStreamHandler { + private let firestore: Firestore + private var listenerRegistration: ListenerRegistration? + + init(firestore: Firestore) { + self.firestore = firestore + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + listenerRegistration = firestore.addSnapshotsInSyncListener { + DispatchQueue.main.async { + events(nil) + } + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + listenerRegistration?.remove() + listenerRegistration = nil + return nil + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift new file mode 100644 index 000000000000..d5325f090992 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift @@ -0,0 +1,144 @@ +// 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 Foundation + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +final class TransactionStreamHandler: NSObject, FlutterStreamHandler { + private let transactionId: String + private let firestore: Firestore + private let timeout: Int + private let maxAttempts: Int + private let started: (Transaction) -> Void + private let ended: () -> Void + private let semaphore = DispatchSemaphore(value: 0) + private var resultType: InternalTransactionResult = .success + private var commands: [InternalTransactionCommand?] = [] + + init(id transactionId: String, + firestore: Firestore, + timeout: Int, + maxAttempts: Int, + started: @escaping (Transaction) -> Void, + ended: @escaping () -> Void) { + self.transactionId = transactionId + self.firestore = firestore + self.timeout = timeout + self.maxAttempts = maxAttempts + self.started = started + self.ended = ended + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + let options = TransactionOptions() + options.maxAttempts = maxAttempts + + firestore.runTransaction( + with: options, + block: { [weak self] transaction, _ in + guard let self else { return nil } + self.started(transaction) + + DispatchQueue.main.async { + events([ + "appName": FLTFirebasePlugin.firebaseAppName(fromIosName: self.firestore.app.name) + as Any, + ]) + } + + let timedOut = self.semaphore.wait( + timeout: .now() + .milliseconds(self.timeout) + ) + if timedOut == .timedOut { + let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage( + from: NSError( + domain: FirestoreErrorDomain, + code: FirestoreErrorCode.deadlineExceeded.rawValue, + userInfo: [:] + ) + ) + DispatchQueue.main.async { + events(["error": ["code": code, "message": message]]) + } + } + + if self.resultType == .failure { + return nil + } + + for command in self.commands { + guard let command else { continue } + let reference = self.firestore.document(command.path) + switch command.type { + case .deleteType: + transaction.deleteDocument(reference) + case .update: + if let data = command.data as? [AnyHashable: Any] { + transaction.updateData(data, forDocument: reference) + } + case .set: + let data = command.data as? [String: Any] ?? [:] + if command.option?.merge == true { + transaction.setData(data, forDocument: reference, merge: true) + } else if let mergeFields = command.option?.mergeFields { + transaction.setData( + data, + forDocument: reference, + mergeFields: PigeonParser.parseFieldPath(mergeFields) + ) + } else { + transaction.setData(data, forDocument: reference) + } + case .get: + break + } + } + return nil + }, + completion: { [weak self] _, error in + if let error { + let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) + DispatchQueue.main.async { + events(["error": ["code": code, "message": message]]) + } + } else { + DispatchQueue.main.async { + events(["complete": true]) + } + } + DispatchQueue.main.async { + events(FlutterEndOfEventStream) + } + self?.ended() + } + ) + + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + semaphore.signal() + return nil + } + + func receiveTransactionResponse(_ resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?) { + self.resultType = resultType + self.commands = commands ?? [] + semaphore.signal() + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h deleted file mode 100644 index fc97756c5d90..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h deleted file mode 100644 index 522a87991de3..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h +++ /dev/null @@ -1,15 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h deleted file mode 100644 index 7f49b71eb7e1..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h deleted file mode 100644 index e04b30b5483e..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h deleted file mode 100644 index f40262d6b98d..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h deleted file mode 100644 index 30dbfcd72dc8..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h deleted file mode 100644 index 97c77f0e2a88..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h deleted file mode 100644 index 8528b72e5af0..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h deleted file mode 100644 index 1a05f121f6e4..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h deleted file mode 100644 index c40a148efa5d..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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 "../Public/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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h deleted file mode 100644 index 5fa20d98759f..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 "../Public/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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h deleted file mode 100644 index 7127b0061f58..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h deleted file mode 100644 index 85e38b5ee2a5..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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 -#endif -#import "FirestoreMessages.g.h" - -@interface FLTFirebaseFirestorePlugin - : FLTFirebasePlugin -+ (NSMutableDictionary *)serverTimestampMap; -@end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h deleted file mode 100644 index 7fd40daf7fcf..000000000000 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h +++ /dev/null @@ -1,457 +0,0 @@ -// 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/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirestoreClientLanguage.mm b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm similarity index 87% rename from packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirestoreClientLanguage.mm rename to packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm index 399944ededf8..1780724473f7 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirestoreClientLanguage.mm +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm @@ -18,9 +18,7 @@ } // namespace firestore } // namespace firebase -@interface FLTFirestoreClientLanguage : NSObject -+ (void)setClientLanguage:(NSString *)language; -@end +#import "FLTFirestoreClientLanguage.h" @implementation FLTFirestoreClientLanguage + (void)setClientLanguage:(NSString *)language { diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h new file mode 100644 index 000000000000..818da8efbfd0 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h @@ -0,0 +1,13 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTFirestoreClientLanguage : NSObject ++ (void)setClientLanguage:(NSString *)language; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore.podspec b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore.podspec index 44815fe6d85a..54e4676ac255 100755 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore.podspec +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore.podspec @@ -43,9 +43,10 @@ Pod::Spec.new do |s| s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'cloud_firestore/Sources/cloud_firestore/**/*.{h,m}' - s.public_header_files = 'cloud_firestore/Sources/cloud_firestore/include/Public/**/*.h' - s.private_header_files = 'cloud_firestore/Sources/cloud_firestore/include/Private/**/*.h' + s.source_files = 'cloud_firestore/Sources/**/*.{swift,h,m,mm}' + s.public_header_files = 'cloud_firestore/Sources/cloud_firestore_objc/include/*.h' + + s.swift_version = '5.0' s.platform = :osx, '10.13' @@ -59,7 +60,6 @@ Pod::Spec.new do |s| s.static_framework = true s.pod_target_xcconfig = { - 'GCC_PREPROCESSOR_DEFINITIONS' => "LIBRARY_VERSION=\\\"#{library_version}\\\" LIBRARY_NAME=\\\"flutter-fire-fst\\\"", 'DEFINES_MODULE' => 'YES' } end diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift index 811b5cc3436a..44fa6d8cf257 100644 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift @@ -7,7 +7,6 @@ import PackageDescription -let libraryVersion = "6.8.0" let firebaseSdkVersion: Version = "12.18.0" let package = Package( @@ -24,22 +23,30 @@ let package = Package( .package(name: "FlutterFramework", path: "../FlutterFramework"), ], targets: [ + // SPM does not allow mixing Swift and ObjC in a single target. + .target( + name: "cloud_firestore_objc", + dependencies: [ + .product(name: "FirebaseFirestore", package: "firebase-ios-sdk") + ], + path: "Sources/cloud_firestore_objc", + publicHeadersPath: "include", + cSettings: [ + .headerSearchPath("include") + ] + ), .target( name: "cloud_firestore", dependencies: [ + "cloud_firestore_objc", .product(name: "FirebaseFirestore", package: "firebase-ios-sdk"), .product(name: "firebase-core", package: "firebase_core"), .product(name: "FlutterFramework", package: "FlutterFramework"), ], + path: "Sources/cloud_firestore", resources: [ .process("Resources") - ], - cSettings: [ - .headerSearchPath("include/cloud_firestore/Private"), - .headerSearchPath("include/cloud_firestore/Public"), - .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), - .define("LIBRARY_NAME", to: "\"flutter-fire-fst\""), ] - ) + ), ] ) diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Constants.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Constants.swift new file mode 120000 index 000000000000..0dd2a14ae8c5 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Constants.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/Constants.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift new file mode 120000 index 000000000000..a41ddb12f40b --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m deleted file mode 120000 index aeb6ebc5c006..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m deleted file mode 120000 index bd4cf1dd5fde..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreExtension.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m deleted file mode 120000 index f791caf0cc3c..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift new file mode 120000 index 000000000000..40a74af9337c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m deleted file mode 120000 index e89ebb8929cc..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m deleted file mode 120000 index babea1c24081..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m deleted file mode 120000 index e4b626a19876..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m deleted file mode 120000 index b4114c3d04d5..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTLoadBundleStreamHandler.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m deleted file mode 120000 index 0ab2d722c4a0..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m deleted file mode 120000 index 1f1bbb9f3bef..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m deleted file mode 120000 index 7e50903ad6be..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTSnapshotsInSyncStreamHandler.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m deleted file mode 120000 index 66555a825643..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift new file mode 120000 index 000000000000..0416128c51e4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreExtension.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift new file mode 120000 index 000000000000..3e7fa59845d2 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift new file mode 120000 index 000000000000..40aee5d2ff3b --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreUtils.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift new file mode 120000 index 000000000000..9c35a48e9cc1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreWriter.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m deleted file mode 120000 index 83b8548becc8..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift new file mode 120000 index 000000000000..020be4407a2d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m deleted file mode 120000 index 32c6ef6bf146..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift new file mode 120000 index 000000000000..9a804a49e3d2 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift new file mode 120000 index 000000000000..d8499f4160c6 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift new file mode 120000 index 000000000000..503c300d6114 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift new file mode 120000 index 000000000000..4eaa2e2e155c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep new file mode 120000 index 000000000000..877cc63276af --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep @@ -0,0 +1 @@ +../../../../../../ios/cloud_firestore/Sources/cloud_firestore/Resources/.gitkeep \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift new file mode 120000 index 000000000000..7108d5049e86 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift new file mode 120000 index 000000000000..c531ab274b29 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h deleted file mode 120000 index 61d4d26dcb13..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTDocumentSnapshotStreamHandler.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h deleted file mode 120000 index 878691155dfe..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreExtension.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h deleted file mode 120000 index 408a177ffc29..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h deleted file mode 120000 index f09ad17e8322..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h deleted file mode 120000 index 4c7ea1f49445..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreWriter.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h deleted file mode 120000 index be3fee9e19a9..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h deleted file mode 120000 index fb086ff6b78c..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h deleted file mode 120000 index 31805df68cc2..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h deleted file mode 120000 index a144ccda21cf..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h deleted file mode 120000 index 25a76252d90e..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h deleted file mode 120000 index 34aeced7af29..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h deleted file mode 120000 index 111ed085e8b8..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h deleted file mode 120000 index 9d774a9ce963..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h deleted file mode 120000 index cfab6484dd12..000000000000 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm new file mode 120000 index 000000000000..8c834e6bc84f --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore_objc/FLTFirestoreClientLanguage.mm \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h new file mode 120000 index 000000000000..30882d4c3beb --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h @@ -0,0 +1 @@ +../../../../../ios/cloud_firestore/Sources/cloud_firestore_objc/include/FLTFirestoreClientLanguage.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h index fcc6a5e80a11..4a39a0af5b9c 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h +++ b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h @@ -194,13 +194,7 @@ class InternalFirebaseSettings { private: static InternalFirebaseSettings FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirestorePigeonFirebaseApp; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; @@ -244,13 +238,7 @@ class FirestorePigeonFirebaseApp { private: static FirestorePigeonFirebaseApp FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::string app_name_; @@ -279,13 +267,8 @@ class InternalSnapshotMetadata { static InternalSnapshotMetadata FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - private: - private: private: friend class InternalDocumentSnapshot; friend class InternalQuerySnapshot; @@ -331,13 +314,8 @@ class InternalDocumentSnapshot { static InternalDocumentSnapshot FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - private: - private: private: friend class InternalDocumentChange; friend class FirebaseFirestoreHostApi; @@ -381,13 +359,8 @@ class InternalDocumentChange { static InternalDocumentChange FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - private: - private: private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; @@ -430,13 +403,7 @@ class InternalQuerySnapshot { private: static InternalQuerySnapshot FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; ::flutter::EncodableList documents_; @@ -482,13 +449,7 @@ class InternalPipelineResult { private: static InternalPipelineResult FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::optional document_path_; @@ -519,13 +480,7 @@ class InternalPipelineSnapshot { private: static InternalPipelineSnapshot FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; ::flutter::EncodableList results_; @@ -555,13 +510,7 @@ class InternalGetOptions { private: static InternalGetOptions FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; Source source_; @@ -595,13 +544,7 @@ class InternalDocumentOption { private: static InternalDocumentOption FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class InternalTransactionCommand; friend class DocumentReferenceRequest; friend class FirebaseFirestoreHostApi; @@ -653,13 +596,7 @@ class InternalTransactionCommand { private: static InternalTransactionCommand FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; InternalTransactionType type_; @@ -714,13 +651,7 @@ class DocumentReferenceRequest { private: static DocumentReferenceRequest FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::string path_; @@ -792,13 +723,7 @@ class InternalQueryParameters { private: static InternalQueryParameters FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::optional<::flutter::EncodableList> where_; @@ -836,13 +761,7 @@ class AggregateQuery { private: static AggregateQuery FromEncodableList(const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; AggregateType type_; @@ -880,13 +799,7 @@ class AggregateQueryResponse { private: static AggregateQueryResponse FromEncodableList( const ::flutter::EncodableList& list); - - public: - public: ::flutter::EncodableList ToEncodableList() const; - - private: - private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; AggregateType type_; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh index 0bf9dabad9d7..dd60c61206b1 100755 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh @@ -20,25 +20,20 @@ sed -i '' 's/private static class PigeonCodec extends StandardMessageCodec {/pub echo "Android modification complete." -# Fix iOS files -FILE_NAME="../../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m" -sed -i '' '/#import "FirestoreMessages.g.h"/a\ -#import "FLTFirebaseFirestoreReader.h"\ -#import "FLTFirebaseFirestoreWriter.h" -' $FILE_NAME -# Pigeon 26 generates ObjC codec classes with a `nullPigeonCodec*` prefix when no -# ObjcOptions prefix is configured. Rename them to stable, readable names first. -sed -i '' 's/nullFirestoreMessagesPigeonCodecReaderWriter/FirebaseFirestoreHostApiCodecReaderWriter/g' $FILE_NAME -sed -i '' 's/nullFirestoreMessagesPigeonCodecReader/FirebaseFirestoreHostApiCodecReader/g' $FILE_NAME -sed -i '' 's/nullFirestoreMessagesPigeonCodecWriter/FirebaseFirestoreHostApiCodecWriter/g' $FILE_NAME -# Rename the public codec getter from `nullGetFirestoreMessagesCodec` so the plugin can reuse -# it on EventChannels without an awkward `null` prefix. -sed -i '' 's/nullGetFirestoreMessagesCodec/GetFirebaseFirestoreHostApiCodec/g' $FILE_NAME -sed -i '' 's/nullGetFirestoreMessagesCodec/GetFirebaseFirestoreHostApiCodec/g' ../../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h -# Reparent the reader/writer onto our custom Firestore reader/writer so Firestore-specific -# types (Timestamp, GeoPoint, FieldValue, DocumentReference, FieldPath, ...) round-trip. -sed -i '' 's/@interface FirebaseFirestoreHostApiCodecReader : FlutterStandardReader/@interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader/' $FILE_NAME -sed -i '' 's/@interface FirebaseFirestoreHostApiCodecWriter : FlutterStandardWriter/@interface FirebaseFirestoreHostApiCodecWriter : FLTFirebaseFirestoreWriter/' $FILE_NAME +# Fix iOS Swift files. +# Pigeon has no custom-codec hook, so reparent the generated reader/writer onto our +# Firestore codec (Timestamp, GeoPoint, FieldValue, DocumentReference, ...) and expose +# the ReaderWriter so EventChannels can reuse it. +FILE_NAME="../../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift" +sed -i '' 's/private class FirestoreMessagesPigeonCodecReader: FlutterStandardReader/class FirestoreMessagesPigeonCodecReader: FirebaseFirestoreReader/' "$FILE_NAME" +sed -i '' 's/private class FirestoreMessagesPigeonCodecWriter: FlutterStandardWriter/class FirestoreMessagesPigeonCodecWriter: FirebaseFirestoreWriter/' "$FILE_NAME" +sed -i '' 's/private class FirestoreMessagesPigeonCodecReaderWriter/class FirestoreMessagesPigeonCodecReaderWriter/' "$FILE_NAME" +# `where` is a Swift keyword; Pigeon emits it as a property name on InternalQueryParameters. +perl -i -pe 's/\bvar where:/var `where`:/g; s/\blet where:/let `where`:/g; s/\bwhere: where,/`where`: `where`,/g; s/\blhs\.where\b/lhs.`where`/g; s/\brhs\.where\b/rhs.`where`/g; s/value: where,/value: `where`,/g' "$FILE_NAME" +# The toList() array entry is a bare `where,` on its own line. +sed -i '' '/func toList()/,/^ }/{ + s/^ where,$/ `where`,/ +}' "$FILE_NAME" echo "iOS modification complete." diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart index 27a61ef98f0d..619fd469d432 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart @@ -16,10 +16,8 @@ import 'package:pigeon/pigeon.dart'; package: 'io.flutter.plugins.firebase.firestore', className: 'GeneratedAndroidFirebaseFirestore', ), - objcHeaderOut: - '../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h', - objcSourceOut: - '../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m', + swiftOut: + '../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift', cppHeaderOut: '../cloud_firestore/windows/messages.g.h', cppSourceOut: '../cloud_firestore/windows/messages.g.cpp', cppOptions: CppOptions(namespace: 'cloud_firestore_windows'), diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart index 726d510a1f69..3877b1886a72 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart @@ -173,10 +173,7 @@ abstract class TestFirebaseFirestoreHostApi { Future loadBundle(FirestorePigeonFirebaseApp app, Uint8List bundle); Future namedQueryGet( - FirestorePigeonFirebaseApp app, - String name, - InternalGetOptions options, - ); + FirestorePigeonFirebaseApp app, String name, InternalGetOptions options); Future clearPersistence(FirestorePigeonFirebaseApp app); @@ -189,101 +186,76 @@ abstract class TestFirebaseFirestoreHostApi { Future waitForPendingWrites(FirestorePigeonFirebaseApp app); Future setIndexConfiguration( - FirestorePigeonFirebaseApp app, - String indexConfiguration, - ); + FirestorePigeonFirebaseApp app, String indexConfiguration); Future setLoggingEnabled(bool loggingEnabled); Future snapshotsInSyncSetup(FirestorePigeonFirebaseApp app); Future transactionCreate( - FirestorePigeonFirebaseApp app, - int timeout, - int maxAttempts, - ); + FirestorePigeonFirebaseApp app, int timeout, int maxAttempts); Future transactionStoreResult( - String transactionId, - InternalTransactionResult resultType, - List? commands, - ); + String transactionId, + InternalTransactionResult resultType, + List? commands); Future transactionGet( - FirestorePigeonFirebaseApp app, - String transactionId, - String path, - ); + FirestorePigeonFirebaseApp app, String transactionId, String path); Future documentReferenceSet( - FirestorePigeonFirebaseApp app, - DocumentReferenceRequest request, - ); + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request); Future documentReferenceUpdate( - FirestorePigeonFirebaseApp app, - DocumentReferenceRequest request, - ); + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request); Future documentReferenceGet( - FirestorePigeonFirebaseApp app, - DocumentReferenceRequest request, - ); + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request); Future documentReferenceDelete( - FirestorePigeonFirebaseApp app, - DocumentReferenceRequest request, - ); + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request); Future queryGet( - FirestorePigeonFirebaseApp app, - String path, - bool isCollectionGroup, - InternalQueryParameters parameters, - InternalGetOptions options, - ); + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + InternalQueryParameters parameters, + InternalGetOptions options); Future> aggregateQuery( - FirestorePigeonFirebaseApp app, - String path, - InternalQueryParameters parameters, - AggregateSource source, - List queries, - bool isCollectionGroup, - ); + FirestorePigeonFirebaseApp app, + String path, + InternalQueryParameters parameters, + AggregateSource source, + List queries, + bool isCollectionGroup); Future writeBatchCommit( - FirestorePigeonFirebaseApp app, - List writes, - ); + FirestorePigeonFirebaseApp app, List writes); Future querySnapshot( - FirestorePigeonFirebaseApp app, - String path, - bool isCollectionGroup, - InternalQueryParameters parameters, - InternalGetOptions options, - bool includeMetadataChanges, - ListenSource source, - ); + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + InternalQueryParameters parameters, + InternalGetOptions options, + bool includeMetadataChanges, + ListenSource source); Future documentReferenceSnapshot( - FirestorePigeonFirebaseApp app, - DocumentReferenceRequest parameters, - bool includeMetadataChanges, - ListenSource source, - ); + FirestorePigeonFirebaseApp app, + DocumentReferenceRequest parameters, + bool includeMetadataChanges, + ListenSource source); Future persistenceCacheIndexManagerRequest( - FirestorePigeonFirebaseApp app, - PersistenceCacheIndexManagerRequest request, - ); + FirestorePigeonFirebaseApp app, + PersistenceCacheIndexManagerRequest request); Future executePipeline( - FirestorePigeonFirebaseApp app, - List?> stages, - Map? options, - ); + FirestorePigeonFirebaseApp app, + List?> stages, + Map? options); static void setUp( TestFirebaseFirestoreHostApi? api, { @@ -294,10 +266,9 @@ abstract class TestFirebaseFirestoreHostApi { messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -316,18 +287,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -348,18 +317,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -377,18 +344,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -406,18 +371,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -435,18 +398,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -464,18 +425,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -493,18 +452,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -523,18 +480,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -551,18 +506,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -580,18 +533,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -606,27 +557,22 @@ abstract class TestFirebaseFirestoreHostApi { final int arg_maxAttempts = args[2]! as int; try { final String output = await api.transactionCreate( - arg_app, - arg_timeout, - arg_maxAttempts, - ); + arg_app, arg_timeout, arg_maxAttempts); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -642,27 +588,22 @@ abstract class TestFirebaseFirestoreHostApi { (args[2] as List?)?.cast(); try { await api.transactionStoreResult( - arg_transactionId, - arg_resultType, - arg_commands, - ); + arg_transactionId, arg_resultType, arg_commands); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -683,18 +624,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -714,18 +653,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -745,18 +682,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -777,18 +712,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -808,18 +741,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -836,30 +767,23 @@ abstract class TestFirebaseFirestoreHostApi { args[3]! as InternalQueryParameters; final InternalGetOptions arg_options = args[4]! as InternalGetOptions; try { - final InternalQuerySnapshot output = await api.queryGet( - arg_app, - arg_path, - arg_isCollectionGroup, - arg_parameters, - arg_options, - ); + final InternalQuerySnapshot output = await api.queryGet(arg_app, + arg_path, arg_isCollectionGroup, arg_parameters, arg_options); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -879,31 +803,23 @@ abstract class TestFirebaseFirestoreHostApi { final bool arg_isCollectionGroup = args[5]! as bool; try { final List output = - await api.aggregateQuery( - arg_app, - arg_path, - arg_parameters, - arg_source, - arg_queries, - arg_isCollectionGroup, - ); + await api.aggregateQuery(arg_app, arg_path, arg_parameters, + arg_source, arg_queries, arg_isCollectionGroup); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -923,18 +839,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -954,31 +868,28 @@ abstract class TestFirebaseFirestoreHostApi { final ListenSource arg_source = args[6]! as ListenSource; try { final String output = await api.querySnapshot( - arg_app, - arg_path, - arg_isCollectionGroup, - arg_parameters, - arg_options, - arg_includeMetadataChanges, - arg_source, - ); + arg_app, + arg_path, + arg_isCollectionGroup, + arg_parameters, + arg_options, + arg_includeMetadataChanges, + arg_source); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -994,29 +905,23 @@ abstract class TestFirebaseFirestoreHostApi { final bool arg_includeMetadataChanges = args[2]! as bool; final ListenSource arg_source = args[3]! as ListenSource; try { - final String output = await api.documentReferenceSnapshot( - arg_app, - arg_parameters, - arg_includeMetadataChanges, - arg_source, - ); + final String output = await api.documentReferenceSnapshot(arg_app, + arg_parameters, arg_includeMetadataChanges, arg_source); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -1036,18 +941,16 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } } { final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, null); @@ -1070,8 +973,7 @@ abstract class TestFirebaseFirestoreHostApi { return wrapResponse(error: e); } catch (e) { return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + error: PlatformException(code: 'error', message: e.toString())); } }); } diff --git a/scripts/generate_versions_spm.dart b/scripts/generate_versions_spm.dart index 2b7766dedcdb..645696665077 100644 --- a/scripts/generate_versions_spm.dart +++ b/scripts/generate_versions_spm.dart @@ -118,6 +118,7 @@ void updateLibraryVersionPureSwiftPlugins() { 'firebase_in_app_messaging', 'firebase_crashlytics', 'firebase_core', + 'cloud_firestore', ]; for (final package in packages) { From e1c99301f4dd445d7ab5e78980deb7be7c88b794 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 24 Aug 2026 13:28:53 +0000 Subject: [PATCH 2/4] format --- .../DocumentSnapshotStreamHandler.swift | 15 +- .../FLTFirebaseFirestorePlugin.swift | 196 +++++---- .../FirebaseFirestoreReader.swift | 2 +- .../FirebaseFirestoreUtils.swift | 2 +- .../FirebaseFirestoreWriter.swift | 11 +- .../cloud_firestore/FirestoreMessages.g.swift | 392 +++++++++++------- .../LoadBundleStreamHandler.swift | 3 +- .../cloud_firestore/PigeonParser.swift | 59 ++- .../cloud_firestore/PipelineParser.swift | 175 +++++--- .../QuerySnapshotStreamHandler.swift | 17 +- .../SnapshotsInSyncStreamHandler.swift | 3 +- .../TransactionStreamHandler.swift | 25 +- 12 files changed, 545 insertions(+), 355 deletions(-) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift index 2c55647ad283..46b12c3945ef 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift @@ -25,11 +25,13 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { private let source: FirebaseFirestore.ListenSource private var listenerRegistration: ListenerRegistration? - init(firestore: Firestore, - reference: DocumentReference, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource) { + init( + firestore: Firestore, + reference: DocumentReference, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource + ) { self.firestore = firestore self.reference = reference self.includeMetadataChanges = includeMetadataChanges @@ -38,7 +40,8 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { let options = SnapshotListenOptions() .withIncludeMetadataChanges(includeMetadataChanges) .withSource(source) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift index 840c5130bd00..6a069084f73e 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift @@ -26,7 +26,8 @@ import Foundation @objc(FLTFirebaseFirestorePlugin) public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, - FirebaseFirestoreHostApi { + FirebaseFirestoreHostApi +{ private var messenger: FlutterBinaryMessenger private var transactions: [String: Transaction] = [:] private var eventChannels: [String: FlutterEventChannel] = [:] @@ -111,9 +112,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } @discardableResult - private func registerEventChannel(prefix: String, - identifier: String = UUID().uuidString.lowercased(), - streamHandler: NSObject & FlutterStreamHandler) -> String { + private func registerEventChannel( + prefix: String, + identifier: String = UUID().uuidString.lowercased(), + streamHandler: NSObject & FlutterStreamHandler + ) -> String { let channelName = "\(prefix)/\(identifier)" let channel = FlutterEventChannel( name: channelName, @@ -169,13 +172,17 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu return firestore } - private func completeOnError(_ error: Error, - _ completion: @escaping (Result) -> Void) { + private func completeOnError( + _ error: Error, + _ completion: @escaping (Result) -> Void + ) { completion(.failure(FirebaseFirestoreUtils.flutterError(from: error))) } - func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, - completion: @escaping (Result) -> Void) { + func loadBundle( + app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreLoadBundleChannelName, @@ -184,8 +191,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, - completion: @escaping (Result) -> Void) { + func namedQueryGet( + app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let source = PigeonParser.parseSource(options.source) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -199,7 +208,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "non-existent-named-query", message: - "Named query has not been found. Please check it has been loaded properly via loadBundle().", + "Named query has not been found. Please check it has been loaded properly via loadBundle().", details: nil ) ) @@ -222,8 +231,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func clearPersistence(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func clearPersistence( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).clearPersistence { error in if let error { self.completeOnError(error, completion) @@ -233,8 +244,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func disableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func disableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).disableNetwork { error in if let error { self.completeOnError(error, completion) @@ -244,8 +257,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func enableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func enableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).enableNetwork { error in if let error { self.completeOnError(error, completion) @@ -255,8 +270,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func terminate(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func terminate( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) firestore.terminate { error in if let error { @@ -271,8 +288,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func waitForPendingWrites(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func waitForPendingWrites( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).waitForPendingWrites { error in if let error { self.completeOnError(error, completion) @@ -282,8 +301,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, - completion: @escaping (Result) -> Void) { + func setIndexConfiguration( + app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void + ) { firestore(from: app).setIndexConfiguration(indexConfiguration) { error in if let error { self.completeOnError(error, completion) @@ -293,14 +314,18 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setLoggingEnabled(loggingEnabled: Bool, - completion: @escaping (Result) -> Void) { + func setLoggingEnabled( + loggingEnabled: Bool, + completion: @escaping (Result) -> Void + ) { Firestore.enableLogging(loggingEnabled) completion(.success(())) } - func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func snapshotsInSyncSetup( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName, @@ -309,8 +334,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, - completion: @escaping (Result) -> Void) { + func transactionCreate( + app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let transactionId = UUID().uuidString.lowercased() let handler = TransactionStreamHandler( @@ -340,15 +367,19 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, - commands: [InternalTransactionCommand?]?, - completion: @escaping (Result) -> Void) { + func transactionStoreResult( + transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void + ) { transactionHandlers[transactionId]?.receiveTransactionResponse(resultType, commands: commands) completion(.success(())) } - func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, - completion: @escaping (Result) -> Void) { + func transactionGet( + app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void + ) { DispatchQueue.global(qos: .default).async { let firestore = self.firestore(from: app) let document = firestore.document(path) @@ -363,7 +394,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "missing-transaction", message: - "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", + "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", details: nil ) ) @@ -386,8 +417,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceSet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { let document = firestore(from: app).document(request.path) let data = request.data as? [String: Any] ?? [:] let finish: (Error?) -> Void = { error in @@ -409,8 +442,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceUpdate( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { let document = firestore(from: app).document(request.path) let data = request.data as? [AnyHashable: Any] ?? [:] document.updateData(data) { error in @@ -422,9 +457,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) - -> Void) { + func documentReferenceGet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: + @escaping (Result) + -> Void + ) { let document = firestore(from: app).document(request.path) let source = PigeonParser.parseSource(request.source ?? .serverAndCache) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -445,8 +483,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceDelete( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { firestore(from: app).document(request.path).delete { error in if let error { self.completeOnError(error, completion) @@ -456,9 +496,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - completion: @escaping (Result) -> Void) { + func queryGet( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -471,7 +513,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -498,10 +540,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, - parameters: InternalQueryParameters, - source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, - completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) { + func aggregateQuery( + app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, + source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void + ) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -514,7 +558,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -582,8 +626,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], - completion: @escaping (Result) -> Void) { + func writeBatchCommit( + app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let batch = firestore.batch() for write in writes.compactMap({ $0 }) { @@ -619,10 +665,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void) { + func querySnapshot( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let query = PigeonParser.parseQuery( parameters: parameters, firestore: firestore, path: path, isCollectionGroup: isCollectionGroup @@ -633,7 +681,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -656,10 +704,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, - parameters: DocumentReferenceRequest, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void) { + func documentReferenceSnapshot( + app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let document = firestore.document(parameters.path) let identifier = registerEventChannel( @@ -677,9 +727,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, - request: PersistenceCacheIndexManagerRequest, - completion: @escaping (Result) -> Void) { + func persistenceCacheIndexManagerRequest( + app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void + ) { if let manager = firestore(from: app).persistentCacheIndexManager { switch request { case .enableIndexAutoCreation: @@ -695,9 +747,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(())) } - func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], - options: [String?: Any?]?, - completion: @escaping (Result) -> Void) { + func executePipeline( + app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let mappedStages: [[String: Any?]] = stages.compactMap { stage in guard let stage else { return nil } @@ -756,7 +810,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu let ref = object.value(forKey: "reference") as AnyObject? let path = (ref?.value(forKey: "path") as? String) - ?? (object.value(forKey: "documentID") as? String) + ?? (object.value(forKey: "documentID") as? String) let data = object.value(forKey: "data") as? [String: Any] let mappedData: [String?: Any?]? = data.map { Dictionary(uniqueKeysWithValues: $0.map { ($0.key as String?, $0.value as Any?) }) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift index b6216fbb9c09..40bc993d6947 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift @@ -50,7 +50,7 @@ class FirebaseFirestoreReader: FlutterStandardReader { let length = readSize() var array: [Any] = [] array.reserveCapacity(Int(length)) - for _ in 0 ..< length { + for _ in 0.. FirebaseFirestore - .ServerTimestampBehavior { + .ServerTimestampBehavior + { switch string { case "estimate": return .estimate @@ -163,7 +166,7 @@ class FirebaseFirestoreWriter: FlutterStandardWriter { let data: Any = documentSnapshot.exists - ? documentSnapshot.data(with: behavior) as Any : NSNull() + ? documentSnapshot.data(with: behavior) as Any : NSNull() return [ "path": documentSnapshot.reference.path, "data": data, diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift index 4fcaa93a1e29..270e4af3b76a 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.swift @@ -306,15 +306,16 @@ struct InternalFirebaseSettings: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.persistenceEnabled, rhs.persistenceEnabled) && - deepEqualsFirestoreMessages( + return deepEqualsFirestoreMessages(lhs.persistenceEnabled, rhs.persistenceEnabled) + && deepEqualsFirestoreMessages( lhs.host, rhs.host - ) && deepEqualsFirestoreMessages(lhs.sslEnabled, rhs.sslEnabled) && - deepEqualsFirestoreMessages( + ) && deepEqualsFirestoreMessages(lhs.sslEnabled, rhs.sslEnabled) + && deepEqualsFirestoreMessages( lhs.cacheSizeBytes, rhs.cacheSizeBytes - ) && deepEqualsFirestoreMessages( + ) + && deepEqualsFirestoreMessages( lhs.ignoreUndefinedProperties, rhs.ignoreUndefinedProperties ) @@ -361,10 +362,11 @@ struct FirestorePigeonFirebaseApp: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.appName, rhs.appName) && deepEqualsFirestoreMessages( - lhs.settings, - rhs.settings - ) && deepEqualsFirestoreMessages(lhs.databaseURL, rhs.databaseURL) + return deepEqualsFirestoreMessages(lhs.appName, rhs.appName) + && deepEqualsFirestoreMessages( + lhs.settings, + rhs.settings + ) && deepEqualsFirestoreMessages(lhs.databaseURL, rhs.databaseURL) } func hash(into hasher: inout Hasher) { @@ -402,8 +404,8 @@ struct InternalSnapshotMetadata: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.hasPendingWrites, rhs.hasPendingWrites) && - deepEqualsFirestoreMessages( + return deepEqualsFirestoreMessages(lhs.hasPendingWrites, rhs.hasPendingWrites) + && deepEqualsFirestoreMessages( lhs.isFromCache, rhs.isFromCache ) @@ -447,10 +449,11 @@ struct InternalDocumentSnapshot: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.path, rhs.path) && deepEqualsFirestoreMessages( - lhs.data, - rhs.data - ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) + return deepEqualsFirestoreMessages(lhs.path, rhs.path) + && deepEqualsFirestoreMessages( + lhs.data, + rhs.data + ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) } func hash(into hasher: inout Hasher) { @@ -496,13 +499,15 @@ struct InternalDocumentChange: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( - lhs.document, - rhs.document - ) && deepEqualsFirestoreMessages(lhs.oldIndex, rhs.oldIndex) && deepEqualsFirestoreMessages( - lhs.newIndex, - rhs.newIndex - ) + return deepEqualsFirestoreMessages(lhs.type, rhs.type) + && deepEqualsFirestoreMessages( + lhs.document, + rhs.document + ) && deepEqualsFirestoreMessages(lhs.oldIndex, rhs.oldIndex) + && deepEqualsFirestoreMessages( + lhs.newIndex, + rhs.newIndex + ) } func hash(into hasher: inout Hasher) { @@ -545,10 +550,11 @@ struct InternalQuerySnapshot: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.documents, rhs.documents) && deepEqualsFirestoreMessages( - lhs.documentChanges, - rhs.documentChanges - ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) + return deepEqualsFirestoreMessages(lhs.documents, rhs.documents) + && deepEqualsFirestoreMessages( + lhs.documentChanges, + rhs.documentChanges + ) && deepEqualsFirestoreMessages(lhs.metadata, rhs.metadata) } func hash(into hasher: inout Hasher) { @@ -595,12 +601,12 @@ struct InternalPipelineResult: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.documentPath, rhs.documentPath) && - deepEqualsFirestoreMessages( + return deepEqualsFirestoreMessages(lhs.documentPath, rhs.documentPath) + && deepEqualsFirestoreMessages( lhs.createTime, rhs.createTime - ) && deepEqualsFirestoreMessages(lhs.updateTime, rhs.updateTime) && - deepEqualsFirestoreMessages( + ) && deepEqualsFirestoreMessages(lhs.updateTime, rhs.updateTime) + && deepEqualsFirestoreMessages( lhs.data, rhs.data ) @@ -642,10 +648,11 @@ struct InternalPipelineSnapshot: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.results, rhs.results) && deepEqualsFirestoreMessages( - lhs.executionTime, - rhs.executionTime - ) + return deepEqualsFirestoreMessages(lhs.results, rhs.results) + && deepEqualsFirestoreMessages( + lhs.executionTime, + rhs.executionTime + ) } func hash(into hasher: inout Hasher) { @@ -682,10 +689,11 @@ struct InternalGetOptions: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.source, rhs.source) && deepEqualsFirestoreMessages( - lhs.serverTimestampBehavior, - rhs.serverTimestampBehavior - ) + return deepEqualsFirestoreMessages(lhs.source, rhs.source) + && deepEqualsFirestoreMessages( + lhs.serverTimestampBehavior, + rhs.serverTimestampBehavior + ) } func hash(into hasher: inout Hasher) { @@ -722,10 +730,11 @@ struct InternalDocumentOption: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.merge, rhs.merge) && deepEqualsFirestoreMessages( - lhs.mergeFields, - rhs.mergeFields - ) + return deepEqualsFirestoreMessages(lhs.merge, rhs.merge) + && deepEqualsFirestoreMessages( + lhs.mergeFields, + rhs.mergeFields + ) } func hash(into hasher: inout Hasher) { @@ -770,13 +779,15 @@ struct InternalTransactionCommand: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( - lhs.path, - rhs.path - ) && deepEqualsFirestoreMessages(lhs.data, rhs.data) && deepEqualsFirestoreMessages( - lhs.option, - rhs.option - ) + return deepEqualsFirestoreMessages(lhs.type, rhs.type) + && deepEqualsFirestoreMessages( + lhs.path, + rhs.path + ) && deepEqualsFirestoreMessages(lhs.data, rhs.data) + && deepEqualsFirestoreMessages( + lhs.option, + rhs.option + ) } func hash(into hasher: inout Hasher) { @@ -827,13 +838,15 @@ struct DocumentReferenceRequest: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.path, rhs.path) && deepEqualsFirestoreMessages( - lhs.data, - rhs.data - ) && deepEqualsFirestoreMessages(lhs.option, rhs.option) && deepEqualsFirestoreMessages( - lhs.source, - rhs.source - ) && deepEqualsFirestoreMessages(lhs.serverTimestampBehavior, rhs.serverTimestampBehavior) + return deepEqualsFirestoreMessages(lhs.path, rhs.path) + && deepEqualsFirestoreMessages( + lhs.data, + rhs.data + ) && deepEqualsFirestoreMessages(lhs.option, rhs.option) + && deepEqualsFirestoreMessages( + lhs.source, + rhs.source + ) && deepEqualsFirestoreMessages(lhs.serverTimestampBehavior, rhs.serverTimestampBehavior) } func hash(into hasher: inout Hasher) { @@ -901,19 +914,23 @@ struct InternalQueryParameters: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.where, rhs.where) && deepEqualsFirestoreMessages( - lhs.orderBy, - rhs.orderBy - ) && deepEqualsFirestoreMessages(lhs.limit, rhs.limit) && deepEqualsFirestoreMessages( - lhs.limitToLast, - rhs.limitToLast - ) && deepEqualsFirestoreMessages(lhs.startAt, rhs.startAt) && deepEqualsFirestoreMessages( - lhs.startAfter, - rhs.startAfter - ) && deepEqualsFirestoreMessages(lhs.endAt, rhs.endAt) && deepEqualsFirestoreMessages( - lhs.endBefore, - rhs.endBefore - ) && deepEqualsFirestoreMessages(lhs.filters, rhs.filters) + return deepEqualsFirestoreMessages(lhs.where, rhs.where) + && deepEqualsFirestoreMessages( + lhs.orderBy, + rhs.orderBy + ) && deepEqualsFirestoreMessages(lhs.limit, rhs.limit) + && deepEqualsFirestoreMessages( + lhs.limitToLast, + rhs.limitToLast + ) && deepEqualsFirestoreMessages(lhs.startAt, rhs.startAt) + && deepEqualsFirestoreMessages( + lhs.startAfter, + rhs.startAfter + ) && deepEqualsFirestoreMessages(lhs.endAt, rhs.endAt) + && deepEqualsFirestoreMessages( + lhs.endBefore, + rhs.endBefore + ) && deepEqualsFirestoreMessages(lhs.filters, rhs.filters) } func hash(into hasher: inout Hasher) { @@ -957,10 +974,11 @@ struct AggregateQuery: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( - lhs.field, - rhs.field - ) + return deepEqualsFirestoreMessages(lhs.type, rhs.type) + && deepEqualsFirestoreMessages( + lhs.field, + rhs.field + ) } func hash(into hasher: inout Hasher) { @@ -1001,10 +1019,11 @@ struct AggregateQueryResponse: Hashable { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsFirestoreMessages(lhs.type, rhs.type) && deepEqualsFirestoreMessages( - lhs.field, - rhs.field - ) && deepEqualsFirestoreMessages(lhs.value, rhs.value) + return deepEqualsFirestoreMessages(lhs.type, rhs.type) + && deepEqualsFirestoreMessages( + lhs.field, + rhs.field + ) && deepEqualsFirestoreMessages(lhs.value, rhs.value) } func hash(into hasher: inout Hasher) { @@ -1205,63 +1224,86 @@ class FirestoreMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Send /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol FirebaseFirestoreHostApi { - func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, - completion: @escaping (Result) -> Void) - func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, - completion: @escaping (Result) -> Void) - func clearPersistence(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func disableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func enableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func terminate(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func waitForPendingWrites(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, - completion: @escaping (Result) -> Void) + func loadBundle( + app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void) + func namedQueryGet( + app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void) + func clearPersistence( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func disableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func enableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func terminate( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func waitForPendingWrites( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func setIndexConfiguration( + app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void) func setLoggingEnabled(loggingEnabled: Bool, completion: @escaping (Result) -> Void) - func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, - completion: @escaping (Result) -> Void) - func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, - commands: [InternalTransactionCommand?]?, - completion: @escaping (Result) -> Void) - func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, - completion: @escaping (Result) -> Void) - func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) - func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) - func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) - func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) - func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - completion: @escaping (Result) -> Void) - func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, - parameters: InternalQueryParameters, source: AggregateSource, - queries: [AggregateQuery?], isCollectionGroup: Bool, - completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) - func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], - completion: @escaping (Result) -> Void) - func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void) - func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, - parameters: DocumentReferenceRequest, includeMetadataChanges: Bool, - source: ListenSource, - completion: @escaping (Result) -> Void) - func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, - request: PersistenceCacheIndexManagerRequest, - completion: @escaping (Result) -> Void) - func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], - options: [String?: Any?]?, - completion: @escaping (Result) -> Void) + func snapshotsInSyncSetup( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func transactionCreate( + app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void) + func transactionStoreResult( + transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void) + func transactionGet( + app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void) + func documentReferenceSet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceUpdate( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceGet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func documentReferenceDelete( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) + func queryGet( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void) + func aggregateQuery( + app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, source: AggregateSource, + queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) + func writeBatchCommit( + app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void) + func querySnapshot( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) + func documentReferenceSnapshot( + app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, includeMetadataChanges: Bool, + source: ListenSource, + completion: @escaping (Result) -> Void) + func persistenceCacheIndexManagerRequest( + app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void) + func executePipeline( + app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -1272,11 +1314,14 @@ class FirebaseFirestoreHostApiSetup { /// Sets up an instance of `FirebaseFirestoreHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseFirestoreHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseFirestoreHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let loadBundleChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1298,7 +1343,8 @@ class FirebaseFirestoreHostApiSetup { loadBundleChannel.setMessageHandler(nil) } let namedQueryGetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1321,7 +1367,8 @@ class FirebaseFirestoreHostApiSetup { namedQueryGetChannel.setMessageHandler(nil) } let clearPersistenceChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1342,7 +1389,8 @@ class FirebaseFirestoreHostApiSetup { clearPersistenceChannel.setMessageHandler(nil) } let disableNetworkChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1363,7 +1411,8 @@ class FirebaseFirestoreHostApiSetup { disableNetworkChannel.setMessageHandler(nil) } let enableNetworkChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1384,7 +1433,8 @@ class FirebaseFirestoreHostApiSetup { enableNetworkChannel.setMessageHandler(nil) } let terminateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1405,7 +1455,8 @@ class FirebaseFirestoreHostApiSetup { terminateChannel.setMessageHandler(nil) } let waitForPendingWritesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1426,7 +1477,8 @@ class FirebaseFirestoreHostApiSetup { waitForPendingWritesChannel.setMessageHandler(nil) } let setIndexConfigurationChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1449,7 +1501,8 @@ class FirebaseFirestoreHostApiSetup { setIndexConfigurationChannel.setMessageHandler(nil) } let setLoggingEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1470,7 +1523,8 @@ class FirebaseFirestoreHostApiSetup { setLoggingEnabledChannel.setMessageHandler(nil) } let snapshotsInSyncSetupChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1491,7 +1545,8 @@ class FirebaseFirestoreHostApiSetup { snapshotsInSyncSetupChannel.setMessageHandler(nil) } let transactionCreateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1502,8 +1557,10 @@ class FirebaseFirestoreHostApiSetup { let timeoutArg = args[1] as! Int64 let maxAttemptsArg = args[2] as! Int64 api - .transactionCreate(app: appArg, timeout: timeoutArg, - maxAttempts: maxAttemptsArg) { result in + .transactionCreate( + app: appArg, timeout: timeoutArg, + maxAttempts: maxAttemptsArg + ) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -1516,7 +1573,8 @@ class FirebaseFirestoreHostApiSetup { transactionCreateChannel.setMessageHandler(nil) } let transactionStoreResultChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1543,7 +1601,8 @@ class FirebaseFirestoreHostApiSetup { transactionStoreResultChannel.setMessageHandler(nil) } let transactionGetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1566,7 +1625,8 @@ class FirebaseFirestoreHostApiSetup { transactionGetChannel.setMessageHandler(nil) } let documentReferenceSetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1588,7 +1648,8 @@ class FirebaseFirestoreHostApiSetup { documentReferenceSetChannel.setMessageHandler(nil) } let documentReferenceUpdateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1610,7 +1671,8 @@ class FirebaseFirestoreHostApiSetup { documentReferenceUpdateChannel.setMessageHandler(nil) } let documentReferenceGetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1632,7 +1694,8 @@ class FirebaseFirestoreHostApiSetup { documentReferenceGetChannel.setMessageHandler(nil) } let documentReferenceDeleteChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1654,7 +1717,8 @@ class FirebaseFirestoreHostApiSetup { documentReferenceDeleteChannel.setMessageHandler(nil) } let queryGetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1685,7 +1749,8 @@ class FirebaseFirestoreHostApiSetup { queryGetChannel.setMessageHandler(nil) } let aggregateQueryChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1718,7 +1783,8 @@ class FirebaseFirestoreHostApiSetup { aggregateQueryChannel.setMessageHandler(nil) } let writeBatchCommitChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1740,7 +1806,8 @@ class FirebaseFirestoreHostApiSetup { writeBatchCommitChannel.setMessageHandler(nil) } let querySnapshotChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1775,7 +1842,8 @@ class FirebaseFirestoreHostApiSetup { querySnapshotChannel.setMessageHandler(nil) } let documentReferenceSnapshotChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1804,7 +1872,8 @@ class FirebaseFirestoreHostApiSetup { documentReferenceSnapshotChannel.setMessageHandler(nil) } let persistenceCacheIndexManagerRequestChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1826,7 +1895,8 @@ class FirebaseFirestoreHostApiSetup { persistenceCacheIndexManagerRequestChannel.setMessageHandler(nil) } let executePipelineChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift index cadeca5616a8..1261c7515fff 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/LoadBundleStreamHandler.swift @@ -28,7 +28,8 @@ final class LoadBundleStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { task = firestore.loadBundle(bundle.data) { _, error in if let error { let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift index 6915bb1c03ae..fb5b59df8e0a 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift @@ -63,10 +63,12 @@ enum PigeonParser { fatalError("Invalid operator") } - static func parseQuery(parameters: InternalQueryParameters, - firestore: Firestore, - path: String, - isCollectionGroup: Bool) -> Query? { + static func parseQuery( + parameters: InternalQueryParameters, + firestore: Firestore, + path: String, + isCollectionGroup: Bool + ) -> Query? { do { var query: Query if isCollectionGroup { @@ -174,7 +176,8 @@ enum PigeonParser { } static func parseServerTimestampBehavior(_ behavior: ServerTimestampBehavior) - -> FirebaseFirestore.ServerTimestampBehavior { + -> FirebaseFirestore.ServerTimestampBehavior + { switch behavior { case .none: return .none @@ -195,16 +198,19 @@ enum PigeonParser { } static func toPigeonSnapshotMetadata(_ snapshotMetadata: SnapshotMetadata) - -> InternalSnapshotMetadata { + -> InternalSnapshotMetadata + { InternalSnapshotMetadata( hasPendingWrites: snapshotMetadata.hasPendingWrites, isFromCache: snapshotMetadata.isFromCache ) } - static func toPigeonDocumentSnapshot(_ documentSnapshot: DocumentSnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalDocumentSnapshot { + static func toPigeonDocumentSnapshot( + _ documentSnapshot: DocumentSnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalDocumentSnapshot { let data = documentSnapshot.data(with: serverTimestampBehavior) let mapped: [String?: Any?]? = data.map { original in Dictionary(uniqueKeysWithValues: original.map { ($0.key as String?, $0.value as Any?) }) @@ -217,17 +223,21 @@ enum PigeonParser { } static func toPigeonDocumentChangeType(_ documentChangeType: DocumentChangeType) - -> DocumentChangeType { + -> DocumentChangeType + { documentChangeType } - static func toPigeonDocumentChange(_ documentChange: DocumentChange, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalDocumentChange { + static func toPigeonDocumentChange( + _ documentChange: DocumentChange, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalDocumentChange { let maxVal = NSNotFound let newIndex: Int64 if documentChange.newIndex == NSNotFound || documentChange.newIndex == 4_294_967_295 - || documentChange.newIndex == maxVal { + || documentChange.newIndex == maxVal + { newIndex = -1 } else { newIndex = Int64(documentChange.newIndex) @@ -235,7 +245,8 @@ enum PigeonParser { let oldIndex: Int64 if documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4_294_967_295 - || documentChange.oldIndex == maxVal { + || documentChange.oldIndex == maxVal + { oldIndex = -1 } else { oldIndex = Int64(documentChange.oldIndex) @@ -263,19 +274,21 @@ enum PigeonParser { ) } - static func toPigeonDocumentChanges(_ documentChanges: [DocumentChange], - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> [ - InternalDocumentChange? - ] { + static func toPigeonDocumentChanges( + _ documentChanges: [DocumentChange], + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> [InternalDocumentChange?] { documentChanges.map { toPigeonDocumentChange($0, serverTimestampBehavior: serverTimestampBehavior) } } - static func toPigeonQuerySnapshot(_ querySnapshot: QuerySnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalQuerySnapshot { + static func toPigeonQuerySnapshot( + _ querySnapshot: QuerySnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalQuerySnapshot { let documents = querySnapshot.documents.map { toPigeonDocumentSnapshot($0, serverTimestampBehavior: serverTimestampBehavior) as InternalDocumentSnapshot? diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift index 1bec34828a42..99a6d46e7e46 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift @@ -4,6 +4,7 @@ import FirebaseFirestore import Foundation + #if os(iOS) import Flutter #elseif os(macOS) @@ -128,7 +129,8 @@ private func exprBridge(from value: Any) -> ExprBridge? { for child in Mirror(reflecting: value).children { if child.label == "expr" || child.label == "constant" || child.label == "field", - let nested = exprBridge(from: child.value) { + let nested = exprBridge(from: child.value) + { return nested } } @@ -136,7 +138,8 @@ private func exprBridge(from value: Any) -> ExprBridge? { } private func sendableExpressions(_ expressions: [any FirebaseFirestore.Expression]) - -> [any Sendable] { + -> [any Sendable] +{ expressions.map { $0 as any Sendable } } @@ -150,7 +153,8 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore } let doubleValue = number.doubleValue if doubleValue.isFinite, doubleValue.rounded() == doubleValue, - doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) { + doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) + { return Constant(number.intValue) } return Constant(number.doubleValue) @@ -191,8 +195,10 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore throw parseError("Unsupported constant value: \(type(of: value))") } -private func functionExpression(name: String, - args: [any FirebaseFirestore.Expression]) -> FunctionExpression { +private func functionExpression( + name: String, + args: [any FirebaseFirestore.Expression] +) -> FunctionExpression { FunctionExpression(functionName: name, args: args) } @@ -212,7 +218,8 @@ private final class PipelineExpressionParser { } private func parseBooleanTypedExpression(_ map: [String: Any]) throws - -> any FirebaseFirestore.BooleanExpression { + -> any FirebaseFirestore.BooleanExpression + { let expression = try parseTypedExpression(map) if let booleanExpression = expression as? any FirebaseFirestore.BooleanExpression { return booleanExpression @@ -220,9 +227,12 @@ private final class PipelineExpressionParser { return expression.asBoolean() } - private func parseTypedExpressions(_ maps: [Any], - errorMessage: String) throws - -> [any FirebaseFirestore.Expression] { + private func parseTypedExpressions( + _ maps: [Any], + errorMessage: String + ) throws + -> [any FirebaseFirestore.Expression] + { var expressions: [any FirebaseFirestore.Expression] = [] for value in maps { guard let map = asMap(value) else { continue } @@ -235,7 +245,8 @@ private final class PipelineExpressionParser { } private func parseTypedExpression(_ map: [String: Any]) throws -> any FirebaseFirestore - .Expression { + .Expression + { let name = map["name"] as? String if name == nil { if let args = asMap(map["args"]), let field = args["field"] as? String { @@ -339,7 +350,8 @@ private final class PipelineExpressionParser { } if resolvedName == "exists" || resolvedName == "is_error" || resolvedName == "is_absent" - || resolvedName == "not" { + || resolvedName == "not" + { guard let exprMap = asMap(args["expression"]) else { throw parseError("\(resolvedName) requires expression") } @@ -390,7 +402,8 @@ private final class PipelineExpressionParser { } if resolvedName == "and" || resolvedName == "or" || resolvedName == "xor" - || resolvedName == "nor" { + || resolvedName == "nor" + { guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { throw parseError("\(resolvedName) requires at least one expression") } @@ -417,7 +430,7 @@ private final class PipelineExpressionParser { if resolvedName == "equal_any" || resolvedName == "not_equal_any" { let valuesMaps = asArray(args["values"]) guard let valueMap = asMap(args["value"]), - let valuesMaps, !valuesMaps.isEmpty + let valuesMaps, !valuesMaps.isEmpty else { throw parseError("\(resolvedName) requires value and non-empty values") } @@ -463,7 +476,8 @@ private final class PipelineExpressionParser { } if resolvedName == "array_contains_all", - let arrayExpressionMap = asMap(args["array_expression"]) { + let arrayExpressionMap = asMap(args["array_expression"]) + { return try arrayExpr.arrayContainsAll(parseTypedExpression(arrayExpressionMap)) } @@ -488,8 +502,8 @@ private final class PipelineExpressionParser { if resolvedName == "substring" { guard let exprMap = asMap(args["expression"]), - let startMap = asMap(args["start"]), - let endMap = asMap(args["end"]) + let startMap = asMap(args["start"]), + let endMap = asMap(args["end"]) else { throw parseError("substring requires expression, start, and end") } @@ -501,8 +515,8 @@ private final class PipelineExpressionParser { if resolvedName == "replace" || resolvedName == "string_replace_all" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("\(resolvedName) requires expression, find, and replacement") } @@ -514,8 +528,8 @@ private final class PipelineExpressionParser { if resolvedName == "string_replace_one" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("string_replace_one requires expression, find, and replacement") } @@ -528,7 +542,7 @@ private final class PipelineExpressionParser { if resolvedName == "string_index_of" || resolvedName == "string_repeat" { let argumentName = resolvedName == "string_index_of" ? "search" : "repetitions" guard let exprMap = asMap(args["expression"]), - let argumentMap = asMap(args[argumentName]) + let argumentMap = asMap(args[argumentName]) else { throw parseError("\(resolvedName) requires expression and \(argumentName)") } @@ -554,7 +568,7 @@ private final class PipelineExpressionParser { if resolvedName == "split" || resolvedName == "join" { guard let exprMap = asMap(args["expression"]), - let delimiterMap = asMap(args["delimiter"]) + let delimiterMap = asMap(args["delimiter"]) else { throw parseError("\(resolvedName) requires expression and delimiter") } @@ -564,8 +578,9 @@ private final class PipelineExpressionParser { return expr.split(delimiter: delimiter) } if let delimiterMap = asMap(args["delimiter"]), - (delimiterMap["name"] as? String) == "constant", - let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String { + (delimiterMap["name"] as? String) == "constant", + let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String + { return expr.join(delimiter: delimiterValue) } return functionExpression(name: "join", args: [expr, delimiter]) @@ -607,8 +622,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_filter" { guard let exprMap = asMap(args["expression"]), - let alias = args["alias"] as? String, - let filterMap = asMap(args["filter"]) + let alias = args["alias"] as? String, + let filterMap = asMap(args["filter"]) else { throw parseError("array_filter requires expression, alias, and filter") } @@ -620,8 +635,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError("array_transform requires expression, element_alias, and transform") } @@ -633,9 +648,9 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform_with_index" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let indexAlias = args["index_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let indexAlias = args["index_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError( "array_transform_with_index requires expression, element_alias, index_alias, and transform" @@ -702,8 +717,8 @@ private final class PipelineExpressionParser { if resolvedName == "conditional" { guard let conditionMap = asMap(args["condition"]), - let thenMap = asMap(args["then"]), - let elseMap = asMap(args["else"]) + let thenMap = asMap(args["then"]), + let elseMap = asMap(args["else"]) else { throw parseError("conditional requires condition, then, and else") } @@ -717,8 +732,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_add" || resolvedName == "timestamp_subtract" { let unitVal = args["unit"] guard let timestampMap = asMap(args["timestamp"]), - unitVal != nil, - let amountMap = asMap(args["amount"]) + unitVal != nil, + let amountMap = asMap(args["amount"]) else { throw parseError("\(resolvedName) requires timestamp, unit, and amount") } @@ -787,8 +802,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_diff" { let unitObj = args["unit"] guard let endMap = asMap(args["end"]), - let startMap = asMap(args["start"]), - unitObj != nil + let startMap = asMap(args["start"]), + unitObj != nil else { throw parseError("timestamp_diff requires end, start, and unit") } @@ -824,7 +839,7 @@ private final class PipelineExpressionParser { if resolvedName == "if_null" { guard let exprMap = asMap(args["expression"]), - let replMap = asMap(args["replacement"]) + let replMap = asMap(args["replacement"]) else { throw parseError("if_null requires expression and replacement") } @@ -850,7 +865,7 @@ private final class PipelineExpressionParser { throw parseError("switch_on requires at least two expressions") } var switchArgs: [any FirebaseFirestore.Expression] = [] - for i in 0 ..< exprMaps.count { + for i in 0.. any FirebaseFirestore.Expression { + -> any FirebaseFirestore.Expression + { let op = args["operator"] as? String let exprMaps = asArray(args["expressions"]) if let op, let exprMaps { @@ -964,10 +980,12 @@ private final class PipelineExpressionParser { } enum PipelineParser { - static func executePipeline(firestore: Firestore, - stages: [[String: Any?]], - options: [String: Any?]?, - completion: @escaping (Any?, Error?) -> Void) { + static func executePipeline( + firestore: Firestore, + stages: [[String: Any?]], + options: [String: Any?]?, + completion: @escaping (Any?, Error?) -> Void + ) { _ = options if NSClassFromString("FIRPipelineBridge") == nil { completion(nil, pipelineUnavailableError()) @@ -1008,10 +1026,10 @@ enum PipelineParser { throw parseError("expression must have alias or be a field reference") } - private static func parseSearchFields(expressionMaps exprMaps: [Any], - exprParser: PipelineExpressionParser) throws -> [ - String: ExprBridge - ] { + private static func parseSearchFields( + expressionMaps exprMaps: [Any], + exprParser: PipelineExpressionParser + ) throws -> [String: ExprBridge] { var fields: [String: ExprBridge] = [:] for em in exprMaps { guard let emMap = asMap(em) else { continue } @@ -1025,8 +1043,10 @@ enum PipelineParser { return fields } - private static func parseSearchStage(args: [String: Any], - exprParser: PipelineExpressionParser) throws -> StageBridge { + private static func parseSearchStage( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws -> StageBridge { let queryType = args["query_type"] as? String let query = args["query"] var options: [String: ExprBridge] = [:] @@ -1082,12 +1102,14 @@ enum PipelineParser { ) } - private static func parseStages(firestore: Firestore, - stages: [[String: Any]]) throws -> [StageBridge] { + private static func parseStages( + firestore: Firestore, + stages: [[String: Any]] + ) throws -> [StageBridge] { let exprParser = PipelineExpressionParser(firestore: firestore) var stageBridges: [StageBridge] = [] - for i in 0 ..< stages.count { + for i in 0.. AggregateFunctionBridge { + private static func aggregateFunction( + from funcMap: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> AggregateFunctionBridge + { guard let name = funcMap["name"] as? String else { throw parseError("Aggregate function must have a 'name'") } @@ -1346,9 +1372,12 @@ enum PipelineParser { return AggregateFunctionBridge(name: iosName, args: argsArray) } - private static func parseAggregateStage(args: [String: Any], - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStage( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { guard let accumulatorMaps = asArray(args["aggregate_functions"]), !accumulatorMaps.isEmpty else { throw parseError("aggregate requires aggregate_functions") @@ -1360,9 +1389,12 @@ enum PipelineParser { ) } - private static func parseAggregateStageWithOptions(args: [String: Any], - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStageWithOptions( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { guard let stageMap = asMap(args["aggregate_stage"]) else { throw parseError("aggregate_with_options requires aggregate_stage") } @@ -1381,10 +1413,13 @@ enum PipelineParser { ) } - private static func parseAggregateStage(accumulatorMaps: [Any], - groupMaps: [Any]?, - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStage( + accumulatorMaps: [Any], + groupMaps: [Any]?, + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { var accumulators: [String: AggregateFunctionBridge] = [:] for accMap in accumulatorMaps { guard let accMap = asMap(accMap) else { continue } @@ -1423,8 +1458,10 @@ enum PipelineParser { return AggregateStageBridge(accumulators: accumulators, groups: groups) } - private static func buildPipeline(firestore: Firestore, - stages: [[String: Any]]) throws -> PipelineBridge { + private static func buildPipeline( + firestore: Firestore, + stages: [[String: Any]] + ) throws -> PipelineBridge { let stageBridges = try parseStages(firestore: firestore, stages: stages) return PipelineBridge(stages: stageBridges, db: firestore) } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift index 4f38f0912e02..5f914d030e25 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift @@ -28,11 +28,13 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { label: "io.flutter.plugins.firebase.firestore.query_snapshot" ) - init(firestore: Firestore, - query: Query?, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource) { + init( + firestore: Firestore, + query: Query?, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource + ) { self.firestore = firestore self.query = query self.includeMetadataChanges = includeMetadataChanges @@ -41,12 +43,13 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { guard let query else { return FlutterError( code: "sdk-error", message: - "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", details: nil ) } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift index 91baad7ab672..88328f057450 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/SnapshotsInSyncStreamHandler.swift @@ -20,7 +20,8 @@ final class SnapshotsInSyncStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { listenerRegistration = firestore.addSnapshotsInSyncListener { DispatchQueue.main.async { events(nil) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift index d5325f090992..97dd6a4f370c 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/TransactionStreamHandler.swift @@ -28,12 +28,14 @@ final class TransactionStreamHandler: NSObject, FlutterStreamHandler { private var resultType: InternalTransactionResult = .success private var commands: [InternalTransactionCommand?] = [] - init(id transactionId: String, - firestore: Firestore, - timeout: Int, - maxAttempts: Int, - started: @escaping (Transaction) -> Void, - ended: @escaping () -> Void) { + init( + id transactionId: String, + firestore: Firestore, + timeout: Int, + maxAttempts: Int, + started: @escaping (Transaction) -> Void, + ended: @escaping () -> Void + ) { self.transactionId = transactionId self.firestore = firestore self.timeout = timeout @@ -43,7 +45,8 @@ final class TransactionStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { let options = TransactionOptions() options.maxAttempts = maxAttempts @@ -56,7 +59,7 @@ final class TransactionStreamHandler: NSObject, FlutterStreamHandler { DispatchQueue.main.async { events([ "appName": FLTFirebasePlugin.firebaseAppName(fromIosName: self.firestore.app.name) - as Any, + as Any ]) } @@ -135,8 +138,10 @@ final class TransactionStreamHandler: NSObject, FlutterStreamHandler { return nil } - func receiveTransactionResponse(_ resultType: InternalTransactionResult, - commands: [InternalTransactionCommand?]?) { + func receiveTransactionResponse( + _ resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]? + ) { self.resultType = resultType self.commands = commands ?? [] semaphore.signal() From 8678e82d5889823427f55d23287c49a48ad75163 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 25 Aug 2026 08:36:55 +0000 Subject: [PATCH 3/4] fix(firestore,apple): address leak, retain-cycle, and parser review notes Unregister event channels when streams cancel, avoid capturing self in snapshot listeners, copy snapshot maps without an intermediate array, cap pipeline nesting, and replace forced casts in query parsing with typed errors. --- .../DocumentSnapshotStreamHandler.swift | 20 +- .../FLTFirebaseFirestorePlugin.swift | 262 +++++++++--------- .../FirebaseFirestoreReader.swift | 72 +++-- .../cloud_firestore/PigeonParser.swift | 138 ++++----- .../cloud_firestore/PipelineParser.swift | 201 ++++++-------- .../QuerySnapshotStreamHandler.swift | 24 +- 6 files changed, 355 insertions(+), 362 deletions(-) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift index 46b12c3945ef..0e9d9e3fd5e0 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift @@ -25,13 +25,11 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { private let source: FirebaseFirestore.ListenSource private var listenerRegistration: ListenerRegistration? - init( - firestore: Firestore, - reference: DocumentReference, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource - ) { + init(firestore: Firestore, + reference: DocumentReference, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource) { self.firestore = firestore self.reference = reference self.includeMetadataChanges = includeMetadataChanges @@ -40,13 +38,13 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? - { + -> FlutterError? { let options = SnapshotListenOptions() .withIncludeMetadataChanges(includeMetadataChanges) .withSource(source) - listenerRegistration = reference.addSnapshotListener(options: options) { snapshot, error in + listenerRegistration = reference.addSnapshotListener(options: options) { + [serverTimestampBehavior] snapshot, error in if let error { let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) DispatchQueue.main.async { @@ -63,7 +61,7 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { DispatchQueue.main.async { events( PigeonParser.toPigeonDocumentSnapshot( - snapshot, serverTimestampBehavior: self.serverTimestampBehavior + snapshot, serverTimestampBehavior: serverTimestampBehavior ) ) } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift index 6a069084f73e..cb394e7611ed 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift @@ -26,14 +26,14 @@ import Foundation @objc(FLTFirebaseFirestorePlugin) public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, - FirebaseFirestoreHostApi -{ + FirebaseFirestoreHostApi { private var messenger: FlutterBinaryMessenger private var transactions: [String: Transaction] = [:] private var eventChannels: [String: FlutterEventChannel] = [:] private var streamHandlers: [String: NSObject & FlutterStreamHandler] = [:] private var transactionHandlers: [String: TransactionStreamHandler] = [:] private let transactionLock = NSLock() + private let listenersLock = NSLock() static let serverTimestampMap = NSCache() @@ -90,19 +90,33 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } private func cleanupEventListeners() { - for channel in eventChannels.values { + listenersLock.lock() + let channels = Array(eventChannels.values) + let handlers = Array(streamHandlers.values) + eventChannels.removeAll() + streamHandlers.removeAll() + transactionHandlers.removeAll() + listenersLock.unlock() + + for channel in channels { channel.setStreamHandler(nil) } - eventChannels.removeAll() - for handler in streamHandlers.values { + for handler in handlers { _ = handler.onCancel(withArguments: nil) } - streamHandlers.removeAll() transactionLock.lock() transactions.removeAll() transactionLock.unlock() } + private func unregisterEventChannel(_ identifier: String) { + listenersLock.lock() + eventChannels.removeValue(forKey: identifier) + streamHandlers.removeValue(forKey: identifier) + transactionHandlers.removeValue(forKey: identifier) + listenersLock.unlock() + } + private func cleanupFirestoreInstances(_ completion: (() -> Void)?) { if FirebaseFirestoreUtils.count > 0 { FirebaseFirestoreUtils.cleanupFirestoreInstances(completion) @@ -112,20 +126,23 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } @discardableResult - private func registerEventChannel( - prefix: String, - identifier: String = UUID().uuidString.lowercased(), - streamHandler: NSObject & FlutterStreamHandler - ) -> String { + private func registerEventChannel(prefix: String, + identifier: String = UUID().uuidString.lowercased(), + streamHandler: NSObject & FlutterStreamHandler) -> String { let channelName = "\(prefix)/\(identifier)" let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: Self.codec ) - channel.setStreamHandler(streamHandler) + let wrapped = EventChannelCleanupHandler(inner: streamHandler) { [weak self] in + self?.unregisterEventChannel(identifier) + } + channel.setStreamHandler(wrapped) + listenersLock.lock() eventChannels[identifier] = channel - streamHandlers[identifier] = streamHandler + streamHandlers[identifier] = wrapped + listenersLock.unlock() return identifier } @@ -172,17 +189,13 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu return firestore } - private func completeOnError( - _ error: Error, - _ completion: @escaping (Result) -> Void - ) { + private func completeOnError(_ error: Error, + _ completion: @escaping (Result) -> Void) { completion(.failure(FirebaseFirestoreUtils.flutterError(from: error))) } - func loadBundle( - app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, - completion: @escaping (Result) -> Void - ) { + func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreLoadBundleChannelName, @@ -191,10 +204,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func namedQueryGet( - app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, - completion: @escaping (Result) -> Void - ) { + func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let source = PigeonParser.parseSource(options.source) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -208,7 +219,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "non-existent-named-query", message: - "Named query has not been found. Please check it has been loaded properly via loadBundle().", + "Named query has not been found. Please check it has been loaded properly via loadBundle().", details: nil ) ) @@ -231,10 +242,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func clearPersistence( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func clearPersistence(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { firestore(from: app).clearPersistence { error in if let error { self.completeOnError(error, completion) @@ -244,10 +253,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func disableNetwork( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func disableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { firestore(from: app).disableNetwork { error in if let error { self.completeOnError(error, completion) @@ -257,10 +264,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func enableNetwork( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func enableNetwork(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { firestore(from: app).enableNetwork { error in if let error { self.completeOnError(error, completion) @@ -270,10 +275,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func terminate( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func terminate(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) firestore.terminate { error in if let error { @@ -288,10 +291,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func waitForPendingWrites( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func waitForPendingWrites(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { firestore(from: app).waitForPendingWrites { error in if let error { self.completeOnError(error, completion) @@ -301,10 +302,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setIndexConfiguration( - app: FirestorePigeonFirebaseApp, indexConfiguration: String, - completion: @escaping (Result) -> Void - ) { + func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void) { firestore(from: app).setIndexConfiguration(indexConfiguration) { error in if let error { self.completeOnError(error, completion) @@ -314,18 +313,14 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setLoggingEnabled( - loggingEnabled: Bool, - completion: @escaping (Result) -> Void - ) { + func setLoggingEnabled(loggingEnabled: Bool, + completion: @escaping (Result) -> Void) { Firestore.enableLogging(loggingEnabled) completion(.success(())) } - func snapshotsInSyncSetup( - app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void - ) { + func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName, @@ -334,10 +329,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionCreate( - app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, - completion: @escaping (Result) -> Void - ) { + func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let transactionId = UUID().uuidString.lowercased() let handler = TransactionStreamHandler( @@ -356,9 +349,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu self.transactionLock.lock() self.transactions.removeValue(forKey: transactionId) self.transactionLock.unlock() + self.unregisterEventChannel(transactionId) } ) + listenersLock.lock() transactionHandlers[transactionId] = handler + listenersLock.unlock() let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreTransactionChannelName, identifier: transactionId, @@ -367,19 +363,18 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionStoreResult( - transactionId: String, resultType: InternalTransactionResult, - commands: [InternalTransactionCommand?]?, - completion: @escaping (Result) -> Void - ) { - transactionHandlers[transactionId]?.receiveTransactionResponse(resultType, commands: commands) + func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void) { + listenersLock.lock() + let handler = transactionHandlers[transactionId] + listenersLock.unlock() + handler?.receiveTransactionResponse(resultType, commands: commands) completion(.success(())) } - func transactionGet( - app: FirestorePigeonFirebaseApp, transactionId: String, path: String, - completion: @escaping (Result) -> Void - ) { + func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void) { DispatchQueue.global(qos: .default).async { let firestore = self.firestore(from: app) let document = firestore.document(path) @@ -394,7 +389,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "missing-transaction", message: - "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", + "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", details: nil ) ) @@ -417,10 +412,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceSet( - app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void - ) { + func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { let document = firestore(from: app).document(request.path) let data = request.data as? [String: Any] ?? [:] let finish: (Error?) -> Void = { error in @@ -442,10 +435,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceUpdate( - app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void - ) { + func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { let document = firestore(from: app).document(request.path) let data = request.data as? [AnyHashable: Any] ?? [:] document.updateData(data) { error in @@ -457,12 +448,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceGet( - app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: - @escaping (Result) - -> Void - ) { + func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: + @escaping (Result) + -> Void) { let document = firestore(from: app).document(request.path) let source = PigeonParser.parseSource(request.source ?? .serverAndCache) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -483,10 +472,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceDelete( - app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void - ) { + func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void) { firestore(from: app).document(request.path).delete { error in if let error { self.completeOnError(error, completion) @@ -496,11 +483,9 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func queryGet( - app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - completion: @escaping (Result) -> Void - ) { + func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -513,7 +498,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -540,12 +525,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func aggregateQuery( - app: FirestorePigeonFirebaseApp, path: String, - parameters: InternalQueryParameters, - source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, - completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void - ) { + func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, + source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -558,7 +541,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -626,10 +609,8 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func writeBatchCommit( - app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], - completion: @escaping (Result) -> Void - ) { + func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let batch = firestore.batch() for write in writes.compactMap({ $0 }) { @@ -665,12 +646,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func querySnapshot( - app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void - ) { + func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let query = PigeonParser.parseQuery( parameters: parameters, firestore: firestore, path: path, isCollectionGroup: isCollectionGroup @@ -681,7 +660,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -704,12 +683,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func documentReferenceSnapshot( - app: FirestorePigeonFirebaseApp, - parameters: DocumentReferenceRequest, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void - ) { + func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let document = firestore.document(parameters.path) let identifier = registerEventChannel( @@ -727,11 +704,9 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func persistenceCacheIndexManagerRequest( - app: FirestorePigeonFirebaseApp, - request: PersistenceCacheIndexManagerRequest, - completion: @escaping (Result) -> Void - ) { + func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void) { if let manager = firestore(from: app).persistentCacheIndexManager { switch request { case .enableIndexAutoCreation: @@ -747,11 +722,9 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(())) } - func executePipeline( - app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], - options: [String?: Any?]?, - completion: @escaping (Result) -> Void - ) { + func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void) { let firestore = firestore(from: app) let mappedStages: [[String: Any?]] = stages.compactMap { stage in guard let stage else { return nil } @@ -810,11 +783,9 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu let ref = object.value(forKey: "reference") as AnyObject? let path = (ref?.value(forKey: "path") as? String) - ?? (object.value(forKey: "documentID") as? String) + ?? (object.value(forKey: "documentID") as? String) let data = object.value(forKey: "data") as? [String: Any] - let mappedData: [String?: Any?]? = data.map { - Dictionary(uniqueKeysWithValues: $0.map { ($0.key as String?, $0.value as Any?) }) - } + let mappedData = PigeonParser.toPigeonMap(data) pigeonResults.append( InternalPipelineResult( documentPath: path, @@ -838,3 +809,24 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } } + +private final class EventChannelCleanupHandler: NSObject, FlutterStreamHandler { + private let inner: NSObject & FlutterStreamHandler + private let onDisposed: () -> Void + + init(inner: NSObject & FlutterStreamHandler, onDisposed: @escaping () -> Void) { + self.inner = inner + self.onDisposed = onDisposed + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? { + inner.onListen(withArguments: arguments, eventSink: events) + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + let error = inner.onCancel(withArguments: arguments) + onDisposed() + return error + } +} diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift index 40bc993d6947..788e73bb62cc 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift @@ -50,7 +50,7 @@ class FirebaseFirestoreReader: FlutterStandardReader { let length = readSize() var array: [Any] = [] array.reserveCapacity(Int(length)) - for _ in 0.. Filter { + private func filterFromJson(_ map: [String: Any]?) throws -> Filter { guard let map else { - NSException( - name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil - ) - .raise() - fatalError("Invalid operator") + throw PigeonParser.queryParseError("Invalid operator") } if map["fieldPath"] != nil { - let op = map["op"] as! String - let fieldPath = map["fieldPath"] as! FieldPath + guard let op = map["op"] as? String else { + throw PigeonParser.queryParseError("Filter is missing an operator") + } + guard let fieldPath = map["fieldPath"] as? FieldPath else { + throw PigeonParser.queryParseError("Filter is missing a field path") + } let value = map["value"] as Any switch op { case "==": @@ -156,17 +156,17 @@ class FirebaseFirestoreReader: FlutterStandardReader { case "not-in": return Filter.whereField(fieldPath, notIn: value as? [Any] ?? []) default: - NSException( - name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil - ) - .raise() - fatalError("Invalid operator") + throw PigeonParser.queryParseError("Invalid operator") } } - let op = map["op"] as! String - let queries = map["queries"] as! [[String: Any]] - let parsedFilters = queries.map { filterFromJson($0) } + guard let op = map["op"] as? String else { + throw PigeonParser.queryParseError("Compound filter is missing an operator") + } + guard let queries = map["queries"] as? [[String: Any]] else { + throw PigeonParser.queryParseError("Compound filter is missing queries") + } + let parsedFilters = try queries.map { try filterFromJson($0) } if op == "OR" { return Filter.orFilter(parsedFilters) @@ -175,9 +175,7 @@ class FirebaseFirestoreReader: FlutterStandardReader { return Filter.andFilter(parsedFilters) } - NSException(name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil) - .raise() - fatalError("Invalid operator") + throw PigeonParser.queryParseError("Invalid operator") } private func readQuery() -> Query? { @@ -197,13 +195,16 @@ class FirebaseFirestoreReader: FlutterStandardReader { } if let filters = parameters["filters"] as? [String: Any] { - query = query.whereFilter(filterFromJson(filters)) + query = try query.whereFilter(filterFromJson(filters)) } for item in whereConditions { - let condition = item as! [Any] - let fieldPath = condition[0] as! FieldPath - let op = condition[1] as! String + guard let condition = item as? [Any], condition.count >= 3, + let fieldPath = condition[0] as? FieldPath, + let op = condition[1] as? String + else { + throw PigeonParser.queryParseError("Invalid query condition") + } let value = condition[2] switch op { case "==": @@ -245,10 +246,22 @@ class FirebaseFirestoreReader: FlutterStandardReader { return query } - for orderByParameters in orderBy as! [[Any]] { - let fieldPath = orderByParameters[0] as! FieldPath - let descending = orderByParameters[1] as! NSNumber - query = query.order(by: fieldPath, descending: descending.boolValue) + guard let orderByEntries = orderBy as? [[Any]] else { + throw PigeonParser.queryParseError("Invalid orderBy parameters") + } + for orderByEntry in orderByEntries { + guard orderByEntry.count >= 2, let fieldPath = orderByEntry[0] as? FieldPath else { + throw PigeonParser.queryParseError("Invalid orderBy field path") + } + let descending: Bool + if let number = orderByEntry[1] as? NSNumber { + descending = number.boolValue + } else if let boolValue = orderByEntry[1] as? Bool { + descending = boolValue + } else { + throw PigeonParser.queryParseError("Invalid orderBy direction") + } + query = query.order(by: fieldPath, descending: descending) } if let startAt = parameters["startAt"], !(startAt is NSNull) { @@ -267,7 +280,8 @@ class FirebaseFirestoreReader: FlutterStandardReader { return query } catch { NSLog( - "An error occurred while parsing query arguments, this is most likely an error with this SDK. %@" + "An error occurred while parsing query arguments, this is most likely an error with this SDK. %@", + error.localizedDescription ) return nil } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift index fb5b59df8e0a..a9fa51fed517 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift @@ -6,18 +6,36 @@ import FirebaseFirestore import Foundation enum PigeonParser { - static func filterFromJson(_ map: [String: Any]?) -> Filter { + static func queryParseError(_ message: String) -> NSError { + NSError( + domain: "FLTFirebaseFirestore", + code: 0, + userInfo: [NSLocalizedDescriptionKey: message] + ) + } + + static func toPigeonMap(_ data: [String: Any]?) -> [String?: Any?]? { + guard let data else { return nil } + var mapped: [String?: Any?] = [:] + mapped.reserveCapacity(data.count) + for (key, value) in data { + mapped[key] = value + } + return mapped + } + + static func filterFromJson(_ map: [String: Any]?) throws -> Filter { guard let map else { - NSException( - name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil - ) - .raise() - fatalError("Invalid operator") + throw queryParseError("Invalid operator") } if map["fieldPath"] != nil { - let op = map["op"] as! String - let fieldPath = map["fieldPath"] as! FieldPath + guard let op = map["op"] as? String else { + throw queryParseError("Filter is missing an operator") + } + guard let fieldPath = map["fieldPath"] as? FieldPath else { + throw queryParseError("Filter is missing a field path") + } let value = map["value"] as Any switch op { case "==": @@ -41,34 +59,30 @@ enum PigeonParser { case "not-in": return Filter.whereField(fieldPath, notIn: value as? [Any] ?? []) default: - NSException( - name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil - ) - .raise() - fatalError("Invalid operator") + throw queryParseError("Invalid operator") } } - let op = map["op"] as! String - let queries = map["queries"] as! [[String: Any]] - let parsedFilters = queries.map { filterFromJson($0) } + guard let op = map["op"] as? String else { + throw queryParseError("Compound filter is missing an operator") + } + guard let queries = map["queries"] as? [[String: Any]] else { + throw queryParseError("Compound filter is missing queries") + } + let parsedFilters = try queries.map { try filterFromJson($0) } if op == "OR" { return Filter.orFilter(parsedFilters) } if op == "AND" { return Filter.andFilter(parsedFilters) } - NSException(name: NSExceptionName("InvalidOperator"), reason: "Invalid operator", userInfo: nil) - .raise() - fatalError("Invalid operator") + throw queryParseError("Invalid operator") } - static func parseQuery( - parameters: InternalQueryParameters, - firestore: Firestore, - path: String, - isCollectionGroup: Bool - ) -> Query? { + static func parseQuery(parameters: InternalQueryParameters, + firestore: Firestore, + path: String, + isCollectionGroup: Bool) -> Query? { do { var query: Query if isCollectionGroup { @@ -78,14 +92,15 @@ enum PigeonParser { } if let filters = parameters.filters as? [String: Any] { - query = query.whereFilter(filterFromJson(filters)) + query = try query.whereFilter(filterFromJson(filters)) } if let whereConditions = parameters.where { for item in whereConditions { guard let condition = item, condition.count >= 3 else { continue } - let fieldPath = condition[0] as! FieldPath - let op = condition[1] as! String + guard let fieldPath = condition[0] as? FieldPath, let op = condition[1] as? String else { + throw queryParseError("Invalid query condition") + } let value = condition[2] switch op { case "==": @@ -130,9 +145,18 @@ enum PigeonParser { for orderByParameters in orderBy { guard let orderByParameters, orderByParameters.count >= 2 else { continue } - let fieldPath = orderByParameters[0] as! FieldPath - let descending = orderByParameters[1] as! NSNumber - query = query.order(by: fieldPath, descending: descending.boolValue) + guard let fieldPath = orderByParameters[0] as? FieldPath else { + throw queryParseError("Invalid orderBy field path") + } + let descending: Bool + if let number = orderByParameters[1] as? NSNumber { + descending = number.boolValue + } else if let boolValue = orderByParameters[1] as? Bool { + descending = boolValue + } else { + throw queryParseError("Invalid orderBy direction") + } + query = query.order(by: fieldPath, descending: descending) } if let startAt = parameters.startAt { @@ -176,8 +200,7 @@ enum PigeonParser { } static func parseServerTimestampBehavior(_ behavior: ServerTimestampBehavior) - -> FirebaseFirestore.ServerTimestampBehavior - { + -> FirebaseFirestore.ServerTimestampBehavior { switch behavior { case .none: return .none @@ -198,46 +221,36 @@ enum PigeonParser { } static func toPigeonSnapshotMetadata(_ snapshotMetadata: SnapshotMetadata) - -> InternalSnapshotMetadata - { + -> InternalSnapshotMetadata { InternalSnapshotMetadata( hasPendingWrites: snapshotMetadata.hasPendingWrites, isFromCache: snapshotMetadata.isFromCache ) } - static func toPigeonDocumentSnapshot( - _ documentSnapshot: DocumentSnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior - ) -> InternalDocumentSnapshot { + static func toPigeonDocumentSnapshot(_ documentSnapshot: DocumentSnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalDocumentSnapshot { let data = documentSnapshot.data(with: serverTimestampBehavior) - let mapped: [String?: Any?]? = data.map { original in - Dictionary(uniqueKeysWithValues: original.map { ($0.key as String?, $0.value as Any?) }) - } return InternalDocumentSnapshot( path: documentSnapshot.reference.path, - data: mapped, + data: toPigeonMap(data), metadata: toPigeonSnapshotMetadata(documentSnapshot.metadata) ) } static func toPigeonDocumentChangeType(_ documentChangeType: DocumentChangeType) - -> DocumentChangeType - { + -> DocumentChangeType { documentChangeType } - static func toPigeonDocumentChange( - _ documentChange: DocumentChange, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior - ) -> InternalDocumentChange { + static func toPigeonDocumentChange(_ documentChange: DocumentChange, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalDocumentChange { let maxVal = NSNotFound let newIndex: Int64 if documentChange.newIndex == NSNotFound || documentChange.newIndex == 4_294_967_295 - || documentChange.newIndex == maxVal - { + || documentChange.newIndex == maxVal { newIndex = -1 } else { newIndex = Int64(documentChange.newIndex) @@ -245,8 +258,7 @@ enum PigeonParser { let oldIndex: Int64 if documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4_294_967_295 - || documentChange.oldIndex == maxVal - { + || documentChange.oldIndex == maxVal { oldIndex = -1 } else { oldIndex = Int64(documentChange.oldIndex) @@ -274,21 +286,17 @@ enum PigeonParser { ) } - static func toPigeonDocumentChanges( - _ documentChanges: [DocumentChange], - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior - ) -> [InternalDocumentChange?] { + static func toPigeonDocumentChanges(_ documentChanges: [DocumentChange], + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> [InternalDocumentChange?] { documentChanges.map { toPigeonDocumentChange($0, serverTimestampBehavior: serverTimestampBehavior) } } - static func toPigeonQuerySnapshot( - _ querySnapshot: QuerySnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior - ) -> InternalQuerySnapshot { + static func toPigeonQuerySnapshot(_ querySnapshot: QuerySnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior) -> InternalQuerySnapshot { let documents = querySnapshot.documents.map { toPigeonDocumentSnapshot($0, serverTimestampBehavior: serverTimestampBehavior) as InternalDocumentSnapshot? diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift index 99a6d46e7e46..9561a5b807ee 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift @@ -15,6 +15,8 @@ private let kPipelineNotAvailable = "Pipeline API is not available. Firestore Pipelines require Firebase iOS SDK with pipeline support." private let kPipelineErrorDomain = "FLTFirebaseFirestore" private let kPipelineParseErrorCode = -1 +private let kMaxPipelineExpressionDepth = 64 +private let kMaxPipelineStageDepth = 32 private let kBinaryNames: [String] = [ "equal", "not_equal", "greater_than", "greater_than_or_equal", "less_than", @@ -104,8 +106,11 @@ private func toExprBridge(_ expression: any FirebaseFirestore.Expression) throws throw parseError("Could not convert pipeline expression into a native bridge") } -/// `BridgeWrapper.bridge` is internal to FirebaseFirestore, so typed expressions are lowered -/// through public `.bridge` members where available and Mirror otherwise. +/// Typed pipeline expressions must be lowered to `ExprBridge` for stage constructors. +/// `Score` and `DocumentMatches` expose a public `.bridge`. Other SDK expression types +/// (`Field`, `Constant`, `FunctionExpression`, internal boolean wrappers) keep `bridge` +/// internal, and `Expression.toBridge()` is also internal, so there is no supported public +/// accessor. Mirror is the same approach React Native Firebase uses until Firebase exposes one. private func exprBridge(from value: Any) -> ExprBridge? { if let bridge = value as? ExprBridge { return bridge @@ -129,8 +134,7 @@ private func exprBridge(from value: Any) -> ExprBridge? { for child in Mirror(reflecting: value).children { if child.label == "expr" || child.label == "constant" || child.label == "field", - let nested = exprBridge(from: child.value) - { + let nested = exprBridge(from: child.value) { return nested } } @@ -138,8 +142,7 @@ private func exprBridge(from value: Any) -> ExprBridge? { } private func sendableExpressions(_ expressions: [any FirebaseFirestore.Expression]) - -> [any Sendable] -{ + -> [any Sendable] { expressions.map { $0 as any Sendable } } @@ -153,8 +156,7 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore } let doubleValue = number.doubleValue if doubleValue.isFinite, doubleValue.rounded() == doubleValue, - doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) - { + doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) { return Constant(number.intValue) } return Constant(number.doubleValue) @@ -195,15 +197,14 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore throw parseError("Unsupported constant value: \(type(of: value))") } -private func functionExpression( - name: String, - args: [any FirebaseFirestore.Expression] -) -> FunctionExpression { +private func functionExpression(name: String, + args: [any FirebaseFirestore.Expression]) -> FunctionExpression { FunctionExpression(functionName: name, args: args) } private final class PipelineExpressionParser { let firestore: Firestore + private var expressionDepth = 0 init(firestore: Firestore) { self.firestore = firestore @@ -218,8 +219,7 @@ private final class PipelineExpressionParser { } private func parseBooleanTypedExpression(_ map: [String: Any]) throws - -> any FirebaseFirestore.BooleanExpression - { + -> any FirebaseFirestore.BooleanExpression { let expression = try parseTypedExpression(map) if let booleanExpression = expression as? any FirebaseFirestore.BooleanExpression { return booleanExpression @@ -227,12 +227,9 @@ private final class PipelineExpressionParser { return expression.asBoolean() } - private func parseTypedExpressions( - _ maps: [Any], - errorMessage: String - ) throws - -> [any FirebaseFirestore.Expression] - { + private func parseTypedExpressions(_ maps: [Any], + errorMessage: String) throws + -> [any FirebaseFirestore.Expression] { var expressions: [any FirebaseFirestore.Expression] = [] for value in maps { guard let map = asMap(value) else { continue } @@ -245,8 +242,13 @@ private final class PipelineExpressionParser { } private func parseTypedExpression(_ map: [String: Any]) throws -> any FirebaseFirestore - .Expression - { + .Expression { + expressionDepth += 1 + defer { expressionDepth -= 1 } + if expressionDepth > kMaxPipelineExpressionDepth { + throw parseError("Pipeline expression nested too deeply") + } + let name = map["name"] as? String if name == nil { if let args = asMap(map["args"]), let field = args["field"] as? String { @@ -350,8 +352,7 @@ private final class PipelineExpressionParser { } if resolvedName == "exists" || resolvedName == "is_error" || resolvedName == "is_absent" - || resolvedName == "not" - { + || resolvedName == "not" { guard let exprMap = asMap(args["expression"]) else { throw parseError("\(resolvedName) requires expression") } @@ -402,8 +403,7 @@ private final class PipelineExpressionParser { } if resolvedName == "and" || resolvedName == "or" || resolvedName == "xor" - || resolvedName == "nor" - { + || resolvedName == "nor" { guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { throw parseError("\(resolvedName) requires at least one expression") } @@ -430,7 +430,7 @@ private final class PipelineExpressionParser { if resolvedName == "equal_any" || resolvedName == "not_equal_any" { let valuesMaps = asArray(args["values"]) guard let valueMap = asMap(args["value"]), - let valuesMaps, !valuesMaps.isEmpty + let valuesMaps, !valuesMaps.isEmpty else { throw parseError("\(resolvedName) requires value and non-empty values") } @@ -476,8 +476,7 @@ private final class PipelineExpressionParser { } if resolvedName == "array_contains_all", - let arrayExpressionMap = asMap(args["array_expression"]) - { + let arrayExpressionMap = asMap(args["array_expression"]) { return try arrayExpr.arrayContainsAll(parseTypedExpression(arrayExpressionMap)) } @@ -502,8 +501,8 @@ private final class PipelineExpressionParser { if resolvedName == "substring" { guard let exprMap = asMap(args["expression"]), - let startMap = asMap(args["start"]), - let endMap = asMap(args["end"]) + let startMap = asMap(args["start"]), + let endMap = asMap(args["end"]) else { throw parseError("substring requires expression, start, and end") } @@ -515,8 +514,8 @@ private final class PipelineExpressionParser { if resolvedName == "replace" || resolvedName == "string_replace_all" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("\(resolvedName) requires expression, find, and replacement") } @@ -528,8 +527,8 @@ private final class PipelineExpressionParser { if resolvedName == "string_replace_one" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("string_replace_one requires expression, find, and replacement") } @@ -542,7 +541,7 @@ private final class PipelineExpressionParser { if resolvedName == "string_index_of" || resolvedName == "string_repeat" { let argumentName = resolvedName == "string_index_of" ? "search" : "repetitions" guard let exprMap = asMap(args["expression"]), - let argumentMap = asMap(args[argumentName]) + let argumentMap = asMap(args[argumentName]) else { throw parseError("\(resolvedName) requires expression and \(argumentName)") } @@ -568,7 +567,7 @@ private final class PipelineExpressionParser { if resolvedName == "split" || resolvedName == "join" { guard let exprMap = asMap(args["expression"]), - let delimiterMap = asMap(args["delimiter"]) + let delimiterMap = asMap(args["delimiter"]) else { throw parseError("\(resolvedName) requires expression and delimiter") } @@ -578,9 +577,8 @@ private final class PipelineExpressionParser { return expr.split(delimiter: delimiter) } if let delimiterMap = asMap(args["delimiter"]), - (delimiterMap["name"] as? String) == "constant", - let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String - { + (delimiterMap["name"] as? String) == "constant", + let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String { return expr.join(delimiter: delimiterValue) } return functionExpression(name: "join", args: [expr, delimiter]) @@ -622,8 +620,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_filter" { guard let exprMap = asMap(args["expression"]), - let alias = args["alias"] as? String, - let filterMap = asMap(args["filter"]) + let alias = args["alias"] as? String, + let filterMap = asMap(args["filter"]) else { throw parseError("array_filter requires expression, alias, and filter") } @@ -635,8 +633,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError("array_transform requires expression, element_alias, and transform") } @@ -648,9 +646,9 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform_with_index" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let indexAlias = args["index_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let indexAlias = args["index_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError( "array_transform_with_index requires expression, element_alias, index_alias, and transform" @@ -717,8 +715,8 @@ private final class PipelineExpressionParser { if resolvedName == "conditional" { guard let conditionMap = asMap(args["condition"]), - let thenMap = asMap(args["then"]), - let elseMap = asMap(args["else"]) + let thenMap = asMap(args["then"]), + let elseMap = asMap(args["else"]) else { throw parseError("conditional requires condition, then, and else") } @@ -732,8 +730,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_add" || resolvedName == "timestamp_subtract" { let unitVal = args["unit"] guard let timestampMap = asMap(args["timestamp"]), - unitVal != nil, - let amountMap = asMap(args["amount"]) + unitVal != nil, + let amountMap = asMap(args["amount"]) else { throw parseError("\(resolvedName) requires timestamp, unit, and amount") } @@ -802,8 +800,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_diff" { let unitObj = args["unit"] guard let endMap = asMap(args["end"]), - let startMap = asMap(args["start"]), - unitObj != nil + let startMap = asMap(args["start"]), + unitObj != nil else { throw parseError("timestamp_diff requires end, start, and unit") } @@ -839,7 +837,7 @@ private final class PipelineExpressionParser { if resolvedName == "if_null" { guard let exprMap = asMap(args["expression"]), - let replMap = asMap(args["replacement"]) + let replMap = asMap(args["replacement"]) else { throw parseError("if_null requires expression and replacement") } @@ -865,7 +863,7 @@ private final class PipelineExpressionParser { throw parseError("switch_on requires at least two expressions") } var switchArgs: [any FirebaseFirestore.Expression] = [] - for i in 0.. any FirebaseFirestore.Expression - { + -> any FirebaseFirestore.Expression { let op = args["operator"] as? String let exprMaps = asArray(args["expressions"]) if let op, let exprMaps { @@ -980,12 +977,10 @@ private final class PipelineExpressionParser { } enum PipelineParser { - static func executePipeline( - firestore: Firestore, - stages: [[String: Any?]], - options: [String: Any?]?, - completion: @escaping (Any?, Error?) -> Void - ) { + static func executePipeline(firestore: Firestore, + stages: [[String: Any?]], + options: [String: Any?]?, + completion: @escaping (Any?, Error?) -> Void) { _ = options if NSClassFromString("FIRPipelineBridge") == nil { completion(nil, pipelineUnavailableError()) @@ -1026,10 +1021,10 @@ enum PipelineParser { throw parseError("expression must have alias or be a field reference") } - private static func parseSearchFields( - expressionMaps exprMaps: [Any], - exprParser: PipelineExpressionParser - ) throws -> [String: ExprBridge] { + private static func parseSearchFields(expressionMaps exprMaps: [Any], + exprParser: PipelineExpressionParser) throws -> [ + String: ExprBridge + ] { var fields: [String: ExprBridge] = [:] for em in exprMaps { guard let emMap = asMap(em) else { continue } @@ -1043,10 +1038,8 @@ enum PipelineParser { return fields } - private static func parseSearchStage( - args: [String: Any], - exprParser: PipelineExpressionParser - ) throws -> StageBridge { + private static func parseSearchStage(args: [String: Any], + exprParser: PipelineExpressionParser) throws -> StageBridge { let queryType = args["query_type"] as? String let query = args["query"] var options: [String: ExprBridge] = [:] @@ -1102,14 +1095,16 @@ enum PipelineParser { ) } - private static func parseStages( - firestore: Firestore, - stages: [[String: Any]] - ) throws -> [StageBridge] { + private static func parseStages(firestore: Firestore, + stages: [[String: Any]], + depth: Int = 0) throws -> [StageBridge] { + if depth > kMaxPipelineStageDepth { + throw parseError("Pipeline nested too deeply") + } let exprParser = PipelineExpressionParser(firestore: firestore) var stageBridges: [StageBridge] = [] - for i in 0.. AggregateFunctionBridge - { + private static func aggregateFunction(from funcMap: [String: Any], + exprParser: PipelineExpressionParser) throws + -> AggregateFunctionBridge { guard let name = funcMap["name"] as? String else { throw parseError("Aggregate function must have a 'name'") } @@ -1372,12 +1365,9 @@ enum PipelineParser { return AggregateFunctionBridge(name: iosName, args: argsArray) } - private static func parseAggregateStage( - args: [String: Any], - exprParser: PipelineExpressionParser - ) throws - -> StageBridge - { + private static func parseAggregateStage(args: [String: Any], + exprParser: PipelineExpressionParser) throws + -> StageBridge { guard let accumulatorMaps = asArray(args["aggregate_functions"]), !accumulatorMaps.isEmpty else { throw parseError("aggregate requires aggregate_functions") @@ -1389,12 +1379,9 @@ enum PipelineParser { ) } - private static func parseAggregateStageWithOptions( - args: [String: Any], - exprParser: PipelineExpressionParser - ) throws - -> StageBridge - { + private static func parseAggregateStageWithOptions(args: [String: Any], + exprParser: PipelineExpressionParser) throws + -> StageBridge { guard let stageMap = asMap(args["aggregate_stage"]) else { throw parseError("aggregate_with_options requires aggregate_stage") } @@ -1413,13 +1400,10 @@ enum PipelineParser { ) } - private static func parseAggregateStage( - accumulatorMaps: [Any], - groupMaps: [Any]?, - exprParser: PipelineExpressionParser - ) throws - -> StageBridge - { + private static func parseAggregateStage(accumulatorMaps: [Any], + groupMaps: [Any]?, + exprParser: PipelineExpressionParser) throws + -> StageBridge { var accumulators: [String: AggregateFunctionBridge] = [:] for accMap in accumulatorMaps { guard let accMap = asMap(accMap) else { continue } @@ -1458,11 +1442,10 @@ enum PipelineParser { return AggregateStageBridge(accumulators: accumulators, groups: groups) } - private static func buildPipeline( - firestore: Firestore, - stages: [[String: Any]] - ) throws -> PipelineBridge { - let stageBridges = try parseStages(firestore: firestore, stages: stages) + private static func buildPipeline(firestore: Firestore, + stages: [[String: Any]], + depth: Int = 0) throws -> PipelineBridge { + let stageBridges = try parseStages(firestore: firestore, stages: stages, depth: depth) return PipelineBridge(stages: stageBridges, db: firestore) } } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift index 5f914d030e25..9a372cbd5fed 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift @@ -28,13 +28,11 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { label: "io.flutter.plugins.firebase.firestore.query_snapshot" ) - init( - firestore: Firestore, - query: Query?, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource - ) { + init(firestore: Firestore, + query: Query?, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource) { self.firestore = firestore self.query = query self.includeMetadataChanges = includeMetadataChanges @@ -43,13 +41,12 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? - { + -> FlutterError? { guard let query else { return FlutterError( code: "sdk-error", message: - "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", details: nil ) } @@ -58,7 +55,8 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { .withIncludeMetadataChanges(includeMetadataChanges) .withSource(source) - listenerRegistration = query.addSnapshotListener(options: options) { snapshot, error in + listenerRegistration = query.addSnapshotListener(options: options) { + [snapshotQueue, serverTimestampBehavior] snapshot, error in if let error { let (code, message) = FirebaseFirestoreUtils.errorCodeAndMessage(from: error) DispatchQueue.main.async { @@ -72,9 +70,9 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { ) } } else if let snapshot { - self.snapshotQueue.async { + snapshotQueue.async { let pigeonSnapshot = PigeonParser.toPigeonQuerySnapshot( - snapshot, serverTimestampBehavior: self.serverTimestampBehavior + snapshot, serverTimestampBehavior: serverTimestampBehavior ) DispatchQueue.main.async { events(pigeonSnapshot) From 31ff0af3c692720e2807441b1691cbc780c02e7a Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 25 Aug 2026 08:45:29 +0000 Subject: [PATCH 4/4] format --- .../DocumentSnapshotStreamHandler.swift | 15 +- .../FLTFirebaseFirestorePlugin.swift | 200 +++++++++++------- .../FirebaseFirestoreReader.swift | 6 +- .../cloud_firestore/PigeonParser.swift | 57 +++-- .../cloud_firestore/PipelineParser.swift | 178 +++++++++------- .../QuerySnapshotStreamHandler.swift | 17 +- 6 files changed, 292 insertions(+), 181 deletions(-) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift index 0e9d9e3fd5e0..98a1da4a8fd2 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/DocumentSnapshotStreamHandler.swift @@ -25,11 +25,13 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { private let source: FirebaseFirestore.ListenSource private var listenerRegistration: ListenerRegistration? - init(firestore: Firestore, - reference: DocumentReference, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource) { + init( + firestore: Firestore, + reference: DocumentReference, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource + ) { self.firestore = firestore self.reference = reference self.includeMetadataChanges = includeMetadataChanges @@ -38,7 +40,8 @@ final class DocumentSnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { let options = SnapshotListenOptions() .withIncludeMetadataChanges(includeMetadataChanges) .withSource(source) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift index cb394e7611ed..97dd6de56037 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.swift @@ -26,7 +26,8 @@ import Foundation @objc(FLTFirebaseFirestorePlugin) public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, - FirebaseFirestoreHostApi { + FirebaseFirestoreHostApi +{ private var messenger: FlutterBinaryMessenger private var transactions: [String: Transaction] = [:] private var eventChannels: [String: FlutterEventChannel] = [:] @@ -126,9 +127,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } @discardableResult - private func registerEventChannel(prefix: String, - identifier: String = UUID().uuidString.lowercased(), - streamHandler: NSObject & FlutterStreamHandler) -> String { + private func registerEventChannel( + prefix: String, + identifier: String = UUID().uuidString.lowercased(), + streamHandler: NSObject & FlutterStreamHandler + ) -> String { let channelName = "\(prefix)/\(identifier)" let channel = FlutterEventChannel( name: channelName, @@ -189,13 +192,17 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu return firestore } - private func completeOnError(_ error: Error, - _ completion: @escaping (Result) -> Void) { + private func completeOnError( + _ error: Error, + _ completion: @escaping (Result) -> Void + ) { completion(.failure(FirebaseFirestoreUtils.flutterError(from: error))) } - func loadBundle(app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, - completion: @escaping (Result) -> Void) { + func loadBundle( + app: FirestorePigeonFirebaseApp, bundle: FlutterStandardTypedData, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreLoadBundleChannelName, @@ -204,8 +211,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func namedQueryGet(app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, - completion: @escaping (Result) -> Void) { + func namedQueryGet( + app: FirestorePigeonFirebaseApp, name: String, options: InternalGetOptions, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let source = PigeonParser.parseSource(options.source) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -219,7 +228,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "non-existent-named-query", message: - "Named query has not been found. Please check it has been loaded properly via loadBundle().", + "Named query has not been found. Please check it has been loaded properly via loadBundle().", details: nil ) ) @@ -242,8 +251,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func clearPersistence(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func clearPersistence( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).clearPersistence { error in if let error { self.completeOnError(error, completion) @@ -253,8 +264,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func disableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func disableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).disableNetwork { error in if let error { self.completeOnError(error, completion) @@ -264,8 +277,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func enableNetwork(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func enableNetwork( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).enableNetwork { error in if let error { self.completeOnError(error, completion) @@ -275,8 +290,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func terminate(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func terminate( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) firestore.terminate { error in if let error { @@ -291,8 +308,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func waitForPendingWrites(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func waitForPendingWrites( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { firestore(from: app).waitForPendingWrites { error in if let error { self.completeOnError(error, completion) @@ -302,8 +321,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setIndexConfiguration(app: FirestorePigeonFirebaseApp, indexConfiguration: String, - completion: @escaping (Result) -> Void) { + func setIndexConfiguration( + app: FirestorePigeonFirebaseApp, indexConfiguration: String, + completion: @escaping (Result) -> Void + ) { firestore(from: app).setIndexConfiguration(indexConfiguration) { error in if let error { self.completeOnError(error, completion) @@ -313,14 +334,18 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func setLoggingEnabled(loggingEnabled: Bool, - completion: @escaping (Result) -> Void) { + func setLoggingEnabled( + loggingEnabled: Bool, + completion: @escaping (Result) -> Void + ) { Firestore.enableLogging(loggingEnabled) completion(.success(())) } - func snapshotsInSyncSetup(app: FirestorePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func snapshotsInSyncSetup( + app: FirestorePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let identifier = registerEventChannel( prefix: kFLTFirebaseFirestoreSnapshotsInSyncEventChannelName, @@ -329,8 +354,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionCreate(app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, - completion: @escaping (Result) -> Void) { + func transactionCreate( + app: FirestorePigeonFirebaseApp, timeout: Int64, maxAttempts: Int64, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let transactionId = UUID().uuidString.lowercased() let handler = TransactionStreamHandler( @@ -363,9 +390,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func transactionStoreResult(transactionId: String, resultType: InternalTransactionResult, - commands: [InternalTransactionCommand?]?, - completion: @escaping (Result) -> Void) { + func transactionStoreResult( + transactionId: String, resultType: InternalTransactionResult, + commands: [InternalTransactionCommand?]?, + completion: @escaping (Result) -> Void + ) { listenersLock.lock() let handler = transactionHandlers[transactionId] listenersLock.unlock() @@ -373,8 +402,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(())) } - func transactionGet(app: FirestorePigeonFirebaseApp, transactionId: String, path: String, - completion: @escaping (Result) -> Void) { + func transactionGet( + app: FirestorePigeonFirebaseApp, transactionId: String, path: String, + completion: @escaping (Result) -> Void + ) { DispatchQueue.global(qos: .default).async { let firestore = self.firestore(from: app) let document = firestore.document(path) @@ -389,7 +420,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "missing-transaction", message: - "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", + "An error occurred while getting the native transaction. It could be caused by a timeout in a preceding transaction operation.", details: nil ) ) @@ -412,8 +443,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceSet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceSet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { let document = firestore(from: app).document(request.path) let data = request.data as? [String: Any] ?? [:] let finish: (Error?) -> Void = { error in @@ -435,8 +468,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceUpdate(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceUpdate( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { let document = firestore(from: app).document(request.path) let data = request.data as? [AnyHashable: Any] ?? [:] document.updateData(data) { error in @@ -448,10 +483,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceGet(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: - @escaping (Result) - -> Void) { + func documentReferenceGet( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: + @escaping (Result) + -> Void + ) { let document = firestore(from: app).document(request.path) let source = PigeonParser.parseSource(request.source ?? .serverAndCache) let serverTimestampBehavior = PigeonParser.parseServerTimestampBehavior( @@ -472,8 +509,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func documentReferenceDelete(app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, - completion: @escaping (Result) -> Void) { + func documentReferenceDelete( + app: FirestorePigeonFirebaseApp, request: DocumentReferenceRequest, + completion: @escaping (Result) -> Void + ) { firestore(from: app).document(request.path).delete { error in if let error { self.completeOnError(error, completion) @@ -483,9 +522,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func queryGet(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - completion: @escaping (Result) -> Void) { + func queryGet( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -498,7 +539,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -525,10 +566,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func aggregateQuery(app: FirestorePigeonFirebaseApp, path: String, - parameters: InternalQueryParameters, - source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, - completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void) { + func aggregateQuery( + app: FirestorePigeonFirebaseApp, path: String, + parameters: InternalQueryParameters, + source: AggregateSource, queries: [AggregateQuery?], isCollectionGroup: Bool, + completion: @escaping (Result<[AggregateQueryResponse?], Error>) -> Void + ) { let firestore = firestore(from: app) guard let query = PigeonParser.parseQuery( @@ -541,7 +584,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -609,8 +652,10 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func writeBatchCommit(app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], - completion: @escaping (Result) -> Void) { + func writeBatchCommit( + app: FirestorePigeonFirebaseApp, writes: [InternalTransactionCommand?], + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let batch = firestore.batch() for write in writes.compactMap({ $0 }) { @@ -646,10 +691,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu } } - func querySnapshot(app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, - parameters: InternalQueryParameters, options: InternalGetOptions, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void) { + func querySnapshot( + app: FirestorePigeonFirebaseApp, path: String, isCollectionGroup: Bool, + parameters: InternalQueryParameters, options: InternalGetOptions, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let query = PigeonParser.parseQuery( parameters: parameters, firestore: firestore, path: path, isCollectionGroup: isCollectionGroup @@ -660,7 +707,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu FlutterError( code: "error-parsing", message: - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this SDK.", details: nil ) ) @@ -683,10 +730,12 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func documentReferenceSnapshot(app: FirestorePigeonFirebaseApp, - parameters: DocumentReferenceRequest, - includeMetadataChanges: Bool, source: ListenSource, - completion: @escaping (Result) -> Void) { + func documentReferenceSnapshot( + app: FirestorePigeonFirebaseApp, + parameters: DocumentReferenceRequest, + includeMetadataChanges: Bool, source: ListenSource, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let document = firestore.document(parameters.path) let identifier = registerEventChannel( @@ -704,9 +753,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(identifier)) } - func persistenceCacheIndexManagerRequest(app: FirestorePigeonFirebaseApp, - request: PersistenceCacheIndexManagerRequest, - completion: @escaping (Result) -> Void) { + func persistenceCacheIndexManagerRequest( + app: FirestorePigeonFirebaseApp, + request: PersistenceCacheIndexManagerRequest, + completion: @escaping (Result) -> Void + ) { if let manager = firestore(from: app).persistentCacheIndexManager { switch request { case .enableIndexAutoCreation: @@ -722,9 +773,11 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu completion(.success(())) } - func executePipeline(app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], - options: [String?: Any?]?, - completion: @escaping (Result) -> Void) { + func executePipeline( + app: FirestorePigeonFirebaseApp, stages: [[String?: Any?]?], + options: [String?: Any?]?, + completion: @escaping (Result) -> Void + ) { let firestore = firestore(from: app) let mappedStages: [[String: Any?]] = stages.compactMap { stage in guard let stage else { return nil } @@ -783,7 +836,7 @@ public class FLTFirebaseFirestorePlugin: NSObject, FlutterPlugin, FLTFirebasePlu let ref = object.value(forKey: "reference") as AnyObject? let path = (ref?.value(forKey: "path") as? String) - ?? (object.value(forKey: "documentID") as? String) + ?? (object.value(forKey: "documentID") as? String) let data = object.value(forKey: "data") as? [String: Any] let mappedData = PigeonParser.toPigeonMap(data) pigeonResults.append( @@ -820,7 +873,8 @@ private final class EventChannelCleanupHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { inner.onListen(withArguments: arguments, eventSink: events) } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift index 788e73bb62cc..c27d0bee4e0e 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirebaseFirestoreReader.swift @@ -50,7 +50,7 @@ class FirebaseFirestoreReader: FlutterStandardReader { let length = readSize() var array: [Any] = [] array.reserveCapacity(Int(length)) - for _ in 0 ..< length { + for _ in 0..= 3, - let fieldPath = condition[0] as? FieldPath, - let op = condition[1] as? String + let fieldPath = condition[0] as? FieldPath, + let op = condition[1] as? String else { throw PigeonParser.queryParseError("Invalid query condition") } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift index a9fa51fed517..1faf23473fb3 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PigeonParser.swift @@ -79,10 +79,12 @@ enum PigeonParser { throw queryParseError("Invalid operator") } - static func parseQuery(parameters: InternalQueryParameters, - firestore: Firestore, - path: String, - isCollectionGroup: Bool) -> Query? { + static func parseQuery( + parameters: InternalQueryParameters, + firestore: Firestore, + path: String, + isCollectionGroup: Bool + ) -> Query? { do { var query: Query if isCollectionGroup { @@ -200,7 +202,8 @@ enum PigeonParser { } static func parseServerTimestampBehavior(_ behavior: ServerTimestampBehavior) - -> FirebaseFirestore.ServerTimestampBehavior { + -> FirebaseFirestore.ServerTimestampBehavior + { switch behavior { case .none: return .none @@ -221,16 +224,19 @@ enum PigeonParser { } static func toPigeonSnapshotMetadata(_ snapshotMetadata: SnapshotMetadata) - -> InternalSnapshotMetadata { + -> InternalSnapshotMetadata + { InternalSnapshotMetadata( hasPendingWrites: snapshotMetadata.hasPendingWrites, isFromCache: snapshotMetadata.isFromCache ) } - static func toPigeonDocumentSnapshot(_ documentSnapshot: DocumentSnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalDocumentSnapshot { + static func toPigeonDocumentSnapshot( + _ documentSnapshot: DocumentSnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalDocumentSnapshot { let data = documentSnapshot.data(with: serverTimestampBehavior) return InternalDocumentSnapshot( path: documentSnapshot.reference.path, @@ -240,17 +246,21 @@ enum PigeonParser { } static func toPigeonDocumentChangeType(_ documentChangeType: DocumentChangeType) - -> DocumentChangeType { + -> DocumentChangeType + { documentChangeType } - static func toPigeonDocumentChange(_ documentChange: DocumentChange, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalDocumentChange { + static func toPigeonDocumentChange( + _ documentChange: DocumentChange, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalDocumentChange { let maxVal = NSNotFound let newIndex: Int64 if documentChange.newIndex == NSNotFound || documentChange.newIndex == 4_294_967_295 - || documentChange.newIndex == maxVal { + || documentChange.newIndex == maxVal + { newIndex = -1 } else { newIndex = Int64(documentChange.newIndex) @@ -258,7 +268,8 @@ enum PigeonParser { let oldIndex: Int64 if documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4_294_967_295 - || documentChange.oldIndex == maxVal { + || documentChange.oldIndex == maxVal + { oldIndex = -1 } else { oldIndex = Int64(documentChange.oldIndex) @@ -286,17 +297,21 @@ enum PigeonParser { ) } - static func toPigeonDocumentChanges(_ documentChanges: [DocumentChange], - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> [InternalDocumentChange?] { + static func toPigeonDocumentChanges( + _ documentChanges: [DocumentChange], + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> [InternalDocumentChange?] { documentChanges.map { toPigeonDocumentChange($0, serverTimestampBehavior: serverTimestampBehavior) } } - static func toPigeonQuerySnapshot(_ querySnapshot: QuerySnapshot, - serverTimestampBehavior: FirebaseFirestore - .ServerTimestampBehavior) -> InternalQuerySnapshot { + static func toPigeonQuerySnapshot( + _ querySnapshot: QuerySnapshot, + serverTimestampBehavior: FirebaseFirestore + .ServerTimestampBehavior + ) -> InternalQuerySnapshot { let documents = querySnapshot.documents.map { toPigeonDocumentSnapshot($0, serverTimestampBehavior: serverTimestampBehavior) as InternalDocumentSnapshot? diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift index 9561a5b807ee..9f565dc9ccca 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/PipelineParser.swift @@ -134,7 +134,8 @@ private func exprBridge(from value: Any) -> ExprBridge? { for child in Mirror(reflecting: value).children { if child.label == "expr" || child.label == "constant" || child.label == "field", - let nested = exprBridge(from: child.value) { + let nested = exprBridge(from: child.value) + { return nested } } @@ -142,7 +143,8 @@ private func exprBridge(from value: Any) -> ExprBridge? { } private func sendableExpressions(_ expressions: [any FirebaseFirestore.Expression]) - -> [any Sendable] { + -> [any Sendable] +{ expressions.map { $0 as any Sendable } } @@ -156,7 +158,8 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore } let doubleValue = number.doubleValue if doubleValue.isFinite, doubleValue.rounded() == doubleValue, - doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) { + doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) + { return Constant(number.intValue) } return Constant(number.doubleValue) @@ -197,8 +200,10 @@ private func constantExpression(from value: Any) throws -> any FirebaseFirestore throw parseError("Unsupported constant value: \(type(of: value))") } -private func functionExpression(name: String, - args: [any FirebaseFirestore.Expression]) -> FunctionExpression { +private func functionExpression( + name: String, + args: [any FirebaseFirestore.Expression] +) -> FunctionExpression { FunctionExpression(functionName: name, args: args) } @@ -219,7 +224,8 @@ private final class PipelineExpressionParser { } private func parseBooleanTypedExpression(_ map: [String: Any]) throws - -> any FirebaseFirestore.BooleanExpression { + -> any FirebaseFirestore.BooleanExpression + { let expression = try parseTypedExpression(map) if let booleanExpression = expression as? any FirebaseFirestore.BooleanExpression { return booleanExpression @@ -227,9 +233,12 @@ private final class PipelineExpressionParser { return expression.asBoolean() } - private func parseTypedExpressions(_ maps: [Any], - errorMessage: String) throws - -> [any FirebaseFirestore.Expression] { + private func parseTypedExpressions( + _ maps: [Any], + errorMessage: String + ) throws + -> [any FirebaseFirestore.Expression] + { var expressions: [any FirebaseFirestore.Expression] = [] for value in maps { guard let map = asMap(value) else { continue } @@ -242,7 +251,8 @@ private final class PipelineExpressionParser { } private func parseTypedExpression(_ map: [String: Any]) throws -> any FirebaseFirestore - .Expression { + .Expression + { expressionDepth += 1 defer { expressionDepth -= 1 } if expressionDepth > kMaxPipelineExpressionDepth { @@ -352,7 +362,8 @@ private final class PipelineExpressionParser { } if resolvedName == "exists" || resolvedName == "is_error" || resolvedName == "is_absent" - || resolvedName == "not" { + || resolvedName == "not" + { guard let exprMap = asMap(args["expression"]) else { throw parseError("\(resolvedName) requires expression") } @@ -403,7 +414,8 @@ private final class PipelineExpressionParser { } if resolvedName == "and" || resolvedName == "or" || resolvedName == "xor" - || resolvedName == "nor" { + || resolvedName == "nor" + { guard let exprMaps = asArray(args["expressions"]), !exprMaps.isEmpty else { throw parseError("\(resolvedName) requires at least one expression") } @@ -430,7 +442,7 @@ private final class PipelineExpressionParser { if resolvedName == "equal_any" || resolvedName == "not_equal_any" { let valuesMaps = asArray(args["values"]) guard let valueMap = asMap(args["value"]), - let valuesMaps, !valuesMaps.isEmpty + let valuesMaps, !valuesMaps.isEmpty else { throw parseError("\(resolvedName) requires value and non-empty values") } @@ -476,7 +488,8 @@ private final class PipelineExpressionParser { } if resolvedName == "array_contains_all", - let arrayExpressionMap = asMap(args["array_expression"]) { + let arrayExpressionMap = asMap(args["array_expression"]) + { return try arrayExpr.arrayContainsAll(parseTypedExpression(arrayExpressionMap)) } @@ -501,8 +514,8 @@ private final class PipelineExpressionParser { if resolvedName == "substring" { guard let exprMap = asMap(args["expression"]), - let startMap = asMap(args["start"]), - let endMap = asMap(args["end"]) + let startMap = asMap(args["start"]), + let endMap = asMap(args["end"]) else { throw parseError("substring requires expression, start, and end") } @@ -514,8 +527,8 @@ private final class PipelineExpressionParser { if resolvedName == "replace" || resolvedName == "string_replace_all" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("\(resolvedName) requires expression, find, and replacement") } @@ -527,8 +540,8 @@ private final class PipelineExpressionParser { if resolvedName == "string_replace_one" { guard let exprMap = asMap(args["expression"]), - let findMap = asMap(args["find"]), - let replacementMap = asMap(args["replacement"]) + let findMap = asMap(args["find"]), + let replacementMap = asMap(args["replacement"]) else { throw parseError("string_replace_one requires expression, find, and replacement") } @@ -541,7 +554,7 @@ private final class PipelineExpressionParser { if resolvedName == "string_index_of" || resolvedName == "string_repeat" { let argumentName = resolvedName == "string_index_of" ? "search" : "repetitions" guard let exprMap = asMap(args["expression"]), - let argumentMap = asMap(args[argumentName]) + let argumentMap = asMap(args[argumentName]) else { throw parseError("\(resolvedName) requires expression and \(argumentName)") } @@ -567,7 +580,7 @@ private final class PipelineExpressionParser { if resolvedName == "split" || resolvedName == "join" { guard let exprMap = asMap(args["expression"]), - let delimiterMap = asMap(args["delimiter"]) + let delimiterMap = asMap(args["delimiter"]) else { throw parseError("\(resolvedName) requires expression and delimiter") } @@ -577,8 +590,9 @@ private final class PipelineExpressionParser { return expr.split(delimiter: delimiter) } if let delimiterMap = asMap(args["delimiter"]), - (delimiterMap["name"] as? String) == "constant", - let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String { + (delimiterMap["name"] as? String) == "constant", + let delimiterValue = asMap(delimiterMap["args"])?["value"] as? String + { return expr.join(delimiter: delimiterValue) } return functionExpression(name: "join", args: [expr, delimiter]) @@ -620,8 +634,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_filter" { guard let exprMap = asMap(args["expression"]), - let alias = args["alias"] as? String, - let filterMap = asMap(args["filter"]) + let alias = args["alias"] as? String, + let filterMap = asMap(args["filter"]) else { throw parseError("array_filter requires expression, alias, and filter") } @@ -633,8 +647,8 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError("array_transform requires expression, element_alias, and transform") } @@ -646,9 +660,9 @@ private final class PipelineExpressionParser { if resolvedName == "array_transform_with_index" { guard let exprMap = asMap(args["expression"]), - let elementAlias = args["element_alias"] as? String, - let indexAlias = args["index_alias"] as? String, - let transformMap = asMap(args["transform"]) + let elementAlias = args["element_alias"] as? String, + let indexAlias = args["index_alias"] as? String, + let transformMap = asMap(args["transform"]) else { throw parseError( "array_transform_with_index requires expression, element_alias, index_alias, and transform" @@ -715,8 +729,8 @@ private final class PipelineExpressionParser { if resolvedName == "conditional" { guard let conditionMap = asMap(args["condition"]), - let thenMap = asMap(args["then"]), - let elseMap = asMap(args["else"]) + let thenMap = asMap(args["then"]), + let elseMap = asMap(args["else"]) else { throw parseError("conditional requires condition, then, and else") } @@ -730,8 +744,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_add" || resolvedName == "timestamp_subtract" { let unitVal = args["unit"] guard let timestampMap = asMap(args["timestamp"]), - unitVal != nil, - let amountMap = asMap(args["amount"]) + unitVal != nil, + let amountMap = asMap(args["amount"]) else { throw parseError("\(resolvedName) requires timestamp, unit, and amount") } @@ -800,8 +814,8 @@ private final class PipelineExpressionParser { if resolvedName == "timestamp_diff" { let unitObj = args["unit"] guard let endMap = asMap(args["end"]), - let startMap = asMap(args["start"]), - unitObj != nil + let startMap = asMap(args["start"]), + unitObj != nil else { throw parseError("timestamp_diff requires end, start, and unit") } @@ -837,7 +851,7 @@ private final class PipelineExpressionParser { if resolvedName == "if_null" { guard let exprMap = asMap(args["expression"]), - let replMap = asMap(args["replacement"]) + let replMap = asMap(args["replacement"]) else { throw parseError("if_null requires expression and replacement") } @@ -863,7 +877,7 @@ private final class PipelineExpressionParser { throw parseError("switch_on requires at least two expressions") } var switchArgs: [any FirebaseFirestore.Expression] = [] - for i in 0 ..< exprMaps.count { + for i in 0.. any FirebaseFirestore.Expression { + -> any FirebaseFirestore.Expression + { let op = args["operator"] as? String let exprMaps = asArray(args["expressions"]) if let op, let exprMaps { @@ -977,10 +992,12 @@ private final class PipelineExpressionParser { } enum PipelineParser { - static func executePipeline(firestore: Firestore, - stages: [[String: Any?]], - options: [String: Any?]?, - completion: @escaping (Any?, Error?) -> Void) { + static func executePipeline( + firestore: Firestore, + stages: [[String: Any?]], + options: [String: Any?]?, + completion: @escaping (Any?, Error?) -> Void + ) { _ = options if NSClassFromString("FIRPipelineBridge") == nil { completion(nil, pipelineUnavailableError()) @@ -1021,10 +1038,10 @@ enum PipelineParser { throw parseError("expression must have alias or be a field reference") } - private static func parseSearchFields(expressionMaps exprMaps: [Any], - exprParser: PipelineExpressionParser) throws -> [ - String: ExprBridge - ] { + private static func parseSearchFields( + expressionMaps exprMaps: [Any], + exprParser: PipelineExpressionParser + ) throws -> [String: ExprBridge] { var fields: [String: ExprBridge] = [:] for em in exprMaps { guard let emMap = asMap(em) else { continue } @@ -1038,8 +1055,10 @@ enum PipelineParser { return fields } - private static func parseSearchStage(args: [String: Any], - exprParser: PipelineExpressionParser) throws -> StageBridge { + private static func parseSearchStage( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws -> StageBridge { let queryType = args["query_type"] as? String let query = args["query"] var options: [String: ExprBridge] = [:] @@ -1095,16 +1114,18 @@ enum PipelineParser { ) } - private static func parseStages(firestore: Firestore, - stages: [[String: Any]], - depth: Int = 0) throws -> [StageBridge] { + private static func parseStages( + firestore: Firestore, + stages: [[String: Any]], + depth: Int = 0 + ) throws -> [StageBridge] { if depth > kMaxPipelineStageDepth { throw parseError("Pipeline nested too deeply") } let exprParser = PipelineExpressionParser(firestore: firestore) var stageBridges: [StageBridge] = [] - for i in 0 ..< stages.count { + for i in 0.. AggregateFunctionBridge { + private static func aggregateFunction( + from funcMap: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> AggregateFunctionBridge + { guard let name = funcMap["name"] as? String else { throw parseError("Aggregate function must have a 'name'") } @@ -1365,9 +1390,12 @@ enum PipelineParser { return AggregateFunctionBridge(name: iosName, args: argsArray) } - private static func parseAggregateStage(args: [String: Any], - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStage( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { guard let accumulatorMaps = asArray(args["aggregate_functions"]), !accumulatorMaps.isEmpty else { throw parseError("aggregate requires aggregate_functions") @@ -1379,9 +1407,12 @@ enum PipelineParser { ) } - private static func parseAggregateStageWithOptions(args: [String: Any], - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStageWithOptions( + args: [String: Any], + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { guard let stageMap = asMap(args["aggregate_stage"]) else { throw parseError("aggregate_with_options requires aggregate_stage") } @@ -1400,10 +1431,13 @@ enum PipelineParser { ) } - private static func parseAggregateStage(accumulatorMaps: [Any], - groupMaps: [Any]?, - exprParser: PipelineExpressionParser) throws - -> StageBridge { + private static func parseAggregateStage( + accumulatorMaps: [Any], + groupMaps: [Any]?, + exprParser: PipelineExpressionParser + ) throws + -> StageBridge + { var accumulators: [String: AggregateFunctionBridge] = [:] for accMap in accumulatorMaps { guard let accMap = asMap(accMap) else { continue } @@ -1442,9 +1476,11 @@ enum PipelineParser { return AggregateStageBridge(accumulators: accumulators, groups: groups) } - private static func buildPipeline(firestore: Firestore, - stages: [[String: Any]], - depth: Int = 0) throws -> PipelineBridge { + private static func buildPipeline( + firestore: Firestore, + stages: [[String: Any]], + depth: Int = 0 + ) throws -> PipelineBridge { let stageBridges = try parseStages(firestore: firestore, stages: stages, depth: depth) return PipelineBridge(stages: stageBridges, db: firestore) } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift index 9a372cbd5fed..5a38338e808a 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/QuerySnapshotStreamHandler.swift @@ -28,11 +28,13 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { label: "io.flutter.plugins.firebase.firestore.query_snapshot" ) - init(firestore: Firestore, - query: Query?, - includeMetadataChanges: Bool, - serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, - source: FirebaseFirestore.ListenSource) { + init( + firestore: Firestore, + query: Query?, + includeMetadataChanges: Bool, + serverTimestampBehavior: FirebaseFirestore.ServerTimestampBehavior, + source: FirebaseFirestore.ListenSource + ) { self.firestore = firestore self.query = query self.includeMetadataChanges = includeMetadataChanges @@ -41,12 +43,13 @@ final class QuerySnapshotStreamHandler: NSObject, FlutterStreamHandler { } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { guard let query else { return FlutterError( code: "sdk-error", message: - "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", details: nil ) }