@react-native-firebase/app 26.3.3 → 27.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +10 -27
  3. package/RNFBApp.podspec +13 -5
  4. package/android/build.gradle +3 -0
  5. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.kt +29 -0
  6. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.kt +146 -0
  7. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.kt +156 -0
  8. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.kt +44 -0
  9. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.kt +100 -0
  10. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.kt +250 -0
  11. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.kt +64 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppInitProvider.java → ReactNativeFirebaseAppInitProvider.kt} +3 -4
  13. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.kt +47 -0
  14. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.kt +33 -0
  15. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
  16. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.kt +185 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.kt +35 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.kt +662 -0
  19. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.kt +103 -0
  20. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.kt +96 -0
  21. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.kt +140 -0
  22. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.kt +109 -0
  23. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.kt +451 -0
  24. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{ContextProvider.java → ContextProvider.kt} +8 -8
  25. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeError.java → NativeError.kt} +8 -8
  26. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeEvent.java → NativeEvent.kt} +6 -6
  27. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.kt +219 -0
  28. package/android/src/test/java/expo/core/ModuleRegistry.java +9 -0
  29. package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +208 -60
  30. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppComponentsJavaCompatibilityTest.java +70 -0
  31. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppJavaCompatibilityTest.java +60 -0
  32. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackageTest.java +165 -0
  33. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppTest.kt +167 -0
  34. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseJavaCompatibilityTest.java +112 -0
  35. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseTest.kt +317 -0
  36. package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.kt +309 -0
  37. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterJavaCompatibilityTest.java +176 -0
  38. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +144 -13
  39. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventTest.kt +61 -0
  40. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONJavaCompatibilityTest.java +85 -0
  41. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.kt +296 -0
  42. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaJavaCompatibilityTest.java +67 -0
  43. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaTest.kt +246 -0
  44. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseModuleTest.kt +254 -0
  45. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesJavaCompatibilityTest.java +87 -0
  46. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesTest.kt +260 -0
  47. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsJavaCompatibilityTest.java +255 -0
  48. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsTest.kt +674 -0
  49. package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.kt +290 -0
  50. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebaseModuleJavaCompatibilityTest.java +178 -0
  51. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesJavaCompatibilityTest.java +99 -0
  52. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesTest.kt +217 -0
  53. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleJavaCompatibilityTest.java +176 -0
  54. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleKotlinCompatibilityTest.kt +52 -0
  55. package/android/src/test/java/io/invertase/firebase/interfaces/InterfacesJavaCompatibilityTest.java +144 -0
  56. package/android/src/test/java/io/invertase/firebase/interfaces/NativeEventTest.kt +56 -0
  57. package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +517 -23
  58. package/dist/module/FirebaseApp.js +10 -0
  59. package/dist/module/FirebaseApp.js.map +1 -1
  60. package/dist/module/common/Base64.js +13 -21
  61. package/dist/module/common/Base64.js.map +1 -1
  62. package/dist/module/common/deeps.js +3 -0
  63. package/dist/module/common/deeps.js.map +1 -1
  64. package/dist/module/common/index.js +17 -9
  65. package/dist/module/common/index.js.map +1 -1
  66. package/dist/module/internal/FirebaseModule.js.map +1 -1
  67. package/dist/module/internal/RNFBNativeEventEmitter.js +12 -1
  68. package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
  69. package/dist/module/internal/SharedEventEmitter.js +3 -1
  70. package/dist/module/internal/SharedEventEmitter.js.map +1 -1
  71. package/dist/module/internal/asyncStorage.js +7 -11
  72. package/dist/module/internal/asyncStorage.js.map +1 -1
  73. package/dist/module/internal/index.js +1 -0
  74. package/dist/module/internal/index.js.map +1 -1
  75. package/dist/module/internal/logger.js +44 -40
  76. package/dist/module/internal/logger.js.map +1 -1
  77. package/dist/module/internal/nativeModuleAndroidIos.js +2 -29
  78. package/dist/module/internal/nativeModuleAndroidIos.js.map +1 -1
  79. package/dist/module/internal/nativeModuleDebug.js +74 -0
  80. package/dist/module/internal/nativeModuleDebug.js.map +1 -0
  81. package/dist/module/internal/nativeModuleWeb.js +9 -29
  82. package/dist/module/internal/nativeModuleWeb.js.map +1 -1
  83. package/dist/module/internal/registry/app.js +8 -2
  84. package/dist/module/internal/registry/app.js.map +1 -1
  85. package/dist/module/internal/registry/modular.js +2 -2
  86. package/dist/module/internal/registry/modular.js.map +1 -1
  87. package/dist/module/internal/registry/nativeModule.js +3 -3
  88. package/dist/module/internal/registry/nativeModule.js.map +1 -1
  89. package/dist/module/internal/web/RNFBAppModule.js +16 -15
  90. package/dist/module/internal/web/RNFBAppModule.js.map +1 -1
  91. package/dist/module/internal/web/memidb/lib/FakeEventTarget.js +3 -1
  92. package/dist/module/internal/web/memidb/lib/FakeEventTarget.js.map +1 -1
  93. package/dist/module/types/app.js.map +1 -1
  94. package/dist/module/types/internal.js +2 -0
  95. package/dist/module/version.js +1 -1
  96. package/dist/typescript/lib/FirebaseApp.d.ts +7 -0
  97. package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
  98. package/dist/typescript/lib/common/Base64.d.ts.map +1 -1
  99. package/dist/typescript/lib/common/deeps.d.ts.map +1 -1
  100. package/dist/typescript/lib/common/index.d.ts.map +1 -1
  101. package/dist/typescript/lib/internal/FirebaseModule.d.ts +2 -3
  102. package/dist/typescript/lib/internal/FirebaseModule.d.ts.map +1 -1
  103. package/dist/typescript/lib/internal/NativeModules.d.ts +4 -1
  104. package/dist/typescript/lib/internal/NativeModules.d.ts.map +1 -1
  105. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts +2 -2
  106. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
  107. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts +3 -3
  108. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts.map +1 -1
  109. package/dist/typescript/lib/internal/asyncStorage.d.ts.map +1 -1
  110. package/dist/typescript/lib/internal/index.d.ts +1 -0
  111. package/dist/typescript/lib/internal/index.d.ts.map +1 -1
  112. package/dist/typescript/lib/internal/logger.d.ts.map +1 -1
  113. package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts.map +1 -1
  114. package/dist/typescript/lib/internal/nativeModuleDebug.d.ts +2 -0
  115. package/dist/typescript/lib/internal/nativeModuleDebug.d.ts.map +1 -0
  116. package/dist/typescript/lib/internal/nativeModuleWeb.d.ts.map +1 -1
  117. package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
  118. package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts +7 -7
  119. package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts.map +1 -1
  120. package/dist/typescript/lib/types/app.d.ts +10 -3
  121. package/dist/typescript/lib/types/app.d.ts.map +1 -1
  122. package/dist/typescript/lib/types/internal.d.ts +12 -0
  123. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  124. package/dist/typescript/lib/version.d.ts +1 -1
  125. package/firebase-schema.json +5 -1
  126. package/firebase_spm.rb +552 -96
  127. package/ios/RNFBApp/RCTConvert+FIRApp.h +2 -5
  128. package/ios/RNFBApp/RCTConvert+FIRApp.m +11 -6
  129. package/ios/RNFBApp/RCTConvert+FIROptions.h +2 -5
  130. package/ios/RNFBApp/RCTConvert+FIROptions.m +11 -10
  131. package/ios/RNFBApp/RCTConvertFIRApp.swift +86 -0
  132. package/ios/RNFBApp/RCTConvertFIROptions.swift +259 -0
  133. package/ios/RNFBApp/RNFBAppCustomAuthDomains.swift +63 -0
  134. package/ios/RNFBApp/RNFBAppInitializeOptionsMapper.swift +113 -0
  135. package/ios/RNFBApp/RNFBAppLogLevelMapper.swift +53 -0
  136. package/ios/RNFBApp/RNFBAppModule+CustomAuthDomain.m +35 -0
  137. package/ios/RNFBApp/RNFBAppModule.h +6 -2
  138. package/ios/RNFBApp/RNFBAppModule.mm +67 -204
  139. package/ios/RNFBApp/RNFBAppModuleFirebase.swift +439 -0
  140. package/ios/RNFBApp/RNFBAppModuleImplementation.h +68 -0
  141. package/ios/RNFBApp/RNFBAppModuleImplementation.m +195 -0
  142. package/ios/RNFBApp/RNFBHandleMap.h +73 -0
  143. package/ios/RNFBApp/RNFBHandleMap.m +87 -0
  144. package/ios/RNFBApp/RNFBHandleMapStorage.swift +90 -0
  145. package/ios/RNFBApp/RNFBInitializeAppArguments.swift +82 -0
  146. package/ios/RNFBApp/RNFBJSON.m +22 -38
  147. package/ios/RNFBApp/RNFBJSONImplementation.swift +71 -0
  148. package/ios/RNFBApp/RNFBMeta.m +24 -26
  149. package/ios/RNFBApp/RNFBMetaStorage.swift +76 -0
  150. package/ios/RNFBApp/RNFBNullSentinelDecoding.swift +135 -0
  151. package/ios/RNFBApp/RNFBNullSentinelInterceptor.h +30 -3
  152. package/ios/RNFBApp/RNFBNullSentinelInterceptor.m +28 -5
  153. package/ios/RNFBApp/RNFBPreferences.m +22 -20
  154. package/ios/RNFBApp/RNFBPreferencesStorage.swift +101 -0
  155. package/ios/RNFBApp/RNFBRCTEventEmitter.m +36 -93
  156. package/ios/RNFBApp/RNFBRCTEventEmitterCore.swift +161 -0
  157. package/ios/RNFBApp/RNFBSharedUtils.h +2 -5
  158. package/ios/RNFBApp/RNFBSharedUtils.m +109 -197
  159. package/ios/RNFBApp/RNFBSharedUtilsConfig.swift +72 -0
  160. package/ios/RNFBApp/RNFBSharedUtilsFIRApp.swift +271 -0
  161. package/ios/RNFBApp/RNFBSharedUtilsFormatting.swift +59 -0
  162. package/ios/RNFBApp/RNFBSharedUtilsPromiseRejection.swift +88 -0
  163. package/ios/RNFBApp/RNFBUtilsHelpers.swift +116 -0
  164. package/ios/RNFBApp/RNFBUtilsModule+Foundation.m +44 -0
  165. package/ios/RNFBApp/RNFBUtilsModule+PhotoAssets.m +47 -0
  166. package/ios/RNFBApp/RNFBUtilsModule.h +11 -1
  167. package/ios/RNFBApp/RNFBUtilsModule.mm +10 -85
  168. package/ios/RNFBApp/RNFBUtilsModuleImplementation.h +32 -0
  169. package/ios/RNFBApp/RNFBUtilsModuleImplementation.m +56 -0
  170. package/ios/RNFBApp/RNFBVersion.m +1 -1
  171. package/ios/RNFBApp.xcodeproj/project.pbxproj +106 -0
  172. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FIRApp.m +171 -0
  173. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FirebaseCore.h +78 -0
  174. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FIRInstallations.m +7 -0
  175. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FirebaseInstallations.h +8 -0
  176. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppModuleHostStub.m +55 -0
  177. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.h +89 -0
  178. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.mm +30 -0
  179. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.h +19 -0
  180. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.m +32 -0
  181. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridgeModule.h +19 -0
  182. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.h +28 -0
  183. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.m +23 -0
  184. package/ios/RNFBAppUnitTests/HostStubs/React/RCTEventEmitter.h +9 -0
  185. package/ios/RNFBAppUnitTests/HostStubs/React/RCTInvalidating.h +24 -0
  186. package/ios/RNFBAppUnitTests/HostStubs/React/RCTUtils.h +9 -0
  187. package/ios/RNFBAppUnitTests/HostStubs/module.modulemap +13 -0
  188. package/ios/RNFBAppUnitTests/RCTConvertFIRAppFacadeTests.m +61 -0
  189. package/ios/RNFBAppUnitTests/RCTConvertFIRAppTests.swift +73 -0
  190. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsFacadeTests.m +57 -0
  191. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsTests.swift +243 -0
  192. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsFacadeTests.m +73 -0
  193. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsTests.swift +70 -0
  194. package/ios/RNFBAppUnitTests/RNFBAppInitializeOptionsMapperTests.swift +190 -0
  195. package/ios/RNFBAppUnitTests/RNFBAppLogLevelFacadeTests.m +76 -0
  196. package/ios/RNFBAppUnitTests/RNFBAppLogLevelMapperTests.swift +59 -0
  197. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseFacadeTests.m +215 -0
  198. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseTests.swift +430 -0
  199. package/ios/RNFBAppUnitTests/RNFBAppModuleLifecycleTests.m +206 -0
  200. package/ios/RNFBAppUnitTests/RNFBAppModuleShellTests.mm +367 -0
  201. package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +590 -0
  202. package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBAppUnitTests.xcscheme +69 -0
  203. package/ios/RNFBAppUnitTests/RNFBFirebaseAppClientTests.swift +165 -0
  204. package/ios/RNFBAppUnitTests/RNFBHandleMapStorageTests.swift +121 -0
  205. package/ios/RNFBAppUnitTests/RNFBHandleMapTests.m +238 -0
  206. package/ios/RNFBAppUnitTests/RNFBInitializeAppArgumentsTests.swift +256 -0
  207. package/ios/RNFBAppUnitTests/RNFBJSONImplementationTests.swift +126 -0
  208. package/ios/RNFBAppUnitTests/RNFBJSONTests.m +172 -0
  209. package/ios/RNFBAppUnitTests/RNFBMetaStorageTests.swift +106 -0
  210. package/ios/RNFBAppUnitTests/RNFBMetaTests.m +65 -0
  211. package/ios/RNFBAppUnitTests/RNFBNullSentinelDecodingTests.swift +286 -0
  212. package/ios/RNFBAppUnitTests/RNFBNullSentinelInterceptorTests.m +312 -0
  213. package/ios/RNFBAppUnitTests/RNFBPreferencesStorageTests.swift +105 -0
  214. package/ios/RNFBAppUnitTests/RNFBPreferencesTests.m +110 -0
  215. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterCoreTests.swift +163 -0
  216. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterFacadeTests.m +141 -0
  217. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigFacadeTests.m +100 -0
  218. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigTests.swift +234 -0
  219. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppFacadeTests.m +145 -0
  220. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppTests.swift +173 -0
  221. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingFacadeTests.m +38 -0
  222. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingTests.swift +82 -0
  223. package/ios/RNFBAppUnitTests/RNFBSharedUtilsNullSentinelTests.m +35 -0
  224. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionFacadeTests.m +107 -0
  225. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionTests.swift +133 -0
  226. package/ios/RNFBAppUnitTests/RNFBUtilsHelpersTests.swift +144 -0
  227. package/ios/RNFBAppUnitTests/RNFBUtilsModuleFacadeTests.m +72 -0
  228. package/ios/RNFBAppUnitTests/RNFBUtilsModuleShellTests.mm +282 -0
  229. package/ios/RNFBFirebase/Package.swift +53 -0
  230. package/ios/RNFBFirebase/Sources/RNFBFirebase/RNFBFirebase.swift +285 -0
  231. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/Public/.gitkeep +0 -0
  232. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/RNFBFirebaseBridge.m +169 -0
  233. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/include/RNFBFirebaseBridge.h +52 -0
  234. package/ios/RNFBFirebaseUnitTests/RNFBAppDynamicFacadeTests.swift +240 -0
  235. package/{android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.java → ios/RNFBFirebaseUnitTests/RNFBFirebaseClientImport.swift} +22 -17
  236. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/project.pbxproj +588 -0
  237. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBFirebaseUnitTests.xcscheme +83 -0
  238. package/ios_config.sh +50 -22
  239. package/lib/FirebaseApp.ts +10 -0
  240. package/lib/common/Base64.ts +10 -16
  241. package/lib/common/deeps.ts +3 -0
  242. package/lib/common/index.ts +20 -9
  243. package/lib/internal/FirebaseModule.ts +6 -3
  244. package/lib/internal/NativeModules.ts +4 -1
  245. package/lib/internal/RNFBNativeEventEmitter.ts +14 -5
  246. package/lib/internal/SharedEventEmitter.ts +5 -1
  247. package/lib/internal/asyncStorage.ts +7 -11
  248. package/lib/internal/index.ts +1 -0
  249. package/lib/internal/logger.ts +53 -43
  250. package/lib/internal/nativeModuleAndroidIos.ts +2 -40
  251. package/lib/internal/nativeModuleDebug.ts +97 -0
  252. package/lib/internal/nativeModuleWeb.ts +10 -40
  253. package/lib/internal/react-native-vendor-emitter.d.ts +11 -0
  254. package/lib/internal/registry/app.ts +8 -2
  255. package/lib/internal/registry/modular.ts +2 -2
  256. package/lib/internal/registry/nativeModule.ts +3 -3
  257. package/lib/internal/web/RNFBAppModule.ts +16 -15
  258. package/lib/internal/web/memidb/lib/FakeEventTarget.js +3 -1
  259. package/lib/types/app.ts +10 -3
  260. package/lib/types/internal.ts +19 -0
  261. package/lib/version.ts +1 -1
  262. package/package.json +9 -8
  263. package/plugin/build/ios/appDelegate.js +19 -9
  264. package/plugin/build/ios/podfile.js +16 -16
  265. package/plugin/build/pluginConfig.d.ts +4 -2
  266. package/plugin/src/ios/appDelegate.ts +45 -15
  267. package/plugin/src/ios/podfile.ts +20 -23
  268. package/plugin/src/pluginConfig.ts +4 -2
  269. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +0 -124
  270. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.java +0 -62
  271. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java +0 -91
  272. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +0 -222
  273. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.java +0 -46
  274. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +0 -49
  275. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +0 -191
  276. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.java +0 -53
  277. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +0 -606
  278. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +0 -107
  279. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +0 -101
  280. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +0 -146
  281. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +0 -96
  282. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.java +0 -441
  283. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.java +0 -223
