@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,27 +1,55 @@
1
1
  import { XcodeProject } from '@expo/config-plugins'
2
2
 
3
- export interface MainAppTargetSettings {
4
- deploymentTarget: string
3
+ /** Code-signing settings read from one of the main app target's build configurations. */
4
+ export interface MainAppSigningSettings {
5
5
  codeSignStyle?: string
6
6
  developmentTeam?: string
7
7
  provisioningProfileSpecifier?: string
8
8
  }
9
9
 
10
+ export interface MainAppTargetSettings {
11
+ /**
12
+ * Signing settings keyed by build-configuration name (e.g. `Debug`, `Release`). Release
13
+ * provisioning often differs from Debug, so the widget must mirror the main app per
14
+ * configuration rather than copying one configuration's signing into both.
15
+ */
16
+ byConfigurationName: Record<string, MainAppSigningSettings>
17
+ /** The first configuration's settings, used for widget configurations with no name match. */
18
+ fallback: MainAppSigningSettings
19
+ }
20
+
21
+ /** Strips surrounding quotes from a raw build-setting value. */
22
+ function unquote(value: unknown): string | undefined {
23
+ if (typeof value !== 'string') {
24
+ return undefined
25
+ }
26
+ return value.replace(/^"|"$/g, '')
27
+ }
28
+
29
+ /** Reads the signing-related build settings from a single XCBuildConfiguration. */
30
+ function readSigningSettings(buildSettings: Record<string, unknown>): MainAppSigningSettings {
31
+ return {
32
+ codeSignStyle: unquote(buildSettings.CODE_SIGN_STYLE),
33
+ developmentTeam: unquote(buildSettings.DEVELOPMENT_TEAM),
34
+ provisioningProfileSpecifier: unquote(buildSettings.PROVISIONING_PROFILE_SPECIFIER),
35
+ }
36
+ }
37
+
10
38
  /**
11
- * Reads build settings from the main app target to synchronize code signing with the widget extension.
39
+ * Reads code-signing build settings from every build configuration of the main app target, so the
40
+ * widget extension can synchronize signing per configuration (Debug→Debug, Release→Release).
12
41
  *
13
42
  * @param xcodeProject The Xcode project instance
14
- * @returns The main app target's build settings, or null if not found
43
+ * @returns Signing settings keyed by configuration name plus a first-configuration fallback, or
44
+ * null when the main app target or its configurations cannot be resolved
15
45
  */
16
46
  export function getMainAppTargetSettings(xcodeProject: XcodeProject): MainAppTargetSettings | null {
17
47
  try {
18
48
  const mainAppTarget = xcodeProject.getFirstTarget()?.firstTarget
19
-
20
49
  if (!mainAppTarget) {
21
50
  return null
22
51
  }
23
52
 
24
- // Get the build configuration list for the main app target
25
53
  const buildConfigurationListId = mainAppTarget.buildConfigurationList
26
54
  if (!buildConfigurationListId) {
27
55
  return null
@@ -32,62 +60,39 @@ export function getMainAppTargetSettings(xcodeProject: XcodeProject): MainAppTar
32
60
 
33
61
  const buildConfigurationList =
34
62
  xcodeProject.hash.project.objects['XCConfigurationList']?.[buildConfigurationListUuid]
35
- if (!buildConfigurationList || !buildConfigurationList.buildConfigurations) {
36
- return null
37
- }
38
-
39
- // Get the first build configuration (usually Debug) to read settings
40
- const firstConfigRef = buildConfigurationList.buildConfigurations[0]
41
- if (!firstConfigRef) {
42
- return null
43
- }
44
-
45
- // Extract UUID from config reference (it might include a comment)
46
- const firstConfigUuid =
47
- typeof firstConfigRef === 'string' ? firstConfigRef.split(' ')[0] : firstConfigRef.value?.split(' ')[0]
48
- if (!firstConfigUuid) {
63
+ if (!buildConfigurationList?.buildConfigurations) {
49
64
  return null
50
65
  }
51
66
 
52
- const buildConfiguration = xcodeProject.hash.project.objects['XCBuildConfiguration']?.[firstConfigUuid]
53
- if (!buildConfiguration || !buildConfiguration.buildSettings) {
54
- return null
67
+ const buildConfigurationSection = xcodeProject.hash.project.objects['XCBuildConfiguration'] ?? {}
68
+ const byConfigurationName: Record<string, MainAppSigningSettings> = {}
69
+ let fallback: MainAppSigningSettings | null = null
70
+
71
+ for (const configRef of buildConfigurationList.buildConfigurations) {
72
+ // Extract UUID from the config reference (it might include a comment)
73
+ const configUuid = typeof configRef === 'string' ? configRef.split(' ')[0] : configRef.value?.split(' ')[0]
74
+ if (!configUuid) {
75
+ continue
76
+ }
77
+
78
+ const buildConfiguration = buildConfigurationSection[configUuid]
79
+ if (!buildConfiguration?.buildSettings) {
80
+ continue
81
+ }
82
+
83
+ const settings = readSigningSettings(buildConfiguration.buildSettings)
84
+ const configName = unquote(buildConfiguration.name)
85
+ if (configName && !(configName in byConfigurationName)) {
86
+ byConfigurationName[configName] = settings
87
+ }
88
+ fallback = fallback ?? settings
55
89
  }
56
90
 
57
- const buildSettings = buildConfiguration.buildSettings
58
-
59
- // Extract deployment target (remove quotes if present)
60
- const deploymentTarget =
61
- buildSettings.IPHONEOS_DEPLOYMENT_TARGET?.replace(/^"|"$/g, '') ||
62
- buildSettings['IPHONEOS_DEPLOYMENT_TARGET']?.replace(/^"|"$/g, '') ||
63
- null
64
-
65
- // Extract code signing settings
66
- const codeSignStyle =
67
- buildSettings.CODE_SIGN_STYLE?.replace(/^"|"$/g, '') ||
68
- buildSettings['CODE_SIGN_STYLE']?.replace(/^"|"$/g, '') ||
69
- null
70
-
71
- const developmentTeam =
72
- buildSettings.DEVELOPMENT_TEAM?.replace(/^"|"$/g, '') ||
73
- buildSettings['DEVELOPMENT_TEAM']?.replace(/^"|"$/g, '') ||
74
- null
75
-
76
- const provisioningProfileSpecifier =
77
- buildSettings.PROVISIONING_PROFILE_SPECIFIER?.replace(/^"|"$/g, '') ||
78
- buildSettings['PROVISIONING_PROFILE_SPECIFIER']?.replace(/^"|"$/g, '') ||
79
- null
80
-
81
- if (!deploymentTarget) {
91
+ if (!fallback) {
82
92
  return null
83
93
  }
84
94
 
85
- return {
86
- deploymentTarget,
87
- codeSignStyle: codeSignStyle || undefined,
88
- developmentTeam: developmentTeam || undefined,
89
- provisioningProfileSpecifier: provisioningProfileSpecifier || undefined,
90
- }
95
+ return { byConfigurationName, fallback }
91
96
  } catch {
92
97
  // If we can't read the settings, return null and use fallback values
93
98
  return null
@@ -42,9 +42,10 @@ function findBuildFileForFileRef(xcodeProject: XcodeProject, fileRef: string): {
42
42
  }
43
43
 
44
44
  /**
45
- * Adds the product file (.appex) for the widget extension.
45
+ * Adds the product file (.appex) for the widget extension. Internal create-only fallback for
46
+ * {@link ensureProductFile}; not part of the public pipeline.
46
47
  */
47
- export function addProductFile(xcodeProject: XcodeProject, options: AddProductFileOptions) {
48
+ function addProductFile(xcodeProject: XcodeProject, options: AddProductFileOptions) {
48
49
  const { targetName, groupName } = options
49
50
 
50
51
  const productFileOptions = {
@@ -6,18 +6,9 @@ import { IOS } from '../../constants'
6
6
  // Types
7
7
  // ============================================================================
8
8
 
9
- export interface ConfigureTargetOptions {
9
+ export interface CreateTargetSkeletonOptions {
10
10
  targetName: string
11
11
  targetUuid: string
12
- productFile: { fileRef: string }
13
- xCConfigurationList: { uuid: string }
14
- }
15
-
16
- interface AddNativeTargetOptions {
17
- targetName: string
18
- targetUuid: string
19
- productFile: { fileRef: string }
20
- xCConfigurationList: { uuid: string }
21
12
  }
22
13
 
23
14
  // ============================================================================
@@ -25,19 +16,39 @@ interface AddNativeTargetOptions {
25
16
  // ============================================================================
26
17
 
27
18
  /**
28
- * Configures the widget extension target in the Xcode project.
19
+ * Creates the minimal widget-extension native target and registers it with the project.
20
+ *
21
+ * A target cannot be "ensured" into existence, so this is the one create-only step in
22
+ * {@link applyXcodeChanges}: it adds an empty `PBXNativeTarget` (no product reference, configuration
23
+ * list or build phases yet) to the `PBXNativeTarget` and `PBXProject` sections. The shared ensure
24
+ * pipeline then fills in the configuration list, product file, group, build phases, attributes and
25
+ * dependency — the same code that reconciles an already-existing target.
29
26
  *
30
- * This:
31
- * - Adds the target to PBXNativeTarget section
32
- * - Adds the target to PBXProject section
33
- * - Adds a dependency from the main app to the widget extension
27
+ * @returns The freshly created native-target object (the value stored in the `PBXNativeTarget`
28
+ * section under `targetUuid`).
34
29
  */
35
- export function configureTarget(xcodeProject: XcodeProject, options: ConfigureTargetOptions) {
36
- const target = addToPbxNativeTargetSection(xcodeProject, options)
37
- addToPbxProjectSection(xcodeProject, target)
38
- addTargetDependency(xcodeProject, target)
30
+ export function createTargetSkeleton(xcodeProject: XcodeProject, options: CreateTargetSkeletonOptions) {
31
+ const { targetName, targetUuid } = options
32
+
33
+ const target = {
34
+ uuid: targetUuid,
35
+ pbxNativeTarget: {
36
+ isa: 'PBXNativeTarget',
37
+ name: targetName,
38
+ productName: targetName,
39
+ productType: `"com.apple.product-type.app-extension"`,
40
+ buildConfigurationList: '',
41
+ productReference: '',
42
+ buildPhases: [],
43
+ buildRules: [],
44
+ dependencies: [],
45
+ },
46
+ }
39
47
 
40
- return target
48
+ xcodeProject.addToPbxNativeTargetSection(target)
49
+ xcodeProject.addToPbxProjectSection(target)
50
+
51
+ return xcodeProject.pbxNativeTargetSection()[targetUuid]
41
52
  }
42
53
 
43
54
  /**
@@ -83,66 +94,3 @@ export function ensureTargetDependency(xcodeProject: XcodeProject, targetUuid: s
83
94
  xcodeProject.addTargetDependency(mainTargetUuid, [targetUuid])
84
95
  }
85
96
  }
86
-
87
- // ============================================================================
88
- // Helper Functions
89
- // ============================================================================
90
-
91
- /**
92
- * Adds the widget extension target to the PBXNativeTarget section.
93
- */
94
- function addToPbxNativeTargetSection(xcodeProject: XcodeProject, options: AddNativeTargetOptions) {
95
- const { targetName, targetUuid, productFile, xCConfigurationList } = options
96
-
97
- const target = {
98
- uuid: targetUuid,
99
- pbxNativeTarget: {
100
- isa: 'PBXNativeTarget',
101
- name: targetName,
102
- productName: targetName,
103
- productReference: productFile.fileRef,
104
- productType: `"com.apple.product-type.app-extension"`,
105
- buildConfigurationList: xCConfigurationList.uuid,
106
- buildPhases: [],
107
- buildRules: [],
108
- dependencies: [],
109
- },
110
- }
111
-
112
- xcodeProject.addToPbxNativeTargetSection(target)
113
-
114
- return target
115
- }
116
-
117
- /**
118
- * Adds the target to the PBXProject section.
119
- */
120
- function addToPbxProjectSection(xcodeProject: XcodeProject, target: { uuid: string }): void {
121
- xcodeProject.addToPbxProjectSection(target)
122
-
123
- // Add target attributes to project section
124
- const projectSection = xcodeProject.pbxProjectSection()
125
- const firstProject = xcodeProject.getFirstProject()
126
-
127
- if (!projectSection[firstProject.uuid].attributes.TargetAttributes) {
128
- projectSection[firstProject.uuid].attributes.TargetAttributes = {}
129
- }
130
-
131
- projectSection[firstProject.uuid].attributes.TargetAttributes[target.uuid] = {
132
- LastSwiftMigration: IOS.LAST_SWIFT_MIGRATION,
133
- }
134
- }
135
-
136
- /**
137
- * Adds a target dependency so the main app depends on the widget extension.
138
- */
139
- function addTargetDependency(xcodeProject: XcodeProject, target: { uuid: string }): void {
140
- if (!xcodeProject.hash.project.objects['PBXTargetDependency']) {
141
- xcodeProject.hash.project.objects['PBXTargetDependency'] = {}
142
- }
143
- if (!xcodeProject.hash.project.objects['PBXContainerItemProxy']) {
144
- xcodeProject.hash.project.objects['PBXContainerItemProxy'] = {}
145
- }
146
-
147
- xcodeProject.addTargetDependency(xcodeProject.getFirstTarget().uuid, [target.uuid])
148
- }
@@ -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,23 +25,58 @@ 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
- "VoltraConfig.swift",
31
- "VoltraElement.swift",
32
29
  "VoltraEvent.swift",
33
30
  "VoltraEventBus.swift",
34
31
  "VoltraImageStore.swift",
35
32
  "VoltraInteractionIntent.swift",
36
33
  "VoltraLiveActivityPayload.swift",
37
- "VoltraNode.swift",
38
34
  "VoltraPersistentEventQueue.swift",
39
35
  ],
40
36
  sources: [
37
+ "DynamicWidgetPropsCodec.swift",
38
+ "DynamicWidgetPropsStorage.swift",
39
+ "DynamicWidgetPropsStore.swift",
40
+ "DynamicWidgetRenderCoordinator.swift",
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",
41
68
  "JSONValue.swift",
69
+ "VoltraConfig.swift",
70
+ "VoltraConstants.swift",
71
+ "VoltraWidgetKind.swift",
42
72
  "VoltraPayloadMigrator.swift",
43
73
  "VoltraRegion.swift",
44
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",
45
80
  ]
46
81
  ),
47
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