@use-voltra/ios-client 2.1.1 → 2.3.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 (365) hide show
  1. package/README.md +61 -8
  2. package/Voltra.podspec +1 -0
  3. package/build/commonjs/dynamic-widget/api.js +15 -0
  4. package/build/commonjs/dynamic-widget/api.js.map +1 -0
  5. package/build/commonjs/events.js +23 -0
  6. package/build/commonjs/events.js.map +1 -1
  7. package/build/commonjs/index.js +74 -7
  8. package/build/commonjs/index.js.map +1 -1
  9. package/build/commonjs/live-activity/api.js +3 -1
  10. package/build/commonjs/live-activity/api.js.map +1 -1
  11. package/build/commonjs/live-activity/dynamic-api.js +173 -0
  12. package/build/commonjs/live-activity/dynamic-api.js.map +1 -0
  13. package/build/commonjs/native/NativeVoltra.js.map +1 -1
  14. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js +47 -0
  15. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  16. package/build/commonjs/utils/enableWidgetHotReload.js +8 -11
  17. package/build/commonjs/utils/enableWidgetHotReload.js.map +1 -1
  18. package/build/commonjs/utils/useUpdateOnHMR.js +3 -7
  19. package/build/commonjs/utils/useUpdateOnHMR.js.map +1 -1
  20. package/build/commonjs/widgets/server-credentials.js +9 -0
  21. package/build/commonjs/widgets/server-credentials.js.map +1 -1
  22. package/build/commonjs/widgets/server-update.js +78 -0
  23. package/build/commonjs/widgets/server-update.js.map +1 -0
  24. package/build/module/dynamic-widget/api.js +10 -0
  25. package/build/module/dynamic-widget/api.js.map +1 -0
  26. package/build/module/events.js +23 -0
  27. package/build/module/events.js.map +1 -1
  28. package/build/module/index.js +4 -0
  29. package/build/module/index.js.map +1 -1
  30. package/build/module/live-activity/api.js +2 -2
  31. package/build/module/live-activity/api.js.map +1 -1
  32. package/build/module/live-activity/dynamic-api.js +167 -0
  33. package/build/module/live-activity/dynamic-api.js.map +1 -0
  34. package/build/module/native/NativeVoltra.js.map +1 -1
  35. package/build/module/utils/enableDynamicLiveActivityHotReload.js +43 -0
  36. package/build/module/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  37. package/build/module/utils/enableWidgetHotReload.js +9 -11
  38. package/build/module/utils/enableWidgetHotReload.js.map +1 -1
  39. package/build/module/utils/useUpdateOnHMR.js +3 -7
  40. package/build/module/utils/useUpdateOnHMR.js.map +1 -1
  41. package/build/module/widgets/server-credentials.js +9 -0
  42. package/build/module/widgets/server-credentials.js.map +1 -1
  43. package/build/module/widgets/server-update.js +72 -0
  44. package/build/module/widgets/server-update.js.map +1 -0
  45. package/build/typescript/commonjs/dynamic-widget/api.d.ts +8 -0
  46. package/build/typescript/commonjs/dynamic-widget/api.d.ts.map +1 -0
  47. package/build/typescript/commonjs/events.d.ts +7 -0
  48. package/build/typescript/commonjs/events.d.ts.map +1 -1
  49. package/build/typescript/commonjs/index.d.ts +4 -0
  50. package/build/typescript/commonjs/index.d.ts.map +1 -1
  51. package/build/typescript/commonjs/live-activity/api.d.ts +7 -0
  52. package/build/typescript/commonjs/live-activity/api.d.ts.map +1 -1
  53. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts +54 -0
  54. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts.map +1 -0
  55. package/build/typescript/commonjs/native/NativeVoltra.d.ts +21 -0
  56. package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
  57. package/build/typescript/commonjs/types.d.ts +1 -1
  58. package/build/typescript/commonjs/types.d.ts.map +1 -1
  59. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  60. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  61. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts +5 -7
  62. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts.map +1 -1
  63. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts +0 -3
  64. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts.map +1 -1
  65. package/build/typescript/commonjs/widgets/server-credentials.d.ts +8 -0
  66. package/build/typescript/commonjs/widgets/server-credentials.d.ts.map +1 -1
  67. package/build/typescript/commonjs/widgets/server-update.d.ts +64 -0
  68. package/build/typescript/commonjs/widgets/server-update.d.ts.map +1 -0
  69. package/build/typescript/module/dynamic-widget/api.d.ts +8 -0
  70. package/build/typescript/module/dynamic-widget/api.d.ts.map +1 -0
  71. package/build/typescript/module/events.d.ts +7 -0
  72. package/build/typescript/module/events.d.ts.map +1 -1
  73. package/build/typescript/module/index.d.ts +4 -0
  74. package/build/typescript/module/index.d.ts.map +1 -1
  75. package/build/typescript/module/live-activity/api.d.ts +7 -0
  76. package/build/typescript/module/live-activity/api.d.ts.map +1 -1
  77. package/build/typescript/module/live-activity/dynamic-api.d.ts +54 -0
  78. package/build/typescript/module/live-activity/dynamic-api.d.ts.map +1 -0
  79. package/build/typescript/module/native/NativeVoltra.d.ts +21 -0
  80. package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
  81. package/build/typescript/module/types.d.ts +1 -1
  82. package/build/typescript/module/types.d.ts.map +1 -1
  83. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  84. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  85. package/build/typescript/module/utils/enableWidgetHotReload.d.ts +5 -7
  86. package/build/typescript/module/utils/enableWidgetHotReload.d.ts.map +1 -1
  87. package/build/typescript/module/utils/useUpdateOnHMR.d.ts +0 -3
  88. package/build/typescript/module/utils/useUpdateOnHMR.d.ts.map +1 -1
  89. package/build/typescript/module/widgets/server-credentials.d.ts +8 -0
  90. package/build/typescript/module/widgets/server-credentials.d.ts.map +1 -1
  91. package/build/typescript/module/widgets/server-update.d.ts +64 -0
  92. package/build/typescript/module/widgets/server-update.d.ts.map +1 -0
  93. package/expo-plugin/build/cjs/constants.js +20 -1
  94. package/expo-plugin/build/cjs/constants.js.map +1 -1
  95. package/expo-plugin/build/cjs/index.js +9 -1
  96. package/expo-plugin/build/cjs/index.js.map +1 -1
  97. package/expo-plugin/build/cjs/ios/index.js +1 -0
  98. package/expo-plugin/build/cjs/ios/index.js.map +1 -1
  99. package/expo-plugin/build/cjs/ios/infoPlist.js +28 -5
  100. package/expo-plugin/build/cjs/ios/infoPlist.js.map +1 -1
  101. package/expo-plugin/build/cjs/ios/serverUpdate.js +25 -0
  102. package/expo-plugin/build/cjs/ios/serverUpdate.js.map +1 -0
  103. package/expo-plugin/build/cjs/ios-widget/clientRendered.js +4 -4
  104. package/expo-plugin/build/cjs/ios-widget/clientRendered.js.map +1 -1
  105. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js +5 -5
  106. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js.map +1 -1
  107. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js +130 -0
  108. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  109. package/expo-plugin/build/cjs/ios-widget/files/index.js +9 -2
  110. package/expo-plugin/build/cjs/ios-widget/files/index.js.map +1 -1
  111. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js +6 -3
  112. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js.map +1 -1
  113. package/expo-plugin/build/cjs/ios-widget/files/manifest.js +24 -0
  114. package/expo-plugin/build/cjs/ios-widget/files/manifest.js.map +1 -1
  115. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js +7 -0
  116. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js.map +1 -0
  117. package/expo-plugin/build/cjs/ios-widget/files/swift.js +56 -26
  118. package/expo-plugin/build/cjs/ios-widget/files/swift.js.map +1 -1
  119. package/expo-plugin/build/cjs/ios-widget/index.js +9 -3
  120. package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
  121. package/expo-plugin/build/cjs/ios-widget/podfile.js +38 -12
  122. package/expo-plugin/build/cjs/ios-widget/podfile.js.map +1 -1
  123. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js +34 -22
  124. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js.map +1 -1
  125. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +194 -118
  126. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
  127. package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js +30 -22
  128. package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js.map +1 -1
  129. package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js +50 -0
  130. package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js.map +1 -0
  131. package/expo-plugin/build/cjs/ios-widget/xcode/groups.js +45 -43
  132. package/expo-plugin/build/cjs/ios-widget/xcode/groups.js.map +1 -1
  133. package/expo-plugin/build/cjs/ios-widget/xcode/index.js +93 -91
  134. package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
  135. package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js +41 -39
  136. package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js.map +1 -1
  137. package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js +2 -2
  138. package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js.map +1 -1
  139. package/expo-plugin/build/cjs/ios-widget/xcode/target.js +29 -63
  140. package/expo-plugin/build/cjs/ios-widget/xcode/target.js.map +1 -1
  141. package/expo-plugin/build/cjs/utils/urlScheme.js +36 -14
  142. package/expo-plugin/build/cjs/utils/urlScheme.js.map +1 -1
  143. package/expo-plugin/build/cjs/validation.js +54 -0
  144. package/expo-plugin/build/cjs/validation.js.map +1 -1
  145. package/expo-plugin/build/esm/constants.js +17 -0
  146. package/expo-plugin/build/esm/constants.js.map +1 -1
  147. package/expo-plugin/build/esm/index.js +9 -1
  148. package/expo-plugin/build/esm/index.js.map +1 -1
  149. package/expo-plugin/build/esm/ios/index.js +1 -0
  150. package/expo-plugin/build/esm/ios/index.js.map +1 -1
  151. package/expo-plugin/build/esm/ios/infoPlist.js +28 -5
  152. package/expo-plugin/build/esm/ios/infoPlist.js.map +1 -1
  153. package/expo-plugin/build/esm/ios/serverUpdate.js +21 -0
  154. package/expo-plugin/build/esm/ios/serverUpdate.js.map +1 -0
  155. package/expo-plugin/build/esm/ios-widget/clientRendered.js +5 -5
  156. package/expo-plugin/build/esm/ios-widget/clientRendered.js.map +1 -1
  157. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js +6 -6
  158. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js.map +1 -1
  159. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js +122 -0
  160. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  161. package/expo-plugin/build/esm/ios-widget/files/index.js +10 -3
  162. package/expo-plugin/build/esm/ios-widget/files/index.js.map +1 -1
  163. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js +6 -3
  164. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js.map +1 -1
  165. package/expo-plugin/build/esm/ios-widget/files/manifest.js +23 -1
  166. package/expo-plugin/build/esm/ios-widget/files/manifest.js.map +1 -1
  167. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js +4 -0
  168. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js.map +1 -0
  169. package/expo-plugin/build/esm/ios-widget/files/swift.js +50 -20
  170. package/expo-plugin/build/esm/ios-widget/files/swift.js.map +1 -1
  171. package/expo-plugin/build/esm/ios-widget/index.js +9 -3
  172. package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
  173. package/expo-plugin/build/esm/ios-widget/podfile.js +36 -11
  174. package/expo-plugin/build/esm/ios-widget/podfile.js.map +1 -1
  175. package/expo-plugin/build/esm/ios-widget/widgetPlist.js +34 -22
  176. package/expo-plugin/build/esm/ios-widget/widgetPlist.js.map +1 -1
  177. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +194 -117
  178. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
  179. package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js +31 -22
  180. package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js.map +1 -1
  181. package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js +45 -0
  182. package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js.map +1 -0
  183. package/expo-plugin/build/esm/ios-widget/xcode/groups.js +45 -42
  184. package/expo-plugin/build/esm/ios-widget/xcode/groups.js.map +1 -1
  185. package/expo-plugin/build/esm/ios-widget/xcode/index.js +97 -96
  186. package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
  187. package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js +41 -39
  188. package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js.map +1 -1
  189. package/expo-plugin/build/esm/ios-widget/xcode/productFile.js +3 -2
  190. package/expo-plugin/build/esm/ios-widget/xcode/productFile.js.map +1 -1
  191. package/expo-plugin/build/esm/ios-widget/xcode/target.js +28 -62
  192. package/expo-plugin/build/esm/ios-widget/xcode/target.js.map +1 -1
  193. package/expo-plugin/build/esm/utils/urlScheme.js +34 -14
  194. package/expo-plugin/build/esm/utils/urlScheme.js.map +1 -1
  195. package/expo-plugin/build/esm/validation.js +54 -1
  196. package/expo-plugin/build/esm/validation.js.map +1 -1
  197. package/expo-plugin/build/types/constants.d.ts +10 -1
  198. package/expo-plugin/build/types/constants.d.ts.map +1 -1
  199. package/expo-plugin/build/types/index.d.ts +1 -1
  200. package/expo-plugin/build/types/index.d.ts.map +1 -1
  201. package/expo-plugin/build/types/ios/index.d.ts +2 -0
  202. package/expo-plugin/build/types/ios/index.d.ts.map +1 -1
  203. package/expo-plugin/build/types/ios/infoPlist.d.ts +2 -0
  204. package/expo-plugin/build/types/ios/infoPlist.d.ts.map +1 -1
  205. package/expo-plugin/build/types/ios/serverUpdate.d.ts +11 -0
  206. package/expo-plugin/build/types/ios/serverUpdate.d.ts.map +1 -0
  207. package/expo-plugin/build/types/ios-widget/clientRendered.d.ts.map +1 -1
  208. package/expo-plugin/build/types/ios-widget/clientRenderedPrerender.d.ts.map +1 -1
  209. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts +7 -0
  210. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts.map +1 -0
  211. package/expo-plugin/build/types/ios-widget/files/index.d.ts +3 -1
  212. package/expo-plugin/build/types/ios-widget/files/index.d.ts.map +1 -1
  213. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts +2 -1
  214. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts.map +1 -1
  215. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts +9 -2
  216. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts.map +1 -1
  217. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts +2 -0
  218. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts.map +1 -0
  219. package/expo-plugin/build/types/ios-widget/files/swift.d.ts +6 -2
  220. package/expo-plugin/build/types/ios-widget/files/swift.d.ts.map +1 -1
  221. package/expo-plugin/build/types/ios-widget/index.d.ts +3 -1
  222. package/expo-plugin/build/types/ios-widget/index.d.ts.map +1 -1
  223. package/expo-plugin/build/types/ios-widget/podfile.d.ts +19 -1
  224. package/expo-plugin/build/types/ios-widget/podfile.d.ts.map +1 -1
  225. package/expo-plugin/build/types/ios-widget/widgetPlist.d.ts.map +1 -1
  226. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +6 -8
  227. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
  228. package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts +15 -10
  229. package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts.map +1 -1
  230. package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts +16 -0
  231. package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts.map +1 -0
  232. package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts +0 -4
  233. package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts.map +1 -1
  234. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +23 -3
  235. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
  236. package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts +16 -4
  237. package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts.map +1 -1
  238. package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts +0 -4
  239. package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts.map +1 -1
  240. package/expo-plugin/build/types/ios-widget/xcode/target.d.ts +11 -26
  241. package/expo-plugin/build/types/ios-widget/xcode/target.d.ts.map +1 -1
  242. package/expo-plugin/build/types/types.d.ts +23 -4
  243. package/expo-plugin/build/types/types.d.ts.map +1 -1
  244. package/expo-plugin/build/types/utils/urlScheme.d.ts +16 -0
  245. package/expo-plugin/build/types/utils/urlScheme.d.ts.map +1 -1
  246. package/expo-plugin/build/types/validation.d.ts +2 -1
  247. package/expo-plugin/build/types/validation.d.ts.map +1 -1
  248. package/expo-plugin/src/constants.ts +24 -1
  249. package/expo-plugin/src/index.ts +14 -1
  250. package/expo-plugin/src/ios/index.ts +3 -0
  251. package/expo-plugin/src/ios/infoPlist.ts +33 -5
  252. package/expo-plugin/src/ios/serverUpdate.ts +29 -0
  253. package/expo-plugin/src/ios-widget/clientRendered.ts +9 -5
  254. package/expo-plugin/src/ios-widget/clientRenderedPrerender.ts +11 -6
  255. package/expo-plugin/src/ios-widget/dynamic-live-activity/swift.ts +138 -0
  256. package/expo-plugin/src/ios-widget/files/index.ts +15 -4
  257. package/expo-plugin/src/ios-widget/files/infoPlist.ts +17 -3
  258. package/expo-plugin/src/ios-widget/files/manifest.ts +42 -2
  259. package/expo-plugin/src/ios-widget/files/swift-utils.ts +3 -0
  260. package/expo-plugin/src/ios-widget/files/swift.ts +69 -22
  261. package/expo-plugin/src/ios-widget/index.ts +13 -3
  262. package/expo-plugin/src/ios-widget/podfile.ts +46 -13
  263. package/expo-plugin/src/ios-widget/widgetPlist.ts +37 -23
  264. package/expo-plugin/src/ios-widget/xcode/__fixtures__/fresh.pbxproj +197 -0
  265. package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-existing-extension.pbxproj +321 -0
  266. package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-per-config-signing.pbxproj +203 -0
  267. package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +363 -0
  268. package/expo-plugin/src/ios-widget/xcode/__tests__/pbxConsistency.ts +130 -0
  269. package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +210 -164
  270. package/expo-plugin/src/ios-widget/xcode/configurationList.ts +52 -35
  271. package/expo-plugin/src/ios-widget/xcode/fileReferences.ts +51 -0
  272. package/expo-plugin/src/ios-widget/xcode/groups.ts +52 -54
  273. package/expo-plugin/src/ios-widget/xcode/index.ts +124 -119
  274. package/expo-plugin/src/ios-widget/xcode/mainAppSettings.ts +59 -54
  275. package/expo-plugin/src/ios-widget/xcode/productFile.ts +3 -2
  276. package/expo-plugin/src/ios-widget/xcode/target.ts +31 -83
  277. package/expo-plugin/src/types.ts +28 -4
  278. package/expo-plugin/src/utils/urlScheme.ts +47 -17
  279. package/expo-plugin/src/validation.ts +76 -1
  280. package/ios/Package.swift +39 -4
  281. package/ios/VoltraWidget.podspec +1 -0
  282. package/ios/app/NativeVoltra.mm +126 -7
  283. package/ios/app/VoltraLiveActivityManager.swift +30 -0
  284. package/ios/app/VoltraLiveActivityService.swift +77 -5
  285. package/ios/app/VoltraModule.swift +117 -1
  286. package/ios/app/VoltraModuleImpl.swift +116 -2
  287. package/ios/app/VoltraViewComponentView.mm +3 -3
  288. package/ios/app/VoltraWidgetService.swift +147 -12
  289. package/ios/app/dynamic-live-activity/VoltraDynamicLiveActivityService.swift +97 -0
  290. package/ios/app/dynamic-live-activity/VoltraLiveActivityChronology.swift +68 -0
  291. package/ios/shared/DynamicWidgetPropsCodec.swift +51 -0
  292. package/ios/shared/DynamicWidgetPropsStorage.swift +53 -0
  293. package/ios/shared/DynamicWidgetPropsStore.swift +117 -0
  294. package/ios/shared/DynamicWidgetRenderCoordinator.swift +31 -0
  295. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift +34 -0
  296. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift +55 -0
  297. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift +146 -0
  298. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift +193 -0
  299. package/ios/shared/DynamicWidgetUpdater.swift +30 -0
  300. package/ios/shared/ServerWidgetContentResolver.swift +60 -0
  301. package/ios/shared/ServerWidgetResponseStore.swift +25 -0
  302. package/ios/shared/ShortNames.swift +6 -0
  303. package/ios/shared/VoltraConfig.swift +41 -0
  304. package/ios/shared/VoltraConstants.swift +19 -0
  305. package/ios/shared/VoltraElement.swift +127 -43
  306. package/ios/shared/VoltraEventBus.swift +52 -7
  307. package/ios/shared/VoltraJSRenderer.swift +93 -23
  308. package/ios/shared/VoltraNode.swift +7 -111
  309. package/ios/shared/VoltraRefreshIntent.swift +1 -1
  310. package/ios/shared/VoltraWidgetDefaults.swift +9 -1
  311. package/ios/shared/VoltraWidgetKind.swift +41 -0
  312. package/ios/shared/VoltraWidgetServerFetcher.swift +52 -107
  313. package/ios/shared/WidgetServer/VoltraWidgetAppearance.swift +17 -0
  314. package/ios/shared/WidgetServer/VoltraWidgetServer.swift +138 -0
  315. package/ios/shared/WidgetServer/WidgetCanonicalConfiguration.swift +77 -0
  316. package/ios/shared/WidgetServer/WidgetScope.swift +55 -0
  317. package/ios/shared/WidgetServer/WidgetServerEtagStore.swift +55 -0
  318. package/ios/shared/WidgetServer/WidgetServerFetcher.swift +179 -0
  319. package/ios/shared/WidgetServer/WidgetServerRequestBuilder.swift +114 -0
  320. package/ios/shared/WidgetServer/WidgetServerSettingsCodec.swift +47 -0
  321. package/ios/shared/WidgetServer/WidgetServerSettingsResolver.swift +129 -0
  322. package/ios/shared/WidgetServer/WidgetServerSettingsStore.swift +170 -0
  323. package/ios/shared/WidgetServer/WidgetServerSettingsValidator.swift +73 -0
  324. package/ios/shared/WidgetServer/WidgetServerUpdateSettings.swift +123 -0
  325. package/ios/shared/WidgetServer/WidgetServerUpdateSettingsJson.swift +115 -0
  326. package/ios/shared/dynamic-live-activity/DynamicLiveActivityPropsCodec.swift +13 -0
  327. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityBundleSource.swift +156 -0
  328. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityObserver.swift +97 -0
  329. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityOperations.swift +105 -0
  330. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift +55 -0
  331. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRegistry.swift +143 -0
  332. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift +158 -0
  333. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureReporter.swift +120 -0
  334. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderer.swift +370 -0
  335. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift +87 -0
  336. package/ios/shared/dynamic-live-activity/VoltraLiveActivityOrder.swift +19 -0
  337. package/ios/target/VoltraClientWidgetRuntime.swift +60 -10
  338. package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
  339. package/ios/target/VoltraHomeWidget.swift +91 -92
  340. package/ios/target/VoltraWidget.swift +3 -1
  341. package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
  342. package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
  343. package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +5 -3
  344. package/ios/ui/Helpers/VoltraProgressDriver.swift +6 -2
  345. package/ios/ui/Layout/VoltraFlexStackLayout.swift +9 -3
  346. package/ios/ui/Style/JSColorParser.swift +24 -8
  347. package/ios/ui/Style/JSGradientParser.swift +36 -12
  348. package/ios/ui/Style/JSStyleParser.swift +36 -12
  349. package/ios/ui/Style/LayoutStyle.swift +12 -4
  350. package/ios/ui/Views/VoltraChart.swift +160 -16
  351. package/ios/ui/Views/VoltraLinearGradient.swift +6 -2
  352. package/package.json +5 -5
  353. package/src/dynamic-widget/api.ts +25 -0
  354. package/src/events.ts +34 -0
  355. package/src/index.ts +23 -0
  356. package/src/live-activity/api.ts +2 -2
  357. package/src/live-activity/dynamic-api.ts +240 -0
  358. package/src/native/NativeVoltra.ts +22 -0
  359. package/src/types.ts +4 -0
  360. package/src/utils/enableDynamicLiveActivityHotReload.ts +50 -0
  361. package/src/utils/enableWidgetHotReload.ts +9 -16
  362. package/src/utils/useUpdateOnHMR.ts +3 -12
  363. package/src/widgets/server-credentials.ts +8 -0
  364. package/src/widgets/server-update.ts +84 -0
  365. /package/ios/shared/{VoltraKeychainHelper.swift → WidgetServer/VoltraKeychainHelper.swift} +0 -0