package/firebase_spm.rb CHANGED
@@ -17,10 +17,30 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
+ require 'digest'
20
21
  require 'json'
21
22
 
22
23
  RNFIREBASE_SPM_EMBED_PHASE_NAME = '[RNFB] Embed Firebase SPM Frameworks'
23
24
  RNFIREBASE_SPM_SIGNATURE_FIX_PHASE_NAME = '[RNFB] Remove duplicate Firebase/Google SPM binary xcframework signature files' # rubocop:disable Layout/LineLength
25
+ RNFIREBASE_SPM_UMBRELLA_PRODUCT = 'RNFBFirebase'
26
+ # Pods aggregate used only by the dynamic probe. It orders the facade product
27
+ # ahead of RNFBApp. It is not an RNFBApp package-product dependency, so Xcode
28
+ # does not copy transitive Firebase module maps onto that pod.
29
+ RNFIREBASE_SPM_PROBE_ORDER_TARGET = 'RNFBFirebaseProbeOrder'
30
+ # Swift compilation condition (`#if RNFB_DYNAMIC_FIREBASE_PROBE`) that selects
31
+ # the `import RNFBFirebase` facade path in RNFBApp and the test-rn-bare
32
+ # AppDelegate. It is the Swift twin of the `RNFB_DYNAMIC_FIREBASE_PROBE=1`
33
+ # GCC preprocessor define and is only set while the probe is on.
34
+ RNFIREBASE_SPM_PROBE_SWIFT_CONDITION = 'RNFB_DYNAMIC_FIREBASE_PROBE'
35
+ RNFIREBASE_SWIFT_CONDITIONS_SETTING = 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'
36
+
37
+ # CI probe only: when `RNFB_TEST_RN_BARE_DYNAMIC_FIREBASE=1`, `firebase_dependency`
38
+ # and the app-target link helpers resolve Firebase through the local dynamic
39
+ # package at `packages/app/ios/RNFBFirebase` instead of remote `firebase-ios-sdk`.
40
+ # Default (unset / any other value) keeps the shipped direct `firebase-ios-sdk` path.
41
+ def rnfirebase_probe_dynamic_firebase?
42
+ ENV.fetch('RNFB_TEST_RN_BARE_DYNAMIC_FIREBASE', '') == '1'
43
+ end
24
44
 
