@use-voltra/ios-client 2.2.0 → 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 (289) hide show
  1. package/Voltra.podspec +1 -0
  2. package/build/commonjs/events.js +23 -0
  3. package/build/commonjs/events.js.map +1 -1
  4. package/build/commonjs/index.js +59 -0
  5. package/build/commonjs/index.js.map +1 -1
  6. package/build/commonjs/live-activity/api.js +3 -1
  7. package/build/commonjs/live-activity/api.js.map +1 -1
  8. package/build/commonjs/live-activity/dynamic-api.js +173 -0
  9. package/build/commonjs/live-activity/dynamic-api.js.map +1 -0
  10. package/build/commonjs/native/NativeVoltra.js.map +1 -1
  11. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js +47 -0
  12. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  13. package/build/commonjs/utils/enableWidgetHotReload.js +8 -11
  14. package/build/commonjs/utils/enableWidgetHotReload.js.map +1 -1
  15. package/build/commonjs/utils/useUpdateOnHMR.js +3 -7
  16. package/build/commonjs/utils/useUpdateOnHMR.js.map +1 -1
  17. package/build/commonjs/widgets/server-credentials.js +9 -0
  18. package/build/commonjs/widgets/server-credentials.js.map +1 -1
  19. package/build/commonjs/widgets/server-update.js +78 -0
  20. package/build/commonjs/widgets/server-update.js.map +1 -0
  21. package/build/module/events.js +23 -0
  22. package/build/module/events.js.map +1 -1
  23. package/build/module/index.js +3 -0
  24. package/build/module/index.js.map +1 -1
  25. package/build/module/live-activity/api.js +2 -2
  26. package/build/module/live-activity/api.js.map +1 -1
  27. package/build/module/live-activity/dynamic-api.js +167 -0
  28. package/build/module/live-activity/dynamic-api.js.map +1 -0
  29. package/build/module/native/NativeVoltra.js.map +1 -1
  30. package/build/module/utils/enableDynamicLiveActivityHotReload.js +43 -0
  31. package/build/module/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  32. package/build/module/utils/enableWidgetHotReload.js +9 -11
  33. package/build/module/utils/enableWidgetHotReload.js.map +1 -1
  34. package/build/module/utils/useUpdateOnHMR.js +3 -7
  35. package/build/module/utils/useUpdateOnHMR.js.map +1 -1
  36. package/build/module/widgets/server-credentials.js +9 -0
  37. package/build/module/widgets/server-credentials.js.map +1 -1
  38. package/build/module/widgets/server-update.js +72 -0
  39. package/build/module/widgets/server-update.js.map +1 -0
  40. package/build/typescript/commonjs/events.d.ts +7 -0
  41. package/build/typescript/commonjs/events.d.ts.map +1 -1
  42. package/build/typescript/commonjs/index.d.ts +3 -0
  43. package/build/typescript/commonjs/index.d.ts.map +1 -1
  44. package/build/typescript/commonjs/live-activity/api.d.ts +7 -0
  45. package/build/typescript/commonjs/live-activity/api.d.ts.map +1 -1
  46. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts +54 -0
  47. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts.map +1 -0
  48. package/build/typescript/commonjs/native/NativeVoltra.d.ts +20 -0
  49. package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
  50. package/build/typescript/commonjs/types.d.ts +1 -1
  51. package/build/typescript/commonjs/types.d.ts.map +1 -1
  52. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  53. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  54. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts +5 -7
  55. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts.map +1 -1
  56. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts +0 -3
  57. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts.map +1 -1
  58. package/build/typescript/commonjs/widgets/server-credentials.d.ts +8 -0
  59. package/build/typescript/commonjs/widgets/server-credentials.d.ts.map +1 -1
  60. package/build/typescript/commonjs/widgets/server-update.d.ts +64 -0
  61. package/build/typescript/commonjs/widgets/server-update.d.ts.map +1 -0
  62. package/build/typescript/module/events.d.ts +7 -0
  63. package/build/typescript/module/events.d.ts.map +1 -1
  64. package/build/typescript/module/index.d.ts +3 -0
  65. package/build/typescript/module/index.d.ts.map +1 -1
  66. package/build/typescript/module/live-activity/api.d.ts +7 -0
  67. package/build/typescript/module/live-activity/api.d.ts.map +1 -1
  68. package/build/typescript/module/live-activity/dynamic-api.d.ts +54 -0
  69. package/build/typescript/module/live-activity/dynamic-api.d.ts.map +1 -0
  70. package/build/typescript/module/native/NativeVoltra.d.ts +20 -0
  71. package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
  72. package/build/typescript/module/types.d.ts +1 -1
  73. package/build/typescript/module/types.d.ts.map +1 -1
  74. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  75. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  76. package/build/typescript/module/utils/enableWidgetHotReload.d.ts +5 -7
  77. package/build/typescript/module/utils/enableWidgetHotReload.d.ts.map +1 -1
  78. package/build/typescript/module/utils/useUpdateOnHMR.d.ts +0 -3
  79. package/build/typescript/module/utils/useUpdateOnHMR.d.ts.map +1 -1
  80. package/build/typescript/module/widgets/server-credentials.d.ts +8 -0
  81. package/build/typescript/module/widgets/server-credentials.d.ts.map +1 -1
  82. package/build/typescript/module/widgets/server-update.d.ts +64 -0
  83. package/build/typescript/module/widgets/server-update.d.ts.map +1 -0
  84. package/expo-plugin/build/cjs/constants.js +20 -1
  85. package/expo-plugin/build/cjs/constants.js.map +1 -1
  86. package/expo-plugin/build/cjs/index.js +6 -0
  87. package/expo-plugin/build/cjs/index.js.map +1 -1
  88. package/expo-plugin/build/cjs/ios/index.js +1 -0
  89. package/expo-plugin/build/cjs/ios/index.js.map +1 -1
  90. package/expo-plugin/build/cjs/ios/infoPlist.js +28 -5
  91. package/expo-plugin/build/cjs/ios/infoPlist.js.map +1 -1
  92. package/expo-plugin/build/cjs/ios/serverUpdate.js +25 -0
  93. package/expo-plugin/build/cjs/ios/serverUpdate.js.map +1 -0
  94. package/expo-plugin/build/cjs/ios-widget/clientRendered.js +4 -4
  95. package/expo-plugin/build/cjs/ios-widget/clientRendered.js.map +1 -1
  96. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js +5 -5
  97. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js.map +1 -1
  98. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js +130 -0
  99. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  100. package/expo-plugin/build/cjs/ios-widget/files/index.js +9 -2
  101. package/expo-plugin/build/cjs/ios-widget/files/index.js.map +1 -1
  102. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js +6 -3
  103. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js.map +1 -1
  104. package/expo-plugin/build/cjs/ios-widget/files/manifest.js +24 -0
  105. package/expo-plugin/build/cjs/ios-widget/files/manifest.js.map +1 -1
  106. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js +7 -0
  107. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js.map +1 -0
  108. package/expo-plugin/build/cjs/ios-widget/files/swift.js +56 -26
  109. package/expo-plugin/build/cjs/ios-widget/files/swift.js.map +1 -1
  110. package/expo-plugin/build/cjs/ios-widget/index.js +9 -3
  111. package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
  112. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js +34 -22
  113. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js.map +1 -1
  114. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +51 -11
  115. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
  116. package/expo-plugin/build/cjs/ios-widget/xcode/index.js +21 -5
  117. package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
  118. package/expo-plugin/build/cjs/utils/urlScheme.js +36 -14
  119. package/expo-plugin/build/cjs/utils/urlScheme.js.map +1 -1
  120. package/expo-plugin/build/cjs/validation.js +54 -0
  121. package/expo-plugin/build/cjs/validation.js.map +1 -1
  122. package/expo-plugin/build/esm/constants.js +17 -0
  123. package/expo-plugin/build/esm/constants.js.map +1 -1
  124. package/expo-plugin/build/esm/index.js +6 -0
  125. package/expo-plugin/build/esm/index.js.map +1 -1
  126. package/expo-plugin/build/esm/ios/index.js +1 -0
  127. package/expo-plugin/build/esm/ios/index.js.map +1 -1
  128. package/expo-plugin/build/esm/ios/infoPlist.js +28 -5
  129. package/expo-plugin/build/esm/ios/infoPlist.js.map +1 -1
  130. package/expo-plugin/build/esm/ios/serverUpdate.js +21 -0
  131. package/expo-plugin/build/esm/ios/serverUpdate.js.map +1 -0
  132. package/expo-plugin/build/esm/ios-widget/clientRendered.js +5 -5
  133. package/expo-plugin/build/esm/ios-widget/clientRendered.js.map +1 -1
  134. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js +6 -6
  135. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js.map +1 -1
  136. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js +122 -0
  137. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  138. package/expo-plugin/build/esm/ios-widget/files/index.js +10 -3
  139. package/expo-plugin/build/esm/ios-widget/files/index.js.map +1 -1
  140. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js +6 -3
  141. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js.map +1 -1
  142. package/expo-plugin/build/esm/ios-widget/files/manifest.js +23 -1
  143. package/expo-plugin/build/esm/ios-widget/files/manifest.js.map +1 -1
  144. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js +4 -0
  145. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js.map +1 -0
  146. package/expo-plugin/build/esm/ios-widget/files/swift.js +50 -20
  147. package/expo-plugin/build/esm/ios-widget/files/swift.js.map +1 -1
  148. package/expo-plugin/build/esm/ios-widget/index.js +9 -3
  149. package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
  150. package/expo-plugin/build/esm/ios-widget/widgetPlist.js +34 -22
  151. package/expo-plugin/build/esm/ios-widget/widgetPlist.js.map +1 -1
  152. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +51 -11
  153. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
  154. package/expo-plugin/build/esm/ios-widget/xcode/index.js +21 -5
  155. package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
  156. package/expo-plugin/build/esm/utils/urlScheme.js +34 -14
  157. package/expo-plugin/build/esm/utils/urlScheme.js.map +1 -1
  158. package/expo-plugin/build/esm/validation.js +54 -1
  159. package/expo-plugin/build/esm/validation.js.map +1 -1
  160. package/expo-plugin/build/types/constants.d.ts +10 -1
  161. package/expo-plugin/build/types/constants.d.ts.map +1 -1
  162. package/expo-plugin/build/types/index.d.ts +1 -1
  163. package/expo-plugin/build/types/index.d.ts.map +1 -1
  164. package/expo-plugin/build/types/ios/index.d.ts +2 -0
  165. package/expo-plugin/build/types/ios/index.d.ts.map +1 -1
  166. package/expo-plugin/build/types/ios/infoPlist.d.ts +2 -0
  167. package/expo-plugin/build/types/ios/infoPlist.d.ts.map +1 -1
  168. package/expo-plugin/build/types/ios/serverUpdate.d.ts +11 -0
  169. package/expo-plugin/build/types/ios/serverUpdate.d.ts.map +1 -0
  170. package/expo-plugin/build/types/ios-widget/clientRendered.d.ts.map +1 -1
  171. package/expo-plugin/build/types/ios-widget/clientRenderedPrerender.d.ts.map +1 -1
  172. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts +7 -0
  173. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts.map +1 -0
  174. package/expo-plugin/build/types/ios-widget/files/index.d.ts +3 -1
  175. package/expo-plugin/build/types/ios-widget/files/index.d.ts.map +1 -1
  176. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts +2 -1
  177. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts.map +1 -1
  178. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts +9 -2
  179. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts.map +1 -1
  180. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts +2 -0
  181. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts.map +1 -0
  182. package/expo-plugin/build/types/ios-widget/files/swift.d.ts +6 -2
  183. package/expo-plugin/build/types/ios-widget/files/swift.d.ts.map +1 -1
  184. package/expo-plugin/build/types/ios-widget/index.d.ts +3 -1
  185. package/expo-plugin/build/types/ios-widget/index.d.ts.map +1 -1
  186. package/expo-plugin/build/types/ios-widget/widgetPlist.d.ts.map +1 -1
  187. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +3 -1
  188. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
  189. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +2 -1
  190. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
  191. package/expo-plugin/build/types/types.d.ts +23 -4
  192. package/expo-plugin/build/types/types.d.ts.map +1 -1
  193. package/expo-plugin/build/types/utils/urlScheme.d.ts +16 -0
  194. package/expo-plugin/build/types/utils/urlScheme.d.ts.map +1 -1
  195. package/expo-plugin/build/types/validation.d.ts +2 -1
  196. package/expo-plugin/build/types/validation.d.ts.map +1 -1
  197. package/expo-plugin/src/constants.ts +24 -1
  198. package/expo-plugin/src/index.ts +9 -0
  199. package/expo-plugin/src/ios/index.ts +3 -0
  200. package/expo-plugin/src/ios/infoPlist.ts +33 -5
  201. package/expo-plugin/src/ios/serverUpdate.ts +29 -0
  202. package/expo-plugin/src/ios-widget/clientRendered.ts +9 -5
  203. package/expo-plugin/src/ios-widget/clientRenderedPrerender.ts +11 -6
  204. package/expo-plugin/src/ios-widget/dynamic-live-activity/swift.ts +138 -0
  205. package/expo-plugin/src/ios-widget/files/index.ts +15 -4
  206. package/expo-plugin/src/ios-widget/files/infoPlist.ts +17 -3
  207. package/expo-plugin/src/ios-widget/files/manifest.ts +42 -2
  208. package/expo-plugin/src/ios-widget/files/swift-utils.ts +3 -0
  209. package/expo-plugin/src/ios-widget/files/swift.ts +69 -22
  210. package/expo-plugin/src/ios-widget/index.ts +13 -3
  211. package/expo-plugin/src/ios-widget/widgetPlist.ts +37 -23
  212. package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +34 -28
  213. package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +59 -12
  214. package/expo-plugin/src/ios-widget/xcode/index.ts +23 -6
  215. package/expo-plugin/src/types.ts +28 -4
  216. package/expo-plugin/src/utils/urlScheme.ts +47 -17
  217. package/expo-plugin/src/validation.ts +76 -1
  218. package/ios/Package.swift +32 -3
  219. package/ios/VoltraWidget.podspec +1 -0
  220. package/ios/app/NativeVoltra.mm +107 -7
  221. package/ios/app/VoltraLiveActivityManager.swift +30 -0
  222. package/ios/app/VoltraLiveActivityService.swift +77 -5
  223. package/ios/app/VoltraModule.swift +99 -1
  224. package/ios/app/VoltraModuleImpl.swift +100 -2
  225. package/ios/app/VoltraViewComponentView.mm +3 -3
  226. package/ios/app/VoltraWidgetService.swift +147 -12
  227. package/ios/app/dynamic-live-activity/VoltraDynamicLiveActivityService.swift +97 -0
  228. package/ios/app/dynamic-live-activity/VoltraLiveActivityChronology.swift +68 -0
  229. package/ios/shared/DynamicWidgetPropsStore.swift +73 -6
  230. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift +34 -0
  231. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift +55 -0
  232. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift +146 -0
  233. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift +193 -0
  234. package/ios/shared/ServerWidgetContentResolver.swift +60 -0
  235. package/ios/shared/ServerWidgetResponseStore.swift +25 -0
  236. package/ios/shared/ShortNames.swift +6 -0
  237. package/ios/shared/VoltraConfig.swift +41 -0
  238. package/ios/shared/VoltraConstants.swift +15 -0
  239. package/ios/shared/VoltraElement.swift +127 -43
  240. package/ios/shared/VoltraEventBus.swift +52 -7
  241. package/ios/shared/VoltraJSRenderer.swift +90 -22
  242. package/ios/shared/VoltraNode.swift +1 -109
  243. package/ios/shared/VoltraRefreshIntent.swift +1 -1
  244. package/ios/shared/VoltraWidgetKind.swift +41 -0
  245. package/ios/shared/VoltraWidgetServerFetcher.swift +52 -107
  246. package/ios/shared/WidgetServer/VoltraWidgetAppearance.swift +17 -0
  247. package/ios/shared/WidgetServer/VoltraWidgetServer.swift +138 -0
  248. package/ios/shared/WidgetServer/WidgetCanonicalConfiguration.swift +77 -0
  249. package/ios/shared/WidgetServer/WidgetScope.swift +55 -0
  250. package/ios/shared/WidgetServer/WidgetServerEtagStore.swift +55 -0
  251. package/ios/shared/WidgetServer/WidgetServerFetcher.swift +179 -0
  252. package/ios/shared/WidgetServer/WidgetServerRequestBuilder.swift +114 -0
  253. package/ios/shared/WidgetServer/WidgetServerSettingsCodec.swift +47 -0
  254. package/ios/shared/WidgetServer/WidgetServerSettingsResolver.swift +129 -0
  255. package/ios/shared/WidgetServer/WidgetServerSettingsStore.swift +170 -0
  256. package/ios/shared/WidgetServer/WidgetServerSettingsValidator.swift +73 -0
  257. package/ios/shared/WidgetServer/WidgetServerUpdateSettings.swift +123 -0
  258. package/ios/shared/WidgetServer/WidgetServerUpdateSettingsJson.swift +115 -0
  259. package/ios/shared/dynamic-live-activity/DynamicLiveActivityPropsCodec.swift +13 -0
  260. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityBundleSource.swift +156 -0
  261. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityObserver.swift +97 -0
  262. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityOperations.swift +105 -0
  263. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift +55 -0
  264. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRegistry.swift +143 -0
  265. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift +158 -0
  266. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureReporter.swift +120 -0
  267. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderer.swift +370 -0
  268. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift +87 -0
  269. package/ios/shared/dynamic-live-activity/VoltraLiveActivityOrder.swift +19 -0
  270. package/ios/target/VoltraClientWidgetRuntime.swift +44 -6
  271. package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
  272. package/ios/target/VoltraHomeWidget.swift +85 -90
  273. package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
  274. package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
  275. package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +2 -2
  276. package/ios/ui/Views/VoltraChart.swift +108 -0
  277. package/package.json +4 -4
  278. package/src/events.ts +34 -0
  279. package/src/index.ts +22 -0
  280. package/src/live-activity/api.ts +2 -2
  281. package/src/live-activity/dynamic-api.ts +240 -0
  282. package/src/native/NativeVoltra.ts +21 -0
  283. package/src/types.ts +4 -0
  284. package/src/utils/enableDynamicLiveActivityHotReload.ts +50 -0
  285. package/src/utils/enableWidgetHotReload.ts +9 -16
  286. package/src/utils/useUpdateOnHMR.ts +3 -12
  287. package/src/widgets/server-credentials.ts +8 -0
  288. package/src/widgets/server-update.ts +84 -0
  289. /package/ios/shared/{VoltraKeychainHelper.swift → WidgetServer/VoltraKeychainHelper.swift} +0 -0
