Просмотр исходного кода

Merge remote-tracking branch 'origin/main' into auth-swift

Paul Beusterien 2 лет назад
Родитель
Сommit
f5cb55371b
100 измененных файлов с 2233 добавлено и 2269 удалено
  1. 0 48
      .github/workflows/health-metrics-presubmit.yml
  2. 2 1
      .github/workflows/storage.yml
  3. 1 1
      Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m
  4. 24 24
      Firebase.podspec
  5. 1 1
      FirebaseABTesting.podspec
  6. 4 4
      FirebaseAnalytics.podspec
  7. 2 2
      FirebaseAnalyticsOnDeviceConversion.podspec
  8. 1 1
      FirebaseAppCheck.podspec
  9. 26 32
      FirebaseAppCheck/Tests/Unit/Swift/AppCheckAPITests.swift
  10. 1 1
      FirebaseAppCheckInterop.podspec
  11. 1 1
      FirebaseAppDistribution.podspec
  12. 1 1
      FirebaseAuth.podspec
  13. 4 0
      FirebaseAuth/CHANGELOG.md
  14. 0 1
      FirebaseAuth/Tests/SampleSwift/SwiftApiTests/AccountInfoTests.swift
  15. 1 0
      FirebaseAuth/Tests/SampleSwift/SwiftApiTests/EmailPasswordTests.swift
  16. 0 8
      FirebaseAuth/Tests/SampleSwift/SwiftApiTests/FacebookTests.swift
  17. 1 1
      FirebaseAuthInterop.podspec
  18. 1 1
      FirebaseCore.podspec
  19. 6 0
      FirebaseCore/CHANGELOG.md
  20. 5 7
      FirebaseCore/Tests/SwiftUnit/CoreAPITests.swift
  21. 1 1
      FirebaseCoreExtension.podspec
  22. 1 1
      FirebaseCoreInternal.podspec
  23. 1 1
      FirebaseCrashlytics.podspec
  24. 1 1
      FirebaseDatabase.podspec
  25. 1 1
      FirebaseDatabase/CHANGELOG.md
  26. 139 169
      FirebaseDatabase/Tests/Unit/Swift/DatabaseAPITests.swift
  27. 1 1
      FirebaseDynamicLinks.podspec
  28. 5 0
      FirebaseDynamicLinks/README.md
  29. 1 1
      FirebaseFirestore.podspec
  30. 1 1
      FirebaseFirestoreInternal.podspec
  31. 1 1
      FirebaseFunctions.podspec
  32. 49 51
      FirebaseFunctions/Sources/Callable+Codable.swift
  33. 25 27
      FirebaseFunctions/Sources/HTTPSCallable.swift
  34. 310 339
      FirebaseFunctions/Tests/Integration/IntegrationTests.swift
  35. 18 22
      FirebaseFunctions/Tests/Unit/FunctionsAPITests.swift
  36. 1 1
      FirebaseInAppMessaging.podspec
  37. 1 1
      FirebaseInstallations.podspec
  38. 26 32
      FirebaseInstallations/Source/Tests/Unit/Swift/InstallationsAPITests.swift
  39. 1 1
      FirebaseMLModelDownloader.podspec
  40. 1 1
      FirebaseMessaging.podspec
  41. 4 0
      FirebaseMessaging/CHANGELOG.md
  42. 3 0
      FirebaseMessaging/Sources/Token/FIRMessagingTokenInfo.h
  43. 21 1
      FirebaseMessaging/Sources/Token/FIRMessagingTokenInfo.m
  44. 16 0
      FirebaseMessaging/Sources/Token/FIRMessagingTokenStore.m
  45. 12 14
      FirebaseMessaging/Tests/UnitTestsSwift/FIRMessagingAPITest.swift
  46. 1 1
      FirebaseMessagingInterop.podspec
  47. 1 1
      FirebasePerformance.podspec
  48. 1 1
      FirebasePerformance/README.md
  49. 1 1
      FirebasePerformance/Tests/Unit/FPRNetworkTraceTest.m
  50. 2 2
      FirebasePerformance/generate_project.sh
  51. 1 1
      FirebaseRemoteConfig.podspec
  52. 93 95
      FirebaseRemoteConfigSwift/Tests/SwiftAPI/AsyncAwaitTests.swift
  53. 171 173
      FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift
  54. 36 38
      FirebaseRemoteConfigSwift/Tests/SwiftAPI/PropertyWrapperDefaultConfigsTests.swift
  55. 200 202
      FirebaseRemoteConfigSwift/Tests/SwiftAPI/PropertyWrapperTests.swift
  56. 168 170
      FirebaseRemoteConfigSwift/Tests/SwiftAPI/Value.swift
  57. 1 1
      FirebaseSessions.podspec
  58. 1 1
      FirebaseSharedSwift.podspec
  59. 1 1
      FirebaseStorage.podspec
  60. 159 162
      FirebaseStorage/Sources/AsyncAwait.swift
  61. 67 77
      FirebaseStorage/Sources/StorageReference.swift
  62. 349 351
      FirebaseStorage/Tests/Integration/StorageAsyncAwait.swift
  63. 5 5
      FirebaseStorage/Tests/Integration/StorageIntegration.swift
  64. 5 0
      Firestore/CHANGELOG.md
  65. 1 1
      Firestore/Example/FuzzTests/FuzzingResources/Serializer/Corpus/ConvertTextToBinary.sh
  66. 1 1
      Firestore/Source/Public/FirebaseFirestore/FIRAggregateSource.h
  67. 8 7
      Firestore/Source/Public/FirebaseFirestore/FIRQuery.h
  68. 18 20
      Firestore/Swift/Source/AsyncAwait/CollectionReference+AsyncAwait.swift
  69. 83 85
      Firestore/Swift/Source/AsyncAwait/Firestore+AsyncAwait.swift
  70. 36 38
      Firestore/Swift/Source/Codable/DocumentReference+ReadDecodable.swift
  71. 1 1
      Firestore/core/CMakeLists.txt
  72. 1 1
      Firestore/core/src/nanopb/pretty_printing.cc
  73. 19 0
      Firestore/core/src/util/filesystem_apple.mm
  74. 2 0
      Firestore/core/src/util/filesystem_posix.cc
  75. 7 7
      Firestore/core/src/util/string_format.h
  76. 3 3
      Firestore/core/test/unit/FSTGoogleTestTests.mm
  77. 3 3
      GoogleAppMeasurement.podspec
  78. 2 2
      GoogleAppMeasurementOnDeviceConversion.podspec
  79. 22 10
      Package.swift
  80. 20 3
      README.md
  81. 1 0
      ReleaseTooling/CarthageJSON/FirebaseABTestingBinary.json
  82. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAdMobBinary.json
  83. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAnalyticsBinary.json
  84. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAnalyticsOnDeviceConversionBinary.json
  85. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAppCheckBinary.json
  86. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAppDistributionBinary.json
  87. 1 0
      ReleaseTooling/CarthageJSON/FirebaseAuthBinary.json
  88. 1 0
      ReleaseTooling/CarthageJSON/FirebaseCrashlyticsBinary.json
  89. 1 0
      ReleaseTooling/CarthageJSON/FirebaseDatabaseBinary.json
  90. 1 0
      ReleaseTooling/CarthageJSON/FirebaseDynamicLinksBinary.json
  91. 1 0
      ReleaseTooling/CarthageJSON/FirebaseFirestoreBinary.json
  92. 1 0
      ReleaseTooling/CarthageJSON/FirebaseFunctionsBinary.json
  93. 1 0
      ReleaseTooling/CarthageJSON/FirebaseGoogleSignInBinary.json
  94. 1 0
      ReleaseTooling/CarthageJSON/FirebaseInAppMessagingBinary.json
  95. 1 0
      ReleaseTooling/CarthageJSON/FirebaseMLModelDownloaderBinary.json
  96. 1 0
      ReleaseTooling/CarthageJSON/FirebaseMessagingBinary.json
  97. 1 0
      ReleaseTooling/CarthageJSON/FirebasePerformanceBinary.json
  98. 1 0
      ReleaseTooling/CarthageJSON/FirebaseRemoteConfigBinary.json
  99. 1 0
      ReleaseTooling/CarthageJSON/FirebaseStorageBinary.json
  100. 1 1
      ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift

+ 0 - 48
.github/workflows/health-metrics-presubmit.yml

@@ -57,54 +57,6 @@ jobs:
             ./scripts/health_metrics/get_updated_files.sh
           fi
 
-  binary_size_metrics:
-    needs: check
-    # Prevent the job from being triggered in fork.
-    # always() will trigger this job when `needs` are skipped in a `merge` pull_request event.
-    if: always() && github.event.pull_request.head.repo.full_name == github.repository && ((github.event.action != 'closed' &&  github.event.pull_request.base.ref == 'main') || github.event.pull_request.merged)
-    runs-on: macos-12
-    strategy:
-      matrix:
-        target: [iOS]
-    steps:
-    - uses: actions/checkout@v4
-    - name: Access to Metrics Service
-      run: |
-        # Install gcloud sdk
-        curl https://sdk.cloud.google.com > install.sh
-        bash install.sh --disable-prompts
-        echo "${HOME}/google-cloud-sdk/bin/" >> $GITHUB_PATH
-        export PATH="${HOME}/google-cloud-sdk/bin/:${PATH}"
-
-        # Activate the service account for Metrics Service.
-        scripts/decrypt_gha_secret.sh scripts/gha-encrypted/metrics_service_access.json.gpg \
-        metrics-access.json "${{ env.METRICS_SERVICE_SECRET }}"
-        gcloud auth activate-service-account --key-file metrics-access.json
-    - name: Build and test
-      run: |
-        FirebaseABTesting=${{ needs.check.outputs.abtesting_run_job }} \
-        FirebaseAnalytics=${{ needs.check.outputs.analytics_run_job }} \
-        FirebaseAppCheck=${{ needs.check.outputs.appcheck_run_job }} \
-        FirebaseAppDistribution=${{ needs.check.outputs.appdistribution_run_job }} \
-        FirebaseAuth=${{ needs.check.outputs.auth_run_job }} \
-        FirebaseCrashlytics=${{ needs.check.outputs.crashlytics_run_job }} \
-        FirebaseDatabase=${{ needs.check.outputs.database_run_job }} \
-        FirebaseDynamicLinks=${{ needs.check.outputs.dynamiclinks_run_job }} \
-        FirebaseFirestore=${{ needs.check.outputs.firestore_run_job }} \
-        FirebaseFunctions=${{ needs.check.outputs.functions_run_job}} \
-        FirebaseInAppMessaging=${{ needs.check.outputs.inappmessaging_run_job }} \
-        FirebaseInstallations=${{ needs.check.outputs.installations_run_job }} \
-        FirebaseMessaging=${{ needs.check.outputs.messaging_run_job}} \
-        FirebasePerformance=${{ needs.check.outputs.performance_run_job }} \
-        FirebaseRemoteConfig=${{ needs.check.outputs.remoteconfig_run_job }} \
-        FirebaseStorage=${{ needs.check.outputs.storage_run_job }} \
-        ./scripts/health_metrics/create_binary_size_report.sh
-      env:
-        PULL_REQUEST_NUM: ${{ github.event.pull_request.number }}
-        BASE_COMMIT: ${{ needs.check.outputs.target_branch_head }}
-        POSTSUBMIT: ${{ github.event.pull_request.merged }}
-        SOURCE_BRANCH: ${{ github.base_ref }}
-
   pod-lib-lint-abtesting:
     needs: check
     # Don't run on private repo unless it is a PR.

+ 2 - 1
.github/workflows/storage.yml

@@ -22,6 +22,7 @@ jobs:
     if: (github.repository == 'Firebase/firebase-ios-sdk' && github.event_name == 'schedule') || github.event_name == 'pull_request'
     strategy:
       matrix:
+        language: [Swift, ObjC]
         include:
           - os: macos-13
             xcode: Xcode_15.1
@@ -51,7 +52,7 @@ jobs:
     - name: Xcode
       run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
     - name: BuildAndTest # can be replaced with pod lib lint with CocoaPods 1.10
-      run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/build.sh Storage all)
+      run: ([ -z $plist_secret ] || scripts/third_party/travis/retry.sh scripts/build.sh Storage${{ matrix.language }} all)
 
   spm:
     # Don't run on private repo unless it is a PR.

+ 1 - 1
Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m

@@ -42,7 +42,7 @@ void FIRCLSHandler(FIRCLSFile* file, thread_t crashedThread, void* uapVoid) {
   FIRCLSHostWriteDiskUsage(file);
 
   // This is the first common point where various crash handlers call into
-  // Store a crash file marker to indicate that a crash has occured
+  // Store a crash file marker to indicate that a crash has occurred
   FIRCLSCreateCrashedMarkerFile();
 
   FIRCLSProcessResumeAllOtherThreads(&process);

+ 24 - 24
Firebase.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'Firebase'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase'
 
   s.description      = <<-DESC
@@ -36,14 +36,14 @@ Simplify your app development, grow your user base, and monetize more effectivel
     ss.ios.deployment_target = '10.0'
     ss.osx.deployment_target = '10.13'
     ss.tvos.deployment_target = '12.0'
-    ss.ios.dependency 'FirebaseAnalytics', '~> 10.20.0'
-    ss.osx.dependency 'FirebaseAnalytics', '~> 10.20.0'
-    ss.tvos.dependency 'FirebaseAnalytics', '~> 10.20.0'
+    ss.ios.dependency 'FirebaseAnalytics', '~> 10.21.0'
+    ss.osx.dependency 'FirebaseAnalytics', '~> 10.21.0'
+    ss.tvos.dependency 'FirebaseAnalytics', '~> 10.21.0'
     ss.dependency 'Firebase/CoreOnly'
   end
 
   s.subspec 'CoreOnly' do |ss|
-    ss.dependency 'FirebaseCore', '10.20.0'
+    ss.dependency 'FirebaseCore', '10.21.0'
     ss.source_files = 'CoreOnly/Sources/Firebase.h'
     ss.preserve_paths = 'CoreOnly/Sources/module.modulemap'
     if ENV['FIREBASE_POD_REPO_FOR_DEV_POD'] then
@@ -79,13 +79,13 @@ Simplify your app development, grow your user base, and monetize more effectivel
     ss.ios.deployment_target = '10.0'
     ss.osx.deployment_target = '10.13'
     ss.tvos.deployment_target = '12.0'
-    ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 10.20.0'
+    ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 10.21.0'
     ss.dependency 'Firebase/CoreOnly'
   end
 
   s.subspec 'ABTesting' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseABTesting', '~> 10.20.0'
+    ss.dependency 'FirebaseABTesting', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -95,13 +95,13 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'AppDistribution' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.ios.dependency 'FirebaseAppDistribution', '~> 10.20.0-beta'
+    ss.ios.dependency 'FirebaseAppDistribution', '~> 10.21.0-beta'
     ss.ios.deployment_target = '11.0'
   end
 
   s.subspec 'AppCheck' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseAppCheck', '~> 10.20.0'
+    ss.dependency 'FirebaseAppCheck', '~> 10.21.0'
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
     ss.tvos.deployment_target = '12.0'
@@ -110,7 +110,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Auth' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseAuth', '~> 10.20.0'
+    ss.dependency 'FirebaseAuth', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -120,7 +120,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Crashlytics' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseCrashlytics', '~> 10.20.0'
+    ss.dependency 'FirebaseCrashlytics', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -130,7 +130,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Database' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseDatabase', '~> 10.20.0'
+    ss.dependency 'FirebaseDatabase', '~> 10.21.0'
     # Standard platforms PLUS watchOS 7.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -140,13 +140,13 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'DynamicLinks' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.ios.dependency 'FirebaseDynamicLinks', '~> 10.20.0'
+    ss.ios.dependency 'FirebaseDynamicLinks', '~> 10.21.0'
     ss.ios.deployment_target = '11.0'
   end
 
   s.subspec 'Firestore' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseFirestore', '~> 10.20.0'
+    ss.dependency 'FirebaseFirestore', '~> 10.21.0'
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
     ss.tvos.deployment_target = '12.0'
@@ -154,7 +154,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Functions' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseFunctions', '~> 10.20.0'
+    ss.dependency 'FirebaseFunctions', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -164,20 +164,20 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'InAppMessaging' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.ios.dependency 'FirebaseInAppMessaging', '~> 10.20.0-beta'
-    ss.tvos.dependency 'FirebaseInAppMessaging', '~> 10.20.0-beta'
+    ss.ios.dependency 'FirebaseInAppMessaging', '~> 10.21.0-beta'
+    ss.tvos.dependency 'FirebaseInAppMessaging', '~> 10.21.0-beta'
     ss.ios.deployment_target = '11.0'
     ss.tvos.deployment_target = '12.0'
   end
 
   s.subspec 'Installations' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseInstallations', '~> 10.20.0'
+    ss.dependency 'FirebaseInstallations', '~> 10.21.0'
   end
 
   s.subspec 'Messaging' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseMessaging', '~> 10.20.0'
+    ss.dependency 'FirebaseMessaging', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -187,7 +187,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'MLModelDownloader' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseMLModelDownloader', '~> 10.20.0-beta'
+    ss.dependency 'FirebaseMLModelDownloader', '~> 10.21.0-beta'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -197,15 +197,15 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Performance' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.ios.dependency 'FirebasePerformance', '~> 10.20.0'
-    ss.tvos.dependency 'FirebasePerformance', '~> 10.20.0'
+    ss.ios.dependency 'FirebasePerformance', '~> 10.21.0'
+    ss.tvos.dependency 'FirebasePerformance', '~> 10.21.0'
     ss.ios.deployment_target = '11.0'
     ss.tvos.deployment_target = '12.0'
   end
 
   s.subspec 'RemoteConfig' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseRemoteConfig', '~> 10.20.0'
+    ss.dependency 'FirebaseRemoteConfig', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'
@@ -215,7 +215,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
 
   s.subspec 'Storage' do |ss|
     ss.dependency 'Firebase/CoreOnly'
-    ss.dependency 'FirebaseStorage', '~> 10.20.0'
+    ss.dependency 'FirebaseStorage', '~> 10.21.0'
     # Standard platforms PLUS watchOS.
     ss.ios.deployment_target = '11.0'
     ss.osx.deployment_target = '10.13'

+ 1 - 1
FirebaseABTesting.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseABTesting'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase ABTesting'
 
   s.description      = <<-DESC

+ 4 - 4
FirebaseAnalytics.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = 'FirebaseAnalytics'
-    s.version          = '10.20.0'
+    s.version          = '10.21.0'
     s.summary          = 'Firebase Analytics for iOS'
 
     s.description      = <<-DESC
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
     s.authors          = 'Google, Inc.'
 
     s.source           = {
-        :http => 'https://dl.google.com/firebase/ios/analytics/85d9c07fe160aa17/FirebaseAnalytics-10.17.0.tar.gz'
+        :http => 'https://dl.google.com/firebase/ios/analytics/0199e7929b47e2d9/FirebaseAnalytics-10.20.0.tar.gz'
     }
 
     s.cocoapods_version = '>= 1.10.0'
@@ -37,12 +37,12 @@ Pod::Spec.new do |s|
     s.default_subspecs = 'AdIdSupport'
 
     s.subspec 'AdIdSupport' do |ss|
-        ss.dependency 'GoogleAppMeasurement', '10.20.0'
+        ss.dependency 'GoogleAppMeasurement', '10.21.0'
         ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework'
     end
 
     s.subspec 'WithoutAdIdSupport' do |ss|
-        ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '10.20.0'
+        ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '10.21.0'
         ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework'
     end
 

+ 2 - 2
FirebaseAnalyticsOnDeviceConversion.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = 'FirebaseAnalyticsOnDeviceConversion'
-    s.version          = '10.20.0'
+    s.version          = '10.21.0'
     s.summary          = 'On device conversion measurement plugin for FirebaseAnalytics. Not intended for direct use.'
 
     s.description      = <<-DESC
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
 
     s.cocoapods_version = '>= 1.10.2'
 
-    s.dependency 'GoogleAppMeasurementOnDeviceConversion', '10.20.0'
+    s.dependency 'GoogleAppMeasurementOnDeviceConversion', '10.21.0'
 
     s.static_framework = true
 

+ 1 - 1
FirebaseAppCheck.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAppCheck'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase App Check SDK.'
 
   s.description      = <<-DESC

+ 26 - 32
FirebaseAppCheck/Tests/Unit/Swift/AppCheckAPITests.swift

@@ -63,18 +63,16 @@ final class AppCheckAPITests {
     }
 
     // Get token (async/await)
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            try await AppCheck.appCheck().token(forcingRefresh: false)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          try await AppCheck.appCheck().token(forcingRefresh: false)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Set `AppCheckProviderFactory`
     AppCheck.setAppCheckProviderFactory(DummyAppCheckProviderFactory())
@@ -97,18 +95,16 @@ final class AppCheckAPITests {
       }
 
       // Get token (async/await)
-      #if compiler(>=5.5.2) && canImport(_Concurrency)
-        if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-          // async/await is a Swift 5.5+ feature available on iOS 15+
-          Task {
-            do {
-              _ = try await debugProvider.getToken()
-            } catch {
-              // ...
-            }
+      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+        // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+        Task {
+          do {
+            _ = try await debugProvider.getToken()
+          } catch {
+            // ...
           }
         }
-      #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+      }
 
       _ = debugProvider.localDebugToken()
       _ = debugProvider.currentDebugToken()
@@ -170,20 +166,18 @@ final class AppCheckAPITests {
             }
           }
           // Get token (async/await)
-          #if compiler(>=5.5.2) && canImport(_Concurrency)
-            if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-              // async/await is a Swift 5.5+ feature available on iOS 15+
-              Task {
-                do {
-                  _ = try await deviceCheckProvider.getToken()
-                } catch AppCheckErrorCode.unsupported {
-                  // ...
-                } catch {
-                  // ...
-                }
+          if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+            // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+            Task {
+              do {
+                _ = try await deviceCheckProvider.getToken()
+              } catch AppCheckErrorCode.unsupported {
+                // ...
+              } catch {
+                // ...
               }
             }
-          #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+          }
         }
       }
     #endif // !os(watchOS)

+ 1 - 1
FirebaseAppCheckInterop.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAppCheckInterop'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Interfaces that allow other Firebase SDKs to use AppCheck functionality.'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseAppDistribution.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAppDistribution'
-  s.version          = '10.20.0-beta'
+  s.version          = '10.21.0-beta'
   s.summary          = 'App Distribution for Firebase iOS SDK.'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseAuth.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAuth'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Apple platform client for Firebase Authentication'
 
   s.description      = <<-DESC

+ 4 - 0
FirebaseAuth/CHANGELOG.md