@@ -1,34 +1,52 @@
1
1
  import { XcodeProject } from '@expo/config-plugins'
2
2
 
3
3
  import { IOS } from '../../constants'
4
+ import type { MainAppSigningSettings, MainAppTargetSettings } from './mainAppSettings'
4
5
 
5
- export interface AddConfigurationListOptions {
6
+ export interface EnsureConfigurationListOptions {
6
7
  targetName: string
7
8
  bundleIdentifier: string
8
9
  deploymentTarget: string
9
- codeSignStyle?: string
10
- developmentTeam?: string
11
- provisioningProfileSpecifier?: string
10
+ /**
11
+ * App marketing version (CFBundleShortVersionString). App Store Connect expects the appex
12
+ * version to match the app's, so this becomes the widget's MARKETING_VERSION.
13
+ */
14
+ version?: string
15
+ /** App build number; becomes the widget's CURRENT_PROJECT_VERSION. */
16
+ buildNumber?: string
17
+ /** Main app signing settings, applied per configuration name (Debug→Debug, Release→Release). */
18
+ mainAppSettings?: MainAppTargetSettings | null
12
19
  }
13
20
 
14
- function createCommonBuildSettings(options: AddConfigurationListOptions) {
15
- const {
16
- targetName,
17
- bundleIdentifier,
18
- deploymentTarget,
19
- codeSignStyle,
20
- developmentTeam,
21
- provisioningProfileSpecifier,
22
- } = options
23
-
24
- const commonBuildSettings: Record<string, string> = {
21
+ /**
22
+ * Resolves the main app signing settings for a widget configuration name: an exact name match
23
+ * wins, otherwise the first available configuration's settings are used as fallback.
24
+ */
25
+ function signingForConfiguration(
26
+ mainAppSettings: MainAppTargetSettings | null | undefined,
27
+ configurationName: string
28
+ ): MainAppSigningSettings {
29
+ if (!mainAppSettings) {
30
+ return {}
31
+ }
32
+ return mainAppSettings.byConfigurationName[configurationName] ?? mainAppSettings.fallback
33
+ }
34
+
35
+ function createBuildSettings(options: EnsureConfigurationListOptions, configurationName: string) {
36
+ const { targetName, bundleIdentifier, deploymentTarget, version, buildNumber, mainAppSettings } = options
37
+ const { codeSignStyle, developmentTeam, provisioningProfileSpecifier } = signingForConfiguration(
38
+ mainAppSettings,
39
+ configurationName
40
+ )
41
+
42
+ const buildSettings: Record<string, string> = {
25
43
  PRODUCT_NAME: `"$(TARGET_NAME)"`,
26
44
  SWIFT_VERSION: IOS.SWIFT_VERSION,
27
45
  TARGETED_DEVICE_FAMILY: `"${IOS.DEVICE_FAMILY}"`,
28
46
  INFOPLIST_FILE: `${targetName}/Info.plist`,
29
47
  INFOPLIST_OUTPUT_FORMAT: `"xml"`,
30
- CURRENT_PROJECT_VERSION: `"1"`,
31
- MARKETING_VERSION: `"1.0"`,
48
+ CURRENT_PROJECT_VERSION: `"${buildNumber ?? '1'}"`,
49
+ MARKETING_VERSION: `"${version ?? '1.0'}"`,
32
50
  IPHONEOS_DEPLOYMENT_TARGET: `"${deploymentTarget}"`,
33
51
  PRODUCT_BUNDLE_IDENTIFIER: `"${bundleIdentifier}"`,
34
52
  SWIFT_OPTIMIZATION_LEVEL: `"-Onone"`,
@@ -37,41 +55,37 @@ function createCommonBuildSettings(options: AddConfigurationListOptions) {
37
55
  ASSETCATALOG_COMPILER_APPICON_NAME: '""',
38
56
  }
39
57
 
40
- // Synchronize code signing settings from main app target
58
+ // Synchronize code signing settings from the main app target's matching configuration
41
59
  if (codeSignStyle) {
42
- commonBuildSettings.CODE_SIGN_STYLE = `"${codeSignStyle}"`
60
+ buildSettings.CODE_SIGN_STYLE = `"${codeSignStyle}"`
43
61
  }
44
62
  if (developmentTeam) {
45
- commonBuildSettings.DEVELOPMENT_TEAM = `"${developmentTeam}"`
63
+ buildSettings.DEVELOPMENT_TEAM = `"${developmentTeam}"`
46
64
  }
47
65
  if (provisioningProfileSpecifier) {
48
- commonBuildSettings.PROVISIONING_PROFILE_SPECIFIER = `"${provisioningProfileSpecifier}"`
66
+ buildSettings.PROVISIONING_PROFILE_SPECIFIER = `"${provisioningProfileSpecifier}"`
49
67
  }
50
68
 
51
- return commonBuildSettings
69
+ return buildSettings
52
70
  }
53
71
 
54
72
  /**
55
- * Adds the XCConfigurationList for the widget extension target.
73
+ * Adds a fresh XCConfigurationList for the widget extension target. Internal create-only fallback
74
+ * for {@link ensureXCConfigurationList}; not part of the public pipeline.
56
75
  */
57
- export function addXCConfigurationList(xcodeProject: XcodeProject, options: AddConfigurationListOptions) {
76
+ function addXCConfigurationList(xcodeProject: XcodeProject, options: EnsureConfigurationListOptions) {
58
77
  const { targetName } = options
59
- const commonBuildSettings = createCommonBuildSettings(options)
60
78
 
61
79
  const buildConfigurationsList = [
62
80
  {
63
81
  name: 'Debug',
64
82
  isa: 'XCBuildConfiguration',
65
- buildSettings: {
66
- ...commonBuildSettings,
67
- },
83
+ buildSettings: createBuildSettings(options, 'Debug'),
68
84
  },
69
85
  {
70
86
  name: 'Release',
71
87
  isa: 'XCBuildConfiguration',
72
- buildSettings: {
73
- ...commonBuildSettings,
74
- },
88
+ buildSettings: createBuildSettings(options, 'Release'),
75
89
  },
76
90
  ]
77
91
 
@@ -85,11 +99,13 @@ export function addXCConfigurationList(xcodeProject: XcodeProject, options: AddC
85
99
  }
86
100
 
87
101
  /**
88
- * Ensures an existing XCConfigurationList is updated, or adds a new one if missing.
102
+ * Ensures an existing XCConfigurationList is updated, or adds a new one if missing. Each widget
103
+ * configuration receives the build settings derived for its own name, so Debug and Release signing
104
+ * stay in sync with the main app's respective configurations.
89
105
  */
90
106
  export function ensureXCConfigurationList(
91
107
  xcodeProject: XcodeProject,
92
- options: AddConfigurationListOptions,
108
+ options: EnsureConfigurationListOptions,
93
109
  existingConfigurationListId?: string | { value?: string }
94
110
  ) {
95
111
  const configurationListId =
@@ -103,7 +119,6 @@ export function ensureXCConfigurationList(
103
119
  return addXCConfigurationList(xcodeProject, options)
104
120
  }
105
121
 
106
- const commonBuildSettings = createCommonBuildSettings(options)
107
122
  const buildConfigurations = configurationList.buildConfigurations
108
123
  const buildConfigurationSection = xcodeProject.pbxXCBuildConfigurationSection()
109
124
 
@@ -117,9 +132,11 @@ export function ensureXCConfigurationList(
117
132
  continue
118
133
  }
119
134
 
135
+ const configurationName =
136
+ typeof buildConfiguration.name === 'string' ? buildConfiguration.name.replace(/^"|"$/g, '') : ''
120
137
  buildConfiguration.buildSettings = {
121
138
  ...(buildConfiguration.buildSettings ?? {}),
122
- ...commonBuildSettings,
139
+ ...createBuildSettings(options, configurationName),
123
140
  }
124
141
  }
125
142
 
@@ -0,0 +1,51 @@
1
+ import { XcodeProject } from '@expo/config-plugins'
2
+
3
+ const pbxFile = require('xcode/lib/pbxFile')
4
+
5
+ /** Strips surrounding quotes from a pbx path/name value. */
6
+ export function normalizePath(value: string | undefined): string {
7
+ if (!value) {
8
+ return ''
9
+ }
10
+ return value.replace(/^"|"$/g, '')
11
+ }
12
+
13
+ /** Strips a trailing `/* comment *\/` and whitespace, yielding the bare UUID of a pbx reference. */
14
+ export function normalizeRef(value: unknown): string {
15
+ return String(value)
16
+ .replace(/\/\*.*?\*\//g, '')
17
+ .trim()
18
+ }
19
+
20
+ /**
21
+ * Resolves the PBXFileReference UUID for a widget file, scoped to the widget's own PBXGroup.
22
+ *
23
+ * pbxproj paths are group-relative, so a bare path like `Assets.xcassets` collides with any other
24
+ * extension's identically named file. Scoping the lookup to the children of the widget group
25
+ * (identified by `targetName`) keeps the widget's references distinct from those, which is what
26
+ * prevents the shared-fileRef corruption behind issues #87/#32. When the widget group does not
27
+ * exist yet, a fresh reference is created.
28
+ */
29
+ export function ensureWidgetFileReference(xcodeProject: XcodeProject, filePath: string, targetName: string): string {
30
+ const file = new pbxFile(filePath)
31
+ const fileReferenceSection = xcodeProject.pbxFileReferenceSection()
32
+ const group = xcodeProject.pbxGroupByName(targetName)
33
+
34
+ if (group?.children) {
35
+ for (const child of group.children) {
36
+ const refKey = normalizeRef(child.value)
37
+ const entry = fileReferenceSection[refKey]
38
+ if (!entry) {
39
+ continue
40
+ }
41
+ const entryPath = normalizePath(entry.path)
42
+ if (entryPath === file.path || entryPath === normalizePath(filePath)) {
43
+ return refKey
44
+ }
45
+ }
46
+ }
47
+
48
+ file.fileRef = xcodeProject.generateUuid()
49
+ xcodeProject.addToPbxFileReferenceSection(file)
50
+ return file.fileRef
51
+ }
@@ -1,6 +1,7 @@
1
1
  import { XcodeProject } from '@expo/config-plugins'
2
2
 
3
3
  import type { IOSWidgetExtensionFiles } from '../../types'
4
+ import { ensureWidgetFileReference } from './fileReferences'
4
5
 
5
6
  const pbxFile = require('xcode/lib/pbxFile')
6
7
 
@@ -9,37 +10,63 @@ export interface AddPbxGroupOptions {
9
10
  widgetFiles: IOSWidgetExtensionFiles
10
11
  }
11
12
 
13
+ function collectGroupFiles(widgetFiles: IOSWidgetExtensionFiles): string[] {
14
+ const { swiftFiles, intentFiles, assetDirectories, entitlementFiles, plistFiles, localizedStringResources } =
15
+ widgetFiles
16
+ return [
17
+ ...swiftFiles,
18
+ ...intentFiles,
19
+ ...entitlementFiles,
20
+ ...plistFiles,
21
+ ...assetDirectories,
22
+ ...localizedStringResources,
23
+ ]
24
+ }
25
+
12
26
  /**
13
27
  * Adds a PBXGroup for the widget extension files.
28
+ *
29
+ * Constructs the PBXGroup and its PBXFileReference children by hand rather than delegating to
30
+ * `xcodeProject.addPbxGroup`, because that helper also creates a PBXBuildFile per file as a side
31
+ * effect. Those extra build files collide with the ones managed by the build-phase functions and
32
+ * produce the "no parent for object" consistency errors from issues #87/#32. File references are
33
+ * resolved through the widget-scoped {@link ensureWidgetFileReference} so they are shared with the
34
+ * build phases instead of duplicated.
35
+ *
36
+ * Internal create-only fallback for {@link ensurePbxGroup}; not part of the public pipeline.
14
37
  */
15
- export function addPbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupOptions): void {
38
+ function addPbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupOptions): void {
16
39
  const { targetName, widgetFiles } = options
17
- const { swiftFiles, intentFiles, assetDirectories, entitlementFiles, plistFiles, localizedStringResources } =
18
- widgetFiles
40
+ const allFiles = collectGroupFiles(widgetFiles)
19
41
 
20
- // Add PBX group with all widget files
21
- const { uuid: pbxGroupUuid } = xcodeProject.addPbxGroup(
22
- [
23
- ...swiftFiles,
24
- ...intentFiles,
25
- ...entitlementFiles,
26
- ...plistFiles,
27
- ...assetDirectories,
28
- ...localizedStringResources,
29
- ],
30
- targetName,
31
- targetName
32
- )
42
+ const pbxGroupUuid = xcodeProject.generateUuid()
43
+ const pbxGroup = {
44
+ isa: 'PBXGroup',
45
+ children: [] as { value: string; comment: string }[],
46
+ name: targetName,
47
+ path: targetName,
48
+ sourceTree: '"<group>"',
49
+ }
33
50
 
34
- // Add PBXGroup to top level group
35
51
  const groups = xcodeProject.hash.project.objects['PBXGroup']
36
- if (pbxGroupUuid) {
37
- Object.keys(groups).forEach(function (key) {
38
- if (groups[key].name === undefined && groups[key].path === undefined) {
39
- xcodeProject.addToPbxGroup(pbxGroupUuid, key)
40
- }
41
- })
52
+ groups[pbxGroupUuid] = pbxGroup
53
+ groups[`${pbxGroupUuid}_comment`] = targetName
54
+
55
+ for (const filePath of allFiles) {
56
+ const file = new pbxFile(filePath)
57
+ const fileRef = ensureWidgetFileReference(xcodeProject, filePath, targetName)
58
+ pbxGroup.children.push({ value: fileRef, comment: file.basename })
42
59
  }
60
+
61
+ // Attach the new group to the top-level (unnamed, path-less) group.
62
+ Object.keys(groups).forEach(function (key) {
63
+ if (/_comment$/.test(key)) {
64
+ return
65
+ }
66
+ if (groups[key].name === undefined && groups[key].path === undefined) {
67
+ xcodeProject.addToPbxGroup(pbxGroupUuid, key)
68
+ }
69
+ })
43
70
  }
44
71
 
45
72
  /**
@@ -47,16 +74,7 @@ export function addPbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupOpti
47
74
  */
48
75
  export function ensurePbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupOptions): void {
49
76
  const { targetName, widgetFiles } = options
50
- const { swiftFiles, intentFiles, assetDirectories, entitlementFiles, plistFiles, localizedStringResources } =
51
- widgetFiles
52
- const allFiles = [
53
- ...swiftFiles,
54
- ...intentFiles,
55
- ...entitlementFiles,
56
- ...plistFiles,
57
- ...assetDirectories,
58
- ...localizedStringResources,
59
- ]
77
+ const allFiles = collectGroupFiles(widgetFiles)
60
78
 
61
79
  const existingGroup = xcodeProject.pbxGroupByName(targetName)
62
80
  if (!existingGroup) {
@@ -70,7 +88,7 @@ export function ensurePbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupO
70
88
 
71
89
  for (const filePath of allFiles) {
72
90
  const file = new pbxFile(filePath)
73
- const fileRef = ensureFileReference(xcodeProject, filePath)
91
+ const fileRef = ensureWidgetFileReference(xcodeProject, filePath, targetName)
74
92
  const alreadyInGroup = existingGroup.children.some(
75
93
  (child: any) => child.value === fileRef || child.comment === file.basename
76
94
  )
@@ -79,23 +97,3 @@ export function ensurePbxGroup(xcodeProject: XcodeProject, options: AddPbxGroupO
79
97
  }
80
98
  }
81
99
  }
82
-
83
- function ensureFileReference(xcodeProject: XcodeProject, filePath: string): string {
84
- const fileReferenceSection = xcodeProject.pbxFileReferenceSection()
85
- const file = new pbxFile(filePath)
86
-
87
- for (const key of Object.keys(fileReferenceSection)) {
88
- if (/_comment$/.test(key)) {
89
- continue
90
- }
91
- const entry = fileReferenceSection[key]
92
- const entryPath = typeof entry?.path === 'string' ? entry.path.replace(/^"|"$/g, '') : ''
93
- if (entryPath === file.path || entryPath === filePath) {
94
- return key
95
- }
96
- }
97
-
98
- file.fileRef = xcodeProject.generateUuid()
99
- xcodeProject.addToPbxFileReferenceSection(file)
100
- return file.fileRef
101
- }
@@ -1,21 +1,133 @@
1
- import { ConfigPlugin, withXcodeProject } from '@expo/config-plugins'
1
+ import { ConfigPlugin, withXcodeProject, XcodeProject } from '@expo/config-plugins'
2
2
  import * as path from 'path'
3
3
 
4
- import type { IOSWidgetConfig } from '../../types'
4
+ import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig, IOSWidgetExtensionFiles } from '../../types'
5
5
  import { getIOSWidgetExtensionFiles } from '../../utils/fileDiscovery'
6
6
  import { detectClientRenderedWidgets } from '../clientRendered'
7
- import { addBuildPhases, ensureBuildPhases, ensureWidgetBundleScriptPhase } from './buildPhases'
8
- import { addXCConfigurationList, ensureXCConfigurationList } from './configurationList'
9
- import { addPbxGroup, ensurePbxGroup } from './groups'
7
+ import { ensureBuildPhases, ensureWidgetBundleScriptPhase } from './buildPhases'
8
+ import { ensureXCConfigurationList } from './configurationList'
9
+ import { ensurePbxGroup } from './groups'
10
10
  import { getMainAppTargetSettings } from './mainAppSettings'
11
- import { addProductFile, ensureProductFile } from './productFile'
12
- import { configureTarget, ensureTargetAttributes, ensureTargetDependency } from './target'
11
+ import { ensureProductFile } from './productFile'
12
+ import { createTargetSkeleton, ensureTargetAttributes, ensureTargetDependency } from './target'
13
13
 
14
14
  export interface ConfigureXcodeProjectProps {
15
15
  targetName: string
16
16
  bundleIdentifier: string
17
17
  deploymentTarget: string
18
+ /** App marketing version; becomes the widget's MARKETING_VERSION so the appex matches the app. */
19
+ version?: string
20
+ /** App build number; becomes the widget's CURRENT_PROJECT_VERSION. */
21
+ buildNumber?: string
18
22
  widgets?: IOSWidgetConfig[]
23
+ liveActivities?: IOSDynamicLiveActivityConfig[]
24
+ }
25
+
26
+ /**
27
+ * Applies all widget-extension changes to an already-parsed Xcode project.
28
+ *
29
+ * This is the pure core of {@link configureXcodeProject}: it takes the parsed project, the plugin
30
+ * props, the discovered widget files, and whether any widget is client-rendered, then mutates the
31
+ * project in place. It performs no filesystem or `modRequest` access, which makes it directly
32
+ * testable against pbxproj fixtures.
33
+ *
34
+ * When the widget target does not exist yet, the minimal native-target skeleton is created first
35
+ * (targets cannot be "ensured" into existence); every other object is then reconciled by the same
36
+ * idempotent ensure pipeline that runs for an already-existing target.
37
+ */
38
+ export function applyXcodeChanges(
39
+ xcodeProject: XcodeProject,
40
+ props: ConfigureXcodeProjectProps,
41
+ widgetFiles: IOSWidgetExtensionFiles,
42
+ hasClientRenderedWidgets = false
43
+ ): void {
44
+ const { targetName, bundleIdentifier, deploymentTarget, version, buildNumber } = props
45
+ const groupName = 'Embed Foundation Extensions'
46
+ const mainTargetUuid = xcodeProject.getFirstTarget().uuid
47
+ // The catalog is compiled into the app even for a legacy-only configuration.
48
+ // Keep it in the extension group too so both targets share one PBX file reference.
49
+ const effectiveWidgetFiles: IOSWidgetExtensionFiles = {
50
+ ...widgetFiles,
51
+ swiftFiles: Array.from(new Set([...widgetFiles.swiftFiles, 'VoltraDynamicLiveActivityTypes.swift'])),
52
+ }
53
+
54
+ // Read main app target settings to synchronize code signing (per configuration).
55
+ const mainAppSettings = getMainAppTargetSettings(xcodeProject)
56
+
57
+ // Use the deploymentTarget from plugin config (or default), ignore main app's deployment target.
58
+ // This allows the widget extension to have its own deployment target independent of the main app.
59
+
60
+ // Resolve (or create) the widget target. A target cannot be reconciled into existence, so the
61
+ // absent case creates the minimal skeleton; from here on both paths share one pipeline.
62
+ let targetUuid = xcodeProject.findTargetKey(targetName)
63
+ if (!targetUuid) {
64
+ targetUuid = xcodeProject.generateUuid()
65
+ createTargetSkeleton(xcodeProject, { targetName, targetUuid })
66
+ }
67
+ const target = xcodeProject.pbxNativeTargetSection()[targetUuid]
68
+
69
+ // Ensure the configuration list is present and its settings are in sync.
70
+ const xCConfigurationList = ensureXCConfigurationList(
71
+ xcodeProject,
72
+ {
73
+ targetName,
74
+ bundleIdentifier,
75
+ deploymentTarget,
76
+ version,
77
+ buildNumber,
78
+ mainAppSettings,
79
+ },
80
+ target.buildConfigurationList
81
+ )
82
+
83
+ // Ensure the product file (.appex) exists.
84
+ const productFile = ensureProductFile(xcodeProject, {
85
+ targetName,
86
+ groupName,
87
+ })
88
+
89
+ // Point the target at the reconciled configuration list and product file.
90
+ target.productReference = productFile.fileRef
91
+ target.buildConfigurationList = xCConfigurationList.uuid
92
+ target.productType = `"com.apple.product-type.app-extension"`
93
+ target.name = targetName
94
+ target.productName = targetName
95
+ if (!target.buildPhases) {
96
+ target.buildPhases = []
97
+ }
98
+
99
+ // Ensure the group first so the widget's file references exist before the build phases
100
+ // reference them (the phases resolve files through the widget-scoped group).
101
+ ensurePbxGroup(xcodeProject, {
102
+ targetName,
103
+ widgetFiles: effectiveWidgetFiles,
104
+ })
105
+
106
+ // Ensure build phases and their files.
107
+ ensureBuildPhases(xcodeProject, {
108
+ targetUuid,
109
+ targetName,
110
+ groupName,
111
+ productFile,
112
+ widgetFiles: effectiveWidgetFiles,
113
+ mainTargetUuid: xcodeProject.getFirstTarget().uuid,
114
+ // The app-side lifecycle service always compiles against the generated catalog.
115
+ // An empty catalog keeps legacy-only apps source-compatible.
116
+ mainSwiftFiles: ['VoltraDynamicLiveActivityTypes.swift'],
117
+ })
118
+
119
+ if (hasClientRenderedWidgets || (props.liveActivities?.length ?? 0) > 0) {
120
+ ensureWidgetBundleScriptPhase(xcodeProject, targetUuid)
121
+ }
122
+ if ((props.liveActivities?.length ?? 0) > 0) {
123
+ // Local Dynamic Live Activity starts preflight the baked definition from
124
+ // the app process, while WidgetKit renders from the extension process.
125
+ // Bake the same manifest into both products.
126
+ ensureWidgetBundleScriptPhase(xcodeProject, mainTargetUuid, 'live-activities')
127
+ }
128
+
129
+ ensureTargetAttributes(xcodeProject, targetUuid)
130
+ ensureTargetDependency(xcodeProject, targetUuid)
19
131
  }
20
132
 
21
133
  /**
@@ -28,10 +140,12 @@ export interface ConfigureXcodeProjectProps {
28
140
  * - Adds build phases (Sources, CopyFiles, Frameworks, Resources)
29
141
  * - Adds PBXGroup for widget files
30
142
  *
31
- * This should run after generateWidgetExtensionFiles so the files exist.
143
+ * This should run after generateWidgetExtensionFiles so the files exist. The wrapper is kept thin:
144
+ * it discovers widget files and detects client-rendered widgets, then delegates the actual pbxproj
145
+ * mutation to {@link applyXcodeChanges}.
32
146
  */
33
147
  export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (config, props) => {
34
- const { targetName, bundleIdentifier, deploymentTarget, widgets } = props
148
+ const { targetName, widgets, liveActivities } = props
35
149
 
36
150
  return withXcodeProject(config, (config) => {
37
151
  if (config.modRequest.introspect) {
@@ -39,7 +153,6 @@ export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (
39
153
  }
40
154
 
41
155
  const xcodeProject = config.modResults
42
- const groupName = 'Embed Foundation Extensions'
43
156
 
44
157
  // The release widget-bundling phase is only needed when a widget is a Dynamic Widget (server
45
158
  // widgets carry no JS bundle). Detect once so both the create and update paths agree.
@@ -47,119 +160,11 @@ export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (
47
160
  !!widgets &&
48
161
  detectClientRenderedWidgets(widgets, config.modRequest.projectRoot).some((widget) => widget.clientRendered)
49
162
 
50
- // Check if target already exists
51
- const nativeTargets = xcodeProject.pbxNativeTargetSection()
52
- const existingTargetKey = xcodeProject.findTargetKey(targetName)
53
- const existingTarget = existingTargetKey ? nativeTargets[existingTargetKey] : null
54
-
55
163
  const { platformProjectRoot } = config.modRequest
56
164
  const targetPath = path.join(platformProjectRoot, targetName)
57
165
  const widgetFiles = getIOSWidgetExtensionFiles(targetPath, targetName)
58
166
 
59
- // Read main app target settings to synchronize code signing
60
- const mainAppSettings = getMainAppTargetSettings(xcodeProject)
61
-
62
- // Use the deploymentTarget from plugin config (or default), ignore main app's deployment target
63
- // This allows the widget extension to have its own deployment target independent of the main app
64
-
65
- if (existingTarget && existingTargetKey) {
66
- // Ensure configuration list is up to date
67
- const xCConfigurationList = ensureXCConfigurationList(
68
- xcodeProject,
69
- {
70
- targetName,
71
- bundleIdentifier,
72
- deploymentTarget,
73
- codeSignStyle: mainAppSettings?.codeSignStyle,
74
- developmentTeam: mainAppSettings?.developmentTeam,
75
- provisioningProfileSpecifier: mainAppSettings?.provisioningProfileSpecifier,
76
- },
77
- existingTarget.buildConfigurationList
78
- )
79
-
80
- // Ensure product file exists
81
- const productFile = ensureProductFile(xcodeProject, {
82
- targetName,
83
- groupName,
84
- })
85
-
86
- // Update target references
87
- existingTarget.productReference = productFile.fileRef
88
- existingTarget.buildConfigurationList = xCConfigurationList.uuid
89
- existingTarget.productType = `"com.apple.product-type.app-extension"`
90
- existingTarget.name = targetName
91
- existingTarget.productName = targetName
92
- if (!existingTarget.buildPhases) {
93
- existingTarget.buildPhases = []
94
- }
95
-
96
- // Ensure build phases and groups
97
- ensureBuildPhases(xcodeProject, {
98
- targetUuid: existingTargetKey,
99
- groupName,
100
- productFile,
101
- widgetFiles,
102
- mainTargetUuid: xcodeProject.getFirstTarget().uuid,
103
- })
104
-
105
- if (hasClientRenderedWidgets) {
106
- ensureWidgetBundleScriptPhase(xcodeProject, existingTargetKey)
107
- }
108
-
109
- ensurePbxGroup(xcodeProject, {
110
- targetName,
111
- widgetFiles,
112
- })
113
-
114
- ensureTargetAttributes(xcodeProject, existingTargetKey)
115
- ensureTargetDependency(xcodeProject, existingTargetKey)
116
-
117
- return config
118
- }
119
-
120
- // Add configuration list
121
- const targetUuid = xcodeProject.generateUuid()
122
-
123
- const xCConfigurationList = addXCConfigurationList(xcodeProject, {
124
- targetName,
125
- bundleIdentifier,
126
- deploymentTarget,
127
- codeSignStyle: mainAppSettings?.codeSignStyle,
128
- developmentTeam: mainAppSettings?.developmentTeam,
129
- provisioningProfileSpecifier: mainAppSettings?.provisioningProfileSpecifier,
130
- })
131
-
132
- // Add product file
133
- const productFile = addProductFile(xcodeProject, {
134
- targetName,
135
- groupName,
136
- })
137
-
138
- // Configure target (native target, project section, dependency)
139
- configureTarget(xcodeProject, {
140
- targetName,
141
- targetUuid,
142
- productFile,
143
- xCConfigurationList,
144
- })
145
-
146
- // Add build phases
147
- addBuildPhases(xcodeProject, {
148
- targetUuid,
149
- groupName,
150
- productFile,
151
- widgetFiles,
152
- })
153
-
154
- if (hasClientRenderedWidgets) {
155
- ensureWidgetBundleScriptPhase(xcodeProject, targetUuid)
156
- }
157
-
158
- // Add PBX group
159
- addPbxGroup(xcodeProject, {
160
- targetName,
161
- widgetFiles,
162
- })
167
+ applyXcodeChanges(xcodeProject, props, widgetFiles, hasClientRenderedWidgets || (liveActivities?.length ?? 0) > 0)
163
168
 
164
169
  return config
165
170
  })