@@ -6,7 +6,8 @@ import { ensureWidgetFileReference, normalizeRef } from './fileReferences'
6
6
 
7
7
  const pbxFile = require('xcode/lib/pbxFile')
8
8
 
9
- const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
9
+ const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Content'
10
+ const LEGACY_WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
10
11
 
11
12
  // Release-only build phase that bakes each Dynamic Widget's production JS bundle into the
12
13
  // extension's resources. Debug builds fetch from Metro (and hot-reload), so this no-ops there. Runs
@@ -14,7 +15,8 @@ const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
14
15
  // voltra-widget-<id>.bundle lands in the .appex (Bundle.main) where the runtime's release loader
15
16
  // reads it. SRCROOT is the ios/ dir; the project root is one level up, matching how Expo's main
16
17
  // "Bundle React Native code and images" phase resolves things.
17
- const WIDGET_BUNDLE_SHELL_SCRIPT = `if [[ "$CONFIGURATION" == *Debug* ]]; then
18
+ function bundleShellScript(content: 'all' | 'live-activities'): string {
19
+ return `if [[ "$CONFIGURATION" == *Debug* ]]; then
18
20
  echo "Voltra: Debug build — Dynamic Widgets load from Metro, skipping bundling"
19
21
  exit 0
20
22
  fi