25
45
  # Every `.binaryTarget` xcframework name reachable in the resolved SPM package
26
46
  # graph for the RNFB test app (firebase-ios-sdk 12.16.0, full module set --
@@ -76,16 +96,32 @@ module RNFirebaseSPM
76
96
  end
77
97
  end
78
98
 
99
+ # Absolute path is intentional: React Native's SPM manager recognizes a
100
+ # local package with `File.exist?`, then writes an XCLocalSwiftPackageReference.
101
+ def umbrella_path
102
+ File.join(__dir__, 'ios', RNFIREBASE_SPM_UMBRELLA_PRODUCT)
103
+ end
104
+
79
105
  # Records that `firebase_dependency` (below) took the SPM path for at
80
106
  # least one podspec in this install, and which Firebase SDK `version` it
81
107
  # resolved with. Called once, from `firebase_dependency` itself, the
82
108
  # first time it successfully takes the SPM path. `version` is stored so
83
- # `rnfirebase_add_spm_core_to_app_target` can declare the same minimum
109
+ # `rnfirebase_add_spm_core_to_app_target` can declare the same exact
84
110
  # version requirement on the app target's own FirebaseCore product
85
111
  # dependency, without needing its own separate copy of it.
86
- def activate!(version)
112
+ #
113
+ # `mode:` is `:remote` (shipped `firebase-ios-sdk`) or `:umbrella` (CI probe
114
+ # local `RNFBFirebase` dynamic package).
115
+ def activate!(version, mode: :remote)
87
116
  @active = true
88
117
  @version = version
118
+ @mode = mode
119
+ end
120
+
121
+ # True when this install resolved Firebase through the local dynamic probe
122
+ # package rather than remote `firebase-ios-sdk`.
123
+ def umbrella?
124
+ active? && @mode == :umbrella
89
125
  end
90
126
 
91
127
  # Whether SPM is active for this install -- read by every `rnfirebase_*`
@@ -96,7 +132,7 @@ module RNFirebaseSPM
96
132
  # without going through `activate!` above (e.g. a future refactor that
97
133
  # assigns the flag directly instead of calling it), and every downstream
98
134
  # helper that trusts this return value -- including one that links
99
- # FirebaseCore into the app target at a specific minimum version -- would
135
+ # FirebaseCore into the app target at a specific exact version -- would
100
136
  # silently operate on incomplete state instead. Raising `Pod::Informative`
101
137
  # here (this file's own user-facing `pod install`-time error class, same
102
138
  # as `rnfirebase_fail_if_spm_static_linkage!` below) turns that into a
@@ -117,7 +153,7 @@ module RNFirebaseSPM
117
153
 
118
154
  # The Firebase SDK version `firebase_dependency` resolved with, recorded
119
155
  # by `activate!` above -- used by `rnfirebase_add_spm_core_to_app_target`
120
- # to declare the same minimum version requirement on the app target's own
156
+ # to declare the same exact version requirement on the app target's own
121
157
  # FirebaseCore product dependency.
122
158
  attr_reader :version
123
159
 
@@ -133,20 +169,20 @@ module RNFirebaseSPM
133
169
  @active = nil
134
170
  @version = nil
135
171
  @url = nil
172
+ @mode = nil
136
173
  end
137
174
  end
138
175
  end
139
176
 
140
- # Helper to declare Firebase dependencies with SPM support and CocoaPods fallback.
177
+ # Helper to declare Firebase dependencies via Swift Package Manager.
141
178
  #
142
179
  # When `spm_dependency` is available (React Native >= 0.75), it declares the
143
- # Firebase iOS SDK as a Swift Package dependency. Otherwise, it falls back to
144
- # the traditional CocoaPods `s.dependency` declaration.
180
+ # Firebase iOS SDK as a Swift Package dependency.
145
181
  #
146
- # Set `$RNFirebaseDisableSPM = true` in your Podfile to force CocoaPods-only
147
- # dependency resolution. You must disable SPM when using `use_frameworks! :linkage => :static`
148
- # because static frameworks cause each pod to embed Firebase SPM products,
149
- # resulting in duplicate symbol linker errors.
182
+ # Firebase Apple SDK 13+ is distributed exclusively through SPM and binary
183
+ # zips — CocoaPods trunk stopped at 12.19.0. `$RNFirebaseDisableSPM = true`
184
+ # and Expo `disableSPM: true` therefore fail `pod install` / prebuild loudly
185
+ # instead of declaring missing CocoaPods Firebase 13 pods.
150
186
  #
151
187
  # firebase-ios-sdk SPM products are plain `.library(...)` (automatic linkage),
152
188
  # not `.library(type: .dynamic)`. See:
@@ -156,7 +192,7 @@ end
156
192
  # build and run; static pod linkage collides those per-pod copies at link time.
157
193
  # Dynamic pods avoid the link failure but still embed a private copy per pod,
158
194
  # so third-party pods cannot share one FirebaseCore/FirebaseApp with RNFB
159
- # under SPM (use `$RNFirebaseDisableSPM = true` + CocoaPods for that).
195
+ # under SPM.
160
196
  #
161
197
  # Returns true only when `$RNFirebaseDisableSPM` has been explicitly set to `true`.
162
198
  #
@@ -167,6 +203,22 @@ def rnfirebase_spm_disabled?
167
203
  defined?($RNFirebaseDisableSPM) && $RNFirebaseDisableSPM == true
168
204
  end
169
205
 
