@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,7 +1,7 @@
1
1
  import * as fs from 'fs'
2
2
  import * as path from 'path'
3
3
 
4
- import { evaluateWidgetModuleExports } from '@use-voltra/expo-plugin'
4
+ import { createPrerenderWidgetModuleLoader, type WidgetModuleLoader } from '@use-voltra/expo-plugin'
5
5
 
6
6
  import type { IOSWidgetConfig } from '../types'
7
7
 
@@ -30,7 +30,8 @@ let hasWarnedExperimental = false
30
30
 
31
31
  /** Inspect every widget once and tag entry-backed widgets as Dynamic Widgets. */
32
32
  export function detectClientRenderedWidgets(widgets: IOSWidgetConfig[], projectRoot: string): DetectedIOSWidget[] {
33
- const detected = widgets.map((widget) => detectSingleWidget(widget, projectRoot))
33
+ const loader = createPrerenderWidgetModuleLoader(projectRoot, 'ios')
34
+ const detected = widgets.map((widget) => detectSingleWidget(widget, projectRoot, loader))
34
35
 
35
36
  if (!hasWarnedExperimental) {
36
37
  const clientWidgetIds = detected.filter((widget) => widget.clientRendered).map((widget) => widget.id)
@@ -47,7 +48,11 @@ export function detectClientRenderedWidgets(widgets: IOSWidgetConfig[], projectR
47
48
  return detected
48
49
  }
49
50
 
50
- function detectSingleWidget(widget: IOSWidgetConfig, projectRoot: string): DetectedIOSWidget {
51
+ function detectSingleWidget(
52
+ widget: IOSWidgetConfig,
53
+ projectRoot: string,
54
+ loader: WidgetModuleLoader
55
+ ): DetectedIOSWidget {
51
56
  if (widget.entry === undefined) {
52
57
  return {
53
58
  ...widget,
@@ -65,8 +70,7 @@ function detectSingleWidget(widget: IOSWidgetConfig, projectRoot: string): Detec
65
70
  )
66
71
  }
67
72
 
68
- const widgetModule = evaluateWidgetModuleExports(projectRoot, sourcePath)
69
- const widgetFn = widgetModule?.default ?? widgetModule
73
+ const widgetFn = loader.loadDefaultExport(sourcePath)
70
74
  if (typeof widgetFn !== 'function') {
71
75
  throw new Error(
72
76
  `[voltra] Dynamic Widget "${widget.id}" at ${path.relative(
@@ -1,4 +1,9 @@
1
- import { evaluateWidgetModuleExports, logger, type PrerenderedWidgetStates } from '@use-voltra/expo-plugin'
1
+ import {
2
+ createPrerenderWidgetModuleLoader,
3
+ logger,
4
+ resolveInstalledPackageVersion,
5
+ type PrerenderedWidgetStates,
6
+ } from '@use-voltra/expo-plugin'
2
7
 
3
8
  import type { DetectedIOSWidget } from './clientRendered'
4
9
 
@@ -31,7 +36,7 @@ const SINGLE_LOCALE_KEY = '__default'
31
36
  * mirror `WidgetEnvironment` from packages/core/src/widget-environment.ts so the widget
32
37
  * function sees the same shape it gets at runtime.
33
38
  */
34
- function buildPlaceholderEnv(): Record<string, unknown> {
39
+ function buildPlaceholderEnv(voltraVersion: string): Record<string, unknown> {
35
40
  return {
36
41
  date: Date.now(),
37
42
  widgetFamily: 'systemMedium',
@@ -44,7 +49,7 @@ function buildPlaceholderEnv(): Record<string, unknown> {
44
49
  isDev: false,
45
50
  metroUrl: null,
46
51
  appVersion: 'unknown',
47
- voltraVersion: '1.4.1',
52
+ voltraVersion,
48
53
  },
49
54
  }
50
55
  }
@@ -75,12 +80,12 @@ export async function prerenderClientRenderedWidgets(
75
80
  renderVoltraVariantToJson: (element: unknown) => unknown
76
81
  }
77
82
 
78
- const placeholderEnv = buildPlaceholderEnv()
83
+ const placeholderEnv = buildPlaceholderEnv(resolveInstalledPackageVersion(projectRoot, '@use-voltra/ios-client'))
84
+ const loader = createPrerenderWidgetModuleLoader(projectRoot, 'ios')
79
85
 
80
86
  for (const widget of clientWidgets) {
81
87
  try {
82
- const widgetModule = evaluateWidgetModuleExports(projectRoot, widget.clientSourcePath)
83
- const widgetFn = widgetModule?.default ?? widgetModule
88
+ const widgetFn = loader.loadDefaultExport(widget.clientSourcePath)
84
89
  if (typeof widgetFn !== 'function') {
85
90
  throw new Error(
86
91
  `Expected the entry module at ${widget.clientSourcePath} to default-export a function or component.`
@@ -0,0 +1,138 @@
1
+ import dedent from 'dedent'
2
+
3
+ import { getDynamicLiveActivityAttributesType } from '@use-voltra/expo-plugin'
4
+
5
+ import type { IOSDynamicLiveActivityConfig } from '../../types'
6
+ import { escapeForSwiftStringLiteral } from '../files/swift-utils'
7
+
8
+ export function generateDynamicLiveActivityWidgetInstances(liveActivities: IOSDynamicLiveActivityConfig[]): string {
9
+ return sortedDefinitions(liveActivities)
10
+ .map((liveActivity) => `VoltraDynamicLiveActivity_${getDynamicLiveActivityAttributesType(liveActivity.id)}()`)
11
+ .join('\n ')
12
+ }
13
+
14
+ /** Generates the Dynamic Live Activity types shared by the app and extension targets. */
15
+ export function generateDynamicLiveActivityTypesSwift(liveActivities: IOSDynamicLiveActivityConfig[]): string {
16
+ const definitions = sortedDefinitions(liveActivities)
17
+ const typeDefinitions = definitions.map(generateDynamicLiveActivitySwift).map(normalizeIndent).join('\n\n')
18
+
19
+ const header = dedent`
20
+ //
21
+ // VoltraDynamicLiveActivityTypes.swift
22
+ //
23
+ // Auto-generated by Voltra config plugin. Do not edit.
24
+ //
25
+
26
+ import ActivityKit
27
+ import Foundation
28
+
29
+ import VoltraRuntime
30
+
31
+ @objc(VoltraGeneratedDynamicLiveActivityRegistration)
32
+ public final class VoltraGeneratedDynamicLiveActivityRegistration: NSObject {
33
+ @objc public static func registerDefinitions() {
34
+ ${definitions
35
+ .map(
36
+ (liveActivity) =>
37
+ ` VoltraDynamicLiveActivityRegistry.shared.register(${getDynamicLiveActivityAttributesType(
38
+ liveActivity.id
39
+ )}.self)`
40
+ )
41
+ .join('\n')}
42
+ }
43
+ }
44
+
45
+ `
46
+ return [header.trim(), typeDefinitions.trim()].filter(Boolean).join('\n\n')
47
+ }
48
+
49
+ /** Generates extension-only ActivityConfiguration declarations for Dynamic Live Activities. */
50
+ export function generateDynamicLiveActivitiesSwift(liveActivities: IOSDynamicLiveActivityConfig[]): string {
51
+ const configurations = sortedDefinitions(liveActivities)
52
+ .map((liveActivity) => {
53
+ const attributesType = getDynamicLiveActivityAttributesType(liveActivity.id)
54
+ const definitionId = escapeForSwiftStringLiteral(liveActivity.id)
55
+ return dedent`
56
+ public struct VoltraDynamicLiveActivity_${attributesType}: Widget {
57
+ public init() {}
58
+
59
+ public var body: some WidgetConfiguration {
60
+ if #available(iOS 18.0, *) {
61
+ return adaptiveConfig()
62
+ } else {
63
+ return defaultConfig()
64
+ }
65
+ }
66
+
67
+ @available(iOS 18.0, *)
68
+ private func adaptiveConfig() -> some WidgetConfiguration {
69
+ ActivityConfiguration(for: ${attributesType}.self) { context in
70
+ VoltraDynamicLiveActivityRenderer.lockScreen(definitionId: "${definitionId}", context: context)
71
+ } dynamicIsland: { context in
72
+ VoltraDynamicLiveActivityRenderer.dynamicIsland(definitionId: "${definitionId}", context: context)
73
+ }
74
+ .supplementalActivityFamilies([.small, .medium])
75
+ }
76
+
77
+ private func defaultConfig() -> some WidgetConfiguration {
78
+ ActivityConfiguration(for: ${attributesType}.self) { context in
79
+ VoltraDynamicLiveActivityRenderer.lockScreen(definitionId: "${definitionId}", context: context)
80
+ } dynamicIsland: { context in
81
+ VoltraDynamicLiveActivityRenderer.dynamicIsland(definitionId: "${definitionId}", context: context)
82
+ }
83
+ }
84
+ }
85
+ `
86
+ })
87
+ .map(normalizeIndent)
88
+ .join('\n\n')
89
+
90
+ const header = dedent`
91
+ //
92
+ // VoltraDynamicLiveActivities.swift
93
+ //
94
+ // Auto-generated by Voltra config plugin. Do not edit.
95
+ //
96
+
97
+ import ActivityKit
98
+ import SwiftUI
99
+ import WidgetKit
100
+ import VoltraRuntime
101
+
102
+ `
103
+ return [header.trim(), configurations.trim()].filter(Boolean).join('\n\n')
104
+ }
105
+
106
+ function generateDynamicLiveActivitySwift(liveActivity: IOSDynamicLiveActivityConfig): string {
107
+ const attributesType = getDynamicLiveActivityAttributesType(liveActivity.id)
108
+ const definitionId = escapeForSwiftStringLiteral(liveActivity.id)
109
+
110
+ return dedent`
111
+ public struct ${attributesType}: ActivityAttributes {
112
+ public typealias ContentState = VoltraDynamicLiveActivityContentState
113
+
114
+ public let name: String
115
+ public let deepLinkUrl: String?
116
+
117
+ public init(name: String, deepLinkUrl: String?) {
118
+ self.name = name
119
+ self.deepLinkUrl = deepLinkUrl
120
+ }
121
+ }
122
+
123
+ extension ${attributesType}: VoltraDynamicLiveActivityDefinition {
124
+ public static let definitionId = "${definitionId}"
125
+ }
126
+
127
+ `
128
+ }
129
+
130
+ function sortedDefinitions(liveActivities: IOSDynamicLiveActivityConfig[]): IOSDynamicLiveActivityConfig[] {
131
+ return [...liveActivities].sort((left, right) => left.id.localeCompare(right.id))
132
+ }
133
+
134
+ function normalizeIndent(source: string): string {
135
+ const lines = source.trim().split('\n')
136
+ const indent = Math.min(...lines.filter(Boolean).map((line) => line.match(/^\s*/)?.[0].length ?? 0))
137
+ return lines.map((line) => line.slice(indent)).join('\n')
138
+ }
@@ -2,20 +2,22 @@ import { ConfigPlugin, withDangerousMod } from '@expo/config-plugins'
2
2
  import * as fs from 'fs'
3
3
  import * as path from 'path'
4
4
 
5
- import type { IOSWidgetConfig } from '../../types'
5
+ import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig } from '../../types'
6
6
  import { generateAssets } from './assets'
7
7
  import { generateEntitlements } from './entitlements'
8
8
  import { generateInfoPlist } from './infoPlist'
9
- import { generateIOSDynamicWidgetsManifest } from './manifest'
9
+ import { generateIOSDynamicLiveActivitiesManifest, generateIOSDynamicWidgetsManifest } from './manifest'
10
10
  import { generateSwiftFiles } from './swift'
11
11
 
12
12
  export interface GenerateWidgetExtensionFilesProps {
13
13
  targetName: string
14
14
  widgets?: IOSWidgetConfig[]
15
+ liveActivities?: IOSDynamicLiveActivityConfig[]
15
16
  groupIdentifier?: string
16
17
  keychainGroup?: string
17
18
  version: string
18
19
  buildNumber: string
20
+ voltraVersion: string
19
21
  }
20
22
 
21
23
  /**
@@ -31,7 +33,8 @@ export interface GenerateWidgetExtensionFilesProps {
31
33
  * This should run before configureXcodeProject so the files exist when Xcode project is configured.
32
34
  */
33
35
  export const generateWidgetExtensionFiles: ConfigPlugin<GenerateWidgetExtensionFilesProps> = (config, props) => {
34
- const { targetName, widgets, groupIdentifier, keychainGroup, version, buildNumber } = props
36
+ const { targetName, widgets, liveActivities, groupIdentifier, keychainGroup, version, buildNumber, voltraVersion } =
37
+ props
35
38
 
36
39
  return withDangerousMod(config, [
37
40
  'ios',
@@ -49,7 +52,7 @@ export const generateWidgetExtensionFiles: ConfigPlugin<GenerateWidgetExtensionF
49
52
  }
50
53
 
51
54
  // Generate Info.plist
52
- generateInfoPlist(targetPath, targetName, version, buildNumber)
55
+ generateInfoPlist(targetPath, targetName, version, buildNumber, voltraVersion)
53
56
 
54
57
  // Generate Assets.xcassets and copy user images
55
58
  generateAssets({ targetPath })
@@ -59,6 +62,7 @@ export const generateWidgetExtensionFiles: ConfigPlugin<GenerateWidgetExtensionF
59
62
  targetPath,
60
63
  projectRoot,
61
64
  widgets,
65
+ liveActivities,
62
66
  })
63
67
 
64
68
  // Write the iOS-owned Dynamic Widgets manifest for Metro to consume later.
@@ -67,6 +71,13 @@ export const generateWidgetExtensionFiles: ConfigPlugin<GenerateWidgetExtensionF
67
71
  widgets,
68
72
  })
69
73
 
74
+ // Dynamic Live Activities have their own Metro manifest so their IDs may overlap with
75
+ // Dynamic Widgets and stale declarations never leak into a later prebuild.
76
+ generateIOSDynamicLiveActivitiesManifest({
77
+ projectRoot,
78
+ liveActivities,
79
+ })
80
+
70
81
  // Generate entitlements file (may be empty if no groupIdentifier or keychainGroup)
71
82
  generateEntitlements({
72
83
  targetPath,
@@ -9,7 +9,12 @@ import { logger } from '@use-voltra/expo-plugin'
9
9
  * This includes version information and the required WidgetKit extension point.
10
10
  * Version keys are written directly to the plist following the Expo pattern.
11
11
  */
12
- function generateInfoPlistContent(targetName: string, version: string, buildNumber: string): string {
12
+ function generateInfoPlistContent(
13
+ targetName: string,
14
+ version: string,
15
+ buildNumber: string,
16
+ voltraVersion: string
17
+ ): string {
13
18
  return `<?xml version="1.0" encoding="UTF-8"?>
14
19
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
15
20
  <plist version="1.0">
@@ -32,6 +37,8 @@ function generateInfoPlistContent(targetName: string, version: string, buildNumb
32
37
  <string>${version}</string>
33
38
  <key>CFBundleVersion</key>
34
39
  <string>${buildNumber}</string>
40
+ <key>Voltra_Version</key>
41
+ <string>${voltraVersion}</string>
35
42
  <key>NSExtension</key>
36
43
  <dict>
37
44
  <key>NSExtensionPointIdentifier</key>
@@ -49,9 +56,16 @@ function generateInfoPlistContent(targetName: string, version: string, buildNumb
49
56
  * @param targetName - The widget extension target name (used for CFBundleDisplayName)
50
57
  * @param version - The app version (CFBundleShortVersionString)
51
58
  * @param buildNumber - The build number (CFBundleVersion)
59
+ * @param voltraVersion - The installed @use-voltra/ios-client package version
52
60
  */
53
- export function generateInfoPlist(targetPath: string, targetName: string, version: string, buildNumber: string): void {
61
+ export function generateInfoPlist(
62
+ targetPath: string,
63
+ targetName: string,
64
+ version: string,
65
+ buildNumber: string,
66
+ voltraVersion: string
67
+ ): void {
54
68
  const infoPlistPath = path.join(targetPath, 'Info.plist')
55
- fs.writeFileSync(infoPlistPath, generateInfoPlistContent(targetName, version, buildNumber))
69
+ fs.writeFileSync(infoPlistPath, generateInfoPlistContent(targetName, version, buildNumber, voltraVersion))
56
70
  logger.info('Generated Info.plist')
57
71
  }
@@ -1,17 +1,28 @@
1
1
  import * as fs from 'fs'
2
2
  import * as path from 'path'
3
3
 
4
- import { type DynamicWidgetManifest, logger, validateWidgetEntry } from '@use-voltra/expo-plugin'
4
+ import {
5
+ type DynamicLiveActivityManifest,
6
+ type DynamicWidgetManifest,
7
+ logger,
8
+ validateWidgetEntry,
9
+ } from '@use-voltra/expo-plugin'
5
10
 
6
- import type { IOSWidgetConfig } from '../../types'
11
+ import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig } from '../../types'
7
12
 
8
13
  const MANIFEST_PATH = path.join('.voltra', 'manifest.ios.json')
14
+ const LIVE_ACTIVITIES_MANIFEST_PATH = path.join('.voltra', 'manifest.ios.live-activities.json')
9
15
 
10
16
  export interface GenerateIOSDynamicWidgetsManifestOptions {
11
17
  projectRoot: string
12
18
  widgets?: IOSWidgetConfig[]
13
19
  }
14
20
 
21
+ export interface GenerateIOSDynamicLiveActivitiesManifestOptions {
22
+ projectRoot: string
23
+ liveActivities?: IOSDynamicLiveActivityConfig[]
24
+ }
25
+
15
26
  export function createIOSDynamicWidgetsManifest(
16
27
  projectRoot: string,
17
28
  widgets: IOSWidgetConfig[]
@@ -36,6 +47,22 @@ export function createIOSDynamicWidgetsManifest(
36
47
  }
37
48
  }
38
49
 
50
+ export function createIOSDynamicLiveActivitiesManifest(
51
+ projectRoot: string,
52
+ liveActivities: IOSDynamicLiveActivityConfig[]
53
+ ): DynamicLiveActivityManifest {
54
+ return {
55
+ version: 1,
56
+ platform: 'ios',
57
+ liveActivities: [...liveActivities]
58
+ .sort((left, right) => left.id.localeCompare(right.id))
59
+ .map((liveActivity) => ({
60
+ id: liveActivity.id,
61
+ entry: validateWidgetEntry(liveActivity.entry, liveActivity.id, projectRoot),
62
+ })),
63
+ }
64
+ }
65
+
39
66
  export function generateIOSDynamicWidgetsManifest(options: GenerateIOSDynamicWidgetsManifestOptions): void {
40
67
  const { projectRoot, widgets } = options
41
68
  const manifestPath = path.join(projectRoot, MANIFEST_PATH)
@@ -45,3 +72,16 @@ export function generateIOSDynamicWidgetsManifest(options: GenerateIOSDynamicWid
45
72
  fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
46
73
  logger.info(`Generated ${MANIFEST_PATH}`)
47
74
  }
75
+
76
+ /** Writes the dedicated Dynamic Live Activity manifest on every prebuild, including when empty. */
77
+ export function generateIOSDynamicLiveActivitiesManifest(
78
+ options: GenerateIOSDynamicLiveActivitiesManifestOptions
79
+ ): void {
80
+ const { projectRoot, liveActivities } = options
81
+ const manifestPath = path.join(projectRoot, LIVE_ACTIVITIES_MANIFEST_PATH)
82
+ const manifest = createIOSDynamicLiveActivitiesManifest(projectRoot, liveActivities ?? [])
83
+
84
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true })
85
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
86
+ logger.info(`Generated ${LIVE_ACTIVITIES_MANIFEST_PATH}`)
87
+ }
@@ -0,0 +1,3 @@
1
+ export function escapeForSwiftStringLiteral(value: string): string {
2
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')
3
+ }
@@ -11,16 +11,24 @@ import {
11
11
  type WidgetLabel,
12
12
  } from '@use-voltra/expo-plugin'
13
13
 
14
- import { DEFAULT_WIDGET_FAMILIES, WIDGET_FAMILY_MAP } from '../../constants'
15
- import type { IOSWidgetConfig } from '../../types'
14
+ import { DEFAULT_WIDGET_FAMILIES, WIDGET_FAMILY_MAP, widgetKind } from '../../constants'
15
+ import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig } from '../../types'
16
16
  import { VOLTRA_WIDGET_STRINGS_BASENAME } from '../../utils/fileDiscovery'
17
17
  import { detectClientRenderedWidgets, type DetectedIOSWidget } from '../clientRendered'
18
18
  import { prerenderClientRenderedWidgets } from '../clientRenderedPrerender'
19
+ import {
20
+ generateDynamicLiveActivitiesSwift,
21
+ generateDynamicLiveActivityTypesSwift,
22
+ generateDynamicLiveActivityWidgetInstances,
23
+ } from '../dynamic-live-activity/swift'
24
+
25
+ import { escapeForSwiftStringLiteral } from './swift-utils'
19
26
 
20
27
  export interface GenerateSwiftFilesOptions {
21
28
  targetPath: string
22
29
  projectRoot: string
23
30
  widgets?: IOSWidgetConfig[]
31
+ liveActivities?: IOSDynamicLiveActivityConfig[]
24
32
  }
25
33
 
26
34
  type RenderWidgetToString = (variants: unknown) => string
@@ -37,7 +45,7 @@ type RenderWidgetToString = (variants: unknown) => string
37
45
  * - VoltraWidgetBundle.swift (widget bundle definition)
38
46
  */
39
47
  export async function generateSwiftFiles(options: GenerateSwiftFilesOptions): Promise<void> {
40
- const { targetPath, projectRoot, widgets } = options
48
+ const { targetPath, projectRoot, widgets, liveActivities } = options
41
49
 
42
50
  // Dynamic import keeps the plugin CommonJS-compatible while resolving the current package entry.
43
51
  const serverModuleId = '@use-voltra/ios/server'
@@ -59,7 +67,7 @@ export async function generateSwiftFiles(options: GenerateSwiftFilesOptions): Pr
59
67
  // stringify). Both produce entries in the same map shape so
60
68
  // VoltraWidgetInitialStates.swift can read either via the same lookup at runtime.
61
69
  const serverWidgets = detectedWidgets.filter((w) => !w.clientRendered)
62
- const serverStates = await prerenderWidgetState(serverWidgets, projectRoot, renderWidgetToString)
70
+ const serverStates = await prerenderWidgetState(serverWidgets, projectRoot, renderWidgetToString, 'ios')
63
71
  const clientStates = await prerenderClientRenderedWidgets(detectedWidgets, projectRoot)
64
72
  const prerenderedStates = new Map([...serverStates, ...clientStates])
65
73
 
@@ -76,12 +84,24 @@ export async function generateSwiftFiles(options: GenerateSwiftFilesOptions): Pr
76
84
 
77
85
  // Generate the widget bundle Swift file
78
86
  const widgetBundleContent =
79
- detectedWidgets.length > 0 ? generateWidgetBundleSwift(detectedWidgets) : generateDefaultWidgetBundleSwift()
87
+ detectedWidgets.length > 0 || (liveActivities?.length ?? 0) > 0
88
+ ? generateWidgetBundleSwift(detectedWidgets, liveActivities ?? [])
89
+ : generateDefaultWidgetBundleSwift()
80
90
 
81
91
  const widgetBundlePath = path.join(targetPath, 'VoltraWidgetBundle.swift')
82
92
  fs.writeFileSync(widgetBundlePath, widgetBundleContent)
83
93
 
84
94
  logger.info(`Generated VoltraWidgetBundle.swift with ${widgets?.length ?? 0} home screen widgets`)
95
+
96
+ fs.writeFileSync(
97
+ path.join(targetPath, 'VoltraDynamicLiveActivityTypes.swift'),
98
+ generateDynamicLiveActivityTypesSwift(liveActivities ?? [])
99
+ )
100
+ fs.writeFileSync(
101
+ path.join(targetPath, 'VoltraDynamicLiveActivities.swift'),
102
+ generateDynamicLiveActivitiesSwift(liveActivities ?? [])
103
+ )
104
+ logger.info(`Generated Dynamic Live Activity Swift scaffolding with ${liveActivities?.length ?? 0} definition(s)`)
85
105
  }
86
106
 
87
107
  const GENERATED_INITIAL_STATE_LOCALE_HELPER = dedent`
@@ -155,10 +175,6 @@ const GENERATED_INITIAL_STATE_LOCALE_HELPER = dedent`
155
175
  // https://developer.apple.com/documentation/foundation/localizedstringresource
156
176
  // ============================================================================
157
177
 
158
- function escapeForSwiftStringLiteral(s: string): string {
159
- return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r')
160
- }
161
-
162
178
  function escapeDotStringsValue(s: string): string {
163
179
  return escapeForSwiftStringLiteral(s)
164
180
  }
@@ -285,6 +301,12 @@ function iosWidgetGalleryLabelSwiftExpr(
285
301
  * - Dynamic Widget → `VoltraClientWidgetProvider` + `VoltraClientWidgetContentView`
286
302
  * (the content view internally renders via VoltraHomeWidgetView so the UI layer is
287
303
  * identical to the fallback state path — see VoltraClientWidgetRuntime.swift)
304
+ * - Dynamic Widget with a `serverUpdate` → `VoltraDynamicWidgetServerUpdateProvider`, which wraps
305
+ * the one above and adds the fetch, the commit and the schedule
306
+ *
307
+ * `entry` picks the render engine and `serverUpdate` picks where the data comes from, so the two
308
+ * keys together select one of four providers. Runtime code never asks "is this server-driven?" —
309
+ * the answer is baked in here, once, at generate time.
288
310
  */
289
311
  function widgetUsesAppIntent(widget: DetectedIOSWidget): boolean {
290
312
  return widget.clientRendered && !!widget.appIntent && widget.appIntent.parameters.length > 0
@@ -306,9 +328,13 @@ function generateWidgetStruct(widget: DetectedIOSWidget): string {
306
328
  const displayNameExpr = iosWidgetGalleryLabelSwiftExpr(widget.id, 'displayName', widget.displayName)
307
329
  const descriptionExpr = iosWidgetGalleryLabelSwiftExpr(widget.id, 'description', widget.description)
308
330
 
331
+ const clientProviderName = widget.serverUpdate
332
+ ? 'VoltraDynamicWidgetServerUpdateProvider'
333
+ : 'VoltraClientWidgetProvider'
334
+
309
335
  const providerAndContent = widget.clientRendered
310
336
  ? dedent`
311
- provider: VoltraClientWidgetProvider(
337
+ provider: ${clientProviderName}(
312
338
  widgetId: widgetId,
313
339
  initialState: VoltraWidgetInitialStates.getInitialState(for: widgetId)
314
340
  )
@@ -335,7 +361,7 @@ function generateWidgetStruct(widget: DetectedIOSWidget): string {
335
361
 
336
362
  public var body: some WidgetConfiguration {
337
363
  StaticConfiguration(
338
- kind: "Voltra_Widget_${widget.id}",
364
+ kind: "${widgetKind(widget)}",
339
365
  ${providerAndContent}
340
366
  .configurationDisplayName(${displayNameExpr})
341
367
  .description(${descriptionExpr})
@@ -377,6 +403,18 @@ function generateClientAppIntentWidgetCode(widget: DetectedIOSWidget): string {
377
403
  const initBody = params.map((p) => ` self.${p.name} = ${p.name}`).join('\n')
378
404
  const configuredDict = dictLiteral(params.map((p) => `"${p.name}": configuration.${p.name}`))
379
405
  const defaultDict = dictLiteral(params.map((p) => `"${p.name}": ${swiftDefault(p)}`))
406
+ // A server-driven Dynamic Widget fetches on every timeline request and schedules the next one
407
+ // from its resolved interval; a plain one has nothing to ask again for, so its policy is .never.
408
+ const appIntentTimelineBody = widget.serverUpdate
409
+ ? dedent`
410
+ return await VoltraDynamicWidgetServerUpdateProvider.timeline(
411
+ widgetId: widgetId,
412
+ family: context.family,
413
+ configuration: ${configuredDict}
414
+ )`
415
+ : dedent`
416
+ let entry = await VoltraClientWidgetProvider.loadEntry(widgetId: widgetId, configuration: ${configuredDict})
417
+ return Timeline(entries: [entry], policy: .never)`
380
418
 
381
419
  return dedent`
382
420
  // MARK: - Client-rendered AppIntent widget: ${widget.id}
@@ -408,9 +446,8 @@ function generateClientAppIntentWidgetCode(widget: DetectedIOSWidget): string {
408
446
  await VoltraClientWidgetProvider.loadEntry(widgetId: widgetId, configuration: ${configuredDict})
409
447
  }
410
448
 
411
- func timeline(for configuration: ${intentName}, in _: Context) async -> Timeline<VoltraClientWidgetEntry> {
412
- let entry = await VoltraClientWidgetProvider.loadEntry(widgetId: widgetId, configuration: ${configuredDict})
413
- return Timeline(entries: [entry], policy: .never)
449
+ func timeline(for configuration: ${intentName}, in context: Context) async -> Timeline<VoltraClientWidgetEntry> {
450
+ ${appIntentTimelineBody}
414
451
  }
415
452
  }
416
453
 
@@ -422,7 +459,7 @@ function generateClientAppIntentWidgetCode(widget: DetectedIOSWidget): string {
422
459
 
423
460
  public var body: some WidgetConfiguration {
424
461
  AppIntentConfiguration(
425
- kind: "Voltra_Widget_${widget.id}",
462
+ kind: "${widgetKind(widget)}",
426
463
  intent: ${intentName}.self,
427
464
  provider: ${providerName}()
428
465
  ) { entry in
@@ -443,7 +480,10 @@ function generateClientAppIntentWidgetCode(widget: DetectedIOSWidget): string {
443
480
  /**
444
481
  * Generates the VoltraWidgetBundle.swift file content with configured widgets
445
482
  */
446
- function generateWidgetBundleSwift(widgets: DetectedIOSWidget[]): string {
483
+ function generateWidgetBundleSwift(
484
+ widgets: DetectedIOSWidget[],
485
+ liveActivities: IOSDynamicLiveActivityConfig[] = []
486
+ ): string {
447
487
  // Generate widget structs
448
488
  const widgetStructs = widgets.map((w) => generateWidgetStruct(w)).join('\n\n')
449
489
 
@@ -457,7 +497,12 @@ function generateWidgetBundleSwift(widgets: DetectedIOSWidget[]): string {
457
497
  .map((w) => `VoltraWidget_${w.id}()`)
458
498
  .join('\n ')}\n }`
459
499
  : ''
460
- const widgetInstances = [plainInstances, appIntentInstances].filter(Boolean).join('\n ')
500
+ const dynamicLiveActivityInstances = generateDynamicLiveActivityWidgetInstances(liveActivities)
501
+ const widgetInstances = [plainInstances, appIntentInstances, dynamicLiveActivityInstances]
502
+ .filter(Boolean)
503
+ .join('\n ')
504
+ const widgetSectionTitle =
505
+ liveActivities.length > 0 ? 'Home Screen Widgets and Dynamic Live Activities' : 'Home Screen Widgets'
461
506
 
462
507
  const needsFoundation = widgets.some(widgetUsesGalleryLocalization)
463
508
  const foundationImport = needsFoundation ? 'import Foundation\n' : ''
@@ -473,7 +518,7 @@ function generateWidgetBundleSwift(widgets: DetectedIOSWidget[]): string {
473
518
 
474
519
  ${foundationImport}${appIntentsImport}import SwiftUI
475
520
  import WidgetKit
476
- import VoltraWidget
521
+ import VoltraRuntime
477
522
 
478
523
  @main
479
524
  struct VoltraWidgetBundle: WidgetBundle {
@@ -481,7 +526,7 @@ function generateWidgetBundleSwift(widgets: DetectedIOSWidget[]): string {
481
526
  // Live Activity (with Watch/CarPlay support)
482
527
  VoltraWidget()
483
528
 
484
- // Home Screen Widgets
529
+ // ${widgetSectionTitle}
485
530
  ${widgetInstances}
486
531
  }
487
532
  }
@@ -493,8 +538,8 @@ function generateWidgetBundleSwift(widgets: DetectedIOSWidget[]): string {
493
538
  }
494
539
 
495
540
  /**
496
- * Generates the VoltraWidgetBundle.swift file content when no widgets are configured
497
- * (only Live Activities)
541
+ * Generates the VoltraWidgetBundle.swift file content when no Home Screen widgets or Dynamic
542
+ * Live Activities are configured. Keep this legacy output stable for existing projects.
498
543
  */
499
544
  function generateDefaultWidgetBundleSwift(): string {
500
545
  return dedent`
@@ -507,7 +552,7 @@ function generateDefaultWidgetBundleSwift(): string {
507
552
 
508
553
  import SwiftUI
509
554
  import WidgetKit
510
- import VoltraWidget // Import Voltra widgets
555
+ import VoltraRuntime // Import Voltra widgets
511
556
 
512
557
  @main
513
558
  struct VoltraWidgetBundle: WidgetBundle {
@@ -624,4 +669,6 @@ function getSwiftRawStringDelimiter(str: string): string {
624
669
  export const __test__ = {
625
670
  generateInitialStatesSwift,
626
671
  generateWidgetBundleSwift,
672
+ generateDynamicLiveActivityTypesSwift,
673
+ generateDynamicLiveActivitiesSwift,
627
674
  }