@@ -48,6 +50,7 @@ try {
48
50
  console.error(error && error.message ? error.message : String(error))
49
51
  process.exit(1)
50
52
  }
53
+
51
54
  NODE
52
55
  )"
53
56
  if [[ -z "$BUNDLER" ]]; then
@@ -56,14 +59,23 @@ if [[ -z "$BUNDLER" ]]; then
56
59
  fi
57
60
 
58
61
  echo "Voltra: widget bundler resolved to $BUNDLER"
59
- "$NODE_BINARY" "$BUNDLER" --out-dir "$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH" --platform ios --project-root "$PROJECT_ROOT"
62
+ "$NODE_BINARY" "$BUNDLER" --out-dir "$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH" --platform ios --project-root "$PROJECT_ROOT" --content ${content}
60
63
  `
64
+ }
65
+
66
+ function pbxQuotedShellScript(script: string): string {
67
+ return `"${script.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`
68
+ }
61
69
 
62
70
  /**
63
71
  * Adds (idempotently) the release-only shell-script phase that bakes Dynamic Widget
64
72
  * bundles into the extension. Safe to call on every prebuild; only added when absent.
65
73
  */
66
- export function ensureWidgetBundleScriptPhase(xcodeProject: XcodeProject, targetUuid: string): void {
74
+ export function ensureWidgetBundleScriptPhase(
75
+ xcodeProject: XcodeProject,
76
+ targetUuid: string,
77
+ content: 'all' | 'live-activities' = 'all'
78
+ ): void {
67
79
  const nativeTargets = xcodeProject.pbxNativeTargetSection()
68
80
  const target = nativeTargets[targetUuid]
69
81
  if (!target) {
@@ -75,15 +87,37 @@ export function ensureWidgetBundleScriptPhase(xcodeProject: XcodeProject, target
75
87
 
76
88
  const shellPhases = xcodeProject.hash.project.objects.PBXShellScriptBuildPhase || {}
77
89
  const quotedName = `"${WIDGET_BUNDLE_PHASE_NAME}"`
78
- const alreadyPresent = target.buildPhases.some((entry: any) => shellPhases[entry.value]?.name === quotedName)
79
- if (alreadyPresent) {
80
- return
81
- }
82
-
83
- xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', WIDGET_BUNDLE_PHASE_NAME, targetUuid, {
84
- shellPath: '/bin/sh',
85
- shellScript: WIDGET_BUNDLE_SHELL_SCRIPT,
90
+ const quotedLegacyName = `"${LEGACY_WIDGET_BUNDLE_PHASE_NAME}"`
91
+ const matchingEntries = target.buildPhases.filter((entry: any) => {
92
+ const name = shellPhases[entry.value]?.name
93
+ return name === quotedName || name === quotedLegacyName
86
94
  })
95
+ let phase: any | undefined
96
+ if (matchingEntries.length > 0) {
97
+ const retained = matchingEntries[0]
98
+ phase = shellPhases[retained.value]
99
+ phase.name = quotedName
100
+ target.buildPhases = target.buildPhases.filter((entry: any) => {
101
+ if (entry === retained || !matchingEntries.includes(entry)) return true
102
+ delete shellPhases[entry.value]
103
+ delete shellPhases[`${entry.value}_comment`]
104
+ return false
105
+ })
106
+ } else {
107
+ xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', WIDGET_BUNDLE_PHASE_NAME, targetUuid, {
108
+ shellPath: '/bin/sh',
109
+ shellScript: bundleShellScript(content),
110
+ })
111
+ phase = Object.values(xcodeProject.hash.project.objects.PBXShellScriptBuildPhase || {}).find(
112
+ (candidate: any) => candidate?.name === quotedName
113
+ )
114
+ }
115
+ if (phase) {
116
+ // xcode's serializer expects an already PBX-quoted value when an existing
117
+ // phase is reconciled directly (addBuildPhase performs this for new ones).
118
+ phase.shellScript = pbxQuotedShellScript(bundleShellScript(content))
119
+ phase.alwaysOutOfDate = 1
120
+ }
87
121
 
88
122
  // The phase intentionally re-bakes on every release build (it can't statically enumerate every
89
123
  // widget source as an input). Mark it always-out-of-date so Xcode doesn't warn about the missing
@@ -112,6 +146,8 @@ export interface EnsureBuildPhasesOptions {
112
146
  }
113
147
  widgetFiles: IOSWidgetExtensionFiles
114
148
  mainTargetUuid?: string
149
+ /** Generated ActivityKit types compiled into both the extension and the app. */
150
+ mainSwiftFiles?: string[]
115
151
  }
116
152
 
117
153
  /**
@@ -191,6 +227,17 @@ export function ensureBuildPhases(xcodeProject: XcodeProject, options: EnsureBui
191
227
  ensureBuildPhaseFiles(xcodeProject, sourcesPhase, [...swiftFiles, ...intentFiles], targetName)
192
228
  }
193
229
 
230
+ if (options.mainSwiftFiles && options.mainSwiftFiles.length > 0) {
231
+ let mainSourcesPhase = findTargetPhaseByType(xcodeProject, mainTargetUuid, 'PBXSourcesBuildPhase')
232
+ if (!mainSourcesPhase) {
233
+ xcodeProject.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', mainTargetUuid, folderType, buildPath)
234
+ mainSourcesPhase = findTargetPhaseByType(xcodeProject, mainTargetUuid, 'PBXSourcesBuildPhase')
235
+ }
236
+ if (mainSourcesPhase) {
237
+ ensureBuildPhaseFiles(xcodeProject, mainSourcesPhase, options.mainSwiftFiles, targetName)
238
+ }
239
+ }
240
+
194
241
  // Copy files build phase (embed extension into main app) — the embed phase is matched purely by
195
242
  // its `dstSubfolderSpec == 13` semantics, both before and after creation
196
243
  let copyFilesPhase = findExistingEmbedExtensionsPhase(xcodeProject, mainTargetUuid)
@@ -1,7 +1,7 @@
1
1
  import { ConfigPlugin, withXcodeProject, XcodeProject } from '@expo/config-plugins'
2
2
  import * as path from 'path'
3
3
 
4
- import type { IOSWidgetConfig, IOSWidgetExtensionFiles } from '../../types'
4
+ import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig, IOSWidgetExtensionFiles } from '../../types'
5
5
  import { getIOSWidgetExtensionFiles } from '../../utils/fileDiscovery'
6
6
  import { detectClientRenderedWidgets } from '../clientRendered'
7
7
  import { ensureBuildPhases, ensureWidgetBundleScriptPhase } from './buildPhases'
@@ -20,6 +20,7 @@ export interface ConfigureXcodeProjectProps {
20
20
  /** App build number; becomes the widget's CURRENT_PROJECT_VERSION. */
21
21
  buildNumber?: string
22
22
  widgets?: IOSWidgetConfig[]
23
+ liveActivities?: IOSDynamicLiveActivityConfig[]
23
24
  }