206
+ # User-facing error when a Podfile or Expo config asks for CocoaPods Firebase
207
+ # (DisableSPM / missing `spm_dependency`). Firebase 13+ is SPM-only.
208
+ def rnfirebase_fail_cocoapods_firebase!(reason)
209
+ raise Pod::Informative, <<~MESSAGE
210
+ [react-native-firebase] Firebase Apple SDK is no longer distributed through CocoaPods (#{reason}).
211
+
212
+ Firebase 13.0.0 and later ship exclusively via Swift Package Manager and binary distributions. React Native Firebase resolves Firebase through SPM and requires:
213
+
214
+ use_frameworks! :linkage => :dynamic
215
+
216
+ Remove `$RNFirebaseDisableSPM = true` from your Podfile (and Expo `ios.disableSPM` / `disableSPM: true` from the `@react-native-firebase/app` config plugin). React Native 0.75+ is required for `spm_dependency`.
217
+
218
+ See https://rnfirebase.io/ios-spm and https://firebase.google.com/docs/ios/cocoapods-deprecation
219
+ MESSAGE
220
+ end
221
+
170
222
  # Normalizes a build setting value that Xcode/Xcodeproj may represent as
171
223
  # `nil`, a whitespace-separated `String`, or an `Array` into a plain `Array`
172
224
  # safe to call `include?`/`push`/`<<` on. Settings like `OTHER_LDFLAGS` or
@@ -185,6 +237,119 @@ def rnfirebase_build_setting_list(current)
185
237
  end
186
238
  end
187
239
 
240
+ # Adds (`enabled`) or removes (`!enabled`) the probe Swift compilation
241
+ # condition on one `build_settings` hash. Never duplicates the entry, and keeps
242
+ # every other condition and the value's form: a non-empty Array stays an Array
243
+ # (token appended / removed), anything else is a space-joined String. Returns
244
+ # whether the hash changed.
245
+ #
246
+ # Removal restores what the add path started from, without persisted state:
247
+ # - Array value: add never writes an Array for an unset value, so an Array is
248
+ # an authored value. Only the token is removed; an explicit `$(inherited)`
249
+ # stays.
250
+ # - String value: add writes `$(inherited) <token>` for an unset value, which
251
+ # is indistinguishable from an authored `$(inherited)` once the token is
252
+ # there. Removal treats a remainder of exactly `$(inherited)` as "was unset"
253
+ # and deletes the key, so a project that never had the setting (the checked-in
254
+ # `testrnbare`) is byte-identical after a probe then flag-off round trip. An
255
+ # authored String `$(inherited)` therefore becomes unset, which resolves to
256
+ # the same compilation conditions.
257
+ # - A remainder with no entries left deletes the key.
258
+ # - String values are split on whitespace and re-joined with single spaces on
259
+ # add, so an authored String with tabs or repeated spaces is normalized, not
260
+ # byte-exact, after a probe then flag-off round trip. This only happens in
261
+ # probe mode, the checked-in `testrnbare` has no such setting, and the
262
+ # resolved value is unchanged.
263
+ def rnfirebase_set_probe_swift_condition(build_settings, enabled) # rubocop:disable Naming/PredicateMethod
264
+ key = RNFIREBASE_SWIFT_CONDITIONS_SETTING
265
+ condition = RNFIREBASE_SPM_PROBE_SWIFT_CONDITION
266
+ original = build_settings[key]
267
+ current = rnfirebase_build_setting_list(original)
268
+ array_form = original.is_a?(Array) && !original.empty?
269
+ write = ->(entries) { build_settings[key] = array_form ? entries : entries.join(' ') }
270
+
271
+ if enabled
272
+ return false if current.include?(condition)
273
+
274
+ write.call(current << condition)
275
+ return true
276
+ end
277
+
278
+ return false unless current.include?(condition)
279
+
280
+ remaining = current.reject { |entry| entry == condition }
281
+ if remaining.empty? || (!array_form && remaining == ['$(inherited)'])
282
+ build_settings.delete(key)
283
+ else
284
+ write.call(remaining)
285
+ end
286
+ true
287
+ end
288
+
289
+ # Exact firebase-ios-sdk SPM requirement. CocoaPods already pins `version`
290
+ # exactly via `spec.dependency pod, version`. SPM used to declare
291
+ # `upToNextMajorVersion`, which lets Xcode resolve a newer 12.x than
292
+ # `sdkVersions.ios.firebase`. `{ kind: 'exactVersion', version: version }`
293
+ # is the Xcode/xcodeproj requirement hash RN's `spm_dependency` passes
294
+ # through unchanged.
295
+ def rnfirebase_spm_package_requirement(version)
296
+ { kind: 'exactVersion', version: version }
297
+ end
298
+
299
+ # Xcodeproj assigns a fresh random UUID on every `project.new`. The dynamic
300
+ # probe removes the app target's FirebaseCore package reference, product
301
+ # dependency, and PBXBuildFile, and a later normal install creates them
302
+ # again. Random IDs would rewrite a committed pbxproj on that round trip.
303
+ # These IDs are a pure function of the object role, so recreation writes the
304
+ # same objects the fixture already has.
305
+ def rnfirebase_stable_object_uuid(key)
306
+ Digest::SHA256.hexdigest("react-native-firebase/spm/#{key}")[0, 24].upcase
307
+ end
308
+
309
+ def rnfirebase_assign_stable_uuid!(project, object, key)
310
+ return unless project.respond_to?(:objects_by_uuid)
311
+ return unless object.respond_to?(:uuid) && !object.uuid.nil?
312
+
313
+ uuid = rnfirebase_stable_object_uuid(key)
314
+ return if object.uuid == uuid
315
+
316
+ objects = project.objects_by_uuid
317
+ # Never steal an unrelated object's id. A collision keeps the random UUID
318
+ # from `project.new` rather than corrupting the project.
319
+ return if objects[uuid] && !objects[uuid].equal?(object)
320
+
321
+ objects.delete(object.uuid)
322
+ object.instance_variable_set(:@uuid, uuid)
323
+ objects[uuid] = object
324
+ end
325
+
326
+ def rnfirebase_stable_package_uuid_key(umbrella)
327
+ if umbrella
328
+ "local-package:#{RNFIREBASE_SPM_UMBRELLA_PRODUCT}"
329
+ else
330
+ "remote-package:#{RNFirebaseSPM.url}"
331
+ end
332
+ end
333
+
334
+ # Rewrites a leftover `upToNextMajorVersion` (or any other kind) on an
335
+ # existing `XCRemoteSwiftPackageReference` to the exact pin. Returns true
336
+ # when the requirement changed so callers can mark the project dirty.
337
+ def rnfirebase_pin_spm_package_requirement!(pkg, version = RNFirebaseSPM.version) # rubocop:disable Naming/PredicateMethod
338
+ return false if pkg.nil? || !pkg.respond_to?(:requirement=)
339
+
340
+ desired = rnfirebase_spm_package_requirement(version)
341
+ return false if pkg.requirement == desired
342
+
343
+ pkg.requirement = desired
344
+ true
345
+ end
346
+
347
+ # True for RNFB CocoaPods targets (`RNFBApp`, `RNFBAnalytics`, ...). Used to
348
+ # apply `-ObjC` on the pod that statically absorbs Analytics/Measurement.
349
+ def rnfirebase_rnfb_pod_target?(target)
350
+ target.respond_to?(:name) && target.name.to_s.start_with?('RNFB')
351
+ end
352
+
188
353
  def rnfirebase_spm_embed_script
189
354
  <<~'SCRIPT'
190
355
  set -euo pipefail
@@ -220,8 +385,15 @@ def rnfirebase_spm_embed_script
220
385
  # Deliberate tradeoff: match `file -b` prose for "dynamically linked"
221
386
  # rather than inspecting Mach-O magic bytes. Locale-independent in
222
387
  # practice on Apple's `file`, but still a CLI string match.
223
- if [ ! -f "${framework}/${binary_name}" ] || \
224
- ! file -b "${framework}/${binary_name}" | grep -q 'dynamically linked'; then
388
+ file_description=""
389
+ if [ -f "${framework}/${binary_name}" ]; then
390
+ file_description="$(file -b "${framework}/${binary_name}")"
391
+ fi
392
+ # Do not use `file ... | grep -q` here. Under this script's
393
+ # `set -o pipefail`, `grep -q` exits after the first matching line of
394
+ # a multi-line universal-binary description, `file` receives SIGPIPE,
395
+ # and the otherwise-successful pipeline becomes false.
396
+ if [[ "${file_description}" != *"dynamically linked"* ]]; then
225
397
  echo "Skipping ${framework_name}: binary missing or not dynamically linked"
226
398
  continue
227
399
  fi
@@ -405,8 +577,7 @@ end
405
577
  # (`duplicate symbol`). Under dynamic pod linkage the app builds, but each
406
578
  # dynamic framework still keeps a private copy (runtime class duplication /
407
579
  # split FirebaseApp registries). There is no supported combination of RNFB's
408
- # SPM mode with static linkage; the fix is always to switch to dynamic
409
- # linkage or opt out of SPM entirely.
580
+ # SPM mode with static linkage; the fix is to switch to dynamic linkage.
410
581
  #
411
582
  # Raises `Pod::Informative` -- CocoaPods' own user-facing error class, which
412
583
  # `pod install` prints as a plain, readable message with no Ruby backtrace --
@@ -432,16 +603,40 @@ def rnfirebase_fail_if_spm_static_linkage!(installer)
432
603
  end
433
604
  return if static_targets.empty?
434
605
 
435
- target_names = static_targets.map(&:name).join(', ')
606
+ # `build_type.static?` is true for two different Podfiles: no
607
+ # `use_frameworks!` at all (CocoaPods' default static libraries -- the stock
608
+ # React Native / bare Expo Podfile) and `use_frameworks! :linkage => :static`.
609
+ # Name the one the user actually has so they can find it in their Podfile.
610
+ library_targets, framework_targets = static_targets.partition do |target|
611
+ !target.target_definition.uses_frameworks?
612
+ end
613
+ detected = []
614
+ unless library_targets.empty?
615
+ library_label = 'no `use_frameworks!` (or `use_frameworks! false`) -- pods build as static libraries'
616
+ detected << " - #{library_label}: #{library_targets.map(&:name).join(', ')}"
617
+ end
618
+ unless framework_targets.empty?
619
+ framework_label = '`use_frameworks! :linkage => :static` ' \
620
+ '(incl. `USE_FRAMEWORKS=static` / Expo `useFrameworks: "static"`)'
621
+ detected << " - #{framework_label}: #{framework_targets.map(&:name).join(', ')}"
622
+ end
436
623
 
437
624
  raise Pod::Informative, <<~MESSAGE
438
- [react-native-firebase] SPM + static linkage is not supported (target(s): #{target_names}).
625
+ [react-native-firebase] SPM + static linkage is not supported.
626
+
627
+ Detected static pod linkage:
628
+ #{detected.join("\n")}
439
629
 
440
- firebase-ios-sdk's Swift Package products are automatic libraries (not `type: .dynamic`), so each react-native-firebase pod that resolves Firebase via SPM embeds its own copy. With `use_frameworks! :linkage => :static` those copies collide at link time as duplicate-symbol errors.
630
+ firebase-ios-sdk's Swift Package products are automatic libraries (not `type: .dynamic`), so each react-native-firebase pod that resolves Firebase via SPM embeds its own copy. With static pod linkage those copies collide at link time as duplicate-symbol errors.
441
631
 
442
- Fix one of the following in your Podfile, then run `pod install` again:
443
- - Use dynamic linkage: `use_frameworks! :linkage => :dynamic`
444
- - Opt out of SPM: set `$RNFirebaseDisableSPM = true` before any target block, then use the static or dynamic linkage your project requires.
632
+ Fix your Podfile to use dynamic linkage, then run `pod install` again:
633
+
634
+ use_frameworks! :linkage => :dynamic
635
+
636
+ With the stock template Podfile you can instead run `USE_FRAMEWORKS=dynamic pod install`, but it must be set for every `pod install` (including CI) or linkage reverts to static; the Podfile line above is the durable fix.
637
+ Expo: set `ios.useFrameworks` to `"dynamic"` in the `expo-build-properties` plugin config, then re-run `npx expo prebuild`. Only if you maintain `ios/` by hand (no prebuild), set `"ios.useFrameworks": "dynamic"` in `ios/Podfile.properties.json`.
638
+
639
+ See https://rnfirebase.io/ios-spm
445
640
  MESSAGE
446
641
  end
447
642
 
@@ -589,7 +784,8 @@ def rnfirebase_run_spm_user_project_hooks(installer)
589
784
  rnfirebase_add_spm_core_to_app_target(installer)
590
785
  rescue StandardError => e
591
786
  if defined?(Pod::UI)
592
- Pod::UI.warn "[react-native-firebase] Couldn't link FirebaseCore into the app target " \
787
+ product = RNFirebaseSPM.umbrella? ? RNFIREBASE_SPM_UMBRELLA_PRODUCT : 'FirebaseCore'
788
+ Pod::UI.warn "[react-native-firebase] Couldn't link #{product} into the app target " \
593
789
  "automatically (#{e.class}: #{e.message}). " \
594
790
  'Add `rnfirebase_add_spm_core_to_app_target(installer)` ' \
595
791
  'to your Podfile\'s post_integrate block as a fallback if your own native code calls ' \
@@ -600,10 +796,10 @@ def rnfirebase_run_spm_user_project_hooks(installer)
600
796
  rnfirebase_remove_spm_core_from_app_target(installer)
601
797
  rescue StandardError => e
602
798
  if defined?(Pod::UI)
603
- Pod::UI.warn "[react-native-firebase] Couldn't remove a stale FirebaseCore SPM link from the " \
799
+ Pod::UI.warn "[react-native-firebase] Couldn't remove a stale Firebase SPM link from the " \
604
800
  "app target automatically (#{e.class}: #{e.message}). If you previously used SPM and have " \
605
- 'since set `$RNFirebaseDisableSPM = true`, remove the "firebase-ios-sdk" Swift Package ' \
606
- 'dependency from your app target manually in Xcode.'
801
+ 'since set `$RNFirebaseDisableSPM = true`, remove the firebase-ios-sdk / RNFBFirebase Swift ' \
802
+ 'Package dependency from your app target manually in Xcode.'
607
803
  end
608
804
  end
609
805
  begin
@@ -630,6 +826,14 @@ def rnfirebase_run_spm_user_project_hooks(installer)
630
826
  '`FirebaseCoreInternal`/`FirebaseSharedSwift` cannot be resolved.'
631
827
  end
632
828
  end
829
+ begin
830
+ rnfirebase_sync_probe_swift_condition_on_app_targets(installer)
831
+ rescue StandardError => e
832
+ if defined?(Pod::UI)
833
+ Pod::UI.warn "[react-native-firebase] Couldn't sync the #{RNFIREBASE_SPM_PROBE_SWIFT_CONDITION} Swift " \
834
+ "compilation condition on the app target (#{e.class}: #{e.message})."
835
+ end
836
+ end
633
837
  end
634
838
 
635
839
  # We wrap `Pod::Installer#generate_pods_project` (restore RNFB dynamic
@@ -819,12 +1023,13 @@ rescue StandardError => e
819
1023
  end
820
1024
  end
821
1025
 
822
- # Adds a direct SPM product dependency on `FirebaseCore` to the *app's own*
823
- # native target(s) -- not just RNFB's pod targets. Runs automatically on every
824
- # `pod install`/`pod update` from `rnfirebase_run_spm_user_project_hooks`
825
- # (post_integrate on current CocoaPods; post_install fallback otherwise) --
826
- # see `rnfirebase_hook_cocoapods_post_install!` -- so you normally never
827
- # need to call this yourself.
1026
+ # Adds a direct SPM product dependency on `FirebaseCore` (shipped path) or the
1027
+ # local `RNFBFirebase` probe product to the *app's own* native target(s) -- not
1028
+ # just RNFB's pod targets. Runs automatically on every `pod install`/`pod update`
1029
+ # from `rnfirebase_run_spm_user_project_hooks` (post_integrate on current
1030
+ # CocoaPods; post_install fallback otherwise) -- see
1031
+ # `rnfirebase_hook_cocoapods_post_install!` -- so you normally never need to
1032
+ # call this yourself.
828
1033
  #
829
1034
  # Why this needs to exist: every react-native-firebase app is required to
830
1035
  # `import Firebase` and call `FirebaseApp.configure()` (Swift) /
@@ -844,8 +1049,14 @@ end
844
1049
  def rnfirebase_add_spm_core_to_app_target(installer)
845
1050
  return unless RNFirebaseSPM.active?
846
1051
 
847
- pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
1052
+ umbrella = RNFirebaseSPM.umbrella?
1053
+ pkg_class = if umbrella
1054
+ Xcodeproj::Project::Object::XCLocalSwiftPackageReference
1055
+ else
1056
+ Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
1057
+ end
848
1058
  ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency
1059
+ product_name = umbrella ? RNFIREBASE_SPM_UMBRELLA_PRODUCT : 'FirebaseCore'
849
1060
 
850
1061
  installer.aggregate_targets.each do |aggregate_target|
851
1062
  project = aggregate_target.user_project
@@ -856,17 +1067,22 @@ def rnfirebase_add_spm_core_to_app_target(installer)
856
1067
  next unless target.respond_to?(:shell_script_build_phases)
857
1068
  next unless target.shell_script_build_phases.any? { |phase| phase.name == '[CP] Embed Pods Frameworks' }
858
1069
 
859
- # A `FirebaseCore` product dependency already being declared on the
860
- # target does *not* by itself mean this target is a genuine no-op:
861
- # a pre-fix RNFB version could have committed that dependency into
862
- # the consumer's `.pbxproj` without ever linking it (see the
863
- # PBXBuildFile comment below) -- that's the exact broken state #9158
864
- # reports, and it's already sitting in every affected consumer's
865
- # project today. So checking `package_product_dependencies` alone
866
- # can't tell that already-affected state apart from a healthy,
867
- # already-linked one -- it has to check the build phase itself.
868
- existing_ref = target.package_product_dependencies.find { |dep| dep.product_name == 'FirebaseCore' }
1070
+ # A product dependency already being declared on the target does *not*
1071
+ # by itself mean this target is a genuine no-op: a pre-fix RNFB version
1072
+ # could have committed that dependency into the consumer's `.pbxproj`
1073
+ # without ever linking it (see the PBXBuildFile comment below) -- that's
1074
+ # the exact broken state #9158 reports, and it's already sitting in every
1075
+ # affected consumer's project today. So checking
1076
+ # `package_product_dependencies` alone can't tell that already-affected
1077
+ # state apart from a healthy, already-linked one -- it has to check the
1078
+ # build phase itself.
1079
+ existing_ref = target.package_product_dependencies.find { |dep| dep.product_name == product_name }
869
1080
  if existing_ref
1081
+ # Pin even when the link is already healthy: a prior RNFB version
1082
+ # may have committed `upToNextMajorVersion` on this package
1083
+ # reference, and leaving that kind in place lets SPM float.
1084
+ # Local packages have no requirement to pin.
1085
+ project_modified ||= rnfirebase_pin_spm_package_requirement!(existing_ref.package) unless umbrella
870
1086
  next if target.frameworks_build_phase.files.any? { |bf| bf.product_ref == existing_ref }
871
1087
 
872
1088
  # Healing path: reuse the dependency (and its package reference)
@@ -875,28 +1091,40 @@ def rnfirebase_add_spm_core_to_app_target(installer)
875
1091
  ref = existing_ref
876
1092
  else
877
1093
  pkg = project.root_object.package_references.find do |candidate|
878
- candidate.instance_of?(pkg_class) && candidate.repositoryURL == RNFirebaseSPM.url
1094
+ if umbrella
1095
+ candidate.instance_of?(pkg_class) && candidate.relative_path == RNFirebaseSPM.umbrella_path
1096
+ else
1097
+ candidate.instance_of?(pkg_class) && candidate.repositoryURL == RNFirebaseSPM.url
1098
+ end
879
1099
  end
880
- unless pkg
1100
+ if pkg
1101
+ project_modified ||= rnfirebase_pin_spm_package_requirement!(pkg) unless umbrella
1102
+ else
881
1103
  pkg = project.new(pkg_class)
882
- pkg.repositoryURL = RNFirebaseSPM.url
883
- pkg.requirement = { kind: 'upToNextMajorVersion', minimumVersion: RNFirebaseSPM.version }
1104
+ rnfirebase_assign_stable_uuid!(project, pkg, rnfirebase_stable_package_uuid_key(umbrella))
1105
+ if umbrella
1106
+ pkg.relative_path = RNFirebaseSPM.umbrella_path
1107
+ else
1108
+ pkg.repositoryURL = RNFirebaseSPM.url
1109
+ pkg.requirement = rnfirebase_spm_package_requirement(RNFirebaseSPM.version)
1110
+ end
884
1111
  project.root_object.package_references << pkg
885
1112
  end
886
1113
 
887
1114
  ref = project.new(ref_class)
1115
+ rnfirebase_assign_stable_uuid!(project, ref, "product-dependency:#{target.name}:#{product_name}")
888
1116
  ref.package = pkg
889
- ref.product_name = 'FirebaseCore'
1117
+ ref.product_name = product_name
890
1118
  target.package_product_dependencies << ref
891
1119
  end
892
1120
 
893
1121
  if defined?(Pod::UI)
894
1122
  message = if existing_ref
895
- 'Repairing FirebaseCore SPM link on the app target (dependency was already declared but ' \
1123
+ "Repairing #{product_name} SPM link on the app target (dependency was already declared but " \
896
1124
  'never linked) so native code that calls FIRApp/FIROptions APIs directly can resolve ' \
897
1125
  'those symbols.'
898
1126
  else
899
- 'Linking FirebaseCore directly into the app target (SPM) so native code that calls ' \
1127
+ "Linking #{product_name} directly into the app target (SPM) so native code that calls " \
900
1128
  'FIRApp/FIROptions APIs directly can resolve those symbols.'
901
1129
  end
902
1130
  Pod::UI.puts "[react-native-firebase] #{target.name}: ".yellow + message
@@ -914,6 +1142,7 @@ def rnfirebase_add_spm_core_to_app_target(installer)
914
1142
  # directly (e.g. Expo's generated AppDelegate) -- even though `pod
915
1143
  # install` itself appears to succeed.
916
1144
  build_file = project.new(Xcodeproj::Project::Object::PBXBuildFile)
1145
+ rnfirebase_assign_stable_uuid!(project, build_file, "build-file:#{target.name}:#{product_name}")
917
1146
  build_file.product_ref = ref
918
1147
  target.frameworks_build_phase.files << build_file
919
1148
 
@@ -965,11 +1194,17 @@ end
965
1194
  # 'Firebase'` at compile time, and as duplicate App-Intents-metadata build
966
1195
  # commands at Archive time.
967
1196
  def rnfirebase_remove_spm_core_from_app_target(installer)
968
- return if RNFirebaseSPM.active?
969
-
970
- pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
1197
+ remote_pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
1198
+ local_pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
971
1199
  ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency
972
1200
 
1201
+ # When the probe umbrella is active, drop a superseded direct FirebaseCore
1202
+ # remote link. When shipped remote SPM is active, drop a leftover local
1203
+ # RNFBFirebase probe link. When SPM is off, drop both.
1204
+ remove_remote_core = !RNFirebaseSPM.active? || RNFirebaseSPM.umbrella?
1205
+ remove_local_umbrella = !RNFirebaseSPM.active? || !RNFirebaseSPM.umbrella?
1206
+ return unless remove_remote_core || remove_local_umbrella
1207
+
973
1208
  installer.aggregate_targets.each do |aggregate_target|
974
1209
  project = aggregate_target.user_project
975
1210
  project_modified = false
@@ -978,12 +1213,37 @@ def rnfirebase_remove_spm_core_from_app_target(installer)
978
1213
  next unless target.respond_to?(:package_product_dependencies)
979
1214
 
980
1215
  stale_refs = target.package_product_dependencies.select do |dep|
981
- dep.instance_of?(ref_class) && dep.product_name == 'FirebaseCore' && dep.package&.repositoryURL == RNFirebaseSPM.url # rubocop:disable Layout/LineLength
1216
+ next false unless dep.instance_of?(ref_class)
1217
+
1218
+ pkg = dep.package
1219
+ next false unless pkg
1220
+
1221
+ if remove_remote_core &&
1222
+ dep.product_name == 'FirebaseCore' &&
1223
+ pkg.respond_to?(:repositoryURL) &&
1224
+ pkg.repositoryURL == RNFirebaseSPM.url
1225
+ next true
1226
+ end
1227
+ if remove_local_umbrella &&
1228
+ dep.product_name == RNFIREBASE_SPM_UMBRELLA_PRODUCT &&
1229
+ pkg.respond_to?(:relative_path) &&
1230
+ pkg.relative_path == RNFirebaseSPM.umbrella_path
1231
+ next true
1232
+ end
1233
+
1234
+ false
982
1235
  end
983
1236
  next if stale_refs.empty?
984
1237
 
985
1238
  if defined?(Pod::UI)
986
- Pod::UI.puts "#{"[react-native-firebase] #{target.name}: ".yellow}SPM disabled -- removing the stale FirebaseCore Swift Package link left on the app target." # rubocop:disable Layout/LineLength
1239
+ reason = if RNFirebaseSPM.umbrella?
1240
+ 'SPM probe umbrella active -- removing the superseded direct FirebaseCore Swift Package link.'
1241
+ elsif RNFirebaseSPM.active?
1242
+ 'SPM remote active -- removing a leftover RNFBFirebase probe Swift Package link.'
1243
+ else
1244
+ 'SPM disabled -- removing the stale Firebase SPM link left on the app target.'
1245
+ end
1246
+ Pod::UI.puts "[react-native-firebase] #{target.name}: ".yellow + reason
987
1247
  end
988
1248
 
989
1249
  stale_refs.each do |ref|
@@ -998,15 +1258,27 @@ def rnfirebase_remove_spm_core_from_app_target(installer)
998
1258
  project_modified = true
999
1259
  end
1000
1260
 
1001
- project.root_object.package_references
1002
- .select { |pkg| pkg.instance_of?(pkg_class) && pkg.repositoryURL == RNFirebaseSPM.url }
1003
- .each do |pkg|
1004
- next if pkg.referrers.any?(ref_class)
1261
+ stale_packages = project.root_object.package_references.select do |pkg|
1262
+ if remove_remote_core &&
1263
+ pkg.instance_of?(remote_pkg_class) &&
1264
+ pkg.repositoryURL == RNFirebaseSPM.url
1265
+ next true
1266
+ end
1267
+ if remove_local_umbrella &&
1268
+ pkg.instance_of?(local_pkg_class) &&
1269
+ pkg.relative_path == RNFirebaseSPM.umbrella_path
1270
+ next true
1271
+ end
1005
1272
 
1006
- project.root_object.package_references.delete(pkg)
1007
- pkg.remove_from_project
1008
- project_modified = true
1009
- end
1273
+ false
1274
+ end
1275
+ stale_packages.each do |pkg|
1276
+ next if pkg.referrers.any?(ref_class)
1277
+
1278
+ project.root_object.package_references.delete(pkg)
1279
+ pkg.remove_from_project
1280
+ project_modified = true
1281
+ end
1010
1282
 
1011
1283
  project.save if project_modified
1012
1284
  end
@@ -1086,17 +1358,18 @@ end
1086
1358
  # `rnfirebase_hook_cocoapods_post_install!` above -- so you normally never
1087
1359
  # need to call this yourself.
1088
1360
  #
1089
- # 1. `-ObjC` in `OTHER_LDFLAGS` (app target, every configuration): under SPM
1090
- # + dynamic linkage, dead-code stripping can drop Objective-C
1091
- # classes/categories that are only ever discovered via runtime reflection
1092
- # rather than a direct static reference -- e.g. Firebase's
1093
- # FIRLibrary/FIRComponent registration used by RNFBCrashlyticsInitProvider
1094
- # -- which otherwise crashes the app at launch, but only in Release/
1095
- # Archive builds (a TestFlight-only failure that's hard to reproduce from
1096
- # a local Debug build). `-ObjC` forces the linker to keep any object file
1097
- # that defines an ObjC class/category, without disabling dead-code
1098
- # stripping or optimizations for anything else, so it doesn't meaningfully
1099
- # grow the binary or slow down Release builds.
1361
+ # 1. `-ObjC` in `OTHER_LDFLAGS` (app target *and* every RNFB pod target,
1362
+ # every configuration): under SPM + dynamic linkage, dead-code stripping
1363
+ # can drop Objective-C classes/categories that are only ever discovered
1364
+ # via runtime reflection rather than a direct static reference -- e.g.
1365
+ # Firebase's FIRLibrary/FIRComponent registration used by
1366
+ # RNFBCrashlyticsInitProvider, and GoogleAppMeasurement categories
1367
+ # statically absorbed into RNFBAnalytics. App-target `-ObjC` does not
1368
+ # keep categories linked into a pod. Without pod-level `-ObjC` those
1369
+ # categories can vanish and Measurement/Remote Config can explode or
1370
+ # hang. `-ObjC` forces the linker to keep any object file that defines
1371
+ # an ObjC class/category, without disabling dead-code stripping or
1372
+ # optimizations for anything else.
1100
1373
  #
1101
1374
  # 2. `SWIFT_ENABLE_EXPLICIT_MODULES = 'NO'` and `CLANG_ENABLE_EXPLICIT_MODULES
1102
1375
  # = 'NO'` (app target and Pods project, every configuration): Xcode 26
@@ -1138,6 +1411,88 @@ end
1138
1411
  # build module 'glog'"/`'cxxreact'`. Confirmed via a real `xcodebuild
1139
1412
  # archive`: with this flag, the build fails on React Native's own C++ pods;
1140
1413
  # without it, `@import` is never reached and the archive succeeds cleanly.
1414
+ # Schedules the local facade product before RNFBApp Swift compiles, without
1415
+ # listing that product in RNFBApp's packageProductDependencies. A product
1416
+ # dependency on the pod itself makes Xcode inject FirebaseCore and
1417
+ # FirebaseInstallations module maps into RNFBApp's swiftc invocation.
1418
+ #
1419
+ # The aggregate holds only a PBXTargetDependency productRef. RNFBApp depends
1420
+ # on the aggregate target. Shipped installs never call this.
1421
+ def rnfirebase_ensure_probe_facade_build_order!(project, app_target)
1422
+ changed = false
1423
+ root = project.root_object
1424
+ pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
1425
+ pkg = root.package_references.find do |candidate|
1426
+ candidate.instance_of?(pkg_class) && candidate.relative_path == RNFirebaseSPM.umbrella_path
1427
+ end
1428
+ unless pkg
1429
+ pkg = project.new(pkg_class)
1430
+ pkg.relative_path = RNFirebaseSPM.umbrella_path
1431
+ root.package_references << pkg
1432
+ changed = true
1433
+ end
1434
+
1435
+ order_target = project.targets.find { |candidate| candidate.name == RNFIREBASE_SPM_PROBE_ORDER_TARGET }
1436
+ unless order_target
1437
+ order_target = project.new_aggregate_target(RNFIREBASE_SPM_PROBE_ORDER_TARGET)
1438
+ changed = true
1439
+ end
1440
+
1441
+ product_ready = order_target.dependencies.any? do |dep|
1442
+ dep.respond_to?(:product_ref) && dep.product_ref&.product_name == RNFIREBASE_SPM_UMBRELLA_PRODUCT
1443
+ end
1444
+ unless product_ready
1445
+ product = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
1446
+ product.package = pkg
1447
+ product.product_name = RNFIREBASE_SPM_UMBRELLA_PRODUCT
1448
+ dependency = project.new(Xcodeproj::Project::Object::PBXTargetDependency)
1449
+ dependency.product_ref = product
1450
+ order_target.dependencies << dependency
1451
+ changed = true
1452
+ end
1453
+
1454
+ already_ordered = app_target.dependencies.any? do |dep|
1455
+ dep.respond_to?(:target) && dep.target == order_target
1456
+ end
1457
+ unless already_ordered
1458
+ app_target.add_dependency(order_target)
1459
+ changed = true
1460
+ end
1461
+
1462
+ changed
1463
+ end
1464
+
1465
+ # Sets `RNFB_DYNAMIC_FIREBASE_PROBE` in `SWIFT_ACTIVE_COMPILATION_CONDITIONS`
1466
+ # on the app's own native targets while the dynamic probe is active, so app
1467
+ # code such as test-rn-bare's AppDelegate.swift selects the `import RNFBFirebase`
1468
+ # path. Pods settings never reach the app target, and the user project is
1469
+ # checked in, so the condition is also removed again whenever the probe is off
1470
+ # (flag unset, remote SPM, or SPM disabled). Runs regardless of whether SPM is
1471
+ # active so a probe condition left in the app project cannot leak into a
1472
+ # shipped-path build.
1473
+ def rnfirebase_sync_probe_swift_condition_on_app_targets(installer)
1474
+ # `umbrella?` is only ever set by `firebase_dependency` while the probe env
1475
+ # var is `1`, so it alone is the probe condition.
1476
+ enabled = RNFirebaseSPM.umbrella?
1477
+
1478
+ installer.aggregate_targets.each do |aggregate_target|
1479
+ project = aggregate_target.user_project
1480
+ # `integrate_targets: false` has no user project: nothing to sync.
1481
+ next if project.nil?
1482
+
1483
+ project_modified = false
1484
+
1485
+ project.native_targets.each do |target|
1486
+ target.build_configurations.each do |config|
1487
+ changed = rnfirebase_set_probe_swift_condition(config.build_settings, enabled)
1488
+ project_modified ||= changed
1489
+ end
1490
+ end
1491
+
1492
+ project.save if project_modified
1493
+ end
1494
+ end
1495
+
1141
1496
  def rnfirebase_apply_spm_build_settings(installer)
1142
1497
  return unless RNFirebaseSPM.active?
1143
1498
 
@@ -1184,9 +1539,102 @@ def rnfirebase_apply_spm_build_settings(installer)
1184
1539
  pods_project = installer.pods_project
1185
1540
  return unless pods_project
1186
1541
 
1542
+ # `project.new` for the probe order target runs after CocoaPods has already
1543
+ # handed the Pods UUID counter to React Native's SPM hook. Raise it again
1544
+ # so the aggregate and productRef objects cannot overwrite PBXProject.
1545
+ rnfirebase_ensure_pods_uuid_counter_safe!(installer) if rnfirebase_probe_dynamic_firebase?
1546
+
1187
1547
  pods_modified = false
1188
1548
  pods_project.targets.each do |target|
1189
1549
  target.build_configurations.each do |config|
1550
+ # Analytics/Measurement ObjC categories live on the pod that
1551
+ # statically absorbs those SPM products. App-target `-ObjC` does
1552
+ # not keep them. Apply to every RNFB pod target (not only
1553
+ # RNFBAnalytics) so each static absorption site retains categories.
1554
+ if rnfirebase_rnfb_pod_target?(target)
1555
+ ldflags_changed = add_flag.call(config.build_settings, 'OTHER_LDFLAGS', '-ObjC')
1556
+ pods_modified ||= ldflags_changed
1557
+ end
1558
+
1559
+ # `RNFB_DYNAMIC_FIREBASE_PROBE` turns on the RNFBAppModule.mm assertions.
1560
+ # Those fail the compile when FirebaseCore or FirebaseInstallations
1561
+ # headers are visible, or when C++ modules are enabled. They do not
1562
+ # ask for either. `CLANG_ENABLE_MODULES = YES` keeps React Native's
1563
+ # prebuilt Core module map resolving; it does not enable
1564
+ # `__has_feature(cxx_modules)`. The macro stays on RNFBApp, and only
1565
+ # while the probe is on, so shipped builds are unaffected.
1566
+ if rnfirebase_probe_dynamic_firebase? && target.name == 'RNFBApp'
1567
+ probe_define_changed = add_flag.call(
1568
+ config.build_settings,
1569
+ 'GCC_PREPROCESSOR_DEFINITIONS',
1570
+ 'RNFB_DYNAMIC_FIREBASE_PROBE=1'
1571
+ )
1572
+ pods_modified ||= probe_define_changed
1573
+ # Swift twin of the define above: RNFBApp's `.swift` files pick the
1574
+ # `import RNFBFirebase` path with `#if RNFB_DYNAMIC_FIREBASE_PROBE`.
1575
+ probe_swift_changed = rnfirebase_set_probe_swift_condition(config.build_settings, true)
1576
+ pods_modified ||= probe_swift_changed
1577
+ # RN prebuilt Core requires its Clang module map. `YES` enables normal
1578
+ # Objective-C modules but does not enable the separate
1579
+ # `__has_feature(cxx_modules)` capability asserted by the probe TU.
1580
+ unless config.build_settings['CLANG_ENABLE_MODULES'] == 'YES'
1581
+ config.build_settings['CLANG_ENABLE_MODULES'] = 'YES'
1582
+ pods_modified = true
1583
+ end
1584
+
1585
+ # The probe package is deliberately owned by the app target rather
1586
+ # than attached to RNFBApp through React Native's `spm_dependency`.
1587
+ # A direct package-product dependency makes Xcode inject every
1588
+ # transitive Firebase target's -I/module-map flags into this pod,
1589
+ # exposing FirebaseCore/FirebaseInstallations to Objective-C++.
1590
+ # Link the already app-owned dynamic framework by name instead: Swift
1591
+ # can import RNFBFirebase, while the underlying product headers remain
1592
+ # outside RNFBApp's compile search paths. Build order is a separate
1593
+ # aggregate productRef (`rnfirebase_ensure_probe_facade_build_order!`);
1594
+ # search paths alone do not wait for that product on an empty DerivedData.
1595
+ #
1596
+ # Do not derive these from BUILT_PRODUCTS_DIR. For a CocoaPods
1597
+ # framework target that setting is target-scoped (for example
1598
+ # .../Release-iphonesimulator/RNFBApp), while Xcode emits package
1599
+ # Swift modules and frameworks into the shared configuration products
1600
+ # directory beside it:
1601
+ # <configuration-products>/RNFBFirebase.swiftmodule
1602
+ # <configuration-products>/PackageFrameworks/RNFBFirebase.framework
1603
+ # BUILD_DIR + CONFIGURATION + EFFECTIVE_PLATFORM_NAME addresses that
1604
+ # shared directory for Debug/Release, simulator/device, and Archive.
1605
+ # Xcode realpaths `swiftmodule/..` back to that directory, so the
1606
+ # swiftc line would not name the facade. Also list the emitted
1607
+ # `.swiftmodule` bundle. Swift still imports from the products
1608
+ # directory; the bundle path keeps `RNFBFirebase` on the invocation
1609
+ # without adding FirebaseCore's module map.
1610
+ package_products_path = '${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}'
1611
+ facade_module_include = "#{package_products_path}/#{RNFIREBASE_SPM_UMBRELLA_PRODUCT}.swiftmodule"
1612
+ swift_include_paths = rnfirebase_build_setting_list(config.build_settings['SWIFT_INCLUDE_PATHS'])
1613
+ [package_products_path, facade_module_include].each do |include_path|
1614
+ next if swift_include_paths.include?(include_path)
1615
+
1616
+ swift_include_paths << include_path
1617
+ pods_modified = true
1618
+ end
1619
+ config.build_settings['SWIFT_INCLUDE_PATHS'] = swift_include_paths.join(' ')
1620
+
1621
+ framework_paths = rnfirebase_build_setting_list(config.build_settings['FRAMEWORK_SEARCH_PATHS'])
1622
+ package_frameworks_path = "#{package_products_path}/PackageFrameworks"
1623
+ unless framework_paths.include?(package_frameworks_path)
1624
+ config.build_settings['FRAMEWORK_SEARCH_PATHS'] = (framework_paths << package_frameworks_path).join(' ')
1625
+ pods_modified = true
1626
+ end
1627
+
1628
+ probe_ldflags = rnfirebase_build_setting_list(config.build_settings['OTHER_LDFLAGS'])
1629
+ unless probe_ldflags.include?(RNFIREBASE_SPM_UMBRELLA_PRODUCT)
1630
+ config.build_settings['OTHER_LDFLAGS'] =
1631
+ (probe_ldflags + ['-framework', RNFIREBASE_SPM_UMBRELLA_PRODUCT]).join(' ')
1632
+ pods_modified = true
1633
+ end
1634
+
1635
+ pods_modified = true if rnfirebase_ensure_probe_facade_build_order!(pods_project, target)
1636
+ end
1637
+
1190
1638
  explicit_modules_settings.each do |setting|
1191
1639
  unless config.build_settings[setting] == 'NO'
1192
1640
  config.build_settings[setting] = 'NO'
@@ -1201,36 +1649,44 @@ end
1201
1649
  rnfirebase_hook_cocoapods_post_install!
1202
1650
 
1203
1651
  # @param spec [Pod::Specification] The podspec object (the `s` in podspec DSL)
1204
- # @param version [String] Firebase SDK version (e.g., '12.10.0')
1652
+ # @param version [String] Firebase SDK version (e.g., '13.0.0')
1205
1653
  # @param spm_products [Array<String>] SPM product names (e.g., ['FirebaseAuth'])
1206
- # @param pods [Array<String>, String] CocoaPods dependency names with optional version
1207
- # Can be a single string like 'Firebase/Auth' or an array like ['Firebase/Messaging', 'FirebaseCoreExtension']
1208
- def firebase_dependency(spec, version, spm_products, pods)
1209
- if defined?(spm_dependency) && !rnfirebase_spm_disabled?
1210
- # Tracked ourselves (rather than inspecting RN's internal `SPM` object's
1211
- # dependency list) so `rnfirebase_add_spm_embed_phase` doesn't depend on
1212
- # any RN-internal state shape -- only on whether *we* ever took this path.
1213
- RNFirebaseSPM.activate!(version)
1654
+ # @param pods [Array<String>, String] Legacy CocoaPods names (ignored — Firebase 13+ is SPM-only)
1655
+ # Kept for call-site compatibility with existing podspecs.
1656
+ def firebase_dependency(spec, version, spm_products, _pods)
1657
+ rnfirebase_fail_cocoapods_firebase!('$RNFirebaseDisableSPM = true') if rnfirebase_spm_disabled?
1658
+ unless defined?(spm_dependency)
1659
+ rnfirebase_fail_cocoapods_firebase!('React Native < 0.75 — `spm_dependency` is unavailable')
1660
+ end
1661
+
1662
+ # Tracked ourselves (rather than inspecting RN's internal `SPM` object's
1663
+ # dependency list) so `rnfirebase_add_spm_embed_phase` doesn't depend on
1664
+ # any RN-internal state shape -- only on whether *we* ever took this path.
1665
+ if rnfirebase_probe_dynamic_firebase?
1666
+ RNFirebaseSPM.activate!(version, mode: :umbrella)
1667
+ if defined?(Pod::UI)
1668
+ Pod::UI.puts "[react-native-firebase] #{spec.name}: ".yellow +
1669
+ "Using local #{RNFIREBASE_SPM_UMBRELLA_PRODUCT} SPM probe " \
1670
+ '(RNFB_TEST_RN_BARE_DYNAMIC_FIREBASE=1)'
1671
+ end
1672
+ # Do not attach the package product to the pod target. Xcode propagates
1673
+ # a direct Swift-package dependency's transitive Clang include/module
1674
+ # paths into that target, which makes FirebaseCore and
1675
+ # FirebaseInstallations headers visible to RNFBApp Objective-C++.
1676
+ # rnfirebase_add_spm_core_to_app_target owns the package product.
1677
+ # rnfirebase_apply_spm_build_settings links the dynamic framework and,
1678
+ # for this probe only, orders it ahead of RNFBApp via an aggregate
1679
+ # productRef that is not RNFBApp's packageProductDependencies entry.
1680
+ else
1681
+ RNFirebaseSPM.activate!(version, mode: :remote)
1214
1682
  if defined?(Pod::UI)
1215
1683
  Pod::UI.puts "[react-native-firebase] #{spec.name}: ".yellow +
1216
1684
  "Using SPM for Firebase dependency resolution (products: #{spm_products.join(', ')})"
1217
1685
  end
1218
1686
  spm_dependency(spec,
1219
1687
  url: RNFirebaseSPM.url,
1220
- requirement: { kind: 'upToNextMajorVersion', minimumVersion: version },
1688
+ requirement: rnfirebase_spm_package_requirement(version),
1221
1689
  products: spm_products)
1222
- else
1223
- if defined?(Pod::UI)
1224
- if rnfirebase_spm_disabled?
1225
- Pod::UI.puts "#{"[react-native-firebase] #{spec.name}: ".yellow}SPM disabled ($RNFirebaseDisableSPM = true), using CocoaPods for Firebase dependencies" # rubocop:disable Layout/LineLength
1226
- elsif !defined?(spm_dependency)
1227
- Pod::UI.puts "#{"[react-native-firebase] #{spec.name}: ".yellow}SPM not available (React Native < 0.75), using CocoaPods for Firebase dependencies" # rubocop:disable Layout/LineLength
1228
- end
1229
- end
1230
- pods = [pods] unless pods.is_a?(Array)
1231
- pods.each do |pod|
1232
- spec.dependency pod, version
1233
- end
1234
1690
  end
1235
1691
  end
1236
1692