@@ -1,3 +1,7 @@
+# 10.21.0
+- [fixed] Fixed multifactor resolver to use the correct Auth instance instead of
+  always the default. (#12265)
+
 # 10.19.0
 - [changed] Deprecate `updateEmail(to email: String)` and `fetchSignInMethods(forEmail email: String)`. (#12081)
 

+ 0 - 1
FirebaseAuth/Tests/SampleSwift/SwiftApiTests/AccountInfoTests.swift

@@ -79,7 +79,6 @@ class AccountInfoTests: TestsBase {
     let auth = Auth.auth()
     do {
       _ = try await auth.createUser(withEmail: kOldUserEmail, password: "password")
-
       XCTFail("Did not get error for recreating a user")
     } catch {
       XCTAssertEqual((error as NSError).code,

+ 1 - 0
FirebaseAuth/Tests/SampleSwift/SwiftApiTests/EmailPasswordTests.swift

@@ -60,6 +60,7 @@ class EmailPasswordTests: TestsBase {
     waitForExpectations(timeout: TestsBase.kExpectationsTimeout)
   }
 
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
   func testSignInExistingUserWithEmailAndPasswordAsync() async throws {
     let auth = Auth.auth()
     try await auth.signIn(withEmail: kExistingEmailToSignIn, password: "password")

+ 0 - 8
FirebaseAuth/Tests/SampleSwift/SwiftApiTests/FacebookTests.swift

@@ -44,7 +44,6 @@ import XCTest
 //    deleteFacebookTestingAccountbyID(facebookAccountID)
 //  }
 //
-//  #if compiler(>=5.5.2) && canImport(_Concurrency)
 //    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 //    func testSignInWithFacebookAsync() async throws {
 //      let auth = Auth.auth()
@@ -60,7 +59,6 @@ import XCTest
 //      try await deleteCurrentUserAsync()
 //      try await deleteFacebookTestingAccountbyIDAsync(facebookAccountID)
 //    }
-//  #endif
 //
 //  func testLinkAnonymousAccountToFacebookAccount() throws {
 //    let auth = Auth.auth()
@@ -90,7 +88,6 @@ import XCTest
 //    deleteFacebookTestingAccountbyID(facebookAccountID)
 //  }
 //
-//  #if compiler(>=5.5.2) && canImport(_Concurrency)
 //    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 //    func testLinkAnonymousAccountToFacebookAccountAsync() async throws {
 //      let auth = Auth.auth()
@@ -111,7 +108,6 @@ import XCTest
 //      try await deleteCurrentUserAsync()
 //      try await deleteFacebookTestingAccountbyIDAsync(facebookAccountID)
 //    }
-//  #endif
 //
 //  /// Creates a Facebook testing account using Facebook Graph API and return a dictionary that
 //  /// constrains "id", "access_token", "login_url", "email" and "password" of the created account.
@@ -151,7 +147,6 @@ import XCTest
 //    return returnValue
 //  }
 //
-//  #if compiler(>=5.5.2) && canImport(_Concurrency)
 //    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 //    /// Creates a Facebook testing account using Facebook Graph API and return a dictionary that
 //    /// constains "id", "access_token", "login_url", "email" and "password" of the created
@@ -174,7 +169,6 @@ import XCTest
 //      }
 //      return returnValue
 //    }
-//  #endif
 //
 //  // ** Delete a Facebook testing account by account Id using Facebook Graph API. */
 //  func deleteFacebookTestingAccountbyID(_ accountID: String) {
@@ -195,7 +189,6 @@ import XCTest
 //    waitForExpectations(timeout: TestsBase.kExpectationsTimeout)
 //  }
 //
-//  #if compiler(>=5.5.2) && canImport(_Concurrency)
 //    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
 //    // ** Delete a Facebook testing account by account Id using Facebook Graph API. */
 //    func deleteFacebookTestingAccountbyIDAsync(_ accountID: String) async throws {
@@ -208,5 +201,4 @@ import XCTest
 //      fetcher.setRequestValue("text/plain", forHTTPHeaderField: "Content-Type")
 //      try await fetcher.beginFetch()
 //    }
-//  #endif
 // }

+ 1 - 1
FirebaseAuthInterop.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseAuthInterop'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Interfaces that allow other Firebase SDKs to use Auth functionality.'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseCore.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseCore'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Core'
 
   s.description      = <<-DESC

+ 6 - 0
FirebaseCore/CHANGELOG.md

@@ -1,3 +1,9 @@
+# Firebase 10.20.0
+- The following change only applies to those using a binary distribution of
+  a Firebase SDK(s): In preparation for supporting Privacy Manifests, each
+  platform framework directory within a static xcframewok no longer contains
+  an `Info.plist` file (#12243).
+
 # Firebase 10.14.0
 - For developers building for visionOS, Xcode 15 beta 6 or later is required.
 

+ 5 - 7
FirebaseCore/Tests/SwiftUnit/CoreAPITests.swift

@@ -51,14 +51,12 @@ final class CoreAPITests {
         // ...
       }
 
-      #if compiler(>=5.5.2) && canImport(_Concurrency)
-        if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-          // async/await is a Swift 5.5+ feature available on iOS 15+
-          Task {
-            await app.delete()
-          }
+      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+        // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+        Task {
+          await app.delete()
         }
-      #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+      }
     }
 
     // Properties

+ 1 - 1
FirebaseCoreExtension.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = 'FirebaseCoreExtension'
-    s.version          = '10.20.0'
+    s.version          = '10.21.0'
     s.summary          = 'Extended FirebaseCore APIs for Firebase product SDKs'
 
     s.description      = <<-DESC

+ 1 - 1
FirebaseCoreInternal.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseCoreInternal'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'APIs for internal FirebaseCore usage.'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseCrashlytics.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseCrashlytics'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Best and lightest-weight crash reporting for mobile, desktop and tvOS.'
   s.description      = 'Firebase Crashlytics helps you track, prioritize, and fix stability issues that erode app quality.'
   s.homepage         = 'https://firebase.google.com/'

+ 1 - 1
FirebaseDatabase.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseDatabase'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Realtime Database'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseDatabase/CHANGELOG.md

@@ -84,7 +84,7 @@
   high precision to be stored correctly in our persistence layer. (#4108)
 
 # 6.1.1
-- [fixed] Fixed an iOS 13 crash that occured in our WebSocket error handling. (#3950)
+- [fixed] Fixed an iOS 13 crash that occurred in our WebSocket error handling. (#3950)
 
 # 6.1.0
 - [fixed] Fix Catalyst Build issue. (#3512)

+ 139 - 169
FirebaseDatabase/Tests/Unit/Swift/DatabaseAPITests.swift

@@ -112,18 +112,16 @@ final class DatabaseAPITests {
       let /* dataSnapshot */ _: DataSnapshot? = dataSnapshot
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let /* dataSnapshot */ _: DataSnapshot = try await DatabaseQuery().getData()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let /* dataSnapshot */ _: DataSnapshot = try await DatabaseQuery().getData()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Observe Single Event
 
@@ -138,16 +136,14 @@ final class DatabaseAPITests {
       let /* optionalString */ _: String? = optionalString
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          // observeSingleEvent(of eventType:)
-          let _: (DataSnapshot, String?) = await DatabaseQuery()
-            .observeSingleEventAndPreviousSiblingKey(of: dataEventType)
-        }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        // observeSingleEvent(of eventType:)
+        let _: (DataSnapshot, String?) = await DatabaseQuery()
+          .observeSingleEventAndPreviousSiblingKey(of: dataEventType)
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // observeSingleEvent(of eventType:with block:withCancel cancelBlock:)
     databaseQuery.observeSingleEvent(of: dataEventType) { dataSnapshot in
@@ -210,19 +206,17 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // setValue(_ value:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference().setValue(value)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // setValue(_ value:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference().setValue(value)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     databaseReference.setValue(value, andPriority: priority)
 
@@ -232,20 +226,18 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // setValue(_ value:andPriority priority:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .setValue(value, andPriority: priority)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // setValue(_ value:andPriority priority:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .setValue(value, andPriority: priority)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Remove value
     databaseReference.removeValue()
@@ -256,18 +248,16 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let /* ref */ _: DatabaseReference = try await DatabaseReference().removeValue()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let /* ref */ _: DatabaseReference = try await DatabaseReference().removeValue()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Set priority
     databaseReference.setPriority(priority)
@@ -278,19 +268,17 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // setPriority(_ priority:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference().setPriority(priority)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // setPriority(_ priority:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference().setPriority(priority)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Update child values
     databaseReference.updateChildValues(values)
@@ -301,20 +289,18 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // updateChildValues(_ values:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .updateChildValues(values)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // updateChildValues(_ values:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .updateChildValues(values)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Observe for data
 
@@ -357,16 +343,14 @@ final class DatabaseAPITests {
       let /* optionalString */ _: String? = optionalString
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          // observeSingleEvent(of eventType:)
-          let _: (DataSnapshot, String?) = await DatabaseReference()
-            .observeSingleEventAndPreviousSiblingKey(of: dataEventType)
-        }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        // observeSingleEvent(of eventType:)
+        let _: (DataSnapshot, String?) = await DatabaseReference()
+          .observeSingleEventAndPreviousSiblingKey(of: dataEventType)
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // observeSingleEvent(of eventType:with block:withCancel cancelBlock:)
     databaseReference.observeSingleEvent(of: dataEventType) { dataSnapshot in
@@ -391,18 +375,16 @@ final class DatabaseAPITests {
       let /* dataSnapshot */ _: DataSnapshot? = dataSnapshot
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let /* dataSnapshot */ _: DataSnapshot = try await DatabaseReference().getData()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let /* dataSnapshot */ _: DataSnapshot = try await DatabaseReference().getData()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Remove Observers
     databaseReference.removeObserver(withHandle: databaseHandle)
@@ -435,20 +417,18 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // onDisconnectSetValue(_ value:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .onDisconnectSetValue(value)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // onDisconnectSetValue(_ value:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .onDisconnectSetValue(value)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     databaseReference.onDisconnectSetValue(value, andPriority: priorityAny)
 
@@ -459,22 +439,20 @@ final class DatabaseAPITests {
         let /* databaseReference */ _: DatabaseReference = databaseReference
       }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // onDisconnectSetValue(_ value:andPriority priority:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference().onDisconnectSetValue(
-              value,
-              andPriority: priority
-            )
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // onDisconnectSetValue(_ value:andPriority priority:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference().onDisconnectSetValue(
+            value,
+            andPriority: priority
+          )
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // onDisconnectRemoveValue
     databaseReference.onDisconnectRemoveValue()
@@ -485,19 +463,17 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .onDisconnectRemoveValue()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .onDisconnectRemoveValue()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // onDisconnectUpdateChildValues
     databaseReference.onDisconnectUpdateChildValues(values)
@@ -508,20 +484,18 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // onDisconnectUpdateChildValues(_ values:)
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .onDisconnectUpdateChildValues(values)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // onDisconnectUpdateChildValues(_ values:)
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .onDisconnectUpdateChildValues(values)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // cancelDisconnectOperations
     databaseReference.cancelDisconnectOperations()
@@ -532,19 +506,17 @@ final class DatabaseAPITests {
       let /* databaseReference */ _: DatabaseReference = databaseReference
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let /* ref */ _: DatabaseReference = try await DatabaseReference()
-              .cancelDisconnectOperations()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let /* ref */ _: DatabaseReference = try await DatabaseReference()
+            .cancelDisconnectOperations()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // runTransactionBlock
 
@@ -564,23 +536,21 @@ final class DatabaseAPITests {
       let /* optionalDataSnapshot */ _: DataSnapshot? = optionalDataSnapshot
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            // runTransactionBlock(_ block:)
-            let _: (Bool, DataSnapshot) = try await DatabaseReference()
-              .runTransactionBlock { mutableData in
-                let /* mutableData */ _: MutableData = mutableData
-                return TransactionResult()
-              }
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          // runTransactionBlock(_ block:)
+          let _: (Bool, DataSnapshot) = try await DatabaseReference()
+            .runTransactionBlock { mutableData in
+              let /* mutableData */ _: MutableData = mutableData
+              return TransactionResult()
+            }
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // runTransactionBlock(_ block:andCompletionBlock completionBlock:withLocalEvents localEvents:)
     databaseReference.runTransactionBlock({ mutableData in

+ 1 - 1
FirebaseDynamicLinks.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseDynamicLinks'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Dynamic Links'
 
   s.description      = <<-DESC

+ 5 - 0
FirebaseDynamicLinks/README.md

@@ -1,5 +1,10 @@
 # Firebase Dynamic Links SDK for iOS
 
+> [!IMPORTANT]
+> Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
+>
+> Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
+
 Firebase Dynamic Links are universal deep links that persist across app installs.
 For more info, see the [Firebase website](https://firebase.google.com/products/dynamic-links).
 

+ 1 - 1
FirebaseFirestore.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseFirestore'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Google Cloud Firestore'
   s.description      = <<-DESC
 Google Cloud Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development.

+ 1 - 1
FirebaseFirestoreInternal.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseFirestoreInternal'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Google Cloud Firestore'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseFunctions.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseFunctions'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Cloud Functions for Firebase'
 
   s.description      = <<-DESC

+ 49 - 51
FirebaseFunctions/Sources/Callable+Codable.swift

@@ -109,56 +109,54 @@ public struct Callable<Request: Encodable, Response: Decodable> {
     call(data, completion: completion)
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    /// Executes this Callable HTTPS trigger asynchronously.
-    ///
-    /// The data passed into the trigger must be of the generic `Request` type:
-    ///
-    /// The request to the Cloud Functions backend made by this method automatically includes a
-    /// FCM token to identify the app instance. If a user is logged in with Firebase
-    /// Auth, an auth ID token for the user is also automatically included.
-    ///
-    /// Firebase Cloud Messaging sends data to the Firebase backend periodically to collect
-    /// information
-    /// regarding the app instance. To stop this, see `Messaging.deleteData()`. It
-    /// resumes with a new FCM Token the next time you call this method.
-    ///
-    /// - Parameter data: The `Request` representing the data to pass to the trigger.
-    ///
-    /// - Throws: An error if any value throws an error during encoding or decoding.
-    /// - Throws: An error if the callable fails to complete
-    ///
-    /// - Returns: The decoded `Response` value
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    public func call(_ data: Request) async throws -> Response {
-      let encoded = try encoder.encode(data)
-      let result = try await callable.call(encoded)
-      return try decoder.decode(Response.self, from: result.data)
-    }
+  /// Executes this Callable HTTPS trigger asynchronously.
+  ///
+  /// The data passed into the trigger must be of the generic `Request` type:
+  ///
+  /// The request to the Cloud Functions backend made by this method automatically includes a
+  /// FCM token to identify the app instance. If a user is logged in with Firebase
+  /// Auth, an auth ID token for the user is also automatically included.
+  ///
+  /// Firebase Cloud Messaging sends data to the Firebase backend periodically to collect
+  /// information
+  /// regarding the app instance. To stop this, see `Messaging.deleteData()`. It
+  /// resumes with a new FCM Token the next time you call this method.
+  ///
+  /// - Parameter data: The `Request` representing the data to pass to the trigger.
+  ///
+  /// - Throws: An error if any value throws an error during encoding or decoding.
+  /// - Throws: An error if the callable fails to complete
+  ///
+  /// - Returns: The decoded `Response` value
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  public func call(_ data: Request) async throws -> Response {
+    let encoded = try encoder.encode(data)
+    let result = try await callable.call(encoded)
+    return try decoder.decode(Response.self, from: result.data)
+  }
 
-    /// Creates a directly callable function.
-    ///
-    /// This allows users to call a HTTPS Callable Function like a normal Swift function:
-    /// ```swift
-    ///     let greeter = functions.httpsCallable("greeter",
-    ///                                           requestType: GreetingRequest.self,
-    ///                                           responseType: GreetingResponse.self)
-    ///     let result = try await greeter(data)
-    ///     print(result.greeting)
-    /// ```
-    /// You can also call a HTTPS Callable function using the following syntax:
-    /// ```swift
-    ///     let greeter: Callable<GreetingRequest, GreetingResponse> =
-    /// functions.httpsCallable("greeter")
-    ///     let result = try await greeter(data)
-    ///     print(result.greeting)
-    /// ```
-    /// - Parameters:
-    ///   - data: Parameters to pass to the trigger.
-    /// - Returns: The decoded `Response` value
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    public func callAsFunction(_ data: Request) async throws -> Response {
-      return try await call(data)
-    }
-  #endif
+  /// Creates a directly callable function.
+  ///
+  /// This allows users to call a HTTPS Callable Function like a normal Swift function:
+  /// ```swift
+  ///     let greeter = functions.httpsCallable("greeter",
+  ///                                           requestType: GreetingRequest.self,
+  ///                                           responseType: GreetingResponse.self)
+  ///     let result = try await greeter(data)
+  ///     print(result.greeting)
+  /// ```
+  /// You can also call a HTTPS Callable function using the following syntax:
+  /// ```swift
+  ///     let greeter: Callable<GreetingRequest, GreetingResponse> =
+  /// functions.httpsCallable("greeter")
+  ///     let result = try await greeter(data)
+  ///     print(result.greeting)
+  /// ```
+  /// - Parameters:
+  ///   - data: Parameters to pass to the trigger.
+  /// - Returns: The decoded `Response` value
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  public func callAsFunction(_ data: Request) async throws -> Response {
+    return try await call(data)
+  }
 }

+ 25 - 27
FirebaseFunctions/Sources/HTTPSCallable.swift

@@ -139,34 +139,32 @@ open class HTTPSCallable: NSObject {
     call(nil, completion: completion)
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    /**
-     * Executes this Callable HTTPS trigger asynchronously.
-     *
-     * The request to the Cloud Functions backend made by this method automatically includes a
-     * FCM token to identify the app instance. If a user is logged in with Firebase
-     * Auth, an auth ID token for the user is also automatically included.
-     *
-     * Firebase Cloud Messaging sends data to the Firebase backend periodically to collect information
-     * regarding the app instance. To stop this, see `Messaging.deleteData()`. It
-     * resumes with a new FCM Token the next time you call this method.
-     *
-     * - Parameter data Parameters to pass to the trigger.
-     * - Throws: An error if the Cloud Functions invocation failed.
-     * - Returns: The result of the call.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    open func call(_ data: Any? = nil) async throws -> HTTPSCallableResult {
-      return try await withCheckedThrowingContinuation { continuation in
-        // TODO(bonus): Use task to handle and cancellation.
-        self.call(data) { callableResult, error in
-          if let callableResult = callableResult {
-            continuation.resume(returning: callableResult)
-          } else {
-            continuation.resume(throwing: error!)
-          }
+  /**
+   * Executes this Callable HTTPS trigger asynchronously.
+   *
+   * The request to the Cloud Functions backend made by this method automatically includes a
+   * FCM token to identify the app instance. If a user is logged in with Firebase
+   * Auth, an auth ID token for the user is also automatically included.
+   *
+   * Firebase Cloud Messaging sends data to the Firebase backend periodically to collect information
+   * regarding the app instance. To stop this, see `Messaging.deleteData()`. It
+   * resumes with a new FCM Token the next time you call this method.
+   *
+   * - Parameter data Parameters to pass to the trigger.
+   * - Throws: An error if the Cloud Functions invocation failed.
+   * - Returns: The result of the call.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  open func call(_ data: Any? = nil) async throws -> HTTPSCallableResult {
+    return try await withCheckedThrowingContinuation { continuation in
+      // TODO(bonus): Use task to handle and cancellation.
+      self.call(data) { callableResult, error in
+        if let callableResult = callableResult {
+          continuation.resume(returning: callableResult)
+        } else {
+          continuation.resume(throwing: error!)
         }
       }
     }
-  #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+  }
 }

+ 310 - 339
FirebaseFunctions/Tests/Integration/IntegrationTests.swift

@@ -118,36 +118,34 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testDataAsync() async throws {
-      let data = DataTestRequest(
-        bool: true,
-        int: 2,
-        long: 9_876_543_210,
-        string: "four",
-        array: [5, 6],
-        null: nil
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testDataAsync() async throws {
+    let data = DataTestRequest(
+      bool: true,
+      int: 2,
+      long: 9_876_543_210,
+      string: "four",
+      array: [5, 6],
+      null: nil
+    )
 
-      let byName = functions.httpsCallable("dataTest",
-                                           requestAs: DataTestRequest.self,
-                                           responseAs: DataTestResponse.self)
-      let byUrl = functions.httpsCallable(emulatorURL("dataTest"),
-                                          requestAs: DataTestRequest.self,
-                                          responseAs: DataTestResponse.self)
-
-      for function in [byName, byUrl] {
-        let response = try await function.call(data)
-        let expected = DataTestResponse(
-          message: "stub response",
-          long: 420,
-          code: 42
-        )
-        XCTAssertEqual(response, expected)
-      }
+    let byName = functions.httpsCallable("dataTest",
+                                         requestAs: DataTestRequest.self,
+                                         responseAs: DataTestResponse.self)
+    let byUrl = functions.httpsCallable(emulatorURL("dataTest"),
+                                        requestAs: DataTestRequest.self,
+                                        responseAs: DataTestResponse.self)
+
+    for function in [byName, byUrl] {
+      let response = try await function.call(data)
+      let expected = DataTestResponse(
+        message: "stub response",
+        long: 420,
+        code: 42
+      )
+      XCTAssertEqual(response, expected)
     }
-  #endif
+  }
 
   func testScalar() {
     let byName = functions.httpsCallable(
@@ -175,37 +173,34 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testScalarAsync() async throws {
-      let byName = functions.httpsCallable(
-        "scalarTest",
-        requestAs: Int16.self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("scalarTest"),
-        requestAs: Int16.self,
-        responseAs: Int.self
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testScalarAsync() async throws {
+    let byName = functions.httpsCallable(
+      "scalarTest",
+      requestAs: Int16.self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("scalarTest"),
+      requestAs: Int16.self,
+      responseAs: Int.self
+    )
 
-      for function in [byName, byURL] {
-        let result = try await function.call(17)
-        XCTAssertEqual(result, 76)
-      }
+    for function in [byName, byURL] {
+      let result = try await function.call(17)
+      XCTAssertEqual(result, 76)
     }
+  }
 
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testScalarAsyncAlternateSignature() async throws {
-      let byName: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
-      let byURL: Callable<Int16, Int> = functions.httpsCallable(emulatorURL("scalarTest"))
-      for function in [byName, byURL] {
-        let result = try await function.call(17)
-        XCTAssertEqual(result, 76)
-      }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testScalarAsyncAlternateSignature() async throws {
+    let byName: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
+    let byURL: Callable<Int16, Int> = functions.httpsCallable(emulatorURL("scalarTest"))
+    for function in [byName, byURL] {
+      let result = try await function.call(17)
+      XCTAssertEqual(result, 76)
     }
-
-  #endif
+  }
 
   func testToken() {
     // Recreate functions with a token.
@@ -245,37 +240,35 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testTokenAsync() async throws {
-      // Recreate functions with a token.
-      let functions = Functions(
-        projectID: "functions-integration-test",
-        region: "us-central1",
-        customDomain: nil,
-        auth: AuthTokenProvider(token: "token"),
-        messaging: MessagingTokenProvider(),
-        appCheck: nil
-      )
-      functions.useEmulator(withHost: "localhost", port: 5005)
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testTokenAsync() async throws {
+    // Recreate functions with a token.
+    let functions = Functions(
+      projectID: "functions-integration-test",
+      region: "us-central1",
+      customDomain: nil,
+      auth: AuthTokenProvider(token: "token"),
+      messaging: MessagingTokenProvider(),
+      appCheck: nil
+    )
+    functions.useEmulator(withHost: "localhost", port: 5005)
 
-      let byName = functions.httpsCallable(
-        "tokenTest",
-        requestAs: [String: Int].self,
-        responseAs: [String: Int].self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("tokenTest"),
-        requestAs: [String: Int].self,
-        responseAs: [String: Int].self
-      )
+    let byName = functions.httpsCallable(
+      "tokenTest",
+      requestAs: [String: Int].self,
+      responseAs: [String: Int].self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("tokenTest"),
+      requestAs: [String: Int].self,
+      responseAs: [String: Int].self
+    )
 
-      for function in [byName, byURL] {
-        let data = try await function.call([:])
-        XCTAssertEqual(data, [:])
-      }
+    for function in [byName, byURL] {
+      let data = try await function.call([:])
+      XCTAssertEqual(data, [:])
     }
-  #endif
+  }
 
   func testFCMToken() {
     let byName = functions.httpsCallable(
@@ -303,26 +296,24 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testFCMTokenAsync() async throws {
-      let byName = functions.httpsCallable(
-        "FCMTokenTest",
-        requestAs: [String: Int].self,
-        responseAs: [String: Int].self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("FCMTokenTest"),
-        requestAs: [String: Int].self,
-        responseAs: [String: Int].self
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testFCMTokenAsync() async throws {
+    let byName = functions.httpsCallable(
+      "FCMTokenTest",
+      requestAs: [String: Int].self,
+      responseAs: [String: Int].self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("FCMTokenTest"),
+      requestAs: [String: Int].self,
+      responseAs: [String: Int].self
+    )
 
-      for function in [byName, byURL] {
-        let data = try await function.call([:])
-        XCTAssertEqual(data, [:])
-      }
+    for function in [byName, byURL] {
+      let data = try await function.call([:])
+      XCTAssertEqual(data, [:])
     }
-  #endif
+  }
 
   func testNull() {
     let byName = functions.httpsCallable(
@@ -350,26 +341,24 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testNullAsync() async throws {
-      let byName = functions.httpsCallable(
-        "nullTest",
-        requestAs: Int?.self,
-        responseAs: Int?.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("nullTest"),
-        requestAs: Int?.self,
-        responseAs: Int?.self
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testNullAsync() async throws {
+    let byName = functions.httpsCallable(
+      "nullTest",
+      requestAs: Int?.self,
+      responseAs: Int?.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("nullTest"),
+      requestAs: Int?.self,
+      responseAs: Int?.self
+    )
 
-      for function in [byName, byURL] {
-        let data = try await function.call(nil)
-        XCTAssertEqual(data, nil)
-      }
+    for function in [byName, byURL] {
+      let data = try await function.call(nil)
+      XCTAssertEqual(data, nil)
     }
-  #endif
+  }
 
   func testMissingResult() {
     let byName = functions.httpsCallable(
@@ -401,31 +390,29 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testMissingResultAsync() async {
-      let byName = functions.httpsCallable(
-        "missingResultTest",
-        requestAs: Int?.self,
-        responseAs: Int?.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("missingResultTest"),
-        requestAs: Int?.self,
-        responseAs: Int?.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call(nil)
-          XCTFail("Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
-          XCTAssertEqual("Response is missing data field.", error.localizedDescription)
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testMissingResultAsync() async {
+    let byName = functions.httpsCallable(
+      "missingResultTest",
+      requestAs: Int?.self,
+      responseAs: Int?.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("missingResultTest"),
+      requestAs: Int?.self,
+      responseAs: Int?.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call(nil)
+        XCTFail("Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
+        XCTAssertEqual("Response is missing data field.", error.localizedDescription)
       }
     }
-  #endif
+  }
 
   func testUnhandledError() {
     let byName = functions.httpsCallable(
@@ -457,31 +444,29 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testUnhandledErrorAsync() async {
-      let byName = functions.httpsCallable(
-        "unhandledErrorTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        "unhandledErrorTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTFail("Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
-          XCTAssertEqual("INTERNAL", error.localizedDescription)
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testUnhandledErrorAsync() async {
+    let byName = functions.httpsCallable(
+      "unhandledErrorTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      "unhandledErrorTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTFail("Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
+        XCTAssertEqual("INTERNAL", error.localizedDescription)
       }
     }
-  #endif
+  }
 
   func testUnknownError() {
     let byName = functions.httpsCallable(
@@ -512,31 +497,29 @@ class IntegrationTests: XCTestCase {
     waitForExpectations(timeout: 5)
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testUnknownErrorAsync() async {
-      let byName = functions.httpsCallable(
-        "unknownErrorTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("unknownErrorTest"),
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTAssertFalse(true, "Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
-          XCTAssertEqual("INTERNAL", error.localizedDescription)
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testUnknownErrorAsync() async {
+    let byName = functions.httpsCallable(
+      "unknownErrorTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("unknownErrorTest"),
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTAssertFalse(true, "Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
+        XCTAssertEqual("INTERNAL", error.localizedDescription)
       }
     }
-  #endif
+  }
 
   func testExplicitError() {
     let byName = functions.httpsCallable(
@@ -569,33 +552,31 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testExplicitErrorAsync() async {
-      let byName = functions.httpsCallable(
-        "explicitErrorTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("explicitErrorTest"),
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTAssertFalse(true, "Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
-          XCTAssertEqual("explicit nope", error.localizedDescription)
-          XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
-                         error.userInfo["details"] as? [String: Int32])
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testExplicitErrorAsync() async {
+    let byName = functions.httpsCallable(
+      "explicitErrorTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("explicitErrorTest"),
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTAssertFalse(true, "Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
+        XCTAssertEqual("explicit nope", error.localizedDescription)
+        XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
+                       error.userInfo["details"] as? [String: Int32])
       }
     }
-  #endif
+  }
 
   func testHttpError() {
     let byName = functions.httpsCallable(
@@ -626,30 +607,28 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testHttpErrorAsync() async {
-      let byName = functions.httpsCallable(
-        "httpErrorTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("httpErrorTest"),
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTAssertFalse(true, "Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testHttpErrorAsync() async {
+    let byName = functions.httpsCallable(
+      "httpErrorTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("httpErrorTest"),
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTAssertFalse(true, "Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
       }
     }
-  #endif
+  }
 
   func testThrowError() {
     let byName = functions.httpsCallable(
@@ -681,31 +660,29 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testThrowErrorAsync() async {
-      let byName = functions.httpsCallable(
-        "throwTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      let byURL = functions.httpsCallable(
-        emulatorURL("throwTest"),
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTAssertFalse(true, "Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
-          XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testThrowErrorAsync() async {
+    let byName = functions.httpsCallable(
+      "throwTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    let byURL = functions.httpsCallable(
+      emulatorURL("throwTest"),
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTAssertFalse(true, "Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
+        XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
       }
     }
-  #endif
+  }
 
   func testTimeout() {
     let byName = functions.httpsCallable(
@@ -738,34 +715,32 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testTimeoutAsync() async {
-      var byName = functions.httpsCallable(
-        "timeoutTest",
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      byName.timeoutInterval = 0.05
-      var byURL = functions.httpsCallable(
-        emulatorURL("timeoutTest"),
-        requestAs: [Int].self,
-        responseAs: Int.self
-      )
-      byURL.timeoutInterval = 0.05
-      for function in [byName, byURL] {
-        do {
-          _ = try await function.call([])
-          XCTAssertFalse(true, "Failed to throw error for missing result")
-        } catch {
-          let error = error as NSError
-          XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
-          XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
-          XCTAssertNil(error.userInfo["details"])
-        }
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testTimeoutAsync() async {
+    var byName = functions.httpsCallable(
+      "timeoutTest",
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    byName.timeoutInterval = 0.05
+    var byURL = functions.httpsCallable(
+      emulatorURL("timeoutTest"),
+      requestAs: [Int].self,
+      responseAs: Int.self
+    )
+    byURL.timeoutInterval = 0.05
+    for function in [byName, byURL] {
+      do {
+        _ = try await function.call([])
+        XCTAssertFalse(true, "Failed to throw error for missing result")
+      } catch {
+        let error = error as NSError
+        XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
+        XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
+        XCTAssertNil(error.userInfo["details"])
       }
     }
-  #endif
+  }
 
   func testCallAsFunction() {
     let data = DataTestRequest(
@@ -802,37 +777,35 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testCallAsFunctionAsync() async throws {
-      let data = DataTestRequest(
-        bool: true,
-        int: 2,
-        long: 9_876_543_210,
-        string: "four",
-        array: [5, 6],
-        null: nil
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testCallAsFunctionAsync() async throws {
+    let data = DataTestRequest(
+      bool: true,
+      int: 2,
+      long: 9_876_543_210,
+      string: "four",
+      array: [5, 6],
+      null: nil
+    )
 
-      let byName = functions.httpsCallable("dataTest",
-                                           requestAs: DataTestRequest.self,
-                                           responseAs: DataTestResponse.self)
-
-      let byURL = functions.httpsCallable(emulatorURL("dataTest"),
-                                          requestAs: DataTestRequest.self,
-                                          responseAs: DataTestResponse.self)
-
-      for function in [byName, byURL] {
-        let response = try await function(data)
-        let expected = DataTestResponse(
-          message: "stub response",
-          long: 420,
-          code: 42
-        )
-        XCTAssertEqual(response, expected)
-      }
+    let byName = functions.httpsCallable("dataTest",
+                                         requestAs: DataTestRequest.self,
+                                         responseAs: DataTestResponse.self)
+
+    let byURL = functions.httpsCallable(emulatorURL("dataTest"),
+                                        requestAs: DataTestRequest.self,
+                                        responseAs: DataTestResponse.self)
+
+    for function in [byName, byURL] {
+      let response = try await function(data)
+      let expected = DataTestResponse(
+        message: "stub response",
+        long: 420,
+        code: 42
+      )
+      XCTAssertEqual(response, expected)
     }
-  #endif
+  }
 
   func testInferredTypes() {
     let data = DataTestRequest(
@@ -867,34 +840,32 @@ class IntegrationTests: XCTestCase {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func testInferredTyesAsync() async throws {
-      let data = DataTestRequest(
-        bool: true,
-        int: 2,
-        long: 9_876_543_210,
-        string: "four",
-        array: [5, 6],
-        null: nil
-      )
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func testInferredTyesAsync() async throws {
+    let data = DataTestRequest(
+      bool: true,
+      int: 2,
+      long: 9_876_543_210,
+      string: "four",
+      array: [5, 6],
+      null: nil
+    )
 
-      let byName: Callable<DataTestRequest, DataTestResponse> = functions
-        .httpsCallable("dataTest")
-      let byURL: Callable<DataTestRequest, DataTestResponse> = functions
-        .httpsCallable(emulatorURL("dataTest"))
-
-      for function in [byName, byURL] {
-        let response = try await function(data)
-        let expected = DataTestResponse(
-          message: "stub response",
-          long: 420,
-          code: 42
-        )
-        XCTAssertEqual(response, expected)
-      }
+    let byName: Callable<DataTestRequest, DataTestResponse> = functions
+      .httpsCallable("dataTest")
+    let byURL: Callable<DataTestRequest, DataTestResponse> = functions
+      .httpsCallable(emulatorURL("dataTest"))
+
+    for function in [byName, byURL] {
+      let response = try await function(data)
+      let expected = DataTestResponse(
+        message: "stub response",
+        long: 420,
+        code: 42
+      )
+      XCTAssertEqual(response, expected)
     }
-  #endif
+  }
 }
 
 private class AuthTokenProvider: AuthInterop {

+ 18 - 22
FirebaseFunctions/Tests/Unit/FunctionsAPITests.swift

@@ -85,19 +85,17 @@ final class FunctionsAPITests: XCTestCase {
       }
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let result = try await callableRef.call(data)
-            _ = result.data
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let result = try await callableRef.call(data)
+          _ = result.data
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     callableRef.call { result, error in
       if let result = result {
@@ -107,19 +105,17 @@ final class FunctionsAPITests: XCTestCase {
       }
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            let result = try await callableRef.call()
-            _ = result.data
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          let result = try await callableRef.call()
+          _ = result.data
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // MARK: - FunctionsErrorCode
 

+ 1 - 1
FirebaseInAppMessaging.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseInAppMessaging'
-  s.version          = '10.20.0-beta'
+  s.version          = '10.21.0-beta'
   s.summary          = 'Firebase In-App Messaging for iOS'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseInstallations.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseInstallations'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Installations'
 
   s.description      = <<-DESC

+ 26 - 32
FirebaseInstallations/Source/Tests/Unit/Swift/InstallationsAPITests.swift

@@ -51,18 +51,16 @@ final class InstallationsAPITests {
       }
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            try await Installations.installations().installationID()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          try await Installations.installations().installationID()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Retrieves an installation auth token
     Installations.installations().authToken { result, error in
@@ -73,18 +71,16 @@ final class InstallationsAPITests {
       }
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            _ = try await Installations.installations().authToken()
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          _ = try await Installations.installations().authToken()
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Retrieves an installation auth token with forcing refresh parameter
     Installations.installations().authTokenForcingRefresh(true) { result, error in
@@ -95,18 +91,16 @@ final class InstallationsAPITests {
       }
     }
 
-    #if compiler(>=5.5.2) && canImport(_Concurrency)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
-        Task {
-          do {
-            _ = try await Installations.installations().authTokenForcingRefresh(true)
-          } catch {
-            // ...
-          }
+    if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
+      Task {
+        do {
+          _ = try await Installations.installations().authTokenForcingRefresh(true)
+        } catch {
+          // ...
         }
       }
-    #endif // compiler(>=5.5.2) && canImport(_Concurrency)
+    }
 
     // Delete installation data
     Installations.installations().delete { error in
@@ -116,8 +110,8 @@ final class InstallationsAPITests {
     }
 
     #if swift(>=5.5)
-      if #available(iOS 13.0, macOS 11.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
-        // async/await is a Swift 5.5+ feature available on iOS 15+
+      if #available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 7.0, *) {
+        // async/await is a Swift Concurrency feature available on iOS 13+ and macOS 10.15+
         Task {
           do {
             _ = try await Installations.installations().delete()

+ 1 - 1
FirebaseMLModelDownloader.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseMLModelDownloader'
-  s.version          = '10.20.0-beta'
+  s.version          = '10.21.0-beta'
   s.summary          = 'Firebase ML Model Downloader'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseMessaging.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseMessaging'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Messaging'
 
   s.description      = <<-DESC

+ 4 - 0
FirebaseMessaging/CHANGELOG.md

@@ -1,3 +1,7 @@
+# 10.20.0
+- [fixed] Fix 10.19.0 regression where the FCM registration token was nil at first app start
+  after update from 10.19.0 or earlier. (#12245)
+
 # 10.19.0
 - [changed] Adopt NSSecureCoding for internal classes. (#12075)
 

+ 3 - 0
FirebaseMessaging/Sources/Token/FIRMessagingTokenInfo.h

@@ -50,6 +50,9 @@ NS_ASSUME_NONNULL_BEGIN
 /// the cacheTime would be updated.
 @property(nonatomic, copy, nullable) NSDate *cacheTime;
 
+/// Indicates the info was stored on the keychain by version 10.18.0 or earlier.
+@property(nonatomic, readonly) BOOL needsMigration;
+
 /**
  *  Initializes a FIRMessagingTokenInfo object with the required parameters. These
  *  parameters represent all the relevant associated data with a token.

+ 21 - 1
FirebaseMessaging/Sources/Token/FIRMessagingTokenInfo.m

@@ -140,6 +140,7 @@ static const NSTimeInterval kDefaultFetchTokenInterval = 7 * 24 * 60 * 60;  // 7
 }
 
 - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
+  BOOL needsMigration = NO;
   // These value cannot be nil
 
   id authorizedEntity = [aDecoder decodeObjectForKey:kFIRInstanceIDAuthorizedEntityKey];
@@ -172,7 +173,25 @@ static const NSTimeInterval kDefaultFetchTokenInterval = 7 * 24 * 60 * 60;  // 7
   FIRMessagingAPNSInfo *rawAPNSInfo = [aDecoder decodeObjectOfClasses:classes
                                                                forKey:kFIRInstanceIDAPNSInfoKey];
   if (rawAPNSInfo && ![rawAPNSInfo isKindOfClass:[FIRMessagingAPNSInfo class]]) {
-    return nil;
+    // If the decoder fails to decode a FIRMessagingAPNSInfo, check if this was archived by a
+    // FirebaseMessaging 10.18.0 or earlier.
+    // TODO(#12246) This block may be replaced with `rawAPNSInfo = nil` once we're confident all
+    // users have upgraded to at least 10.19.0. Perhaps, after privacy manifests have been required
+    // for awhile?
+    @try {
+      [NSKeyedUnarchiver setClass:[FIRMessagingAPNSInfo class]
+                     forClassName:@"FIRInstanceIDAPNSInfo"];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+      rawAPNSInfo = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)rawAPNSInfo];
+      needsMigration = YES;
+#pragma clang diagnostic pop
+    } @catch (NSException *exception) {
+      FIRMessagingLoggerInfo(kFIRMessagingMessageCodeTokenInfoBadAPNSInfo,
+                             @"Could not parse raw APNS Info while parsing archived token info.");
+      rawAPNSInfo = nil;
+    } @finally {
+    }
   }
 
   id cacheTime = [aDecoder decodeObjectForKey:kFIRInstanceIDCacheTimeKey];
@@ -189,6 +208,7 @@ static const NSTimeInterval kDefaultFetchTokenInterval = 7 * 24 * 60 * 60;  // 7
     _firebaseAppID = [firebaseAppID copy];
     _APNSInfo = [rawAPNSInfo copy];
     _cacheTime = cacheTime;
+    _needsMigration = needsMigration;
   }
   return self;
 }

+ 16 - 0
FirebaseMessaging/Sources/Token/FIRMessagingTokenStore.m

@@ -63,6 +63,21 @@ static NSString *const kFIRMessagingTokenKeychainId = @"com.google.iid-tokens";
   }
   // Token infos created from legacy storage don't have appVersion, firebaseAppID, or APNSInfo.
   FIRMessagingTokenInfo *tokenInfo = [[self class] tokenInfoFromKeychainItem:item];
+  if ([tokenInfo needsMigration]) {
+    [self
+        saveTokenInfo:tokenInfo
+              handler:^(NSError *error) {
+                if (error) {
+                  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTokenManager001,
+                                          @"Failed to migrate token: %@ account: %@ service %@",
+                                          tokenInfo, account, service);
+                } else {
+                  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTokenManager001,
+                                          @"Successful token migration: %@ account: %@ service %@",
+                                          tokenInfo, account, service);
+                }
+              }];
+  }
   return tokenInfo;
 }
 
@@ -94,6 +109,7 @@ static NSString *const kFIRMessagingTokenKeychainId = @"com.google.iid-tokens";
       [NSKeyedUnarchiver setClass:[FIRMessagingTokenInfo class]
                      forClassName:@"FIRInstanceIDTokenInfo"];
       tokenInfo = [NSKeyedUnarchiver unarchiveObjectWithData:item];
+
 #pragma clang diagnostic pop
 
     } @catch (NSException *exception) {

+ 12 - 14
FirebaseMessaging/Tests/UnitTestsSwift/FIRMessagingAPITest.swift

@@ -127,25 +127,23 @@ class CustomDelegate: NSObject, MessagingDelegate {
 func apiAsync() async throws {
   let messaging = Messaging.messaging()
   let topic = "cat_video"
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    try await messaging.subscribe(toTopic: topic)
+  try await messaging.subscribe(toTopic: topic)
 
-    try await messaging.unsubscribe(fromTopic: topic)
+  try await messaging.unsubscribe(fromTopic: topic)
 
-    try await messaging.token()
+  try await messaging.token()
 
-    try await messaging.retrieveFCMToken(forSenderID: "fakeSenderID")
+  try await messaging.retrieveFCMToken(forSenderID: "fakeSenderID")
 
-    try await messaging.deleteToken()
+  try await messaging.deleteToken()
 
-    try await messaging.deleteFCMToken(forSenderID: "fakeSenderID")
+  try await messaging.deleteFCMToken(forSenderID: "fakeSenderID")
 
-    try await messaging.deleteData()
+  try await messaging.deleteData()
 
-    // Test new handling of errors
-    do {
-      try await messaging.unsubscribe(fromTopic: topic)
-    } catch MessagingError.timeout {
-    } catch {}
-  #endif
+  // Test new handling of errors
+  do {
+    try await messaging.unsubscribe(fromTopic: topic)
+  } catch MessagingError.timeout {
+  } catch {}
 }

+ 1 - 1
FirebaseMessagingInterop.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseMessagingInterop'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Interfaces that allow other Firebase SDKs to use Messaging functionality.'
 
   s.description      = <<-DESC

+ 1 - 1
FirebasePerformance.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebasePerformance'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Performance'
 
   s.description      = <<-DESC

+ 1 - 1
FirebasePerformance/README.md

@@ -23,6 +23,6 @@ The above command should be sufficient for most scenarios. Few more options list
 
 - `sh generate_project.sh` (or) `sh generate_project.sh -e "autopush"`
 
-### Re-generate XCode project by deleting old XCode project
+### Re-generate Xcode project by deleting old Xcode project
 
 - `sh generate_project.sh -c`

+ 1 - 1
FirebasePerformance/Tests/Unit/FPRNetworkTraceTest.m

@@ -237,7 +237,7 @@
   NSDictionary<NSString *, NSNumber *> *states = [trace checkpointStates];
   XCTAssertEqual(states.count, 1);
 
-  // Validate if the first checkpoint occured before the second checkpoint time.
+  // Validate if the first checkpoint occurred before the second checkpoint time.
   XCTAssertLessThan(firstCheckpointTime, secondCheckpointTime);
   // Validate if the time has not changed even after rec checkpointing.
   XCTAssertEqual(firstInitiatedTime, secondInitiatedTime);

+ 2 - 2
FirebasePerformance/generate_project.sh

@@ -24,7 +24,7 @@ helpFunction()
   echo ""
   echo "Usage: $0 -e (prod/autopush*) -p (platform)"
   echo -e "\tEvent upload environment - prod (or) autopush. Default: autopush"
-  echo -c "\tRecreate the Xcode project from scratch. Default: Reuse same XCode project"
+  echo -c "\tRecreate the Xcode project from scratch. Default: Reuse same Xcode project"
   exit 1 # Exit script after printing help
 }
 
@@ -67,7 +67,7 @@ if [ -z "$clean" ]
 then
   pod gen "$DIR/FirebasePerformance.podspec" --local-sources="$DIR/" --auto-open --gen-directory="$DIR/gen" --platforms="$platform"
 else
-  echo "\nCreating a fresh Fireperf XCode project."
+  echo "\nCreating a fresh Fireperf Xcode project."
   rm -f "$DIR/FirebasePerformance/ProtoSupport/*.[hm]"
   protoc --proto_path="$DIR/FirebasePerformance/ProtoSupport/" --objc_out="$DIR/FirebasePerformance/ProtoSupport/" "$DIR/FirebasePerformance/ProtoSupport/perf_metric.proto"
   pod gen "$DIR/FirebasePerformance.podspec" --local-sources="$DIR/" --auto-open --gen-directory="$DIR/gen" --platforms="$platform" --clean

+ 1 - 1
FirebaseRemoteConfig.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseRemoteConfig'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Remote Config'
 
   s.description      = <<-DESC

+ 93 - 95
FirebaseRemoteConfigSwift/Tests/SwiftAPI/AsyncAwaitTests.swift

@@ -17,118 +17,116 @@ import FirebaseCore
 
 import XCTest
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  class AsyncAwaitTests: APITestBase {
-    func testFetchThenActivate() async throws {
-      let status = try await config.fetch()
-      XCTAssertEqual(status, RemoteConfigFetchStatus.success)
-      let success = try await config.activate()
-      XCTAssertTrue(success)
-    }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+class AsyncAwaitTests: APITestBase {
+  func testFetchThenActivate() async throws {
+    let status = try await config.fetch()
+    XCTAssertEqual(status, RemoteConfigFetchStatus.success)
+    let success = try await config.activate()
+    XCTAssertTrue(success)
+  }
 
-    func testFetchWithExpirationThenActivate() async throws {
-      let status = try await config.fetch(withExpirationDuration: 0)
-      XCTAssertEqual(status, RemoteConfigFetchStatus.success)
-      _ = try await config.activate()
-      XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
-    }
+  func testFetchWithExpirationThenActivate() async throws {
+    let status = try await config.fetch(withExpirationDuration: 0)
+    XCTAssertEqual(status, RemoteConfigFetchStatus.success)
+    _ = try await config.activate()
+    XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
+  }
 
-    func testFetchAndActivate() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
-    }
+  func testFetchAndActivate() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
+  }
 
-    func testFetchAndActivateGenericValue() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
-    }
+  func testFetchAndActivateGenericValue() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
+  }
 
-    // Contrast with testChangedActivateWillNotFlag in FakeConsole.swift.
-    func testUnchangedActivateWillFlag() async throws {
-      let status = try await config.fetch()
-      XCTAssertEqual(status, RemoteConfigFetchStatus.success)
-      let changed = try await config.activate()
-      XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
-      XCTAssertTrue(!APITests.useFakeConfig || changed)
-      XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
-    }
+  // Contrast with testChangedActivateWillNotFlag in FakeConsole.swift.
+  func testUnchangedActivateWillFlag() async throws {
+    let status = try await config.fetch()
+    XCTAssertEqual(status, RemoteConfigFetchStatus.success)
+    let changed = try await config.activate()
+    XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
+    XCTAssertTrue(!APITests.useFakeConfig || changed)
+    XCTAssertEqual(config[Constants.key1].stringValue, Constants.value1)
+  }
 
-    func testFetchAndActivateUnchangedConfig() async throws {
-      guard APITests.useFakeConfig == false else { return }
-
-      XCTAssertEqual(config.settings.minimumFetchInterval, 0)
-
-      // Represents pre-fetch occurring sometime in past.
-      let status = try await config.fetch()
-      XCTAssertEqual(status, .success)
-
-      // Represents a `fetchAndActivate` being made to pull latest changes from Remote Config.
-      let status2 = try await config.fetchAndActivate()
-      // Since no updates to remote config have occurred we use the `.successUsingPreFetchedData`.
-      // The behavior of the next test changed in Firebase 7.0.0.
-      // It's an open question which is correct, but it should only
-      // be changed in a major release.
-      // See https://github.com/firebase/firebase-ios-sdk/pull/8788
-      // XCTAssertEqual(status, .successUsingPreFetchedData)
-      XCTAssertEqual(status2, .successFetchedFromRemote)
-      // The `lastETagUpdateTime` should either be older or the same time as `lastFetchTime`.
-      if let lastFetchTime = try? XCTUnwrap(config.lastFetchTime) {
-        XCTAssertLessThanOrEqual(Double(config.settings.lastETagUpdateTime),
-                                 Double(lastFetchTime.timeIntervalSince1970))
-      } else {
-        XCTFail("Could not unwrap lastFetchTime.")
-      }
+  func testFetchAndActivateUnchangedConfig() async throws {
+    guard APITests.useFakeConfig == false else { return }
+
+    XCTAssertEqual(config.settings.minimumFetchInterval, 0)
+
+    // Represents pre-fetch occurring sometime in past.
+    let status = try await config.fetch()
+    XCTAssertEqual(status, .success)
+
+    // Represents a `fetchAndActivate` being made to pull latest changes from Remote Config.
+    let status2 = try await config.fetchAndActivate()
+    // Since no updates to remote config have occurred we use the `.successUsingPreFetchedData`.
+    // The behavior of the next test changed in Firebase 7.0.0.
+    // It's an open question which is correct, but it should only
+    // be changed in a major release.
+    // See https://github.com/firebase/firebase-ios-sdk/pull/8788
+    // XCTAssertEqual(status, .successUsingPreFetchedData)
+    XCTAssertEqual(status2, .successFetchedFromRemote)
+    // The `lastETagUpdateTime` should either be older or the same time as `lastFetchTime`.
+    if let lastFetchTime = try? XCTUnwrap(config.lastFetchTime) {
+      XCTAssertLessThanOrEqual(Double(config.settings.lastETagUpdateTime),
+                               Double(lastFetchTime.timeIntervalSince1970))
+    } else {
+      XCTFail("Could not unwrap lastFetchTime.")
     }
+  }
 
-    // MARK: - RemoteConfigConsole Tests
+  // MARK: - RemoteConfigConsole Tests
 
-    func testFetchConfigThenUpdateConsoleThenFetchAgain() async throws {
-      guard APITests.useFakeConfig == false else { return }
+  func testFetchConfigThenUpdateConsoleThenFetchAgain() async throws {
+    guard APITests.useFakeConfig == false else { return }
 
-      _ = try await config.fetchAndActivate()
-      let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
-      XCTAssertEqual(configValue, Constants.obiwan)
+    _ = try await config.fetchAndActivate()
+    let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
+    XCTAssertEqual(configValue, Constants.obiwan)
 
-      // Synchronously update the console.
-      console.updateRemoteConfigValue(Constants.yoda, forKey: Constants.jedi)
+    // Synchronously update the console.
+    console.updateRemoteConfigValue(Constants.yoda, forKey: Constants.jedi)
 
-      _ = try await config.fetchAndActivate()
-      let configValue2 = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
-      XCTAssertEqual(configValue2, Constants.yoda)
-    }
+    _ = try await config.fetchAndActivate()
+    let configValue2 = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
+    XCTAssertEqual(configValue2, Constants.yoda)
+  }
 
-    func testFetchConfigThenAddValueOnConsoleThenFetchAgain() async throws {
-      guard APITests.useFakeConfig == false else { return }
+  func testFetchConfigThenAddValueOnConsoleThenFetchAgain() async throws {
+    guard APITests.useFakeConfig == false else { return }
 
-      // Ensure no Sith Lord has been written to Remote Config yet.
-      _ = try await config.fetchAndActivate()
-      XCTAssertTrue(config.configValue(forKey: Constants.sith).dataValue.isEmpty)
+    // Ensure no Sith Lord has been written to Remote Config yet.
+    _ = try await config.fetchAndActivate()
+    XCTAssertTrue(config.configValue(forKey: Constants.sith).dataValue.isEmpty)
 
-      // Synchronously update the console
-      console.updateRemoteConfigValue(Constants.darthSidious, forKey: Constants.sith)
+    // Synchronously update the console
+    console.updateRemoteConfigValue(Constants.darthSidious, forKey: Constants.sith)
 
-      // Verify the Sith Lord can now be fetched from Remote Config
-      _ = try await config.fetchAndActivate()
-      let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.sith).stringValue)
-      XCTAssertEqual(configValue, Constants.darthSidious)
-    }
+    // Verify the Sith Lord can now be fetched from Remote Config
+    _ = try await config.fetchAndActivate()
+    let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.sith).stringValue)
+    XCTAssertEqual(configValue, Constants.darthSidious)
+  }
 
-    func testFetchConfigThenDeleteValueOnConsoleThenFetchAgain() async throws {
-      guard APITests.useFakeConfig == false else { return }
+  func testFetchConfigThenDeleteValueOnConsoleThenFetchAgain() async throws {
+    guard APITests.useFakeConfig == false else { return }
 
-      _ = try await config.fetchAndActivate()
-      let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
-      XCTAssertEqual(configValue, Constants.obiwan)
+    _ = try await config.fetchAndActivate()
+    let configValue = try? XCTUnwrap(config.configValue(forKey: Constants.jedi).stringValue)
+    XCTAssertEqual(configValue, Constants.obiwan)
 
-      // Synchronously delete value on the console.
-      console.removeRemoteConfigValue(forKey: Constants.jedi)
+    // Synchronously delete value on the console.
+    console.removeRemoteConfigValue(forKey: Constants.jedi)
 
-      _ = try await config.fetchAndActivate()
-      XCTAssertTrue(config.configValue(forKey: Constants.jedi).dataValue.isEmpty,
-                    "Remote config should have been deleted.")
-    }
+    _ = try await config.fetchAndActivate()
+    XCTAssertTrue(config.configValue(forKey: Constants.jedi).dataValue.isEmpty,
+                  "Remote config should have been deleted.")
   }
-#endif
+}

+ 171 - 173
FirebaseRemoteConfigSwift/Tests/SwiftAPI/Codable.swift

@@ -16,196 +16,194 @@ import FirebaseRemoteConfig
 
 import XCTest
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  class CodableTests: APITestBase {
-    // MARK: - Test decoding Remote Config JSON values
-
-    // Contrast this test with the subsequent one to see the value of the Codable API.
-    func testFetchAndActivateWithoutCodable() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let dict = try XCTUnwrap(config[Constants.jsonKey].jsonValue as? [String: AnyHashable])
-      XCTAssertEqual(dict["recipeName"], "PB&J")
-      XCTAssertEqual(dict["ingredients"], ["bread", "peanut butter", "jelly"])
-      XCTAssertEqual(dict["cookTime"], 7)
-      XCTAssertEqual(
-        config[Constants.jsonKey].jsonValue as! [String: AnyHashable],
-        Constants.jsonValue
-      )
-    }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+class CodableTests: APITestBase {
+  // MARK: - Test decoding Remote Config JSON values
+
+  // Contrast this test with the subsequent one to see the value of the Codable API.
+  func testFetchAndActivateWithoutCodable() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let dict = try XCTUnwrap(config[Constants.jsonKey].jsonValue as? [String: AnyHashable])
+    XCTAssertEqual(dict["recipeName"], "PB&J")
+    XCTAssertEqual(dict["ingredients"], ["bread", "peanut butter", "jelly"])
+    XCTAssertEqual(dict["cookTime"], 7)
+    XCTAssertEqual(
+      config[Constants.jsonKey].jsonValue as! [String: AnyHashable],
+      Constants.jsonValue
+    )
+  }
 
-    struct Recipe: Decodable {
-      var recipeName: String
-      var ingredients: [String]
-      var cookTime: Int
-    }
+  struct Recipe: Decodable {
+    var recipeName: String
+    var ingredients: [String]
+    var cookTime: Int
+  }
 
-    func testFetchAndActivateWithCodable() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let recipe = try XCTUnwrap(config[Constants.jsonKey].decoded(asType: Recipe.self))
-      XCTAssertEqual(recipe.recipeName, "PB&J")
-      XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
-      XCTAssertEqual(recipe.cookTime, 7)
-    }
+  func testFetchAndActivateWithCodable() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let recipe = try XCTUnwrap(config[Constants.jsonKey].decoded(asType: Recipe.self))
+    XCTAssertEqual(recipe.recipeName, "PB&J")
+    XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
+    XCTAssertEqual(recipe.cookTime, 7)
+  }
 
-    func testFetchAndActivateWithCodableAlternativeAPI() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let recipe: Recipe = try XCTUnwrap(config[Constants.jsonKey].decoded())
-      XCTAssertEqual(recipe.recipeName, "PB&J")
-      XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
-      XCTAssertEqual(recipe.cookTime, 7)
-    }
+  func testFetchAndActivateWithCodableAlternativeAPI() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let recipe: Recipe = try XCTUnwrap(config[Constants.jsonKey].decoded())
+    XCTAssertEqual(recipe.recipeName, "PB&J")
+    XCTAssertEqual(recipe.ingredients, ["bread", "peanut butter", "jelly"])
+    XCTAssertEqual(recipe.cookTime, 7)
+  }
 
-    func testFetchAndActivateWithCodableBadJson() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      do {
-        _ = try config[Constants.nonJsonKey].decoded(asType: Recipe.self)
-      } catch let DecodingError.typeMismatch(_, context) {
-        XCTAssertEqual(context.debugDescription,
-                       "Expected to decode Dictionary<String, Any> but found " +
-                         "FirebaseRemoteConfigValueDecoderHelper instead.")
-        return
-      }
-      XCTFail("Failed to catch trying to decode non-JSON key as JSON")
+  func testFetchAndActivateWithCodableBadJson() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    do {
+      _ = try config[Constants.nonJsonKey].decoded(asType: Recipe.self)
+    } catch let DecodingError.typeMismatch(_, context) {
+      XCTAssertEqual(context.debugDescription,
+                     "Expected to decode Dictionary<String, Any> but found " +
+                       "FirebaseRemoteConfigValueDecoderHelper instead.")
+      return
     }
+    XCTFail("Failed to catch trying to decode non-JSON key as JSON")
+  }
 
-    // MARK: - Test setting Remote Config defaults via an encodable struct
+  // MARK: - Test setting Remote Config defaults via an encodable struct
 
-    struct DataTestDefaults: Encodable {
-      var bool: Bool
-      var int: Int32
-      var long: Int64
-      var string: String
-    }
+  struct DataTestDefaults: Encodable {
+    var bool: Bool
+    var int: Int32
+    var long: Int64
+    var string: String
+  }
 
-    func testSetEncodeableDefaults() throws {
-      let data = DataTestDefaults(
-        bool: true,
-        int: 2,
-        long: 9_876_543_210,
-        string: "four"
-      )
-      try config.setDefaults(from: data)
-      let boolValue = try XCTUnwrap(config.defaultValue(forKey: "bool")).numberValue.boolValue
-      XCTAssertTrue(boolValue)
-      let intValue = try XCTUnwrap(config.defaultValue(forKey: "int")).numberValue.intValue
-      XCTAssertEqual(intValue, 2)
-      let longValue = try XCTUnwrap(config.defaultValue(forKey: "long")).numberValue.int64Value
-      XCTAssertEqual(longValue, 9_876_543_210)
-      let stringValue = try XCTUnwrap(config.defaultValue(forKey: "string")).stringValue
-      XCTAssertEqual(stringValue, "four")
-    }
+  func testSetEncodeableDefaults() throws {
+    let data = DataTestDefaults(
+      bool: true,
+      int: 2,
+      long: 9_876_543_210,
+      string: "four"
+    )
+    try config.setDefaults(from: data)
+    let boolValue = try XCTUnwrap(config.defaultValue(forKey: "bool")).numberValue.boolValue
+    XCTAssertTrue(boolValue)
+    let intValue = try XCTUnwrap(config.defaultValue(forKey: "int")).numberValue.intValue
+    XCTAssertEqual(intValue, 2)
+    let longValue = try XCTUnwrap(config.defaultValue(forKey: "long")).numberValue.int64Value
+    XCTAssertEqual(longValue, 9_876_543_210)
+    let stringValue = try XCTUnwrap(config.defaultValue(forKey: "string")).stringValue
+    XCTAssertEqual(stringValue, "four")
+  }
 
-    func testSetEncodeableDefaultsInvalid() throws {
-      do {
-        _ = try config.setDefaults(from: 7)
-      } catch let RemoteConfigCodableError.invalidSetDefaultsInput(message) {
-        XCTAssertEqual(message,
-                       "The setDefaults input: 7, must be a Struct that encodes to a Dictionary")
-        return
-      }
-      XCTFail("Failed to catch trying to encode an invalid input to setDefaults.")
+  func testSetEncodeableDefaultsInvalid() throws {
+    do {
+      _ = try config.setDefaults(from: 7)
+    } catch let RemoteConfigCodableError.invalidSetDefaultsInput(message) {
+      XCTAssertEqual(message,
+                     "The setDefaults input: 7, must be a Struct that encodes to a Dictionary")
+      return
     }
+    XCTFail("Failed to catch trying to encode an invalid input to setDefaults.")
+  }
 
-    // MARK: - Test extracting config to an decodable struct.
+  // MARK: - Test extracting config to an decodable struct.
 
+  struct MyConfig: Decodable {
+    var Recipe: Recipe
+    var notJSON: String
+    var myInt: Int
+    var myFloat: Float
+    var myDecimal: Decimal
+    var myTrue: Bool
+    var myData: Data
+  }
+
+  func testExtractConfig() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let myConfig: MyConfig = try config.decoded()
+    XCTAssertEqual(myConfig.notJSON, Constants.nonJsonValue)
+    XCTAssertEqual(myConfig.myInt, Constants.intValue)
+    XCTAssertEqual(myConfig.myTrue, true)
+    XCTAssertEqual(myConfig.myFloat, Constants.floatValue)
+    XCTAssertEqual(myConfig.myDecimal, Constants.decimalValue)
+    XCTAssertEqual(myConfig.myData, Constants.dataValue)
+    XCTAssertEqual(myConfig.Recipe.recipeName, "PB&J")
+    XCTAssertEqual(myConfig.Recipe.ingredients, ["bread", "peanut butter", "jelly"])
+    XCTAssertEqual(myConfig.Recipe.cookTime, 7)
+  }
+
+  // Additional fields in config are ignored.
+  func testExtractConfigExtra() async throws {
+    guard APITests.useFakeConfig else { return }
+    fakeConsole.config["extra"] = "extra Value"
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let myConfig: MyConfig = try config.decoded()
+    XCTAssertEqual(myConfig.notJSON, Constants.nonJsonValue)
+    XCTAssertEqual(myConfig.Recipe.recipeName, "PB&J")
+    XCTAssertEqual(myConfig.Recipe.ingredients, ["bread", "peanut butter", "jelly"])
+    XCTAssertEqual(myConfig.Recipe.cookTime, 7)
+  }
+
+  // Failure if requested field does not exist.
+  func testExtractConfigMissing() async throws {
     struct MyConfig: Decodable {
-      var Recipe: Recipe
+      var missing: String
+      var Recipe: String
       var notJSON: String
-      var myInt: Int
-      var myFloat: Float
-      var myDecimal: Decimal
-      var myTrue: Bool
-      var myData: Data
-    }
-
-    func testExtractConfig() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let myConfig: MyConfig = try config.decoded()
-      XCTAssertEqual(myConfig.notJSON, Constants.nonJsonValue)
-      XCTAssertEqual(myConfig.myInt, Constants.intValue)
-      XCTAssertEqual(myConfig.myTrue, true)
-      XCTAssertEqual(myConfig.myFloat, Constants.floatValue)
-      XCTAssertEqual(myConfig.myDecimal, Constants.decimalValue)
-      XCTAssertEqual(myConfig.myData, Constants.dataValue)
-      XCTAssertEqual(myConfig.Recipe.recipeName, "PB&J")
-      XCTAssertEqual(myConfig.Recipe.ingredients, ["bread", "peanut butter", "jelly"])
-      XCTAssertEqual(myConfig.Recipe.cookTime, 7)
     }
-
-    // Additional fields in config are ignored.
-    func testExtractConfigExtra() async throws {
-      guard APITests.useFakeConfig else { return }
-      fakeConsole.config["extra"] = "extra Value"
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let myConfig: MyConfig = try config.decoded()
-      XCTAssertEqual(myConfig.notJSON, Constants.nonJsonValue)
-      XCTAssertEqual(myConfig.Recipe.recipeName, "PB&J")
-      XCTAssertEqual(myConfig.Recipe.ingredients, ["bread", "peanut butter", "jelly"])
-      XCTAssertEqual(myConfig.Recipe.cookTime, 7)
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    do {
+      let _: MyConfig = try config.decoded()
+    } catch let DecodingError.keyNotFound(codingKey, context) {
+      XCTAssertEqual(codingKey.stringValue, "missing")
+      print(codingKey, context)
+      return
     }
+    XCTFail("Failed to throw on missing field")
+  }
 
-    // Failure if requested field does not exist.
-    func testExtractConfigMissing() async throws {
-      struct MyConfig: Decodable {
-        var missing: String
-        var Recipe: String
-        var notJSON: String
-      }
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      do {
-        let _: MyConfig = try config.decoded()
-      } catch let DecodingError.keyNotFound(codingKey, context) {
-        XCTAssertEqual(codingKey.stringValue, "missing")
-        print(codingKey, context)
-        return
-      }
-      XCTFail("Failed to throw on missing field")
+  func testCodableAfterPlistDefaults() throws {
+    struct Defaults: Codable {
+      let format: String
+      let isPaidUser: Bool
+      let newItem: Double
+      let Languages: String
+      let dictValue: [String: String]
+      let arrayValue: [String]
+      let arrayIntValue: [Int]
     }
-
-    func testCodableAfterPlistDefaults() throws {
-      struct Defaults: Codable {
-        let format: String
-        let isPaidUser: Bool
-        let newItem: Double
-        let Languages: String
-        let dictValue: [String: String]
-        let arrayValue: [String]
-        let arrayIntValue: [Int]
-      }
-      // setDefaults(fromPlist:) doesn't work because of dynamic linking.
-      // More details in RCNRemoteConfigTest.m
-      var findPlist: String?
-      #if SWIFT_PACKAGE
-        findPlist = Bundle.module.path(forResource: "Defaults-testInfo", ofType: "plist")
-      #else
-        for b in Bundle.allBundles {
-          findPlist = b.path(forResource: "Defaults-testInfo", ofType: "plist")
-          if findPlist != nil {
-            break
-          }
+    // setDefaults(fromPlist:) doesn't work because of dynamic linking.
+    // More details in RCNRemoteConfigTest.m
+    var findPlist: String?
+    #if SWIFT_PACKAGE
+      findPlist = Bundle.module.path(forResource: "Defaults-testInfo", ofType: "plist")
+    #else
+      for b in Bundle.allBundles {
+        findPlist = b.path(forResource: "Defaults-testInfo", ofType: "plist")
+        if findPlist != nil {
+          break
         }
-      #endif
-      let plistFile = try XCTUnwrap(findPlist)
-      let defaults = NSDictionary(contentsOfFile: plistFile)
-      config.setDefaults(defaults as? [String: NSObject])
-      let readDefaults: Defaults = try config.decoded()
-      XCTAssertEqual(readDefaults.format, "key to value.")
-      XCTAssertEqual(readDefaults.isPaidUser, true)
-      XCTAssertEqual(readDefaults.newItem, 2.4)
-      XCTAssertEqual(readDefaults.Languages, "English")
-      XCTAssertEqual(readDefaults.dictValue, ["foo": "foo",
-                                              "bar": "bar",
-                                              "baz": "baz"])
-      XCTAssertEqual(readDefaults.arrayValue, ["foo", "bar", "baz"])
-      XCTAssertEqual(readDefaults.arrayIntValue, [1, 2, 0, 3])
-    }
+      }
+    #endif
+    let plistFile = try XCTUnwrap(findPlist)
+    let defaults = NSDictionary(contentsOfFile: plistFile)
+    config.setDefaults(defaults as? [String: NSObject])
+    let readDefaults: Defaults = try config.decoded()
+    XCTAssertEqual(readDefaults.format, "key to value.")
+    XCTAssertEqual(readDefaults.isPaidUser, true)
+    XCTAssertEqual(readDefaults.newItem, 2.4)
+    XCTAssertEqual(readDefaults.Languages, "English")
+    XCTAssertEqual(readDefaults.dictValue, ["foo": "foo",
+                                            "bar": "bar",
+                                            "baz": "baz"])
+    XCTAssertEqual(readDefaults.arrayValue, ["foo", "bar", "baz"])
+    XCTAssertEqual(readDefaults.arrayIntValue, [1, 2, 0, 3])
   }
-#endif
+}

+ 36 - 38
FirebaseRemoteConfigSwift/Tests/SwiftAPI/PropertyWrapperDefaultConfigsTests.swift

@@ -21,50 +21,48 @@ import XCTest
 
 let ConfigKeyForThisTestOnly = "PropertyWrapperDefaultConfigsTestsKey"
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
-  class PropertyWrapperDefaultConfigsTests: XCTestCase {
-    struct Recipe: Decodable, Encodable {
-      var recipeName: String
-      var ingredients: [String]
-      var cookTime: Int
-    }
+@available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
+class PropertyWrapperDefaultConfigsTests: XCTestCase {
+  struct Recipe: Decodable, Encodable {
+    var recipeName: String
+    var ingredients: [String]
+    var cookTime: Int
+  }
 
-    static let defaultRecipe = Recipe(
-      recipeName: "muffin", ingredients: ["flour", "sugar"], cookTime: 45
-    )
+  static let defaultRecipe = Recipe(
+    recipeName: "muffin", ingredients: ["flour", "sugar"], cookTime: 45
+  )
 
-    // MARK: - Test Remote Config default values with property wrapper
+  // MARK: - Test Remote Config default values with property wrapper
 
-    struct DefaultsValuesTester {
-      @RemoteConfigProperty(
-        key: ConfigKeyForThisTestOnly,
-        fallback: Recipe(recipeName: "test", ingredients: [], cookTime: 0)
-      )
-      var dictValue: Recipe
-    }
+  struct DefaultsValuesTester {
+    @RemoteConfigProperty(
+      key: ConfigKeyForThisTestOnly,
+      fallback: Recipe(recipeName: "test", ingredients: [], cookTime: 0)
+    )
+    var dictValue: Recipe
+  }
 
-    override class func setUp() {
-      if FirebaseApp.app() == nil {
-        let options = FirebaseOptions(googleAppID: "1:123:ios:123abc",
-                                      gcmSenderID: "correct_gcm_sender_id")
-        options.apiKey = "A23456789012345678901234567890123456789"
-        options.projectID = "Fake Project"
-        FirebaseApp.configure(options: options)
-      }
+  override class func setUp() {
+    if FirebaseApp.app() == nil {
+      let options = FirebaseOptions(googleAppID: "1:123:ios:123abc",
+                                    gcmSenderID: "correct_gcm_sender_id")
+      options.apiKey = "A23456789012345678901234567890123456789"
+      options.projectID = "Fake Project"
+      FirebaseApp.configure(options: options)
     }
+  }
 
-    func testDefaultValues() async throws {
-      try? RemoteConfig.remoteConfig().setDefaults(
-        from: [ConfigKeyForThisTestOnly: PropertyWrapperDefaultConfigsTests.defaultRecipe]
-      )
+  func testDefaultValues() async throws {
+    try? RemoteConfig.remoteConfig().setDefaults(
+      from: [ConfigKeyForThisTestOnly: PropertyWrapperDefaultConfigsTests.defaultRecipe]
+    )
 
-      let tester = await DefaultsValuesTester()
-      let dictValue = await tester.dictValue
+    let tester = await DefaultsValuesTester()
+    let dictValue = await tester.dictValue
 
-      XCTAssertEqual(dictValue.recipeName, "muffin")
-      XCTAssertEqual(dictValue.cookTime, 45)
-      XCTAssertEqual(dictValue.ingredients, ["flour", "sugar"])
-    }
+    XCTAssertEqual(dictValue.recipeName, "muffin")
+    XCTAssertEqual(dictValue.cookTime, 45)
+    XCTAssertEqual(dictValue.ingredients, ["flour", "sugar"])
   }
-#endif
+}

+ 200 - 202
FirebaseRemoteConfigSwift/Tests/SwiftAPI/PropertyWrapperTests.swift

@@ -18,282 +18,280 @@ import FirebaseRemoteConfig
 
 import XCTest
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
-  class PropertyWrapperTests: APITestBase {
-    // MARK: - Test fetching Remote Config JSON values into struct property
-
-    struct Recipe: Decodable {
-      var recipeName: String
-      var ingredients: [String]
-      var cookTime: Int
-    }
-
-    static let fallbackString = "fallback"
-    static let fallbackInt = 50
-    static let fallbackFloat: Float = 50.2
-    static let fallbackDouble: Double = 16_777_216.333921
-    static let fallbackDecimal: Decimal = 235
-    static let fallbackData = "hello".data(using: .utf8)!
-    static let fallbackArray = ["mango", "pineapple", "papaya"]
-    static let fallbackDict = [
-      "session 0": "breakfast", "session 1": "keynote", "session 2": "state of union",
-    ]
-    static let fallbackJSON = Recipe(
-      recipeName: "muffin", ingredients: ["flour", "sugar"], cookTime: 45
-    )
-
-    struct PropertyWrapperTester {
-      @RemoteConfigProperty(key: Constants.stringKey, fallback: "")
-      var stringValue: String!
+@available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
+class PropertyWrapperTests: APITestBase {
+  // MARK: - Test fetching Remote Config JSON values into struct property
+
+  struct Recipe: Decodable {
+    var recipeName: String
+    var ingredients: [String]
+    var cookTime: Int
+  }
 
-      var stringKeyName: String {
-        return _stringValue.key
-      }
+  static let fallbackString = "fallback"
+  static let fallbackInt = 50
+  static let fallbackFloat: Float = 50.2
+  static let fallbackDouble: Double = 16_777_216.333921
+  static let fallbackDecimal: Decimal = 235
+  static let fallbackData = "hello".data(using: .utf8)!
+  static let fallbackArray = ["mango", "pineapple", "papaya"]
+  static let fallbackDict = [
+    "session 0": "breakfast", "session 1": "keynote", "session 2": "state of union",
+  ]
+  static let fallbackJSON = Recipe(
+    recipeName: "muffin", ingredients: ["flour", "sugar"], cookTime: 45
+  )
+
+  struct PropertyWrapperTester {
+    @RemoteConfigProperty(key: Constants.stringKey, fallback: "")
+    var stringValue: String!
+
+    var stringKeyName: String {
+      return _stringValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.intKey, fallback: 0)
-      var intValue: Int!
+    @RemoteConfigProperty(key: Constants.intKey, fallback: 0)
+    var intValue: Int!
 
-      var intKeyName: String {
-        return _intValue.key
-      }
+    var intKeyName: String {
+      return _intValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.floatKey, fallback: 0)
-      var floatValue: Float!
+    @RemoteConfigProperty(key: Constants.floatKey, fallback: 0)
+    var floatValue: Float!
 
-      var floatKeyName: String {
-        return _floatValue.key
-      }
+    var floatKeyName: String {
+      return _floatValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.floatKey, fallback: 0)
-      var doubleValue: Double!
+    @RemoteConfigProperty(key: Constants.floatKey, fallback: 0)
+    var doubleValue: Double!
 
-      var doubleKeyName: String {
-        return _doubleValue.key
-      }
+    var doubleKeyName: String {
+      return _doubleValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.decimalKey, fallback: 0)
-      var decimalValue: Decimal!
+    @RemoteConfigProperty(key: Constants.decimalKey, fallback: 0)
+    var decimalValue: Decimal!
 
-      var decimalKeyName: String {
-        return _decimalValue.key
-      }
+    var decimalKeyName: String {
+      return _decimalValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.trueKey, fallback: false)
-      var trueValue: Bool!
+    @RemoteConfigProperty(key: Constants.trueKey, fallback: false)
+    var trueValue: Bool!
 
-      var trueKeyName: String {
-        return _trueValue.key
-      }
+    var trueKeyName: String {
+      return _trueValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.falseKey, fallback: false)
-      var falseValue: Bool!
+    @RemoteConfigProperty(key: Constants.falseKey, fallback: false)
+    var falseValue: Bool!
 
-      var falseKeyName: String {
-        return _falseValue.key
-      }
+    var falseKeyName: String {
+      return _falseValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.dataKey, fallback: Data())
-      var dataValue: Data!
+    @RemoteConfigProperty(key: Constants.dataKey, fallback: Data())
+    var dataValue: Data!
 
-      var dataKeyName: String {
-        return _dataValue.key
-      }
+    var dataKeyName: String {
+      return _dataValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.jsonKey, fallback: nil)
-      var recipeValue: Recipe!
+    @RemoteConfigProperty(key: Constants.jsonKey, fallback: nil)
+    var recipeValue: Recipe!
 
-      var recipeKeyName: String {
-        _recipeValue.key
-      }
+    var recipeKeyName: String {
+      _recipeValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.arrayKey, fallback: [])
-      var arrayValue: [String]!
+    @RemoteConfigProperty(key: Constants.arrayKey, fallback: [])
+    var arrayValue: [String]!
 
-      var arrayKeyName: String {
-        _arrayValue.key
-      }
+    var arrayKeyName: String {
+      _arrayValue.key
+    }
 
-      @RemoteConfigProperty(key: Constants.dictKey, fallback: [:])
-      var dictValue: [String: String]!
+    @RemoteConfigProperty(key: Constants.dictKey, fallback: [:])
+    var dictValue: [String: String]!
 
-      var dictKeyName: String {
-        _dictValue.key
-      }
+    var dictKeyName: String {
+      _dictValue.key
     }
+  }
 
-    struct PlaceholderValueTester {
-      @RemoteConfigProperty(key: "NewKeyNotInSystem", fallback: fallbackString)
-      var stringValue: String
+  struct PlaceholderValueTester {
+    @RemoteConfigProperty(key: "NewKeyNotInSystem", fallback: fallbackString)
+    var stringValue: String
 
-      @RemoteConfigProperty(key: "NewIntKeyNotInSystem", fallback: fallbackInt)
-      var intValue: Int!
+    @RemoteConfigProperty(key: "NewIntKeyNotInSystem", fallback: fallbackInt)
+    var intValue: Int!
 
-      @RemoteConfigProperty(key: "NewZeroKey", fallback: 0)
-      var zeroIntValue: Int!
+    @RemoteConfigProperty(key: "NewZeroKey", fallback: 0)
+    var zeroIntValue: Int!
 
-      @RemoteConfigProperty(key: "newFloatKey", fallback: fallbackFloat)
-      var floatValue: Float!
+    @RemoteConfigProperty(key: "newFloatKey", fallback: fallbackFloat)
+    var floatValue: Float!
 
-      @RemoteConfigProperty(key: "newDoubleKey", fallback: fallbackDouble)
-      var doubleValue: Double!
+    @RemoteConfigProperty(key: "newDoubleKey", fallback: fallbackDouble)
+    var doubleValue: Double!
 
-      @RemoteConfigProperty(key: "newDecimalKey", fallback: fallbackDecimal)
-      var decimalValue: Decimal!
+    @RemoteConfigProperty(key: "newDecimalKey", fallback: fallbackDecimal)
+    var decimalValue: Decimal!
 
-      @RemoteConfigProperty(key: "newTrueKey", fallback: false)
-      var trueKeyFalseValue: Bool!
+    @RemoteConfigProperty(key: "newTrueKey", fallback: false)
+    var trueKeyFalseValue: Bool!
 
-      @RemoteConfigProperty(key: "newTrueKey2", fallback: true)
-      var trueKeyTrueValue: Bool!
+    @RemoteConfigProperty(key: "newTrueKey2", fallback: true)
+    var trueKeyTrueValue: Bool!
 
-      @RemoteConfigProperty(key: "newFalseKey", fallback: true)
-      var falseKeyTrueValue: Bool!
+    @RemoteConfigProperty(key: "newFalseKey", fallback: true)
+    var falseKeyTrueValue: Bool!
 
-      @RemoteConfigProperty(key: "newFalseKey2", fallback: false)
-      var falseKeyFalseValue: Bool!
+    @RemoteConfigProperty(key: "newFalseKey2", fallback: false)
+    var falseKeyFalseValue: Bool!
 
-      @RemoteConfigProperty(key: "newDataKey", fallback: fallbackData)
-      var dataValue: Data
+    @RemoteConfigProperty(key: "newDataKey", fallback: fallbackData)
+    var dataValue: Data
 
-      @RemoteConfigProperty(key: "newJSONKey", fallback: fallbackJSON)
-      var recipeValue: Recipe!
+    @RemoteConfigProperty(key: "newJSONKey", fallback: fallbackJSON)
+    var recipeValue: Recipe!
 
-      @RemoteConfigProperty(key: "newArrayKey", fallback: fallbackArray)
-      var arrayValue: [String]!
+    @RemoteConfigProperty(key: "newArrayKey", fallback: fallbackArray)
+    var arrayValue: [String]!
 
-      @RemoteConfigProperty(key: "newDictKey", fallback: fallbackDict)
-      var dictValue: [String: String]!
-    }
+    @RemoteConfigProperty(key: "newDictKey", fallback: fallbackDict)
+    var dictValue: [String: String]!
+  }
 
-    func testFetchAndActivateWithPropertyWrapper() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
+  func testFetchAndActivateWithPropertyWrapper() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
 
-      let tester = await PropertyWrapperTester()
+    let tester = await PropertyWrapperTester()
 
-      let stringValue = await tester.stringValue
-      XCTAssertEqual(stringValue, Constants.stringValue)
+    let stringValue = await tester.stringValue
+    XCTAssertEqual(stringValue, Constants.stringValue)
 
-      let intValue = await tester.intValue
-      XCTAssertEqual(intValue, Constants.intValue)
+    let intValue = await tester.intValue
+    XCTAssertEqual(intValue, Constants.intValue)
 
-      let floatValue = await tester.floatValue
-      XCTAssertEqual(floatValue, Constants.floatValue)
+    let floatValue = await tester.floatValue
+    XCTAssertEqual(floatValue, Constants.floatValue)
 
-      let doubleValue = await tester.doubleValue
-      XCTAssertEqual(doubleValue, Constants.doubleValue)
+    let doubleValue = await tester.doubleValue
+    XCTAssertEqual(doubleValue, Constants.doubleValue)
 
-      let decimalValue = await tester.decimalValue
-      XCTAssertEqual(decimalValue, Constants.decimalValue)
+    let decimalValue = await tester.decimalValue
+    XCTAssertEqual(decimalValue, Constants.decimalValue)
 
-      let trueValue = await tester.trueValue
-      XCTAssertEqual(trueValue, true)
+    let trueValue = await tester.trueValue
+    XCTAssertEqual(trueValue, true)
 
-      let falseValue = await tester.falseValue
-      XCTAssertEqual(falseValue, false)
+    let falseValue = await tester.falseValue
+    XCTAssertEqual(falseValue, false)
 
-      let dataValue = await tester.dataValue
-      XCTAssertEqual(dataValue, Constants.dataValue)
+    let dataValue = await tester.dataValue
+    XCTAssertEqual(dataValue, Constants.dataValue)
 
-      let recipe = try XCTUnwrap(config[Constants.jsonKey].decoded(asType: Recipe.self))
-      let recipeValue = await tester.recipeValue
-      XCTAssertEqual(recipeValue?.recipeName, recipe.recipeName)
-      XCTAssertEqual(recipeValue?.ingredients, recipe.ingredients)
-      XCTAssertEqual(recipeValue?.cookTime, recipe.cookTime)
+    let recipe = try XCTUnwrap(config[Constants.jsonKey].decoded(asType: Recipe.self))
+    let recipeValue = await tester.recipeValue
+    XCTAssertEqual(recipeValue?.recipeName, recipe.recipeName)
+    XCTAssertEqual(recipeValue?.ingredients, recipe.ingredients)
+    XCTAssertEqual(recipeValue?.cookTime, recipe.cookTime)
 
-      let arrayValue = await tester.arrayValue
-      XCTAssertEqual(arrayValue, Constants.arrayValue)
+    let arrayValue = await tester.arrayValue
+    XCTAssertEqual(arrayValue, Constants.arrayValue)
 
-      let dictValue = await tester.dictValue
-      XCTAssertEqual(dictValue, Constants.dictValue)
-    }
+    let dictValue = await tester.dictValue
+    XCTAssertEqual(dictValue, Constants.dictValue)
+  }
 
-    func testPropertyWrapperInstanceValues() async {
-      let tester = await PropertyWrapperTester()
+  func testPropertyWrapperInstanceValues() async {
+    let tester = await PropertyWrapperTester()
 
-      let stringKeyName = await tester.stringKeyName
-      XCTAssertEqual(Constants.stringKey, stringKeyName)
+    let stringKeyName = await tester.stringKeyName
+    XCTAssertEqual(Constants.stringKey, stringKeyName)
 
-      let intKeyName = await tester.intKeyName
-      XCTAssertEqual(Constants.intKey, intKeyName)
+    let intKeyName = await tester.intKeyName
+    XCTAssertEqual(Constants.intKey, intKeyName)
 
-      let floatKeyName = await tester.floatKeyName
-      XCTAssertEqual(Constants.floatKey, floatKeyName)
+    let floatKeyName = await tester.floatKeyName
+    XCTAssertEqual(Constants.floatKey, floatKeyName)
 
-      let doubleKeyName = await tester.doubleKeyName
-      XCTAssertEqual(Constants.floatKey, doubleKeyName)
+    let doubleKeyName = await tester.doubleKeyName
+    XCTAssertEqual(Constants.floatKey, doubleKeyName)
 
-      let decimalKeyName = await tester.decimalKeyName
-      XCTAssertEqual(Constants.decimalKey, decimalKeyName)
+    let decimalKeyName = await tester.decimalKeyName
+    XCTAssertEqual(Constants.decimalKey, decimalKeyName)
 
-      let trueKeyName = await tester.trueKeyName
-      XCTAssertEqual(Constants.trueKey, trueKeyName)
+    let trueKeyName = await tester.trueKeyName
+    XCTAssertEqual(Constants.trueKey, trueKeyName)
 
-      let falseKeyName = await tester.falseKeyName
-      XCTAssertEqual(Constants.falseKey, falseKeyName)
+    let falseKeyName = await tester.falseKeyName
+    XCTAssertEqual(Constants.falseKey, falseKeyName)
 
-      let dataKeyName = await tester.dataKeyName
-      XCTAssertEqual(Constants.dataKey, dataKeyName)
+    let dataKeyName = await tester.dataKeyName
+    XCTAssertEqual(Constants.dataKey, dataKeyName)
 
-      let recipeKeyName = await tester.recipeKeyName
-      XCTAssertEqual(Constants.jsonKey, recipeKeyName)
+    let recipeKeyName = await tester.recipeKeyName
+    XCTAssertEqual(Constants.jsonKey, recipeKeyName)
 
-      let arrayKeyName = await tester.arrayKeyName
-      XCTAssertEqual(Constants.arrayKey, arrayKeyName)
+    let arrayKeyName = await tester.arrayKeyName
+    XCTAssertEqual(Constants.arrayKey, arrayKeyName)
 
-      let dictKeyName = await tester.dictKeyName
-      XCTAssertEqual(Constants.dictKey, dictKeyName)
-    }
+    let dictKeyName = await tester.dictKeyName
+    XCTAssertEqual(Constants.dictKey, dictKeyName)
+  }
 
-    func testPlaceHolderValues() async throws {
-      // Make sure the values below are consistent with the property wrapper
-      // in PlaceholderValueTester
-      let tester = await PlaceholderValueTester()
+  func testPlaceHolderValues() async throws {
+    // Make sure the values below are consistent with the property wrapper
+    // in PlaceholderValueTester
+    let tester = await PlaceholderValueTester()
 
-      let stringValue = await tester.stringValue
-      XCTAssertEqual(stringValue, PropertyWrapperTests.fallbackString)
+    let stringValue = await tester.stringValue
+    XCTAssertEqual(stringValue, PropertyWrapperTests.fallbackString)
 
-      let intValue = await tester.intValue
-      XCTAssertEqual(intValue, PropertyWrapperTests.fallbackInt)
+    let intValue = await tester.intValue
+    XCTAssertEqual(intValue, PropertyWrapperTests.fallbackInt)
 
-      let zeroValue = await tester.zeroIntValue
-      XCTAssertEqual(zeroValue, 0)
+    let zeroValue = await tester.zeroIntValue
+    XCTAssertEqual(zeroValue, 0)
 
-      let floatValue = await tester.floatValue
-      XCTAssertEqual(floatValue, PropertyWrapperTests.fallbackFloat)
+    let floatValue = await tester.floatValue
+    XCTAssertEqual(floatValue, PropertyWrapperTests.fallbackFloat)
 
-      let doubleValue = await tester.doubleValue
-      XCTAssertEqual(doubleValue, PropertyWrapperTests.fallbackDouble)
+    let doubleValue = await tester.doubleValue
+    XCTAssertEqual(doubleValue, PropertyWrapperTests.fallbackDouble)
 
-      let decimalValue = await tester.decimalValue
-      XCTAssertEqual(decimalValue, PropertyWrapperTests.fallbackDecimal)
+    let decimalValue = await tester.decimalValue
+    XCTAssertEqual(decimalValue, PropertyWrapperTests.fallbackDecimal)
 
-      let trueKeyFalseValue = await tester.trueKeyFalseValue
-      XCTAssertEqual(trueKeyFalseValue, false)
+    let trueKeyFalseValue = await tester.trueKeyFalseValue
+    XCTAssertEqual(trueKeyFalseValue, false)
 
-      let trueKeyTrueValue = await tester.trueKeyTrueValue
-      XCTAssertEqual(trueKeyTrueValue, true)
+    let trueKeyTrueValue = await tester.trueKeyTrueValue
+    XCTAssertEqual(trueKeyTrueValue, true)
 
-      let falseKeyTrueValue = await tester.falseKeyTrueValue
-      XCTAssertEqual(falseKeyTrueValue, true)
+    let falseKeyTrueValue = await tester.falseKeyTrueValue
+    XCTAssertEqual(falseKeyTrueValue, true)
 
-      let falseKeyFalseValue = await tester.falseKeyFalseValue
-      XCTAssertEqual(falseKeyFalseValue, false)
+    let falseKeyFalseValue = await tester.falseKeyFalseValue
+    XCTAssertEqual(falseKeyFalseValue, false)
 
-      let dataValue = await tester.dataValue
-      XCTAssertEqual(dataValue, PropertyWrapperTests.fallbackData)
+    let dataValue = await tester.dataValue
+    XCTAssertEqual(dataValue, PropertyWrapperTests.fallbackData)
 
-      let arrayValue = await tester.arrayValue
-      XCTAssertEqual(arrayValue, PropertyWrapperTests.fallbackArray)
+    let arrayValue = await tester.arrayValue
+    XCTAssertEqual(arrayValue, PropertyWrapperTests.fallbackArray)
 
-      let dictValue = await tester.dictValue
-      XCTAssertEqual(dictValue, PropertyWrapperTests.fallbackDict)
+    let dictValue = await tester.dictValue
+    XCTAssertEqual(dictValue, PropertyWrapperTests.fallbackDict)
 
-      let recipeValue = await tester.recipeValue
-      XCTAssertEqual(recipeValue?.recipeName, "muffin")
-      XCTAssertEqual(recipeValue?.ingredients, ["flour", "sugar"])
-      XCTAssertEqual(recipeValue?.cookTime, 45)
-    }
+    let recipeValue = await tester.recipeValue
+    XCTAssertEqual(recipeValue?.recipeName, "muffin")
+    XCTAssertEqual(recipeValue?.ingredients, ["flour", "sugar"])
+    XCTAssertEqual(recipeValue?.cookTime, 45)
   }
-#endif
+}

+ 168 - 170
FirebaseRemoteConfigSwift/Tests/SwiftAPI/Value.swift

@@ -16,181 +16,179 @@ import FirebaseRemoteConfig
 
 import XCTest
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  class ValueTests: APITestBase {
-    func testFetchAndActivateAllTypes() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      XCTAssertEqual(config[Constants.stringKey].stringValue, Constants.stringValue)
-      XCTAssertEqual(config[Constants.intKey].numberValue.intValue, Constants.intValue)
-      XCTAssertEqual(config[Constants.intKey].numberValue.int8Value, Int8(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.int16Value, Int16(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.int32Value, Int32(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.int64Value, Int64(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.uintValue, UInt(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.uint8Value, UInt8(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.uint16Value, UInt16(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.uint32Value, UInt32(Constants.intValue))
-      XCTAssertEqual(config[Constants.intKey].numberValue.uint64Value, UInt64(Constants.intValue))
-      XCTAssertEqual(
-        config[Constants.floatKey].numberValue.decimalValue,
-        Decimal(Constants.doubleValue)
-      )
-      XCTAssertEqual(config[Constants.floatKey].numberValue.floatValue, Constants.floatValue)
-      XCTAssertEqual(config[Constants.floatKey].numberValue.doubleValue, Constants.doubleValue)
-      XCTAssertEqual(config[Constants.trueKey].boolValue, true)
-      XCTAssertEqual(config[Constants.falseKey].boolValue, false)
-      XCTAssertEqual(
-        config[Constants.stringKey].dataValue,
-        Constants.stringValue.data(using: .utf8)
-      )
-      XCTAssertEqual(
-        config[Constants.jsonKey].jsonValue as! [String: AnyHashable],
-        Constants.jsonValue
-      )
-    }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+class ValueTests: APITestBase {
+  func testFetchAndActivateAllTypes() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    XCTAssertEqual(config[Constants.stringKey].stringValue, Constants.stringValue)
+    XCTAssertEqual(config[Constants.intKey].numberValue.intValue, Constants.intValue)
+    XCTAssertEqual(config[Constants.intKey].numberValue.int8Value, Int8(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.int16Value, Int16(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.int32Value, Int32(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.int64Value, Int64(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.uintValue, UInt(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.uint8Value, UInt8(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.uint16Value, UInt16(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.uint32Value, UInt32(Constants.intValue))
+    XCTAssertEqual(config[Constants.intKey].numberValue.uint64Value, UInt64(Constants.intValue))
+    XCTAssertEqual(
+      config[Constants.floatKey].numberValue.decimalValue,
+      Decimal(Constants.doubleValue)
+    )
+    XCTAssertEqual(config[Constants.floatKey].numberValue.floatValue, Constants.floatValue)
+    XCTAssertEqual(config[Constants.floatKey].numberValue.doubleValue, Constants.doubleValue)
+    XCTAssertEqual(config[Constants.trueKey].boolValue, true)
+    XCTAssertEqual(config[Constants.falseKey].boolValue, false)
+    XCTAssertEqual(
+      config[Constants.stringKey].dataValue,
+      Constants.stringValue.data(using: .utf8)
+    )
+    XCTAssertEqual(
+      config[Constants.jsonKey].jsonValue as! [String: AnyHashable],
+      Constants.jsonValue
+    )
+  }
 
-    func testStrongTypingViaSubscriptApi() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      XCTAssertEqual(config[decodedValue: Constants.stringKey], Constants.stringValue)
-      XCTAssertEqual(config[decodedValue: Constants.intKey], Constants.intValue)
-      XCTAssertEqual(config[decodedValue: Constants.floatKey], Constants.floatValue)
-      XCTAssertEqual(config[decodedValue: Constants.floatKey], Constants.doubleValue)
-      XCTAssertEqual(config[decodedValue: Constants.trueKey], true)
-      XCTAssertEqual(config[decodedValue: Constants.falseKey], false)
-      XCTAssertEqual(
-        config[decodedValue: Constants.stringKey],
-        Constants.stringValue.data(using: .utf8)
-      )
-      XCTAssertEqual(try XCTUnwrap(config[jsonValue: Constants.jsonKey]), Constants.jsonValue)
-    }
+  func testStrongTypingViaSubscriptApi() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    XCTAssertEqual(config[decodedValue: Constants.stringKey], Constants.stringValue)
+    XCTAssertEqual(config[decodedValue: Constants.intKey], Constants.intValue)
+    XCTAssertEqual(config[decodedValue: Constants.floatKey], Constants.floatValue)
+    XCTAssertEqual(config[decodedValue: Constants.floatKey], Constants.doubleValue)
+    XCTAssertEqual(config[decodedValue: Constants.trueKey], true)
+    XCTAssertEqual(config[decodedValue: Constants.falseKey], false)
+    XCTAssertEqual(
+      config[decodedValue: Constants.stringKey],
+      Constants.stringValue.data(using: .utf8)
+    )
+    XCTAssertEqual(try XCTUnwrap(config[jsonValue: Constants.jsonKey]), Constants.jsonValue)
+  }
 
-    func testStrongTypingViaDecoder() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      XCTAssertEqual(
-        try config[Constants.stringKey].decoded(asType: String.self),
-        Constants.stringValue
-      )
-      XCTAssertEqual(try config[Constants.intKey].decoded(asType: Int.self), Constants.intValue)
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: Int8.self),
-        Int8(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: Int16.self),
-        Int16(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: Int32.self),
-        Int32(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: Int64.self),
-        Int64(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: UInt.self),
-        UInt(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: UInt8.self),
-        UInt8(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: UInt16.self),
-        UInt16(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: UInt32.self),
-        UInt32(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.intKey].decoded(asType: UInt64.self),
-        UInt64(Constants.intValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.floatKey].decoded(asType: Decimal.self),
-        Decimal(Constants.doubleValue)
-      )
-      XCTAssertEqual(
-        try config[Constants.floatKey].decoded(asType: Float.self),
-        Constants.floatValue
-      )
-      XCTAssertEqual(
-        try config[Constants.floatKey].decoded(asType: Double.self),
-        Constants.doubleValue
-      )
-      XCTAssertEqual(try config[Constants.trueKey].decoded(asType: Bool.self), true)
-      XCTAssertEqual(try config[Constants.falseKey].decoded(asType: Bool.self), false)
-      XCTAssertEqual(
-        try config[Constants.stringKey].decoded(asType: Data.self),
-        Constants.stringValue.data(using: .utf8)
-      )
-    }
+  func testStrongTypingViaDecoder() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    XCTAssertEqual(
+      try config[Constants.stringKey].decoded(asType: String.self),
+      Constants.stringValue
+    )
+    XCTAssertEqual(try config[Constants.intKey].decoded(asType: Int.self), Constants.intValue)
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: Int8.self),
+      Int8(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: Int16.self),
+      Int16(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: Int32.self),
+      Int32(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: Int64.self),
+      Int64(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: UInt.self),
+      UInt(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: UInt8.self),
+      UInt8(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: UInt16.self),
+      UInt16(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: UInt32.self),
+      UInt32(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.intKey].decoded(asType: UInt64.self),
+      UInt64(Constants.intValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.floatKey].decoded(asType: Decimal.self),
+      Decimal(Constants.doubleValue)
+    )
+    XCTAssertEqual(
+      try config[Constants.floatKey].decoded(asType: Float.self),
+      Constants.floatValue
+    )
+    XCTAssertEqual(
+      try config[Constants.floatKey].decoded(asType: Double.self),
+      Constants.doubleValue
+    )
+    XCTAssertEqual(try config[Constants.trueKey].decoded(asType: Bool.self), true)
+    XCTAssertEqual(try config[Constants.falseKey].decoded(asType: Bool.self), false)
+    XCTAssertEqual(
+      try config[Constants.stringKey].decoded(asType: Data.self),
+      Constants.stringValue.data(using: .utf8)
+    )
+  }
 
-    func testStrongTypingViaDecoderAlternateDecoderApi() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      let myString: String = try config[Constants.stringKey].decoded()
-      XCTAssertEqual(myString, Constants.stringValue)
-      let myInt: Int = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myInt, Constants.intValue)
-      let myInt8: Int8 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myInt8, Int8(Constants.intValue))
-      let myInt16: Int16 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myInt16, Int16(Constants.intValue))
-      let myInt32: Int32 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myInt32, Int32(Constants.intValue))
-      let myInt64: Int64 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myInt64, Int64(Constants.intValue))
-      let myUInt: UInt = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myUInt, UInt(Constants.intValue))
-      let myUInt8: UInt8 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myUInt8, UInt8(Constants.intValue))
-      let myUInt16: UInt16 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myUInt16, UInt16(Constants.intValue))
-      let myUInt32: UInt32 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myUInt32, UInt32(Constants.intValue))
-      let myUInt64: UInt64 = try config[Constants.intKey].decoded()
-      XCTAssertEqual(myUInt64, UInt64(Constants.intValue))
-      let myDecimal: Decimal = try config[Constants.floatKey].decoded()
-      XCTAssertEqual(myDecimal, Decimal(Constants.doubleValue))
-      let myFloat: Float = try config[Constants.floatKey].decoded()
-      XCTAssertEqual(myFloat, Constants.floatValue)
-      let myDouble: Double = try config[Constants.floatKey].decoded()
-      XCTAssertEqual(myDouble, Constants.doubleValue)
-      let myTrue: Bool = try config[Constants.trueKey].decoded()
-      XCTAssertEqual(myTrue, true)
-      let myFalse: Bool = try config[Constants.falseKey].decoded()
-      XCTAssertEqual(myFalse, false)
-      let myData: Data = try config[Constants.stringKey].decoded()
-      XCTAssertEqual(myData, Constants.stringValue.data(using: .utf8))
-    }
+  func testStrongTypingViaDecoderAlternateDecoderApi() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    let myString: String = try config[Constants.stringKey].decoded()
+    XCTAssertEqual(myString, Constants.stringValue)
+    let myInt: Int = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myInt, Constants.intValue)
+    let myInt8: Int8 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myInt8, Int8(Constants.intValue))
+    let myInt16: Int16 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myInt16, Int16(Constants.intValue))
+    let myInt32: Int32 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myInt32, Int32(Constants.intValue))
+    let myInt64: Int64 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myInt64, Int64(Constants.intValue))
+    let myUInt: UInt = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myUInt, UInt(Constants.intValue))
+    let myUInt8: UInt8 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myUInt8, UInt8(Constants.intValue))
+    let myUInt16: UInt16 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myUInt16, UInt16(Constants.intValue))
+    let myUInt32: UInt32 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myUInt32, UInt32(Constants.intValue))
+    let myUInt64: UInt64 = try config[Constants.intKey].decoded()
+    XCTAssertEqual(myUInt64, UInt64(Constants.intValue))
+    let myDecimal: Decimal = try config[Constants.floatKey].decoded()
+    XCTAssertEqual(myDecimal, Decimal(Constants.doubleValue))
+    let myFloat: Float = try config[Constants.floatKey].decoded()
+    XCTAssertEqual(myFloat, Constants.floatValue)
+    let myDouble: Double = try config[Constants.floatKey].decoded()
+    XCTAssertEqual(myDouble, Constants.doubleValue)
+    let myTrue: Bool = try config[Constants.trueKey].decoded()
+    XCTAssertEqual(myTrue, true)
+    let myFalse: Bool = try config[Constants.falseKey].decoded()
+    XCTAssertEqual(myFalse, false)
+    let myData: Data = try config[Constants.stringKey].decoded()
+    XCTAssertEqual(myData, Constants.stringValue.data(using: .utf8))
+  }
 
-    func testStringFails() {
-      XCTAssertEqual(config[decodedValue: "UndefinedKey"], "")
-    }
+  func testStringFails() {
+    XCTAssertEqual(config[decodedValue: "UndefinedKey"], "")
+  }
 
-    func testJSONFails() {
-      XCTAssertNil(config[jsonValue: "UndefinedKey"])
-      XCTAssertNil(config[jsonValue: Constants.stringKey])
-    }
+  func testJSONFails() {
+    XCTAssertNil(config[jsonValue: "UndefinedKey"])
+    XCTAssertNil(config[jsonValue: Constants.stringKey])
+  }
 
-    func testDateDecodingNotYetSupported() async throws {
-      let status = try await config.fetchAndActivate()
-      XCTAssertEqual(status, .successFetchedFromRemote)
-      do {
-        let _: Date = try config[Constants.stringKey].decoded()
-      } catch let RemoteConfigValueCodableError.unsupportedType(message) {
-        XCTAssertEqual(message,
-                       "Date type is not currently supported for  Remote Config Value decoding. " +
-                         "Please file a feature request")
-        return
-      }
-      XCTFail("Failed to throw unsupported Date error.")
+  func testDateDecodingNotYetSupported() async throws {
+    let status = try await config.fetchAndActivate()
+    XCTAssertEqual(status, .successFetchedFromRemote)
+    do {
+      let _: Date = try config[Constants.stringKey].decoded()
+    } catch let RemoteConfigValueCodableError.unsupportedType(message) {
+      XCTAssertEqual(message,
+                     "Date type is not currently supported for  Remote Config Value decoding. " +
+                       "Please file a feature request")
+      return
     }
+    XCTFail("Failed to throw unsupported Date error.")
   }
-#endif
+}

+ 1 - 1
FirebaseSessions.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseSessions'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Sessions'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseSharedSwift.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name                    = 'FirebaseSharedSwift'
-  s.version                 = '10.20.0'
+  s.version                 = '10.21.0'
   s.summary                 = 'Shared Swift Extensions for Firebase'
 
   s.description      = <<-DESC

+ 1 - 1
FirebaseStorage.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'FirebaseStorage'
-  s.version          = '10.20.0'
+  s.version          = '10.21.0'
   s.summary          = 'Firebase Storage'
 
   s.description      = <<-DESC

+ 159 - 162
FirebaseStorage/Sources/AsyncAwait.swift

@@ -14,191 +14,188 @@
 
 import Foundation
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  public extension StorageReference {
-    /// Asynchronously downloads the object at the StorageReference to a Data object in memory.
-    /// A Data object of the provided max size will be allocated, so ensure that the device has
-    /// enough free memory to complete the download. For downloading large files, the `write`
-    /// API may be a better option.
-    ///
-    /// - Parameters:
-    ///   - size: The maximum size in bytes to download. If the download exceeds this size,
-    ///           the task will be cancelled and an error will be thrown.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if the data exceeded `maxSize`.
-    /// - Returns: Data object.
-    func data(maxSize: Int64) async throws -> Data {
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+public extension StorageReference {
+  /// Asynchronously downloads the object at the StorageReference to a Data object in memory.
+  /// A Data object of the provided max size will be allocated, so ensure that the device has
+  /// enough free memory to complete the download. For downloading large files, the `write`
+  /// API may be a better option.
+  ///
+  /// - Parameters:
+  ///   - size: The maximum size in bytes to download. If the download exceeds this size,
+  ///           the task will be cancelled and an error will be thrown.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if the data exceeded `maxSize`.
+  /// - Returns: Data object.
+  func data(maxSize: Int64) async throws -> Data {
+    return try await withCheckedThrowingContinuation { continuation in
+      _ = self.getData(maxSize: maxSize) { result in
+        continuation.resume(with: result)
+      }
+    }
+  }
+
+  /// Asynchronously uploads data to the currently specified StorageReference.
+  /// This is not recommended for large files, and one should instead upload a file from disk
+  /// from the Firebase Console.
+  ///
+  /// - Parameters:
+  ///   - uploadData: The Data to upload.
+  ///   - metadata: Optional StorageMetadata containing additional information (MIME type, etc.)
+  ///              about the object being uploaded.
+  ///   - onProgress: An optional closure function to return a `Progress` instance while the
+  /// upload proceeds.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if Storage was unreachable.
+  /// - Returns: StorageMetadata with additional information about the object being uploaded.
+  func putDataAsync(_ uploadData: Data,
+                    metadata: StorageMetadata? = nil,
+                    onProgress: ((Progress?) -> Void)? = nil) async throws -> StorageMetadata {
+    guard let onProgress = onProgress else {
       return try await withCheckedThrowingContinuation { continuation in
-        _ = self.getData(maxSize: maxSize) { result in
+        self.putData(uploadData, metadata: metadata) { result in
           continuation.resume(with: result)
         }
       }
     }
-
-    /// Asynchronously uploads data to the currently specified StorageReference.
-    /// This is not recommended for large files, and one should instead upload a file from disk
-    /// from the Firebase Console.
-    ///
-    /// - Parameters:
-    ///   - uploadData: The Data to upload.
-    ///   - metadata: Optional StorageMetadata containing additional information (MIME type, etc.)
-    ///              about the object being uploaded.
-    ///   - onProgress: An optional closure function to return a `Progress` instance while the
-    /// upload proceeds.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if Storage was unreachable.
-    /// - Returns: StorageMetadata with additional information about the object being uploaded.
-    func putDataAsync(_ uploadData: Data,
-                      metadata: StorageMetadata? = nil,
-                      onProgress: ((Progress?) -> Void)? = nil) async throws -> StorageMetadata {
-      guard let onProgress = onProgress else {
-        return try await withCheckedThrowingContinuation { continuation in
-          self.putData(uploadData, metadata: metadata) { result in
-            continuation.resume(with: result)
-          }
-        }
+    let uploadTask = putData(uploadData, metadata: metadata)
+    return try await withCheckedThrowingContinuation { continuation in
+      uploadTask.observe(.progress) {
+        onProgress($0.progress)
       }
-      let uploadTask = putData(uploadData, metadata: metadata)
-      return try await withCheckedThrowingContinuation { continuation in
-        uploadTask.observe(.progress) {
-          onProgress($0.progress)
-        }
-        uploadTask.observe(.success) { _ in
-          continuation.resume(with: .success(uploadTask.metadata!))
-        }
-        uploadTask.observe(.failure) { snapshot in
-          continuation.resume(with: .failure(
-            snapshot.error ?? StorageError.internalError("Internal Storage Error in putDataAsync")
-          ))
-        }
+      uploadTask.observe(.success) { _ in
+        continuation.resume(with: .success(uploadTask.metadata!))
+      }
+      uploadTask.observe(.failure) { snapshot in
+        continuation.resume(with: .failure(
+          snapshot.error ?? StorageError.internalError("Internal Storage Error in putDataAsync")
+        ))
       }
     }
+  }
 
-    /// Asynchronously uploads a file to the currently specified StorageReference.
-    /// `putDataAsync` should be used instead of `putFileAsync` in Extensions.
-    ///
-    /// - Parameters:
-    ///   - url: A URL representing the system file path of the object to be uploaded.
-    ///   - metadata: Optional StorageMetadata containing additional information (MIME type, etc.)
-    ///              about the object being uploaded.
-    ///   - onProgress: An optional closure function to return a `Progress` instance while the
-    /// upload proceeds.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if no file was present at the specified
-    /// `url`.
-    /// - Returns: `StorageMetadata` with additional information about the object being uploaded.
-    func putFileAsync(from url: URL,
-                      metadata: StorageMetadata? = nil,
-                      onProgress: ((Progress?) -> Void)? = nil) async throws -> StorageMetadata {
-      guard let onProgress = onProgress else {
-        return try await withCheckedThrowingContinuation { continuation in
-          self.putFile(from: url, metadata: metadata) { result in
-            continuation.resume(with: result)
-          }
-        }
-      }
-      let uploadTask = putFile(from: url, metadata: metadata)
+  /// Asynchronously uploads a file to the currently specified StorageReference.
+  /// `putDataAsync` should be used instead of `putFileAsync` in Extensions.
+  ///
+  /// - Parameters:
+  ///   - url: A URL representing the system file path of the object to be uploaded.
+  ///   - metadata: Optional StorageMetadata containing additional information (MIME type, etc.)
+  ///              about the object being uploaded.
+  ///   - onProgress: An optional closure function to return a `Progress` instance while the
+  /// upload proceeds.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if no file was present at the specified
+  /// `url`.
+  /// - Returns: `StorageMetadata` with additional information about the object being uploaded.
+  func putFileAsync(from url: URL,
+                    metadata: StorageMetadata? = nil,
+                    onProgress: ((Progress?) -> Void)? = nil) async throws -> StorageMetadata {
+    guard let onProgress = onProgress else {
       return try await withCheckedThrowingContinuation { continuation in
-        uploadTask.observe(.progress) {
-          onProgress($0.progress)
-        }
-        uploadTask.observe(.success) { _ in
-          continuation.resume(with: .success(uploadTask.metadata!))
-        }
-        uploadTask.observe(.failure) { snapshot in
-          continuation.resume(with: .failure(
-            snapshot.error ?? StorageError.internalError("Internal Storage Error in putFileAsync")
-          ))
+        self.putFile(from: url, metadata: metadata) { result in
+          continuation.resume(with: result)
         }
       }
     }
-
-    /// Asynchronously downloads the object at the current path to a specified system filepath.
-    ///
-    /// - Parameters:
-    ///   - fileUrl: A URL representing the system file path of the object to be uploaded.
-    ///   - onProgress: An optional closure function to return a `Progress` instance while the
-    /// download proceeds.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if Storage was unreachable
-    ///   or `fileURL` did not reference a valid path on disk.
-    /// - Returns: A `URL` pointing to the file path of the downloaded file.
-    func writeAsync(toFile fileURL: URL,
-                    onProgress: ((Progress?) -> Void)? = nil) async throws -> URL {
-      guard let onProgress = onProgress else {
-        return try await withCheckedThrowingContinuation { continuation in
-          _ = self.write(toFile: fileURL) { result in
-            continuation.resume(with: result)
-          }
-        }
+    let uploadTask = putFile(from: url, metadata: metadata)
+    return try await withCheckedThrowingContinuation { continuation in
+      uploadTask.observe(.progress) {
+        onProgress($0.progress)
       }
-      let downloadTask = write(toFile: fileURL)
-      return try await withCheckedThrowingContinuation { continuation in
-        downloadTask.observe(.progress) {
-          onProgress($0.progress)
-        }
-        downloadTask.observe(.success) { _ in
-          continuation.resume(with: .success(fileURL))
-        }
-        downloadTask.observe(.failure) { snapshot in
-          continuation.resume(with: .failure(
-            snapshot.error ?? StorageError.internalError("Internal Storage Error in writeAsync")
-          ))
-        }
+      uploadTask.observe(.success) { _ in
+        continuation.resume(with: .success(uploadTask.metadata!))
+      }
+      uploadTask.observe(.failure) { snapshot in
+        continuation.resume(with: .failure(
+          snapshot.error ?? StorageError.internalError("Internal Storage Error in putFileAsync")
+        ))
       }
     }
+  }
 
-    /// List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
-    ///
-    /// "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
-    /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
-    /// filtered.
-    ///
-    /// Only available for projects using Firebase Rules Version 2.
-    ///
-    /// - Parameters:
-    ///   - maxResults The maximum number of results to return in a single page. Must be
-    ///                greater than 0 and at most 1000.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if Storage was unreachable
-    ///   or the storage reference referenced an invalid path.
-    /// - Returns:
-    ///   - A `StorageListResult` containing the contents of the storage reference.
-    func list(maxResults: Int64) async throws -> StorageListResult {
-      typealias ListContinuation = CheckedContinuation<StorageListResult, Error>
-      return try await withCheckedThrowingContinuation { (continuation: ListContinuation) in
-        self.list(maxResults: maxResults) { result in
+  /// Asynchronously downloads the object at the current path to a specified system filepath.
+  ///
+  /// - Parameters:
+  ///   - fileUrl: A URL representing the system file path of the object to be uploaded.
+  ///   - onProgress: An optional closure function to return a `Progress` instance while the
+  /// download proceeds.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if Storage was unreachable
+  ///   or `fileURL` did not reference a valid path on disk.
+  /// - Returns: A `URL` pointing to the file path of the downloaded file.
+  func writeAsync(toFile fileURL: URL,
+                  onProgress: ((Progress?) -> Void)? = nil) async throws -> URL {
+    guard let onProgress = onProgress else {
+      return try await withCheckedThrowingContinuation { continuation in
+        _ = self.write(toFile: fileURL) { result in
           continuation.resume(with: result)
         }
       }
     }
+    let downloadTask = write(toFile: fileURL)
+    return try await withCheckedThrowingContinuation { continuation in
+      downloadTask.observe(.progress) {
+        onProgress($0.progress)
+      }
+      downloadTask.observe(.success) { _ in
+        continuation.resume(with: .success(fileURL))
+      }
+      downloadTask.observe(.failure) { snapshot in
+        continuation.resume(with: .failure(
+          snapshot.error ?? StorageError.internalError("Internal Storage Error in writeAsync")
+        ))
+      }
+    }
+  }
 
-    /// List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
-    ///
-    /// "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
-    /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
-    /// filtered.
-    ///
-    /// Only available for projects using Firebase Rules Version 2.
-    ///
-    /// - Parameters:
-    ///   - maxResults The maximum number of results to return in a single page. Must be
-    ///                greater than 0 and at most 1000.
-    ///   - pageToken A page token from a previous call to list.
-    /// - Throws:
-    ///   - An error if the operation failed, for example if Storage was unreachable
-    ///   or the storage reference referenced an invalid path.
-    /// - Returns:
-    ///   - completion A `Result` enum with either the list or an `Error`.
-    func list(maxResults: Int64, pageToken: String) async throws -> StorageListResult {
-      typealias ListContinuation = CheckedContinuation<StorageListResult, Error>
-      return try await withCheckedThrowingContinuation { (continuation: ListContinuation) in
-        self.list(maxResults: maxResults, pageToken: pageToken) { result in
-          continuation.resume(with: result)
-        }
+  /// List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
+  ///
+  /// "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
+  /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
+  /// filtered.
+  ///
+  /// Only available for projects using Firebase Rules Version 2.
+  ///
+  /// - Parameters:
+  ///   - maxResults The maximum number of results to return in a single page. Must be
+  ///                greater than 0 and at most 1000.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if Storage was unreachable
+  ///   or the storage reference referenced an invalid path.
+  /// - Returns:
+  ///   - A `StorageListResult` containing the contents of the storage reference.
+  func list(maxResults: Int64) async throws -> StorageListResult {
+    typealias ListContinuation = CheckedContinuation<StorageListResult, Error>
+    return try await withCheckedThrowingContinuation { (continuation: ListContinuation) in
+      self.list(maxResults: maxResults) { result in
+        continuation.resume(with: result)
       }
     }
   }
 
-#endif
+  /// List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
+  ///
+  /// "/" is treated as a path delimiter. Firebase Storage does not support unsupported object
+  /// paths that end with "/" or contain two consecutive "/"s. All invalid objects in GCS will be
+  /// filtered.
+  ///
+  /// Only available for projects using Firebase Rules Version 2.
+  ///
+  /// - Parameters:
+  ///   - maxResults The maximum number of results to return in a single page. Must be
+  ///                greater than 0 and at most 1000.
+  ///   - pageToken A page token from a previous call to list.
+  /// - Throws:
+  ///   - An error if the operation failed, for example if Storage was unreachable
+  ///   or the storage reference referenced an invalid path.
+  /// - Returns:
+  ///   - completion A `Result` enum with either the list or an `Error`.
+  func list(maxResults: Int64, pageToken: String) async throws -> StorageListResult {
+    typealias ListContinuation = CheckedContinuation<StorageListResult, Error>
+    return try await withCheckedThrowingContinuation { (continuation: ListContinuation) in
+      self.list(maxResults: maxResults, pageToken: pageToken) { result in
+        continuation.resume(with: result)
+      }
+    }
+  }
+}

+ 67 - 77
FirebaseStorage/Sources/StorageReference.swift

@@ -270,23 +270,21 @@ import Foundation
     task.enqueue()
   }
 
-  #if compiler(>=5.5) && canImport(_Concurrency)
-    /**
-     * Asynchronously retrieves a long lived download URL with a revokable token.
-     * This can be used to share the file with others, but can be revoked by a developer
-     * in the Firebase Console.
-     * - Throws: An error if the download URL could not be retrieved.
-     * - Returns: The URL on success.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
-    open func downloadURL() async throws -> URL {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.downloadURL { result in
-          continuation.resume(with: result)
-        }
+  /**
+   * Asynchronously retrieves a long lived download URL with a revokable token.
+   * This can be used to share the file with others, but can be revoked by a developer
+   * in the Firebase Console.
+   * - Throws: An error if the download URL could not be retrieved.
+   * - Returns: The URL on success.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
+  open func downloadURL() async throws -> URL {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.downloadURL { result in
+        continuation.resume(with: result)
       }
     }
-  #endif // compiler(>=5.5) && canImport(_Concurrency)
+  }
 
   /**
    * Asynchronously downloads the object at the current path to a specified system filepath.
@@ -398,28 +396,26 @@ import Foundation
     task.enqueue()
   }
 
-  #if compiler(>=5.5) && canImport(_Concurrency)
-    /**
-     * Lists all items (files) and prefixes (folders) under this StorageReference.
-     *
-     * This is a helper method for calling list() repeatedly until there are no more results.
-     * Consistency of the result is not guaranteed if objects are inserted or removed while this
-     * operation is executing. All results are buffered in memory.
-     *
-     * `listAll()` is only available for projects using Firebase Rules Version 2.
-     *
-     * - Throws: An error if the list operation failed.
-     * - Returns: All items and prefixes under the current `StorageReference`.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
-    open func listAll() async throws -> StorageListResult {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.listAll { result in
-          continuation.resume(with: result)
-        }
+  /**
+   * Lists all items (files) and prefixes (folders) under this StorageReference.
+   *
+   * This is a helper method for calling list() repeatedly until there are no more results.
+   * Consistency of the result is not guaranteed if objects are inserted or removed while this
+   * operation is executing. All results are buffered in memory.
+   *
+   * `listAll()` is only available for projects using Firebase Rules Version 2.
+   *
+   * - Throws: An error if the list operation failed.
+   * - Returns: All items and prefixes under the current `StorageReference`.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
+  open func listAll() async throws -> StorageListResult {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.listAll { result in
+        continuation.resume(with: result)
       }
     }
-  #endif // compiler(>=5.5) && canImport(_Concurrency)
+  }
 
   /**
    * List up to `maxResults` items (files) and prefixes (folders) under this StorageReference.
@@ -514,21 +510,19 @@ import Foundation
     task.enqueue()
   }
 
-  #if compiler(>=5.5) && canImport(_Concurrency)
-    /**
-     * Retrieves metadata associated with an object at the current path.
-     * - Throws: An error if the object metadata could not be retrieved.
-     * - Returns: The object metadata on success.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
-    open func getMetadata() async throws -> StorageMetadata {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.getMetadata { result in
-          continuation.resume(with: result)
-        }
+  /**
+   * Retrieves metadata associated with an object at the current path.
+   * - Throws: An error if the object metadata could not be retrieved.
+   * - Returns: The object metadata on success.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
+  open func getMetadata() async throws -> StorageMetadata {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.getMetadata { result in
+        continuation.resume(with: result)
       }
     }
-  #endif // compiler(>=5.5) && canImport(_Concurrency)
+  }
 
   /**
    * Updates the metadata associated with an object at the current path.
@@ -549,22 +543,20 @@ import Foundation
     task.enqueue()
   }
 
-  #if compiler(>=5.5) && canImport(_Concurrency)
-    /**
-     * Updates the metadata associated with an object at the current path.
-     * - Parameter metadata A `StorageMetadata` object with the metadata to update.
-     * - Throws: An error if the metadata update operation failed.
-     * - Returns: The object metadata on success.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
-    open func updateMetadata(_ metadata: StorageMetadata) async throws -> StorageMetadata {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.updateMetadata(metadata) { result in
-          continuation.resume(with: result)
-        }
+  /**
+   * Updates the metadata associated with an object at the current path.
+   * - Parameter metadata A `StorageMetadata` object with the metadata to update.
+   * - Throws: An error if the metadata update operation failed.
+   * - Returns: The object metadata on success.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
+  open func updateMetadata(_ metadata: StorageMetadata) async throws -> StorageMetadata {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.updateMetadata(metadata) { result in
+        continuation.resume(with: result)
       }
     }
-  #endif // compiler(>=5.5) && canImport(_Concurrency)
+  }
 
   // MARK: - Delete
 
@@ -582,24 +574,22 @@ import Foundation
     task.enqueue()
   }
 
-  #if compiler(>=5.5) && canImport(_Concurrency)
-    /**
-     * Deletes the object at the current path.
-     * - Throws: An error if the delete operation failed.
-     */
-    @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
-    open func delete() async throws {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.delete { error in
-          if let error = error {
-            continuation.resume(throwing: error)
-          } else {
-            continuation.resume()
-          }
+  /**
+   * Deletes the object at the current path.
+   * - Throws: An error if the delete operation failed.
+   */
+  @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
+  open func delete() async throws {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.delete { error in
+        if let error = error {
+          continuation.resume(throwing: error)
+        } else {
+          continuation.resume()
         }
       }
     }
-  #endif // compiler(>=5.5) && canImport(_Concurrency)
+  }
 
   // MARK: - NSObject overrides
 

+ 349 - 351
FirebaseStorage/Tests/Integration/StorageAsyncAwait.swift

@@ -17,407 +17,405 @@ import FirebaseCore
 import FirebaseStorage
 import XCTest
 
-#if swift(>=5.5) && canImport(_Concurrency)
-  @available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
-  class StorageAsyncAwait: StorageIntegrationCommon {
-    func testGetMetadata() async throws {
-      let ref = storage.reference().child("ios/public/1mb2")
-      let result = try await ref.getMetadata()
-      XCTAssertNotNil(result)
-    }
+@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
+class StorageAsyncAwait: StorageIntegrationCommon {
+  func testGetMetadata() async throws {
+    let ref = storage.reference().child("ios/public/1mb2")
+    let result = try await ref.getMetadata()
+    XCTAssertNotNil(result)
+  }
 
-    func testUpdateMetadata() async throws {
-      let meta = StorageMetadata()
-      meta.contentType = "lol/custom"
-      meta.customMetadata = ["lol": "custom metadata is neat",
-                             "ちかてつ": "🚇",
-                             "shinkansen": "新幹線"]
-
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-      let metadata = try await ref.updateMetadata(meta)
-      XCTAssertEqual(meta.contentType, metadata.contentType)
-      XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
-      XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
-      XCTAssertEqual(meta.customMetadata!["shinkansen"],
-                     metadata.customMetadata!["shinkansen"])
-    }
+  func testUpdateMetadata() async throws {
+    let meta = StorageMetadata()
+    meta.contentType = "lol/custom"
+    meta.customMetadata = ["lol": "custom metadata is neat",
+                           "ちかてつ": "🚇",
+                           "shinkansen": "新幹線"]
+
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+    let metadata = try await ref.updateMetadata(meta)
+    XCTAssertEqual(meta.contentType, metadata.contentType)
+    XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
+    XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
+    XCTAssertEqual(meta.customMetadata!["shinkansen"],
+                   metadata.customMetadata!["shinkansen"])
+  }
 
-    func testDelete() async throws {
-      let objectLocation = "ios/public/fileToDelete"
-      let ref = storage.reference(withPath: objectLocation)
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
+  func testDelete() async throws {
+    let objectLocation = "ios/public/fileToDelete"
+    let ref = storage.reference(withPath: objectLocation)
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+    _ = try await ref.delete()
+    // Next delete should fail and verify the first delete succeeded.
+    var caughtError = false
+    do {
       _ = try await ref.delete()
-      // Next delete should fail and verify the first delete succeeded.
-      var caughtError = false
-      do {
-        _ = try await ref.delete()
-      } catch {
-        caughtError = true
-        let nsError = error as NSError
-        XCTAssertEqual(nsError.code, StorageErrorCode.objectNotFound.rawValue)
-        XCTAssertEqual(nsError.userInfo["ResponseErrorCode"] as? Int, 404)
-        let underlyingError = try XCTUnwrap(nsError.userInfo[NSUnderlyingErrorKey] as? NSError)
-        XCTAssertEqual(underlyingError.code, 404)
-        XCTAssertEqual(underlyingError.domain, "com.google.HTTPStatus")
-      }
-      XCTAssertTrue(caughtError)
+    } catch {
+      caughtError = true
+      let nsError = error as NSError
+      XCTAssertEqual(nsError.code, StorageErrorCode.objectNotFound.rawValue)
+      XCTAssertEqual(nsError.userInfo["ResponseErrorCode"] as? Int, 404)
+      let underlyingError = try XCTUnwrap(nsError.userInfo[NSUnderlyingErrorKey] as? NSError)
+      XCTAssertEqual(underlyingError.code, 404)
+      XCTAssertEqual(underlyingError.domain, "com.google.HTTPStatus")
     }
+    XCTAssertTrue(caughtError)
+  }
 
-    func testDeleteAfterPut() async throws {
-      let ref = storage.reference(withPath: "ios/public/fileToDelete")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
-      let result2: Void = try await ref.delete()
-      XCTAssertNotNil(result2)
-    }
+  func testDeleteAfterPut() async throws {
+    let ref = storage.reference(withPath: "ios/public/fileToDelete")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+    let result2: Void = try await ref.delete()
+    XCTAssertNotNil(result2)
+  }
 
-    func testSimplePutData() async throws {
-      let ref = storage.reference(withPath: "ios/public/testBytesUpload")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
-    }
+  func testSimplePutData() async throws {
+    let ref = storage.reference(withPath: "ios/public/testBytesUpload")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutSpecialCharacter() async throws {
-      let ref = storage.reference(withPath: "ios/public/-._~!$'()*,=:@&+;")
-      let data = try XCTUnwrap("Hello Swift World-._~!$'()*,=:@&+;".data(using: .utf8),
-                               "Data construction failed")
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
-    }
+  func testSimplePutSpecialCharacter() async throws {
+    let ref = storage.reference(withPath: "ios/public/-._~!$'()*,=:@&+;")
+    let data = try XCTUnwrap("Hello Swift World-._~!$'()*,=:@&+;".data(using: .utf8),
+                             "Data construction failed")
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutDataInBackgroundQueue() async throws {
-      actor Background {
-        func uploadData(_ ref: StorageReference) async throws -> StorageMetadata {
-          let data = try XCTUnwrap(
-            "Hello Swift World".data(using: .utf8),
-            "Data construction failed"
-          )
-          XCTAssertFalse(Thread.isMainThread)
-          return try await ref.putDataAsync(data)
-        }
+  func testSimplePutDataInBackgroundQueue() async throws {
+    actor Background {
+      func uploadData(_ ref: StorageReference) async throws -> StorageMetadata {
+        let data = try XCTUnwrap(
+          "Hello Swift World".data(using: .utf8),
+          "Data construction failed"
+        )
+        XCTAssertFalse(Thread.isMainThread)
+        return try await ref.putDataAsync(data)
       }
-      let ref = storage.reference(withPath: "ios/public/testBytesUpload")
-      let result = try await Background().uploadData(ref)
-      XCTAssertNotNil(result)
     }
+    let ref = storage.reference(withPath: "ios/public/testBytesUpload")
+    let result = try await Background().uploadData(ref)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutEmptyData() async throws {
-      let ref = storage.reference(withPath: "ios/public/testSimplePutEmptyData")
-      let data = Data()
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
-    }
+  func testSimplePutEmptyData() async throws {
+    let ref = storage.reference(withPath: "ios/public/testSimplePutEmptyData")
+    let data = Data()
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutDataUnauthorized() async throws {
-      let objectLocation = "ios/private/secretfile.txt"
-      let ref = storage.reference(withPath: objectLocation)
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      do {
-        _ = try await ref.putDataAsync(data)
-        XCTFail("Unexpected success from unauthorized putData")
-      } catch let StorageError.unauthorized(bucket, object) {
-        XCTAssertEqual(bucket, "ios-opensource-samples.appspot.com")
-        XCTAssertEqual(object, objectLocation)
-      } catch {
-        XCTFail("error failed to convert to StorageError.unauthorized")
-      }
+  func testSimplePutDataUnauthorized() async throws {
+    let objectLocation = "ios/private/secretfile.txt"
+    let ref = storage.reference(withPath: objectLocation)
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    do {
+      _ = try await ref.putDataAsync(data)
+      XCTFail("Unexpected success from unauthorized putData")
+    } catch let StorageError.unauthorized(bucket, object) {
+      XCTAssertEqual(bucket, "ios-opensource-samples.appspot.com")
+      XCTAssertEqual(object, objectLocation)
+    } catch {
+      XCTFail("error failed to convert to StorageError.unauthorized")
     }
+  }
 
-    func testAttemptToUploadDirectoryShouldFail() async throws {
-      // This `.numbers` file is actually a directory.
-      let fileName = "HomeImprovement.numbers"
-      let bundle = Bundle(for: StorageIntegrationCommon.self)
-      let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
-                                  "Failed to get filePath")
-      let ref = storage.reference(withPath: "ios/public/" + fileName)
-      do {
-        _ = try await ref.putFileAsync(from: fileURL)
-        XCTFail("Unexpected success from putFile of a directory")
-      } catch let StorageError.unknown(reason) {
-        XCTAssertTrue(reason.starts(with: "File at URL:"))
-        XCTAssertTrue(reason.hasSuffix(
-          "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
-        ))
-      } catch {
-        XCTFail("error failed to convert to StorageError.unknown")
-      }
+  func testAttemptToUploadDirectoryShouldFail() async throws {
+    // This `.numbers` file is actually a directory.
+    let fileName = "HomeImprovement.numbers"
+    let bundle = Bundle(for: StorageIntegrationCommon.self)
+    let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
+                                "Failed to get filePath")
+    let ref = storage.reference(withPath: "ios/public/" + fileName)
+    do {
+      _ = try await ref.putFileAsync(from: fileURL)
+      XCTFail("Unexpected success from putFile of a directory")
+    } catch let StorageError.unknown(reason) {
+      XCTAssertTrue(reason.starts(with: "File at URL:"))
+      XCTAssertTrue(reason.hasSuffix(
+        "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
+      ))
+    } catch {
+      XCTFail("error failed to convert to StorageError.unknown")
     }
+  }
 
-    func testPutFileWithSpecialCharacters() async throws {
-      let fileName = "hello&+@_ .txt"
-      let ref = storage.reference(withPath: "ios/public/" + fileName)
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-      let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
-      try data.write(to: fileURL, options: .atomicWrite)
-      let metadata = try await ref.putFileAsync(from: fileURL)
-      XCTAssertEqual(fileName, metadata.name)
-      let result = try await ref.getMetadata()
-      XCTAssertNotNil(result)
-    }
+  func testPutFileWithSpecialCharacters() async throws {
+    let fileName = "hello&+@_ .txt"
+    let ref = storage.reference(withPath: "ios/public/" + fileName)
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
+    try data.write(to: fileURL, options: .atomicWrite)
+    let metadata = try await ref.putFileAsync(from: fileURL)
+    XCTAssertEqual(fileName, metadata.name)
+    let result = try await ref.getMetadata()
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutDataNoMetadata() async throws {
-      let ref = storage.reference(withPath: "ios/public/testSimplePutDataNoMetadata")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let result = try await ref.putDataAsync(data)
-      XCTAssertNotNil(result)
-    }
+  func testSimplePutDataNoMetadata() async throws {
+    let ref = storage.reference(withPath: "ios/public/testSimplePutDataNoMetadata")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let result = try await ref.putDataAsync(data)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutFileNoMetadata() async throws {
-      let fileName = "hello&+@_ .txt"
-      let ref = storage.reference(withPath: "ios/public/" + fileName)
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-      let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
-      try data.write(to: fileURL, options: .atomicWrite)
-      let result = try await ref.putFileAsync(from: fileURL)
-      XCTAssertNotNil(result)
-    }
+  func testSimplePutFileNoMetadata() async throws {
+    let fileName = "hello&+@_ .txt"
+    let ref = storage.reference(withPath: "ios/public/" + fileName)
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
+    let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
+    try data.write(to: fileURL, options: .atomicWrite)
+    let result = try await ref.putFileAsync(from: fileURL)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimplePutFileWithAsyncProgress() async throws {
-      var checkedProgress = false
-      let ref = storage.reference(withPath: "ios/public/testSimplePutFile")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-      let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-      let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
-      try data.write(to: fileURL, options: .atomicWrite)
-      var uploadedBytes: Int64 = -1
-      let successMetadata = try await ref.putFileAsync(from: fileURL) { progress in
-        if let completed = progress?.completedUnitCount {
-          checkedProgress = true
-          XCTAssertGreaterThanOrEqual(completed, uploadedBytes)
-          uploadedBytes = completed
-        }
+  func testSimplePutFileWithAsyncProgress() async throws {
+    var checkedProgress = false
+    let ref = storage.reference(withPath: "ios/public/testSimplePutFile")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
+    try data.write(to: fileURL, options: .atomicWrite)
+    var uploadedBytes: Int64 = -1
+    let successMetadata = try await ref.putFileAsync(from: fileURL) { progress in
+      if let completed = progress?.completedUnitCount {
+        checkedProgress = true
+        XCTAssertGreaterThanOrEqual(completed, uploadedBytes)
+        uploadedBytes = completed
       }
-      XCTAssertEqual(successMetadata.size, 17)
-      XCTAssertTrue(checkedProgress)
     }
+    XCTAssertEqual(successMetadata.size, 17)
+    XCTAssertTrue(checkedProgress)
+  }
 
-    func testSimpleGetData() async throws {
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-      let result = try await ref.data(maxSize: 1024 * 1024)
-      XCTAssertNotNil(result)
-    }
+  func testSimpleGetData() async throws {
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+    let result = try await ref.data(maxSize: 1024 * 1024)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimpleGetDataWithTask() async throws {
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-      let result = try await ref.data(maxSize: 1024 * 1024)
-      XCTAssertNotNil(result)
-    }
+  func testSimpleGetDataWithTask() async throws {
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+    let result = try await ref.data(maxSize: 1024 * 1024)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimpleGetDataInBackgroundQueue() async throws {
-      actor Background {
-        func data(from ref: StorageReference) async throws -> Data {
-          XCTAssertFalse(Thread.isMainThread)
-          return try await ref.data(maxSize: 1024 * 1024)
-        }
+  func testSimpleGetDataInBackgroundQueue() async throws {
+    actor Background {
+      func data(from ref: StorageReference) async throws -> Data {
+        XCTAssertFalse(Thread.isMainThread)
+        return try await ref.data(maxSize: 1024 * 1024)
       }
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-      let result = try await Background().data(from: ref)
-      XCTAssertNotNil(result)
     }
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+    let result = try await Background().data(from: ref)
+    XCTAssertNotNil(result)
+  }
 
-    func testSimpleGetDataTooSmall() async {
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-      let max: Int64 = 1024
-      do {
-        _ = try await ref.data(maxSize: max)
-        XCTFail("Unexpected success from getData too small")
-      } catch let StorageError.downloadSizeExceeded(total, maxSize) {
-        XCTAssertEqual(total, 1_048_576)
-        XCTAssertEqual(maxSize, max)
-      } catch {
-        XCTFail("error failed to convert to StorageError.downloadSizeExceeded")
-      }
+  func testSimpleGetDataTooSmall() async {
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+    let max: Int64 = 1024
+    do {
+      _ = try await ref.data(maxSize: max)
+      XCTFail("Unexpected success from getData too small")
+    } catch let StorageError.downloadSizeExceeded(total, maxSize) {
+      XCTAssertEqual(total, 1_048_576)
+      XCTAssertEqual(maxSize, max)
+    } catch {
+      XCTFail("error failed to convert to StorageError.downloadSizeExceeded")
     }
+  }
 
-    func testSimpleGetDownloadURL() async throws {
-      let ref = storage.reference(withPath: "ios/public/1mb2")
+  func testSimpleGetDownloadURL() async throws {
+    let ref = storage.reference(withPath: "ios/public/1mb2")
 
-      // Download URL format is
-      // "https://firebasestorage.googleapis.com:443/v0/b/{bucket}/o/{path}?alt=media&token={token}"
-      let downloadURLPattern =
-        "^https:\\/\\/firebasestorage.googleapis.com:443\\/v0\\/b\\/[^\\/]*\\/o\\/" +
-        "ios%2Fpublic%2F1mb2\\?alt=media&token=[a-z0-9-]*$"
+    // Download URL format is
+    // "https://firebasestorage.googleapis.com:443/v0/b/{bucket}/o/{path}?alt=media&token={token}"
+    let downloadURLPattern =
+      "^https:\\/\\/firebasestorage.googleapis.com:443\\/v0\\/b\\/[^\\/]*\\/o\\/" +
+      "ios%2Fpublic%2F1mb2\\?alt=media&token=[a-z0-9-]*$"
 
-      let downloadURL = try await ref.downloadURL()
-      let testRegex = try NSRegularExpression(pattern: downloadURLPattern)
-      let urlString = downloadURL.absoluteString
-      let range = NSRange(location: 0, length: urlString.count)
-      XCTAssertNotNil(testRegex.firstMatch(in: urlString, options: [], range: range))
-    }
+    let downloadURL = try await ref.downloadURL()
+    let testRegex = try NSRegularExpression(pattern: downloadURLPattern)
+    let urlString = downloadURL.absoluteString
+    let range = NSRange(location: 0, length: urlString.count)
+    XCTAssertNotNil(testRegex.firstMatch(in: urlString, options: [], range: range))
+  }
 
-    func testAsyncWrite() async throws {
-      let ref = storage.reference(withPath: "ios/public/helloworld")
-      let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-      let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
+  func testAsyncWrite() async throws {
+    let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
+    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
 
-      _ = try await ref.putDataAsync(data)
-      let url = try await ref.writeAsync(toFile: fileURL)
-      XCTAssertEqual(url.lastPathComponent, "hello.txt")
-    }
+    _ = try await ref.putDataAsync(data)
+    let url = try await ref.writeAsync(toFile: fileURL)
+    XCTAssertEqual(url.lastPathComponent, #function + "hello.txt")
+  }
 
-    func testSimpleGetFile() throws {
-      let expectation = self.expectation(description: #function)
-      let ref = storage.reference(withPath: "ios/public/helloworld")
-      let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-      let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
-      let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
-
-      Task {
-        _ = try await ref.putDataAsync(data)
-        let task = ref.write(toFile: fileURL)
-
-        task.observe(StorageTaskStatus.success) { snapshot in
-          do {
-            let stringData = try String(contentsOf: fileURL, encoding: .utf8)
-            XCTAssertEqual(stringData, "Hello Swift World")
-            XCTAssertEqual(snapshot.description, "<State: Success>")
-          } catch {
-            XCTFail("Error processing success snapshot")
-          }
-          expectation.fulfill()
-        }
+  func testSimpleGetFile() throws {
+    let expectation = self.expectation(description: #function)
+    let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
+    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
+    let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
 
-        task.observe(StorageTaskStatus.progress) { snapshot in
-          XCTAssertNil(snapshot.error, "Error should be nil")
-          guard snapshot.progress != nil else {
-            XCTFail("Missing progress")
-            return
-          }
-        }
-        task.observe(StorageTaskStatus.failure) { snapshot in
-          XCTAssertNil(snapshot.error, "Error should be nil")
+    Task {
+      _ = try await ref.putDataAsync(data)
+      let task = ref.write(toFile: fileURL)
+
+      task.observe(StorageTaskStatus.success) { snapshot in
+        do {
+          let stringData = try String(contentsOf: fileURL, encoding: .utf8)
+          XCTAssertEqual(stringData, "Hello Swift World")
+          XCTAssertEqual(snapshot.description, "<State: Success>")
+        } catch {
+          XCTFail("Error processing success snapshot")
         }
+        expectation.fulfill()
       }
-      waitForExpectations()
-    }
 
-    func testSimpleGetFileWithAsyncProgressCallbackAPI() async throws {
-      var checkedProgress = false
-      let ref = storage.reference().child("ios/public/1mb")
-      let url = URL(fileURLWithPath: "\(NSTemporaryDirectory())/hello.txt")
-      let fileURL = url
-      var downloadedBytes: Int64 = 0
-      var resumeAtBytes = 256 * 1024
-      let successURL = try await ref.writeAsync(toFile: fileURL) { progress in
-        if let completed = progress?.completedUnitCount {
-          checkedProgress = true
-          XCTAssertGreaterThanOrEqual(completed, downloadedBytes)
-          downloadedBytes = completed
-          if completed > resumeAtBytes {
-            resumeAtBytes = Int.max
-          }
+      task.observe(StorageTaskStatus.progress) { snapshot in
+        XCTAssertNil(snapshot.error, "Error should be nil")
+        guard snapshot.progress != nil else {
+          XCTFail("Missing progress")
+          return
         }
       }
-      XCTAssertTrue(checkedProgress)
-      XCTAssertEqual(successURL, url)
-      XCTAssertEqual(resumeAtBytes, Int.max)
+      task.observe(StorageTaskStatus.failure) { snapshot in
+        XCTAssertNil(snapshot.error, "Error should be nil")
+      }
     }
+    waitForExpectations()
+  }
 
-    private func assertMetadata(actualMetadata: StorageMetadata,
-                                expectedContentType: String,
-                                expectedCustomMetadata: [String: String]) {
-      XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
-      XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
-      XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
-      XCTAssertEqual(actualMetadata.contentLanguage, "de")
-      XCTAssertEqual(actualMetadata.contentType, expectedContentType)
-      XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
-      for (key, value) in expectedCustomMetadata {
-        XCTAssertEqual(actualMetadata.customMetadata![key], value)
+  func testSimpleGetFileWithAsyncProgressCallbackAPI() async throws {
+    var checkedProgress = false
+    let ref = storage.reference().child("ios/public/1mb")
+    let url = URL(fileURLWithPath: "\(NSTemporaryDirectory())/hello.txt")
+    let fileURL = url
+    var downloadedBytes: Int64 = 0
+    var resumeAtBytes = 256 * 1024
+    let successURL = try await ref.writeAsync(toFile: fileURL) { progress in
+      if let completed = progress?.completedUnitCount {
+        checkedProgress = true
+        XCTAssertGreaterThanOrEqual(completed, downloadedBytes)
+        downloadedBytes = completed
+        if completed > resumeAtBytes {
+          resumeAtBytes = Int.max
+        }
       }
     }
+    XCTAssertTrue(checkedProgress)
+    XCTAssertEqual(successURL, url)
+    XCTAssertEqual(resumeAtBytes, Int.max)
+  }
 
-    private func assertMetadataNil(actualMetadata: StorageMetadata) {
-      XCTAssertNil(actualMetadata.cacheControl)
-      XCTAssertNil(actualMetadata.contentDisposition)
-      XCTAssertEqual(actualMetadata.contentEncoding, "identity")
-      XCTAssertNil(actualMetadata.contentLanguage)
-      XCTAssertNil(actualMetadata.contentType)
-      XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
-      XCTAssertNil(actualMetadata.customMetadata)
+  private func assertMetadata(actualMetadata: StorageMetadata,
+                              expectedContentType: String,
+                              expectedCustomMetadata: [String: String]) {
+    XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
+    XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
+    XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
+    XCTAssertEqual(actualMetadata.contentLanguage, "de")
+    XCTAssertEqual(actualMetadata.contentType, expectedContentType)
+    XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
+    for (key, value) in expectedCustomMetadata {
+      XCTAssertEqual(actualMetadata.customMetadata![key], value)
     }
+  }
 
-    func testUpdateMetadata2() async throws {
-      let ref = storage.reference(withPath: "ios/public/1mb2")
-
-      let metadata = StorageMetadata()
-      metadata.cacheControl = "cache-control"
-      metadata.contentDisposition = "content-disposition"
-      metadata.contentEncoding = "gzip"
-      metadata.contentLanguage = "de"
-      metadata.contentType = "content-type-a"
-      metadata.customMetadata = ["a": "b"]
-
-      let updatedMetadata = try await ref.updateMetadata(metadata)
-      assertMetadata(actualMetadata: updatedMetadata,
-                     expectedContentType: "content-type-a",
-                     expectedCustomMetadata: ["a": "b"])
-
-      let metadata2 = updatedMetadata
-      metadata2.contentType = "content-type-b"
-      metadata2.customMetadata = ["a": "b", "c": "d"]
-
-      let metadata3 = try await ref.updateMetadata(metadata2)
-      assertMetadata(actualMetadata: metadata3,
-                     expectedContentType: "content-type-b",
-                     expectedCustomMetadata: ["a": "b", "c": "d"])
-      metadata.cacheControl = nil
-      metadata.contentDisposition = nil
-      metadata.contentEncoding = nil
-      metadata.contentLanguage = nil
-      metadata.contentType = nil
-      metadata.customMetadata = nil
-      let metadata4 = try await ref.updateMetadata(metadata)
-      XCTAssertNotNil(metadata4)
-    }
+  private func assertMetadataNil(actualMetadata: StorageMetadata) {
+    XCTAssertNil(actualMetadata.cacheControl)
+    XCTAssertNil(actualMetadata.contentDisposition)
+    XCTAssertEqual(actualMetadata.contentEncoding, "identity")
+    XCTAssertNil(actualMetadata.contentLanguage)
+    XCTAssertNil(actualMetadata.contentType)
+    XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
+    XCTAssertNil(actualMetadata.customMetadata)
+  }
 
-    func testPagedListFiles() async throws {
-      let ref = storage.reference(withPath: "ios/public/list")
-      let listResult = try await ref.list(maxResults: 2)
-      XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
-      XCTAssertEqual(listResult.prefixes, [])
-      let pageToken = try XCTUnwrap(listResult.pageToken)
-      let listResult2 = try await ref.list(maxResults: 2, pageToken: pageToken)
-      XCTAssertEqual(listResult2.items, [])
-      XCTAssertEqual(listResult2.prefixes, [ref.child("prefix")])
-      XCTAssertNil(listResult2.pageToken, "pageToken should be nil")
-    }
+  func testUpdateMetadata2() async throws {
+    let ref = storage.reference(withPath: "ios/public/1mb2")
+
+    let metadata = StorageMetadata()
+    metadata.cacheControl = "cache-control"
+    metadata.contentDisposition = "content-disposition"
+    metadata.contentEncoding = "gzip"
+    metadata.contentLanguage = "de"
+    metadata.contentType = "content-type-a"
+    metadata.customMetadata = ["a": "b"]
+
+    let updatedMetadata = try await ref.updateMetadata(metadata)
+    assertMetadata(actualMetadata: updatedMetadata,
+                   expectedContentType: "content-type-a",
+                   expectedCustomMetadata: ["a": "b"])
+
+    let metadata2 = updatedMetadata
+    metadata2.contentType = "content-type-b"
+    metadata2.customMetadata = ["a": "b", "c": "d"]
+
+    let metadata3 = try await ref.updateMetadata(metadata2)
+    assertMetadata(actualMetadata: metadata3,
+                   expectedContentType: "content-type-b",
+                   expectedCustomMetadata: ["a": "b", "c": "d"])
+    metadata.cacheControl = nil
+    metadata.contentDisposition = nil
+    metadata.contentEncoding = nil
+    metadata.contentLanguage = nil
+    metadata.contentType = nil
+    metadata.customMetadata = nil
+    let metadata4 = try await ref.updateMetadata(metadata)
+    XCTAssertNotNil(metadata4)
+  }
 
-    func testPagedListFilesError() async throws {
-      let ref = storage.reference(withPath: "ios/public/list")
-      do {
-        let _: StorageListResult = try await ref.list(maxResults: 22222)
-        XCTFail("Unexpected success from ref.list")
-      } catch let StorageError.invalidArgument(message) {
-        XCTAssertEqual(message, "Argument 'maxResults' must be between 1 and 1000 inclusive.")
-      } catch {
-        XCTFail("Unexpected error")
-      }
-    }
+  func testPagedListFiles() async throws {
+    let ref = storage.reference(withPath: "ios/public/list")
+    let listResult = try await ref.list(maxResults: 2)
+    XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
+    XCTAssertEqual(listResult.prefixes, [])
+    let pageToken = try XCTUnwrap(listResult.pageToken)
+    let listResult2 = try await ref.list(maxResults: 2, pageToken: pageToken)
+    XCTAssertEqual(listResult2.items, [])
+    XCTAssertEqual(listResult2.prefixes, [ref.child("prefix")])
+    XCTAssertNil(listResult2.pageToken, "pageToken should be nil")
+  }
 
-    func testListAllFiles() async throws {
-      let ref = storage.reference(withPath: "ios/public/list")
-      let listResult = try await ref.listAll()
-      XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
-      XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
-      XCTAssertNil(listResult.pageToken, "pageToken should be nil")
+  func testPagedListFilesError() async throws {
+    let ref = storage.reference(withPath: "ios/public/list")
+    do {
+      let _: StorageListResult = try await ref.list(maxResults: 22222)
+      XCTFail("Unexpected success from ref.list")
+    } catch let StorageError.invalidArgument(message) {
+      XCTAssertEqual(message, "Argument 'maxResults' must be between 1 and 1000 inclusive.")
+    } catch {
+      XCTFail("Unexpected error")
     }
+  }
 
-    private func waitForExpectations() {
-      let kTestTimeout = 60.0
-      waitForExpectations(timeout: kTestTimeout,
-                          handler: { error in
-                            if let error = error {
-                              print(error)
-                            }
-                          })
-    }
+  func testListAllFiles() async throws {
+    let ref = storage.reference(withPath: "ios/public/list")
+    let listResult = try await ref.listAll()
+    XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
+    XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
+    XCTAssertNil(listResult.pageToken, "pageToken should be nil")
+  }
+
+  private func waitForExpectations() {
+    let kTestTimeout = 60.0
+    waitForExpectations(timeout: kTestTimeout,
+                        handler: { error in
+                          if let error = error {
+                            print(error)
+                          }
+                        })
   }
-#endif
+}

+ 5 - 5
FirebaseStorage/Tests/Integration/StorageIntegration.swift

@@ -535,9 +535,9 @@ class StorageResultTests: StorageIntegrationCommon {
 
   func testSimpleGetFile() throws {
     let expectation = self.expectation(description: #function)
-    let ref = storage.reference(withPath: "ios/public/helloworld")
+    let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
     let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-    let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
     let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
 
     ref.putData(data) { result in
@@ -577,16 +577,15 @@ class StorageResultTests: StorageIntegrationCommon {
 
   func testCancelErrorCode() throws {
     let expectation = self.expectation(description: #function)
-    let ref = storage.reference(withPath: "ios/public/helloworld")
+    let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
     let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
-    let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
+    let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
     let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
 
     ref.putData(data) { result in
       switch result {
       case .success:
         let task = ref.write(toFile: fileURL)
-        task.cancel()
 
         task.observe(StorageTaskStatus.success) { snapshot in
           XCTFail("Error processing success snapshot")
@@ -603,6 +602,7 @@ class StorageResultTests: StorageIntegrationCommon {
           }
           expectation.fulfill()
         }
+        task.cancel()
       case let .failure(error):
         XCTFail("Unexpected error \(error) from putData")
         expectation.fulfill()

+ 5 - 0
Firestore/CHANGELOG.md

@@ -1,3 +1,8 @@
+# Unreleased
+- Add an error when trying to build Firestore's binary SPM distribution for
+  visionOS (#12279). See Firestore's 10.12.0 release note for a supported
+  workaround.
+
 # 10.19.0
 - [fixed] Made an optimization to the synchronization logic for resumed queries
   to only re-download locally-cached documents that are known to be out-of-sync. (#12044)

+ 1 - 1
Firestore/Example/FuzzTests/FuzzingResources/Serializer/Corpus/ConvertTextToBinary.sh

@@ -18,7 +18,7 @@
 # folder defined by SCRIPT_INPUT_FILE_0 and the generated binary protos are
 # stored in the folder defined by SCRIPT_OUTPUT_FILE_0. Both SCRIPT_INPUT_FILE_0
 # and SCRIPT_OUTPUT_FILE_0 are defined in the Run Script Build Phase of the
-# XCode build target Firestore_FuzzTests_iOS that executes this script. XCode
+# Xcode build target Firestore_FuzzTests_iOS that executes this script. Xcode
 # defines these environment variables and makes them available to the script.
 #
 # By default Xcode build phase scripts run in a stripped down environment that

+ 1 - 1
Firestore/Source/Public/FirebaseFirestore/FIRAggregateSource.h

@@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, FIRAggregateSource) {
    * The result received from the server is presented, unaltered, without considering any local
    * state. That is, documents in the local cache are not taken into consideration, neither are
    * local modifications not yet synchronized with the server. Previously-downloaded results, if
-   * any, are not used: every request using this source necessarily involves a round trip to the
+   * any, are not used. Every request using this source necessarily involves a round trip to the
    * server.
    *
    * The `AggregateQuery` will fail if the server cannot be reached, such as if the client is

+ 8 - 7
Firestore/Source/Public/FirebaseFirestore/FIRQuery.h

@@ -556,22 +556,23 @@ NS_SWIFT_NAME(Query)
 #pragma mark - Aggregation
 
 /**
- * A query that counts the documents in the result set of this query, without actually downloading
+ * A query that counts the documents in the result set of this query without actually downloading
  * the documents.
  *
- * Using this `AggregateQuery` to count the documents is efficient because only the final count,
- * not the documents' data, is downloaded. This allows for counting document collections that would
- * otherwise be too large to download (e.g. containing thousands of documents).
+ * Using this `AggregateQuery` to count the documents is efficient because only the final count, not
+ * the documents' data, is downloaded. The `AggregateQuery` can count the documents in cases where
+ * the result set is prohibitively large to download entirely (thousands of documents).
  */
 @property(nonatomic, readonly) FIRAggregateQuery *count;
 
 /**
  * Creates and returns a new `AggregateQuery` that aggregates the documents in the result set
- * of this query, without actually downloading the documents.
+ * of this query without actually downloading the documents.
  *
  * Using an `AggregateQuery` to perform aggregations is efficient because only the final aggregation
- * values, not the documents' data, is downloaded. This allows for aggregating document collections
- * that would otherwise be too large to download (e.g. containing thousands of documents).
+ * values, not the documents' data, is downloaded. The returned `AggregateQuery` can perform
+ * aggregations of the documents in cases where the result set is prohibitively large to download
+ * entirely (thousands of documents).
  *
  * @param aggregateFields Specifies the aggregate operations to perform on the result set of this
  * query.

+ 18 - 20
Firestore/Swift/Source/AsyncAwait/CollectionReference+AsyncAwait.swift

@@ -21,27 +21,25 @@
 #endif // SWIFT_PACKAGE
 import Foundation
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  public extension CollectionReference {
-    /// Adds a new document to this collection with the specified data, assigning it a document ID
-    /// automatically.
-    /// - Parameter data: A `Dictionary` containing the data for the new document.
-    /// - Throws: `Error` if the backend rejected the write.
-    /// - Returns: A `DocumentReference` pointing to the newly created document.
-    @discardableResult
-    func addDocument(data: [String: Any]) async throws -> DocumentReference {
-      return try await withCheckedThrowingContinuation { continuation in
-        var document: DocumentReference?
-        document = self.addDocument(data: data) { error in
-          if let err = error {
-            continuation.resume(throwing: err)
-          } else {
-            // Our callbacks guarantee that we either return an error or a document.
-            continuation.resume(returning: document!)
-          }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+public extension CollectionReference {
+  /// Adds a new document to this collection with the specified data, assigning it a document ID
+  /// automatically.
+  /// - Parameter data: A `Dictionary` containing the data for the new document.
+  /// - Throws: `Error` if the backend rejected the write.
+  /// - Returns: A `DocumentReference` pointing to the newly created document.
+  @discardableResult
+  func addDocument(data: [String: Any]) async throws -> DocumentReference {
+    return try await withCheckedThrowingContinuation { continuation in
+      var document: DocumentReference?
+      document = self.addDocument(data: data) { error in
+        if let err = error {
+          continuation.resume(throwing: err)
+        } else {
+          // Our callbacks guarantee that we either return an error or a document.
+          continuation.resume(returning: document!)
         }
       }
     }
   }
-#endif
+}

+ 83 - 85
Firestore/Swift/Source/AsyncAwait/Firestore+AsyncAwait.swift

@@ -21,100 +21,98 @@
 #endif // SWIFT_PACKAGE
 import Foundation
 
-#if compiler(>=5.5.2) && canImport(_Concurrency)
-  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-  public extension Firestore {
-    /// Loads a Firestore bundle into the local cache.
-    /// - Parameter bundleData: Data from the bundle to be loaded.
-    /// - Throws: `Error` if the bundle data cannot be parsed.
-    /// - Returns: The final `LoadBundleTaskProgress` that contains the total number of documents
-    /// loaded.
-    func loadBundle(_ bundleData: Data) async throws -> LoadBundleTaskProgress {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.loadBundle(bundleData) { progress, error in
-          if let err = error {
-            continuation.resume(throwing: err)
-          } else {
-            // Our callbacks guarantee that we either return an error or a progress event.
-            continuation.resume(returning: progress!)
-          }
+@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+public extension Firestore {
+  /// Loads a Firestore bundle into the local cache.
+  /// - Parameter bundleData: Data from the bundle to be loaded.
+  /// - Throws: `Error` if the bundle data cannot be parsed.
+  /// - Returns: The final `LoadBundleTaskProgress` that contains the total number of documents
+  /// loaded.
+  func loadBundle(_ bundleData: Data) async throws -> LoadBundleTaskProgress {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.loadBundle(bundleData) { progress, error in
+        if let err = error {
+          continuation.resume(throwing: err)
+        } else {
+          // Our callbacks guarantee that we either return an error or a progress event.
+          continuation.resume(returning: progress!)
         }
       }
     }
+  }
 
-    /// Loads a Firestore bundle into the local cache.
-    /// - Parameter bundleStream: An input stream from which the bundle can be read.
-    /// - Throws: `Error` if the bundle stream cannot be parsed.
-    /// - Returns: The final `LoadBundleTaskProgress` that contains the total number of documents
-    /// loaded.
-    func loadBundle(_ bundleStream: InputStream) async throws -> LoadBundleTaskProgress {
-      return try await withCheckedThrowingContinuation { continuation in
-        self.loadBundle(bundleStream) { progress, error in
-          if let err = error {
-            continuation.resume(throwing: err)
-          } else {
-            // Our callbacks guarantee that we either return an error or a progress event.
-            continuation.resume(returning: progress!)
-          }
+  /// Loads a Firestore bundle into the local cache.
+  /// - Parameter bundleStream: An input stream from which the bundle can be read.
+  /// - Throws: `Error` if the bundle stream cannot be parsed.
+  /// - Returns: The final `LoadBundleTaskProgress` that contains the total number of documents
+  /// loaded.
+  func loadBundle(_ bundleStream: InputStream) async throws -> LoadBundleTaskProgress {
+    return try await withCheckedThrowingContinuation { continuation in
+      self.loadBundle(bundleStream) { progress, error in
+        if let err = error {
+          continuation.resume(throwing: err)
+        } else {
+          // Our callbacks guarantee that we either return an error or a progress event.
+          continuation.resume(returning: progress!)
         }
       }
     }
+  }
 
-    /// Executes the given updateBlock and then attempts to commit the changes applied within an
-    /// atomic
-    /// transaction.
-    ///
-    /// The maximum number of writes allowed in a single transaction is 500, but note that each
-    /// usage of
-    /// `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
-    /// `FieldValue.increment()` inside a transaction counts as an additional write.
-    ///
-    /// In the `updateBlock`, a set of reads and writes can be performed atomically using the
-    /// `Transaction` object passed to the block. After the `updateBlock` is run, Firestore will
-    /// attempt
-    /// to apply the changes to the server. If any of the data read has been modified outside of
-    /// this
-    /// transaction since being read, then the transaction will be retried by executing the
-    /// `updateBlock`
-    /// again. If the transaction still fails after 5 retries, then the transaction will fail.
-    ///
-    /// Since the `updateBlock` may be executed multiple times, it should avoiding doing anything
-    /// that
-    /// would cause side effects.
-    ///
-    /// Any value maybe be returned from the `updateBlock`. If the transaction is successfully
-    /// committed,
-    /// then the completion block will be passed that value. The `updateBlock` also has an `NSError`
-    /// out
-    /// parameter. If this is set, then the transaction will not attempt to commit, and the given
-    /// error
-    /// will be returned.
-    ///
-    /// The `Transaction` object passed to the `updateBlock` contains methods for accessing
-    /// documents
-    /// and collections. Unlike other firestore access, data accessed with the transaction will not
-    /// reflect local changes that have not been committed. For this reason, it is required that all
-    /// reads are performed before any writes. Transactions must be performed while online.
-    /// Otherwise,
-    /// reads will fail, the final commit will fail, and this function will return an error.
-    ///
-    /// - Parameter updateBlock The block to execute within the transaction context.
-    /// - Throws Throws an error if the transaction could not be committed, or if an error was
-    /// explicitly specified in the `updateBlock` parameter.
-    /// - Returns Returns the value returned in the `updateBlock` parameter if no errors occurred.
-    func runTransaction(_ updateBlock: @escaping (Transaction, NSErrorPointer)
-      -> Any?) async throws -> Any? {
-      // This needs to be wrapped in order to express a nullable return value upon success.
-      // See https://github.com/firebase/firebase-ios-sdk/issues/9426 for more details.
-      return try await withCheckedThrowingContinuation { continuation in
-        self.runTransaction(updateBlock) { anyValue, error in
-          if let err = error {
-            continuation.resume(throwing: err)
-          } else {
-            continuation.resume(returning: anyValue)
-          }
+  /// Executes the given updateBlock and then attempts to commit the changes applied within an
+  /// atomic
+  /// transaction.
+  ///
+  /// The maximum number of writes allowed in a single transaction is 500, but note that each
+  /// usage of
+  /// `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
+  /// `FieldValue.increment()` inside a transaction counts as an additional write.
+  ///
+  /// In the `updateBlock`, a set of reads and writes can be performed atomically using the
+  /// `Transaction` object passed to the block. After the `updateBlock` is run, Firestore will
+  /// attempt
+  /// to apply the changes to the server. If any of the data read has been modified outside of
+  /// this
+  /// transaction since being read, then the transaction will be retried by executing the
+  /// `updateBlock`
+  /// again. If the transaction still fails after 5 retries, then the transaction will fail.
+  ///
+  /// Since the `updateBlock` may be executed multiple times, it should avoiding doing anything
+  /// that
+  /// would cause side effects.
+  ///
+  /// Any value maybe be returned from the `updateBlock`. If the transaction is successfully
+  /// committed,
+  /// then the completion block will be passed that value. The `updateBlock` also has an `NSError`
+  /// out
+  /// parameter. If this is set, then the transaction will not attempt to commit, and the given
+  /// error
+  /// will be returned.
+  ///
+  /// The `Transaction` object passed to the `updateBlock` contains methods for accessing
+  /// documents
+  /// and collections. Unlike other firestore access, data accessed with the transaction will not
+  /// reflect local changes that have not been committed. For this reason, it is required that all
+  /// reads are performed before any writes. Transactions must be performed while online.
+  /// Otherwise,
+  /// reads will fail, the final commit will fail, and this function will return an error.
+  ///
+  /// - Parameter updateBlock The block to execute within the transaction context.
+  /// - Throws Throws an error if the transaction could not be committed, or if an error was
+  /// explicitly specified in the `updateBlock` parameter.
+  /// - Returns Returns the value returned in the `updateBlock` parameter if no errors occurred.
+  func runTransaction(_ updateBlock: @escaping (Transaction, NSErrorPointer)
+    -> Any?) async throws -> Any? {
+    // This needs to be wrapped in order to express a nullable return value upon success.
+    // See https://github.com/firebase/firebase-ios-sdk/issues/9426 for more details.
+    return try await withCheckedThrowingContinuation { continuation in
+      self.runTransaction(updateBlock) { anyValue, error in
+        if let err = error {
+          continuation.resume(throwing: err)
+        } else {
+          continuation.resume(returning: anyValue)
         }
       }
     }
   }
-#endif
+}

+ 36 - 38
Firestore/Swift/Source/Codable/DocumentReference+ReadDecodable.swift

@@ -76,42 +76,40 @@ public extension DocumentReference {
     }
   }
 
-  #if compiler(>=5.5.2) && canImport(_Concurrency)
-    /// Fetches and decodes the document referenced by this `DocumentReference`.
-    ///
-    /// This allows users to retrieve a Firestore document and have it decoded
-    /// to an instance of caller-specified type as follows:
-    /// ```swift
-    /// do {
-    ///   let book = try await ref.getDocument(as: Book.self)
-    /// } catch {
-    ///   // Handle error
-    /// }
-    /// ```
-    ///
-    /// This method attempts to provide up-to-date data when possible by waiting
-    /// for data from the server, but it may return cached data or fail if you
-    /// are offline and the server cannot be reached. If `T` denotes
-    /// an optional type, the method returns a successful status with a value
-    /// of `nil` for non-existing documents.
-    ///
-    /// - Parameters:
-    ///   - as: A `Decodable` type to convert the document fields to.
-    ///   - serverTimestampBehavior: Configures how server timestamps that have
-    ///     not yet been set to their final value are returned from the
-    ///     snapshot.
-    ///   - decoder: The decoder to use to convert the document. Defaults to use
-    ///     the default decoder.
-    /// - Returns: This instance of the supplied `Decodable` type `T`.
-    @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
-    func getDocument<T: Decodable>(as type: T.Type,
-                                   with serverTimestampBehavior: ServerTimestampBehavior =
-                                     .none,
-                                   decoder: Firestore.Decoder = .init()) async throws -> T {
-      let snapshot = try await getDocument()
-      return try snapshot.data(as: T.self,
-                               with: serverTimestampBehavior,
-                               decoder: decoder)
-    }
-  #endif
+  /// Fetches and decodes the document referenced by this `DocumentReference`.
+  ///
+  /// This allows users to retrieve a Firestore document and have it decoded
+  /// to an instance of caller-specified type as follows:
+  /// ```swift
+  /// do {
+  ///   let book = try await ref.getDocument(as: Book.self)
+  /// } catch {
+  ///   // Handle error
+  /// }
+  /// ```
+  ///
+  /// This method attempts to provide up-to-date data when possible by waiting
+  /// for data from the server, but it may return cached data or fail if you
+  /// are offline and the server cannot be reached. If `T` denotes
+  /// an optional type, the method returns a successful status with a value
+  /// of `nil` for non-existing documents.
+  ///
+  /// - Parameters:
+  ///   - as: A `Decodable` type to convert the document fields to.
+  ///   - serverTimestampBehavior: Configures how server timestamps that have
+  ///     not yet been set to their final value are returned from the
+  ///     snapshot.
+  ///   - decoder: The decoder to use to convert the document. Defaults to use
+  ///     the default decoder.
+  /// - Returns: This instance of the supplied `Decodable` type `T`.
+  @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
+  func getDocument<T: Decodable>(as type: T.Type,
+                                 with serverTimestampBehavior: ServerTimestampBehavior =
+                                   .none,
+                                 decoder: Firestore.Decoder = .init()) async throws -> T {
+    let snapshot = try await getDocument()
+    return try snapshot.data(as: T.self,
+                             with: serverTimestampBehavior,
+                             decoder: decoder)
+  }
 }

+ 1 - 1
Firestore/core/CMakeLists.txt

@@ -276,7 +276,7 @@ endif()
 
 ## gRPC Certificates
 
-# Source files should be generated in place so that the XCode build can pick
+# Source files should be generated in place so that the Xcode build can pick
 # them up.
 set(OUTPUT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/remote)
 

+ 1 - 1
Firestore/core/src/nanopb/pretty_printing.cc

@@ -46,7 +46,7 @@ std::string ToString(float value) {
   // TODO(varconst): raise the precision.
   // The Objective-C protobuf library would use higher precision. E.g., it would
   // output 1.79769313486232e+308 in case where this implementation would only
-  // output 1.79769e+308 (tested in XCode 11, iOS simulator).
+  // output 1.79769e+308 (tested in Xcode 11, iOS simulator).
   std::ostringstream stream;
   stream << value;
   return stream.str();

+ 19 - 0
Firestore/core/src/util/filesystem_apple.mm

@@ -22,6 +22,7 @@
 
 #include "Firestore/core/src/util/path.h"
 #include "Firestore/core/src/util/statusor.h"
+#include "Firestore/core/src/util/string_format.h"
 #include "absl/strings/str_cat.h"
 
 namespace firebase {
@@ -90,6 +91,24 @@ Path Filesystem::TempDir() {
   return Path::FromUtf8("/tmp");
 }
 
+Status Filesystem::IsDirectory(const Path& path) {
+  NSFileManager* file_manager = NSFileManager.defaultManager;
+  NSString* ns_path_str = path.ToNSString();
+  BOOL is_directory = NO;
+
+  if (![file_manager fileExistsAtPath:ns_path_str isDirectory:&is_directory]) {
+    return Status{Error::kErrorNotFound, path.ToUtf8String()};
+  }
+
+  if (!is_directory) {
+    return Status{Error::kErrorFailedPrecondition,
+                  StringFormat("Path %s exists but is not a directory",
+                               path.ToUtf8String())};
+  }
+
+  return Status::OK();
+}
+
 }  // namespace util
 }  // namespace firestore
 }  // namespace firebase

+ 2 - 0
Firestore/core/src/util/filesystem_posix.cc

@@ -130,6 +130,7 @@ Path Filesystem::TempDir() {
 }
 #endif  // !__APPLE__ && !_WIN32
 
+#if !__APPLE__
 Status Filesystem::IsDirectory(const Path& path) {
   struct stat buffer {};
   if (::stat(path.c_str(), &buffer)) {
@@ -165,6 +166,7 @@ Status Filesystem::IsDirectory(const Path& path) {
 
   return Status::OK();
 }
+#endif  // !__APPLE__
 
 StatusOr<int64_t> Filesystem::FileSize(const Path& path) {
   struct stat st {};

+ 7 - 7
Firestore/core/src/util/string_format.h

@@ -80,7 +80,7 @@ class FormatArg : public absl::AlphaNum {
   template <typename T,
             typename = typename std::enable_if<std::is_same<bool, T>{}>::type>
   FormatArg(T bool_value, internal::FormatChoice<0>)
-      : AlphaNum{bool_value ? "true" : "false"} {
+      : AlphaNum(bool_value ? "true" : "false") {
   }
 
 #if __OBJC__
@@ -91,7 +91,7 @@ class FormatArg : public absl::AlphaNum {
       typename T,
       typename = typename std::enable_if<objc::is_objc_pointer<T>{}>::type>
   FormatArg(T object, internal::FormatChoice<1>)
-      : AlphaNum{MakeStringView([object description])} {
+      : AlphaNum(MakeStringView([object description])) {
   }
 
   /**
@@ -99,7 +99,7 @@ class FormatArg : public absl::AlphaNum {
    * types are a special struct that aren't of a type derived from NSObject.
    */
   FormatArg(Class object, internal::FormatChoice<1>)
-      : AlphaNum{MakeStringView(NSStringFromClass(object))} {
+      : AlphaNum(MakeStringView(NSStringFromClass(object))) {
   }
 #endif
 
@@ -108,7 +108,7 @@ class FormatArg : public absl::AlphaNum {
    * handled specially to avoid ambiguity with generic pointers, which are
    * handled differently.
    */
-  FormatArg(std::nullptr_t, internal::FormatChoice<2>) : AlphaNum{"null"} {
+  FormatArg(std::nullptr_t, internal::FormatChoice<2>) : AlphaNum("null") {
   }
 
   /**
@@ -117,7 +117,7 @@ class FormatArg : public absl::AlphaNum {
    * handled differently.
    */
   FormatArg(const char* string_value, internal::FormatChoice<3>)
-      : AlphaNum{string_value == nullptr ? "null" : string_value} {
+      : AlphaNum(string_value == nullptr ? "null" : string_value) {
   }
 
   /**
@@ -126,7 +126,7 @@ class FormatArg : public absl::AlphaNum {
    */
   template <typename T>
   FormatArg(T* pointer_value, internal::FormatChoice<4>)
-      : AlphaNum{absl::Hex{reinterpret_cast<uintptr_t>(pointer_value)}} {
+      : AlphaNum(absl::Hex(reinterpret_cast<uintptr_t>(pointer_value))) {
   }
 
   /**
@@ -135,7 +135,7 @@ class FormatArg : public absl::AlphaNum {
    */
   template <typename T>
   FormatArg(T&& value, internal::FormatChoice<5>)
-      : AlphaNum{std::forward<T>(value)} {
+      : AlphaNum(std::forward<T>(value)) {
   }
 };
 

+ 3 - 3
Firestore/core/test/unit/FSTGoogleTestTests.mm

@@ -231,19 +231,19 @@ void XCTestMethod(XCTestCase* self, SEL _cmd) {
 
   const testing::TestResult* result = testInfo->result();
   if (result->Passed()) {
-    // Let XCode know that the test ran and succeeded.
+    // Let Xcode know that the test ran and succeeded.
     XCTAssertTrue(true);
     return;
   } else if (result->Skipped()) {
 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400 || \
     __TV_OS_VERSION_MAX_ALLOWED >= 130400 ||     \
     __MAC_OS_X_VERSION_MAX_ALLOWED >= 101504
-    // Let XCode know that the test was skipped.
+    // Let Xcode know that the test was skipped.
     XCTSkip();
 #endif
   }
 
-  // Test failed :-(. Record the failure such that XCode will navigate directly
+  // Test failed :-(. Record the failure such that Xcode will navigate directly
   // to the file:line.
   int parts = result->total_part_count();
   for (int i = 0; i < parts; i++) {

+ 3 - 3
GoogleAppMeasurement.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = 'GoogleAppMeasurement'
-    s.version          = '10.20.0'
+    s.version          = '10.21.0'
     s.summary          = 'Shared measurement methods for Google libraries. Not intended for direct use.'
 
     s.description      = <<-DESC
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
     s.authors          = 'Google, Inc.'
 
     s.source           = {
-        :http => 'https://dl.google.com/firebase/ios/analytics/ef96177ad70fa3e1/GoogleAppMeasurement-10.17.0.tar.gz'
+        :http => 'https://dl.google.com/firebase/ios/analytics/3fcc7b954e5d5458/GoogleAppMeasurement-10.20.0.tar.gz'
     }
 
     s.cocoapods_version = '>= 1.10.2'
@@ -37,7 +37,7 @@ Pod::Spec.new do |s|
     s.default_subspecs = 'AdIdSupport'
 
     s.subspec 'AdIdSupport' do |ss|
-        ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '10.20.0'
+        ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '10.21.0'
         ss.vendored_frameworks = 'Frameworks/GoogleAppMeasurementIdentitySupport.xcframework'
     end
 

+ 2 - 2
GoogleAppMeasurementOnDeviceConversion.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
     s.name             = 'GoogleAppMeasurementOnDeviceConversion'
-    s.version          = '10.20.0'
+    s.version          = '10.21.0'
     s.summary          = <<-SUMMARY
     On device conversion measurement plugin for Google App Measurement. Not
     intended for direct use.
@@ -17,7 +17,7 @@ Pod::Spec.new do |s|
     s.authors          = 'Google, Inc.'
 
     s.source           = {
-        :http => 'https://dl.google.com/firebase/ios/analytics/8cd8d72af8f1a22b/GoogleAppMeasurementOnDeviceConversion-10.17.0.tar.gz'
+        :http => 'https://dl.google.com/firebase/ios/analytics/4ab453c686c6aac4/GoogleAppMeasurementOnDeviceConversion-10.20.0.tar.gz'
     }
 
     s.cocoapods_version = '>= 1.10.2'

+ 22 - 10
Package.swift

@@ -19,7 +19,7 @@
 import class Foundation.ProcessInfo
 import PackageDescription
 
-let firebaseVersion = "10.20.0"
+let firebaseVersion = "10.21.0"
 
 let package = Package(
   name: "Firebase",
@@ -310,8 +310,8 @@ let package = Package(
     ),
     .binaryTarget(
       name: "FirebaseAnalytics",
-      url: "https://dl.google.com/firebase/ios/swiftpm/10.17.0/FirebaseAnalytics.zip",
-      checksum: "94a559d3801a445802c4bc6e6a50b283eff7dd25f8994e63a06589dbb6c18bdf"
+      url: "https://dl.google.com/firebase/ios/swiftpm/10.20.0/FirebaseAnalytics.zip",
+      checksum: "169e9983be26e31bff373ea3ae5b56559a49f6bf14986f8813be5af03c00b251"
     ),
     .target(
       name: "FirebaseAnalyticsSwiftTarget",
@@ -1319,7 +1319,7 @@ func googleAppMeasurementDependency() -> Package.Dependency {
     return .package(url: appMeasurementURL, branch: "main")
   }
 
-  return .package(url: appMeasurementURL, exact: "10.17.0")
+  return .package(url: appMeasurementURL, exact: "10.20.0")
 }
 
 func abseilDependency() -> Package.Dependency {
@@ -1371,7 +1371,8 @@ func firestoreWrapperTarget() -> Target {
     name: "FirebaseFirestoreTarget",
     dependencies: [.target(name: "FirebaseFirestore",
                            condition: .when(platforms: [.iOS, .tvOS, .macOS, .macCatalyst]))],
-    path: "SwiftPM-PlatformExclude/FirebaseFirestoreWrap"
+    path: "SwiftPM-PlatformExclude/FirebaseFirestoreWrap",
+    cSettings: [.define("FIREBASE_BINARY_FIRESTORE", to: "1")]
   )
 }
 
@@ -1487,8 +1488,8 @@ func firestoreTargets() -> [Target] {
     } else {
       return .binaryTarget(
         name: "FirebaseFirestoreInternal",
-        url: "https://dl.google.com/firebase/ios/bin/firestore/10.19.0/FirebaseFirestoreInternal.zip",
-        checksum: "1096ff78a24822bb0218120dfd11859b3b661eb81fc525c9b2cfc044b59804c9"
+        url: "https://dl.google.com/firebase/ios/bin/firestore/10.20.0/FirebaseFirestoreInternal.zip",
+        checksum: "7fe8f913d35e257979eddc8e2df0fedd3b89735c7030494307079747f03279c7"
       )
     }
   }()
@@ -1501,8 +1502,16 @@ func firestoreTargets() -> [Target] {
           name: "FirebaseFirestoreInternalWrapper",
           condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
         ),
-        .product(name: "abseil", package: "abseil-cpp-binary"),
-        .product(name: "gRPC-C++", package: "grpc-binary"),
+        .product(
+          name: "abseil",
+          package: "abseil-cpp-binary",
+          condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
+        ),
+        .product(
+          name: "gRPC-C++",
+          package: "grpc-binary",
+          condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
+        ),
         .product(name: "nanopb", package: "nanopb"),
         "FirebaseAppCheckInterop",
         "FirebaseCore",
@@ -1519,7 +1528,10 @@ func firestoreTargets() -> [Target] {
     ),
     .target(
       name: "FirebaseFirestoreInternalWrapper",
-      dependencies: ["FirebaseFirestoreInternal"],
+      dependencies: [.target(
+        name: "FirebaseFirestoreInternal",
+        condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
+      )],
       path: "FirebaseFirestoreInternal",
       publicHeadersPath: "."
     ),

+ 20 - 3
README.md

@@ -199,6 +199,12 @@ To run against a production instance, provide a valid `GoogleServices-Info.plist
 [public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
 running.
 
+### Firebase Dynamic Links
+
+Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
+
+Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
+
 ### Firebase Performance Monitoring
 
 For specific Firebase Performance Monitoring development, see
@@ -231,14 +237,25 @@ To receive push notifications, follow the steps above and run the app on a physi
 
 ## Building with Firebase on Apple platforms
 
-Firebase 8.9.0 introduced official beta support for macOS, Catalyst, and tvOS. watchOS continues
-to be community supported. Thanks to community contributions for many of the multi-platform PRs.
+Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
+are community supported. Thanks to community contributions for many of the multi-platform PRs.
 
 At this time, most of Firebase's products are available across Apple platforms. There are still
-a few gaps, especially on watchOS. For details about the current support matrix, see
+a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
 [this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
 in Firebase's documentation.
 
+### visionOS
+
+Where supported, visionOS works as expected with the exception of Firestore via Swift Package
+Manager where it is required to use the source distribution.
+
+To enable the Firestore source distribution, quit Xcode and open the desired
+project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
+variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
+To go back to using the binary distribution of Firestore, quit Xcode and open
+Xcode like normal, without the environment variable.
+
 ### watchOS
 Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
 work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseABTestingBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseABTesting-60386b1807639d7f.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseABTesting-1a1916232af9cd1a.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseABTesting-3c0e5c9ddc52bccd.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseABTesting-8dad3d6af34cb26c.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseABTesting-e87c686cee02758a.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseABTesting-6a65ab8b888172af.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseABTesting-197f0cb4125363b6.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAdMobBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/Google-Mobile-Ads-SDK-bdeb97bf3c25008b.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/Google-Mobile-Ads-SDK-db6e557426f37394.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/Google-Mobile-Ads-SDK-c8bc252ed3323212.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/Google-Mobile-Ads-SDK-5f8bb98bb2467b85.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/Google-Mobile-Ads-SDK-8b0d1ce3d1162b67.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/Google-Mobile-Ads-SDK-046511c3fd0189eb.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/Google-Mobile-Ads-SDK-50008c143ad8f268.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAnalyticsBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseAnalytics-a31cc40d8ff01594.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseAnalytics-040a907770027c1e.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseAnalytics-6f8b70c8ee2efc85.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseAnalytics-4d7ca295e8b44c0c.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseAnalytics-95669fcf109f74a2.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseAnalytics-c0db6cb0e858e397.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseAnalytics-e8ebe991b5743f71.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAnalyticsOnDeviceConversionBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseAnalyticsOnDeviceConversion-17d27742d2da3a56.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseAnalyticsOnDeviceConversion-219e668e914bee4c.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseAnalyticsOnDeviceConversion-37cf6277991d7d75.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseAnalyticsOnDeviceConversion-d3913995b7344202.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseAnalyticsOnDeviceConversion-091f5252d693a9f9.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseAnalyticsOnDeviceConversion-7bbb73d46383a042.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseAnalyticsOnDeviceConversion-eca2f83d40e0278d.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAppCheckBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseAppCheck-19e0aad072b96709.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseAppCheck-09ecedc08c2562d0.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseAppCheck-b0ead84a126d24d4.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseAppCheck-8f7dfe411eeaccdf.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseAppCheck-d19e46a728b1ac4f.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseAppCheck-8339fde989fe8f24.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseAppCheck-3ce0f074bfcd2596.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAppDistributionBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseAppDistribution-40a7981d53107dd8.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseAppDistribution-30aec5c329204ede.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseAppDistribution-45b5c85bba08a85b.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseAppDistribution-264a5e036b72a526.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseAppDistribution-cefc3327ddfceda6.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseAppDistribution-7931e42d39575534.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseAppDistribution-79dc2b1348d9aee9.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseAuthBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseAuth-457adc38d20fc396.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseAuth-9b66f8a440352f9b.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseAuth-2165e27f89d4959e.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseAuth-222a2417c3c21b41.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseAuth-e43e66353617f093.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseAuth-8a9591e6daa7e207.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseAuth-7e18a510d0a5b02e.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseCrashlyticsBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseCrashlytics-b6bcce010b638cff.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseCrashlytics-8126c26e6374c8b1.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseCrashlytics-054718c61ef054f9.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseCrashlytics-029c76d79754388c.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseCrashlytics-d29d3285a7d9fa1d.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseCrashlytics-165beb64483b4278.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseCrashlytics-53604573442e756b.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseDatabaseBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseDatabase-bcf8e0a3091629be.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseDatabase-a62dd446dac4b89f.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseDatabase-8b7048f7890bb665.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseDatabase-a7f5c6d032473b01.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseDatabase-5b22f689cb66d83a.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseDatabase-e1a9d1f0c4222cf7.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseDatabase-aea9249d81841ee1.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseDynamicLinksBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseDynamicLinks-fe77330a0c9267b7.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseDynamicLinks-a4f0deb13e7b39fd.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseDynamicLinks-bfdce6ac5d591ab3.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseDynamicLinks-693c6213bc87f8c0.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseDynamicLinks-7cf4ae5e96882ca8.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseDynamicLinks-c3bdeb37651a5d5d.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseDynamicLinks-bcb5df6ec32f6684.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseFirestoreBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseFirestore-c0e26a80058623cb.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseFirestore-b46da5ea686c3422.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseFirestore-4c3d1568e379a98c.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseFirestore-88b0aaac6fe277fe.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseFirestore-73ba0700b1aa6d6a.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseFirestore-02eb8da05f81fca5.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseFirestore-46fa68ddf287f76e.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseFunctionsBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseFunctions-9de21260bf5ac1cd.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseFunctions-8e91f34c0289f5ba.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseFunctions-b949cfeca4e7a80f.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseFunctions-23d6ba97d95db62c.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseFunctions-47189f2c99cdf806.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseFunctions-17c4b760141e38ad.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseFunctions-688a38b567392fcf.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseGoogleSignInBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/GoogleSignIn-e1d7f1f5e02bdb80.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/GoogleSignIn-7d252d83bad9f2b6.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/GoogleSignIn-c887dbc6bd07c787.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/GoogleSignIn-e55954e1a3ca9ee8.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/GoogleSignIn-a5b49807be66100b.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/GoogleSignIn-0d2e746eb3ff9f92.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/GoogleSignIn-5cb2a2f1f74efd5e.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseInAppMessagingBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseInAppMessaging-0729150e08035b53.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseInAppMessaging-ab8486f9415476ca.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseInAppMessaging-f29d7b7839cda915.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseInAppMessaging-c6a82f2dccc9a092.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseInAppMessaging-91e5426eade46bca.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseInAppMessaging-10801bd111df59de.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseInAppMessaging-91d4dd9878a06b7e.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseMLModelDownloaderBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseMLModelDownloader-e2d1f325f5000ac6.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseMLModelDownloader-865fc851458ff97e.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseMLModelDownloader-ee2af587027e74d3.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseMLModelDownloader-e45969e88bf879cd.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseMLModelDownloader-559cb113c0cfd8f2.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseMLModelDownloader-9c909894999c92e4.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseMLModelDownloader-9abf9b0e24bfb921.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseMessagingBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseMessaging-c6e5b81bc953e88f.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseMessaging-536acbc043d0e42a.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseMessaging-289a04c85f7e771d.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseMessaging-236bb6f578c05ed1.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseMessaging-59ef1cc63c660712.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseMessaging-76c02a69e3fe1008.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseMessaging-439a17dcc8b8172b.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebasePerformanceBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebasePerformance-28f3f8f88f887bd4.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebasePerformance-5ef59eac9e09086e.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebasePerformance-7a7398acc615dbb6.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebasePerformance-6494eb8091be4e03.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebasePerformance-36ac6dfb99caa11b.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebasePerformance-f9f5be8ffad5cbb0.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebasePerformance-0ffe559f7554d8a5.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseRemoteConfigBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseRemoteConfig-2d73f0e2258c7ac5.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseRemoteConfig-13b390a0fae2a496.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseRemoteConfig-45a7f541a654884c.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseRemoteConfig-44d640335ebdfea7.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseRemoteConfig-edd1b427b8bbe782.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseRemoteConfig-10b62ee5663aaab3.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseRemoteConfig-2237eb5fcd4a4525.zip",

+ 1 - 0
ReleaseTooling/CarthageJSON/FirebaseStorageBinary.json

@@ -12,6 +12,7 @@
   "10.18.0": "https://dl.google.com/dl/firebase/ios/carthage/10.18.0/FirebaseStorage-05669862cf21ca8d.zip",
   "10.19.0": "https://dl.google.com/dl/firebase/ios/carthage/10.19.0/FirebaseStorage-93e56fb9268ffd5f.zip",
   "10.2.0": "https://dl.google.com/dl/firebase/ios/carthage/10.2.0/FirebaseStorage-347e6a3402706596.zip",
+  "10.20.0": "https://dl.google.com/dl/firebase/ios/carthage/10.20.0/FirebaseStorage-fac47c17aae220e0.zip",
   "10.3.0": "https://dl.google.com/dl/firebase/ios/carthage/10.3.0/FirebaseStorage-ac463d14593d10a8.zip",
   "10.4.0": "https://dl.google.com/dl/firebase/ios/carthage/10.4.0/FirebaseStorage-fdf8479115660ce6.zip",
   "10.5.0": "https://dl.google.com/dl/firebase/ios/carthage/10.5.0/FirebaseStorage-04f255ea8c3a7420.zip",

+ 1 - 1
ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift

@@ -21,7 +21,7 @@ import Foundation
 /// The version and releasing fields of the non-Firebase pods should be reviewed every release.
 /// The array should be ordered so that any pod's dependencies precede it in the list.
 public let shared = Manifest(
-  version: "10.20.0",
+  version: "10.21.0",
   pods: [
     Pod("FirebaseSharedSwift"),
     Pod("FirebaseCoreInternal"),

Некоторые файлы не были показаны из-за большого количества измененных файлов