24
25
 
25
26
  /**
@@ -42,6 +43,13 @@ export function applyXcodeChanges(
42
43
  ): void {
43
44
  const { targetName, bundleIdentifier, deploymentTarget, version, buildNumber } = props
44
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
+ }
45
53
 
46
54
  // Read main app target settings to synchronize code signing (per configuration).
47
55
  const mainAppSettings = getMainAppTargetSettings(xcodeProject)
@@ -92,7 +100,7 @@ export function applyXcodeChanges(
92
100
  // reference them (the phases resolve files through the widget-scoped group).
93
101
  ensurePbxGroup(xcodeProject, {
94
102
  targetName,
95
- widgetFiles,
103
+ widgetFiles: effectiveWidgetFiles,
96
104
  })
97
105
 
98
106
  // Ensure build phases and their files.
@@ -101,13 +109,22 @@ export function applyXcodeChanges(
101
109
  targetName,
102
110
  groupName,
103
111
  productFile,
104
- widgetFiles,
112
+ widgetFiles: effectiveWidgetFiles,
105
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'],
106
117
  })
107
118
 
108
- if (hasClientRenderedWidgets) {
119
+ if (hasClientRenderedWidgets || (props.liveActivities?.length ?? 0) > 0) {
109
120
  ensureWidgetBundleScriptPhase(xcodeProject, targetUuid)
110
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
+ }
111
128
 
112
129
  ensureTargetAttributes(xcodeProject, targetUuid)
113
130
  ensureTargetDependency(xcodeProject, targetUuid)
@@ -128,7 +145,7 @@ export function applyXcodeChanges(
128
145
  * mutation to {@link applyXcodeChanges}.
129
146
  */
130
147
  export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (config, props) => {
131
- const { targetName, widgets } = props
148
+ const { targetName, widgets, liveActivities } = props
132
149
 
133
150
  return withXcodeProject(config, (config) => {
134
151
  if (config.modRequest.introspect) {
@@ -147,7 +164,7 @@ export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (
147
164
  const targetPath = path.join(platformProjectRoot, targetName)
148
165
  const widgetFiles = getIOSWidgetExtensionFiles(targetPath, targetName)
149
166
 
150
- applyXcodeChanges(xcodeProject, props, widgetFiles, hasClientRenderedWidgets)
167
+ applyXcodeChanges(xcodeProject, props, widgetFiles, hasClientRenderedWidgets || (liveActivities?.length ?? 0) > 0)
151
168
 
152
169
  return config
153
170
  })
@@ -1,6 +1,11 @@
1
1
  import type { ConfigPlugin } from '@expo/config-plugins'
2
2
 
3
- import type { DynamicWidgetEntryConfig, WidgetInitialStatePath, WidgetLabel } from '@use-voltra/expo-plugin'
3
+ import type {
4
+ DynamicLiveActivityEntryConfig,
5
+ DynamicWidgetEntryConfig,
6
+ WidgetInitialStatePath,
7
+ WidgetLabel,
8
+ } from '@use-voltra/expo-plugin'
4
9
 
5
10
  /**
6
11
  * Supported iOS Home Screen widget size families.
@@ -42,9 +47,15 @@ export interface IOSWidgetAppIntentConfig {
42
47
  */
43
48
  export interface IOSWidgetConfig extends DynamicWidgetEntryConfig {
44
49
  /**
45
- * Unique identifier for the widget (used as the widget kind and in JS API)
50
+ * Unique identifier for the widget (used in the JS API and, unless `kind` is set, as the widget kind)
46
51
  */
47
52
  id: string
53
+ /**
54
+ * WidgetKit `kind` of the generated widget. Defaults to `Voltra_Widget_<id>`.
55
+ * Pin it to the kind of a pre-Voltra widget so already placed instances survive the migration
56
+ * (WidgetKit identifies a placed widget by extension bundle id + kind).
57
+ */
58
+ kind?: string
48
59
  displayName: WidgetLabel
49
60
  description: WidgetLabel
50
61
  /** @default ['systemSmall', 'systemMedium', 'systemLarge'] */
@@ -60,12 +71,22 @@ export interface IOSWidgetConfig extends DynamicWidgetEntryConfig {
60
71
  appIntent?: IOSWidgetAppIntentConfig
61
72
  }
62
73
 
74
+ /**
75
+ * A Dynamic Live Activity bundled with the app.
76
+ * @experimental
77
+ */
78
+ export interface IOSDynamicLiveActivityConfig extends DynamicLiveActivityEntryConfig {}
79
+
63
80
  /**
64
81
  * Server-driven iOS widget updates (WidgetKit background refresh).
65
82
  */
66
83
  export interface IOSWidgetServerUpdateConfig {
67
- url: string
68
- /** @default 15 */
84
+ /**
85
+ * Server endpoint that returns widget state updates. Omit it to mark the widget
86
+ * server-driven and supply the URL at runtime with `setWidgetServerUpdate`.
87
+ */
88
+ url?: string
89
+ /** @default 15, or 15 when the widget has an `entry` */
69
90
  intervalMinutes?: number
70
91
  /** @default false */
71
92
  refresh?: boolean
@@ -91,6 +112,8 @@ export interface IOSConfigPluginProps {
91
112
  enablePushNotifications?: boolean
92
113
  groupIdentifier?: string
93
114
  widgets?: IOSWidgetConfig[]
115
+ /** @experimental Dynamic Live Activities rendered from bundled JavaScript entries. */
116
+ liveActivities?: IOSDynamicLiveActivityConfig[]
94
117
  deploymentTarget?: string
95
118
  targetName?: string
96
119
  fonts?: string[]
@@ -111,6 +134,7 @@ export interface IOSWidgetExtensionPluginProps {
111
134
  bundleIdentifier: string
112
135
  deploymentTarget: string
113
136
  widgets?: IOSWidgetConfig[]
137
+ liveActivities?: IOSDynamicLiveActivityConfig[]
114
138
  groupIdentifier?: string
115
139
  keychainGroup?: string
116
140
  fonts?: string[]
@@ -1,5 +1,42 @@
1
1
  import type { ExpoConfig } from 'expo/config'
2
2
 
3
+ type URLType = { CFBundleURLSchemes?: unknown }
4
+
5
+ /**
6
+ * The URL schemes the app is configured with: every `scheme` and `ios.scheme` value (string or
7
+ * array), or the bundle identifier when neither is set.
8
+ */
9
+ export function getAppURLSchemes(config: ExpoConfig): string[] {
10
+ const configured = [config.scheme, (config.ios as { scheme?: string | string[] } | undefined)?.scheme]
11
+ .flat()
12
+ .filter((scheme): scheme is string => typeof scheme === 'string' && scheme.length > 0)
13
+
14
+ if (configured.length > 0) {
15
+ return [...new Set(configured)]
16
+ }
17
+
18
+ return config.ios?.bundleIdentifier ? [config.ios.bundleIdentifier] : []
19
+ }
20
+
21
+ /**
22
+ * Appends a URL type for every scheme not already listed. Returns `types` itself when nothing is
23
+ * missing.
24
+ */
25
+ export function appendMissingURLSchemes<T extends URLType>(
26
+ types: T[],
27
+ schemes: string[]
28
+ ): (T | { CFBundleURLSchemes: string[] })[] {
29
+ const missingSchemes = schemes.filter(
30
+ (scheme) => !types.some((t) => Array.isArray(t?.CFBundleURLSchemes) && t.CFBundleURLSchemes.includes(scheme))
31
+ )
32
+
33
+ if (missingSchemes.length === 0) {
34
+ return types
35
+ }
36
+
37
+ return [...types, ...missingSchemes.map((scheme) => ({ CFBundleURLSchemes: [scheme] }))]
38
+ }
39
+
3
40
  /**
4
41
  * Ensures the main app has a URL scheme set so widgetURL can open it.
5
42
  * This is an optional feature for deep linking from widgets.
@@ -8,37 +45,30 @@ import type { ExpoConfig } from 'expo/config'
8
45
  * @returns The modified config with URL scheme ensured
9
46
  */
10
47
  export function ensureURLScheme(config: ExpoConfig): ExpoConfig {
11
- const scheme = typeof (config as any).scheme === 'string' ? (config as any).scheme : config.ios?.bundleIdentifier
48
+ const existingInfoPlist = config.ios?.infoPlist
49
+ const existingTypes = existingInfoPlist?.CFBundleURLTypes as URLType[] | undefined
12
50
 
13
- if (!scheme) {
51
+ // Expo's own withScheme fills CFBundleURLTypes from `scheme` / `ios.scheme` and always appends
52
+ // ios.bundleIdentifier, but it skips the app entirely once ios.infoPlist.CFBundleURLTypes is set
53
+ // (createInfoPlistPluginWithPropertyGuard). Writing the key here when the app hasn't set it
54
+ // therefore drops every configured scheme instead of adding one. Only top up a list the app owns.
55
+ if (!existingTypes) {
14
56
  return config
15
57
  }
16
58
 
17
- const existingInfoPlist = config.ios?.infoPlist || {}
18
- const existingTypes = (existingInfoPlist.CFBundleURLTypes as any[]) || []
19
-
20
- // Check if scheme already exists
21
- const hasScheme = existingTypes.some(
22
- (t) => Array.isArray(t?.CFBundleURLSchemes) && t.CFBundleURLSchemes.includes(scheme)
23
- )
59
+ const types = appendMissingURLSchemes(existingTypes, getAppURLSchemes(config))
24
60
 
25
- if (hasScheme) {
61
+ if (types === existingTypes) {
26
62
  return config
27
63
  }
28
64
 
29
- // Add the URL scheme
30
65
  return {
31
66
  ...config,
32
67
  ios: {
33
68
  ...config.ios,
34
69
  infoPlist: {
35
70
  ...existingInfoPlist,
36
- CFBundleURLTypes: [
37
- ...existingTypes,
38
- {
39
- CFBundleURLSchemes: [scheme],
40
- },
41
- ],
71
+ CFBundleURLTypes: types,
42
72
  },
43
73
  },
44
74
  }
@@ -3,9 +3,14 @@ import {
3
3
  validateInitialStatePath,
4
4
  validateWidgetEntry,
5
5
  validateWidgetLabel,
6
+ validateWidgetServerUpdate,
6
7
  } from '@use-voltra/expo-plugin'
7
8
 
8
- import type { IOSConfigPluginProps, IOSWidgetConfig, IOSWidgetFamily } from './types'
9
+ import { iosServerUpdateRules } from './ios/serverUpdate'
10
+ import { getDynamicLiveActivityAttributesType } from '@use-voltra/expo-plugin'
11
+
12
+ import { widgetKind } from './constants'
13
+ import type { IOSConfigPluginProps, IOSDynamicLiveActivityConfig, IOSWidgetConfig, IOSWidgetFamily } from './types'
9
14
 
10
15
  const VALID_FAMILIES: Set<IOSWidgetFamily> = new Set([
11
16
  'systemSmall',
@@ -16,9 +21,31 @@ const VALID_FAMILIES: Set<IOSWidgetFamily> = new Set([
16
21
  'accessoryRectangular',
17
22
  'accessoryInline',
18
23
  ])
24
+ const DYNAMIC_LIVE_ACTIVITY_ID_PATTERN = /^[a-zA-Z0-9_]+$/
25
+
26
+ export function validateIOSDynamicLiveActivityConfig(
27
+ liveActivity: IOSDynamicLiveActivityConfig,
28
+ projectRoot?: string
29
+ ): void {
30
+ if (!liveActivity.id || typeof liveActivity.id !== 'string') {
31
+ throw new Error('Dynamic Live Activity ID is required and must be a string')
32
+ }
33
+
34
+ if (!DYNAMIC_LIVE_ACTIVITY_ID_PATTERN.test(liveActivity.id)) {
35
+ throw new Error(
36
+ `Dynamic Live Activity ID '${liveActivity.id}' is invalid. ` +
37
+ 'It must be non-empty and contain only alphanumeric characters and underscores.'
38
+ )
39
+ }
40
+
41
+ validateWidgetEntry(liveActivity.entry, liveActivity.id, projectRoot)
42
+ }
19
43
 
20
44
  export function validateIOSWidgetConfig(widget: IOSWidgetConfig, projectRoot?: string): void {
21
45
  validateHomeScreenWidgetId(widget.id)
46
+ if (widget.kind !== undefined && (typeof widget.kind !== 'string' || widget.kind.trim() === '')) {
47
+ throw new Error(`Widget '${widget.id}': kind must be a non-empty string`)
48
+ }
22
49
  validateWidgetLabel(widget.displayName, widget.id, 'displayName')
23
50
  validateWidgetLabel(widget.description, widget.id, 'description')
24
51
  validateInitialStatePath(widget.initialStatePath, widget.id, projectRoot)
@@ -27,6 +54,8 @@ export function validateIOSWidgetConfig(widget: IOSWidgetConfig, projectRoot?: s
27
54
  validateWidgetEntry(widget.entry, widget.id, projectRoot)
28
55
  }
29
56
 
57
+ validateWidgetServerUpdate(widget.serverUpdate, widget.id, iosServerUpdateRules(widget))
58
+
30
59
  if (widget.supportedFamilies) {
31
60
  if (!Array.isArray(widget.supportedFamilies)) {
32
61
  throw new Error(`Widget '${widget.id}': supportedFamilies must be an array`)
@@ -60,13 +89,59 @@ export function validateIOSConfigPluginProps(props: IOSConfigPluginProps, projec
60
89
  }
61
90
 
62
91
  const seenIds = new Set<string>()
92
+ const seenKinds = new Set<string>()
63
93
  for (const widget of props.widgets) {
64
94
  validateIOSWidgetConfig(widget, projectRoot)
65
95
 
96
+ // A server-driven Dynamic Widget commits fetched props to the App Group so the widget
97
+ // extension can read them. Without one it would fetch and have nowhere to put the result.
98
+ if (widget.entry !== undefined && widget.serverUpdate !== undefined && !props.groupIdentifier) {
99
+ throw new Error(
100
+ `Widget '${widget.id}' has both entry and serverUpdate, which requires groupIdentifier ` +
101
+ 'so fetched props can be shared with the widget extension.'
102
+ )
103
+ }
104
+
66
105
  if (seenIds.has(widget.id)) {
67
106
  throw new Error(`Duplicate widget ID: '${widget.id}'`)
68
107
  }
69
108
  seenIds.add(widget.id)
109
+
110
+ const kind = widgetKind(widget)
111
+ if (seenKinds.has(kind)) {
112
+ throw new Error(`Duplicate widget kind: '${kind}'`)
113
+ }
114
+ seenKinds.add(kind)
115
+ }
116
+ }
117
+
118
+ if (props.liveActivities !== undefined) {
119
+ if (!Array.isArray(props.liveActivities)) {
120
+ throw new Error('liveActivities must be an array')
121
+ }
122
+
123
+ if (props.liveActivities.length > 0 && !props.groupIdentifier) {
124
+ throw new Error('groupIdentifier is required when liveActivities is non-empty')
125
+ }
126
+
127
+ const seenIds = new Set<string>()
128
+ const seenAttributesTypes = new Map<string, string>()
129
+ for (const liveActivity of props.liveActivities) {
130
+ validateIOSDynamicLiveActivityConfig(liveActivity, projectRoot)
131
+
132
+ if (seenIds.has(liveActivity.id)) {
133
+ throw new Error(`Duplicate Dynamic Live Activity ID: '${liveActivity.id}'`)
134
+ }
135
+ seenIds.add(liveActivity.id)
136
+
137
+ const attributesType = getDynamicLiveActivityAttributesType(liveActivity.id)
138
+ const conflictingId = seenAttributesTypes.get(attributesType)
139
+ if (conflictingId) {
140
+ throw new Error(
141
+ `Dynamic Live Activity IDs '${conflictingId}' and '${liveActivity.id}' generate the same ActivityKit attributes type '${attributesType}'`
142
+ )
143
+ }
144
+ seenAttributesTypes.set(attributesType, liveActivity.id)
70
145
  }
71
146
  }
72
147
  }
package/ios/Package.swift CHANGED
@@ -25,15 +25,12 @@ let package = Package(
25
25
  "BrotliCompression.swift",
26
26
  "Data+hexString.swift",
27
27
  "Date+toTimerInterval.swift",
28
- "ShortNames.swift",
29
28
  "VoltraAttributes.swift",
30
- "VoltraElement.swift",
31
29
  "VoltraEvent.swift",
32
30
  "VoltraEventBus.swift",
33
31
  "VoltraImageStore.swift",
34
32
  "VoltraInteractionIntent.swift",
35
33
  "VoltraLiveActivityPayload.swift",
36
- "VoltraNode.swift",
37
34
  "VoltraPersistentEventQueue.swift",
38
35
  ],
39
36
  sources: [
@@ -42,12 +39,44 @@ let package = Package(
42
39
  "DynamicWidgetPropsStore.swift",
43
40
  "DynamicWidgetRenderCoordinator.swift",
44
41
  "DynamicWidgetUpdater.swift",
42
+ "ServerWidgetContentResolver.swift",
43
+ "ServerWidgetResponseStore.swift",
44
+ // The settings stack's pure half. The Keychain-, Bundle- and URLSession-backed files in
45
+ // this folder are compiled only by the podspec, which ships the whole tree.
46
+ "WidgetServer/WidgetScope.swift",
47
+ "WidgetServer/WidgetCanonicalConfiguration.swift",
48
+ "WidgetServer/WidgetServerUpdateSettings.swift",
49
+ "WidgetServer/WidgetServerSettingsResolver.swift",
50
+ "WidgetServer/WidgetServerSettingsCodec.swift",
51
+ "WidgetServer/WidgetServerSettingsValidator.swift",
52
+ "WidgetServer/WidgetServerRequestBuilder.swift",
53
+ "WidgetServer/WidgetServerFetcher.swift",
54
+ "WidgetServer/WidgetServerSettingsStore.swift",
55
+ "WidgetServer/WidgetServerEtagStore.swift",
56
+ "WidgetServer/VoltraKeychainHelper.swift",
57
+ "WidgetServer/VoltraWidgetServer.swift",
58
+ "WidgetServer/WidgetServerUpdateSettingsJson.swift",
59
+ "DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift",
60
+ "DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift",
61
+ "DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift",
62
+ "DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift",
63
+ "VoltraLogger.swift",
64
+ "dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift",
65
+ "dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift",
66
+ "dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift",
67
+ "dynamic-live-activity/VoltraLiveActivityOrder.swift",
45
68
  "JSONValue.swift",
46
69
  "VoltraConfig.swift",
47
70
  "VoltraConstants.swift",
71
+ "VoltraWidgetKind.swift",
48
72
  "VoltraPayloadMigrator.swift",
49
73
  "VoltraRegion.swift",
50
74
  "ComponentTypeID.swift",
75
+ // The JSON -> AST types. They carry no SwiftUI dependency; rendering is attached
76
+ // by `ui/VoltraNode+View.swift`, which the podspecs compile alongside them.
77
+ "ShortNames.swift",
78
+ "VoltraElement.swift",
79
+ "VoltraNode.swift",
51
80
  ]
52
81
  ),
53
82
  .testTarget(
@@ -14,6 +14,7 @@ Pod::Spec.new do |s|
14
14
  :ios => '16.4',
15
15
  }
16
16
  s.swift_version = '5.9'
17
+ s.module_name = 'VoltraRuntime'
17
18
  s.source = { git: 'https://github.com/callstackincubator/voltra' }
18
19
  s.static_framework = true
19
20