@use-voltra/ios-client 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/Voltra.podspec +1 -0
  2. package/build/commonjs/events.js +23 -0
  3. package/build/commonjs/events.js.map +1 -1
  4. package/build/commonjs/index.js +59 -0
  5. package/build/commonjs/index.js.map +1 -1
  6. package/build/commonjs/live-activity/api.js +3 -1
  7. package/build/commonjs/live-activity/api.js.map +1 -1
  8. package/build/commonjs/live-activity/dynamic-api.js +173 -0
  9. package/build/commonjs/live-activity/dynamic-api.js.map +1 -0
  10. package/build/commonjs/native/NativeVoltra.js.map +1 -1
  11. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js +47 -0
  12. package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  13. package/build/commonjs/utils/enableWidgetHotReload.js +8 -11
  14. package/build/commonjs/utils/enableWidgetHotReload.js.map +1 -1
  15. package/build/commonjs/utils/useUpdateOnHMR.js +3 -7
  16. package/build/commonjs/utils/useUpdateOnHMR.js.map +1 -1
  17. package/build/commonjs/widgets/server-credentials.js +9 -0
  18. package/build/commonjs/widgets/server-credentials.js.map +1 -1
  19. package/build/commonjs/widgets/server-update.js +78 -0
  20. package/build/commonjs/widgets/server-update.js.map +1 -0
  21. package/build/module/events.js +23 -0
  22. package/build/module/events.js.map +1 -1
  23. package/build/module/index.js +3 -0
  24. package/build/module/index.js.map +1 -1
  25. package/build/module/live-activity/api.js +2 -2
  26. package/build/module/live-activity/api.js.map +1 -1
  27. package/build/module/live-activity/dynamic-api.js +167 -0
  28. package/build/module/live-activity/dynamic-api.js.map +1 -0
  29. package/build/module/native/NativeVoltra.js.map +1 -1
  30. package/build/module/utils/enableDynamicLiveActivityHotReload.js +43 -0
  31. package/build/module/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
  32. package/build/module/utils/enableWidgetHotReload.js +9 -11
  33. package/build/module/utils/enableWidgetHotReload.js.map +1 -1
  34. package/build/module/utils/useUpdateOnHMR.js +3 -7
  35. package/build/module/utils/useUpdateOnHMR.js.map +1 -1
  36. package/build/module/widgets/server-credentials.js +9 -0
  37. package/build/module/widgets/server-credentials.js.map +1 -1
  38. package/build/module/widgets/server-update.js +72 -0
  39. package/build/module/widgets/server-update.js.map +1 -0
  40. package/build/typescript/commonjs/events.d.ts +7 -0
  41. package/build/typescript/commonjs/events.d.ts.map +1 -1
  42. package/build/typescript/commonjs/index.d.ts +3 -0
  43. package/build/typescript/commonjs/index.d.ts.map +1 -1
  44. package/build/typescript/commonjs/live-activity/api.d.ts +7 -0
  45. package/build/typescript/commonjs/live-activity/api.d.ts.map +1 -1
  46. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts +54 -0
  47. package/build/typescript/commonjs/live-activity/dynamic-api.d.ts.map +1 -0
  48. package/build/typescript/commonjs/native/NativeVoltra.d.ts +20 -0
  49. package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
  50. package/build/typescript/commonjs/types.d.ts +1 -1
  51. package/build/typescript/commonjs/types.d.ts.map +1 -1
  52. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  53. package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  54. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts +5 -7
  55. package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts.map +1 -1
  56. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts +0 -3
  57. package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts.map +1 -1
  58. package/build/typescript/commonjs/widgets/server-credentials.d.ts +8 -0
  59. package/build/typescript/commonjs/widgets/server-credentials.d.ts.map +1 -1
  60. package/build/typescript/commonjs/widgets/server-update.d.ts +64 -0
  61. package/build/typescript/commonjs/widgets/server-update.d.ts.map +1 -0
  62. package/build/typescript/module/events.d.ts +7 -0
  63. package/build/typescript/module/events.d.ts.map +1 -1
  64. package/build/typescript/module/index.d.ts +3 -0
  65. package/build/typescript/module/index.d.ts.map +1 -1
  66. package/build/typescript/module/live-activity/api.d.ts +7 -0
  67. package/build/typescript/module/live-activity/api.d.ts.map +1 -1
  68. package/build/typescript/module/live-activity/dynamic-api.d.ts +54 -0
  69. package/build/typescript/module/live-activity/dynamic-api.d.ts.map +1 -0
  70. package/build/typescript/module/native/NativeVoltra.d.ts +20 -0
  71. package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
  72. package/build/typescript/module/types.d.ts +1 -1
  73. package/build/typescript/module/types.d.ts.map +1 -1
  74. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
  75. package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
  76. package/build/typescript/module/utils/enableWidgetHotReload.d.ts +5 -7
  77. package/build/typescript/module/utils/enableWidgetHotReload.d.ts.map +1 -1
  78. package/build/typescript/module/utils/useUpdateOnHMR.d.ts +0 -3
  79. package/build/typescript/module/utils/useUpdateOnHMR.d.ts.map +1 -1
  80. package/build/typescript/module/widgets/server-credentials.d.ts +8 -0
  81. package/build/typescript/module/widgets/server-credentials.d.ts.map +1 -1
  82. package/build/typescript/module/widgets/server-update.d.ts +64 -0
  83. package/build/typescript/module/widgets/server-update.d.ts.map +1 -0
  84. package/expo-plugin/build/cjs/constants.js +20 -1
  85. package/expo-plugin/build/cjs/constants.js.map +1 -1
  86. package/expo-plugin/build/cjs/index.js +6 -0
  87. package/expo-plugin/build/cjs/index.js.map +1 -1
  88. package/expo-plugin/build/cjs/ios/index.js +1 -0
  89. package/expo-plugin/build/cjs/ios/index.js.map +1 -1
  90. package/expo-plugin/build/cjs/ios/infoPlist.js +28 -5
  91. package/expo-plugin/build/cjs/ios/infoPlist.js.map +1 -1
  92. package/expo-plugin/build/cjs/ios/serverUpdate.js +25 -0
  93. package/expo-plugin/build/cjs/ios/serverUpdate.js.map +1 -0
  94. package/expo-plugin/build/cjs/ios-widget/clientRendered.js +4 -4
  95. package/expo-plugin/build/cjs/ios-widget/clientRendered.js.map +1 -1
  96. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js +5 -5
  97. package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js.map +1 -1
  98. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js +130 -0
  99. package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  100. package/expo-plugin/build/cjs/ios-widget/files/index.js +9 -2
  101. package/expo-plugin/build/cjs/ios-widget/files/index.js.map +1 -1
  102. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js +6 -3
  103. package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js.map +1 -1
  104. package/expo-plugin/build/cjs/ios-widget/files/manifest.js +24 -0
  105. package/expo-plugin/build/cjs/ios-widget/files/manifest.js.map +1 -1
  106. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js +7 -0
  107. package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js.map +1 -0
  108. package/expo-plugin/build/cjs/ios-widget/files/swift.js +56 -26
  109. package/expo-plugin/build/cjs/ios-widget/files/swift.js.map +1 -1
  110. package/expo-plugin/build/cjs/ios-widget/index.js +9 -3
  111. package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
  112. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js +34 -22
  113. package/expo-plugin/build/cjs/ios-widget/widgetPlist.js.map +1 -1
  114. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +51 -11
  115. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
  116. package/expo-plugin/build/cjs/ios-widget/xcode/index.js +21 -5
  117. package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
  118. package/expo-plugin/build/cjs/utils/urlScheme.js +36 -14
  119. package/expo-plugin/build/cjs/utils/urlScheme.js.map +1 -1
  120. package/expo-plugin/build/cjs/validation.js +54 -0
  121. package/expo-plugin/build/cjs/validation.js.map +1 -1
  122. package/expo-plugin/build/esm/constants.js +17 -0
  123. package/expo-plugin/build/esm/constants.js.map +1 -1
  124. package/expo-plugin/build/esm/index.js +6 -0
  125. package/expo-plugin/build/esm/index.js.map +1 -1
  126. package/expo-plugin/build/esm/ios/index.js +1 -0
  127. package/expo-plugin/build/esm/ios/index.js.map +1 -1
  128. package/expo-plugin/build/esm/ios/infoPlist.js +28 -5
  129. package/expo-plugin/build/esm/ios/infoPlist.js.map +1 -1
  130. package/expo-plugin/build/esm/ios/serverUpdate.js +21 -0
  131. package/expo-plugin/build/esm/ios/serverUpdate.js.map +1 -0
  132. package/expo-plugin/build/esm/ios-widget/clientRendered.js +5 -5
  133. package/expo-plugin/build/esm/ios-widget/clientRendered.js.map +1 -1
  134. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js +6 -6
  135. package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js.map +1 -1
  136. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js +122 -0
  137. package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js.map +1 -0
  138. package/expo-plugin/build/esm/ios-widget/files/index.js +10 -3
  139. package/expo-plugin/build/esm/ios-widget/files/index.js.map +1 -1
  140. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js +6 -3
  141. package/expo-plugin/build/esm/ios-widget/files/infoPlist.js.map +1 -1
  142. package/expo-plugin/build/esm/ios-widget/files/manifest.js +23 -1
  143. package/expo-plugin/build/esm/ios-widget/files/manifest.js.map +1 -1
  144. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js +4 -0
  145. package/expo-plugin/build/esm/ios-widget/files/swift-utils.js.map +1 -0
  146. package/expo-plugin/build/esm/ios-widget/files/swift.js +50 -20
  147. package/expo-plugin/build/esm/ios-widget/files/swift.js.map +1 -1
  148. package/expo-plugin/build/esm/ios-widget/index.js +9 -3
  149. package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
  150. package/expo-plugin/build/esm/ios-widget/widgetPlist.js +34 -22
  151. package/expo-plugin/build/esm/ios-widget/widgetPlist.js.map +1 -1
  152. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +51 -11
  153. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
  154. package/expo-plugin/build/esm/ios-widget/xcode/index.js +21 -5
  155. package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
  156. package/expo-plugin/build/esm/utils/urlScheme.js +34 -14
  157. package/expo-plugin/build/esm/utils/urlScheme.js.map +1 -1
  158. package/expo-plugin/build/esm/validation.js +54 -1
  159. package/expo-plugin/build/esm/validation.js.map +1 -1
  160. package/expo-plugin/build/types/constants.d.ts +10 -1
  161. package/expo-plugin/build/types/constants.d.ts.map +1 -1
  162. package/expo-plugin/build/types/index.d.ts +1 -1
  163. package/expo-plugin/build/types/index.d.ts.map +1 -1
  164. package/expo-plugin/build/types/ios/index.d.ts +2 -0
  165. package/expo-plugin/build/types/ios/index.d.ts.map +1 -1
  166. package/expo-plugin/build/types/ios/infoPlist.d.ts +2 -0
  167. package/expo-plugin/build/types/ios/infoPlist.d.ts.map +1 -1
  168. package/expo-plugin/build/types/ios/serverUpdate.d.ts +11 -0
  169. package/expo-plugin/build/types/ios/serverUpdate.d.ts.map +1 -0
  170. package/expo-plugin/build/types/ios-widget/clientRendered.d.ts.map +1 -1
  171. package/expo-plugin/build/types/ios-widget/clientRenderedPrerender.d.ts.map +1 -1
  172. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts +7 -0
  173. package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts.map +1 -0
  174. package/expo-plugin/build/types/ios-widget/files/index.d.ts +3 -1
  175. package/expo-plugin/build/types/ios-widget/files/index.d.ts.map +1 -1
  176. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts +2 -1
  177. package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts.map +1 -1
  178. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts +9 -2
  179. package/expo-plugin/build/types/ios-widget/files/manifest.d.ts.map +1 -1
  180. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts +2 -0
  181. package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts.map +1 -0
  182. package/expo-plugin/build/types/ios-widget/files/swift.d.ts +6 -2
  183. package/expo-plugin/build/types/ios-widget/files/swift.d.ts.map +1 -1
  184. package/expo-plugin/build/types/ios-widget/index.d.ts +3 -1
  185. package/expo-plugin/build/types/ios-widget/index.d.ts.map +1 -1
  186. package/expo-plugin/build/types/ios-widget/widgetPlist.d.ts.map +1 -1
  187. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +3 -1
  188. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
  189. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +2 -1
  190. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
  191. package/expo-plugin/build/types/types.d.ts +23 -4
  192. package/expo-plugin/build/types/types.d.ts.map +1 -1
  193. package/expo-plugin/build/types/utils/urlScheme.d.ts +16 -0
  194. package/expo-plugin/build/types/utils/urlScheme.d.ts.map +1 -1
  195. package/expo-plugin/build/types/validation.d.ts +2 -1
  196. package/expo-plugin/build/types/validation.d.ts.map +1 -1
  197. package/expo-plugin/src/constants.ts +24 -1
  198. package/expo-plugin/src/index.ts +9 -0
  199. package/expo-plugin/src/ios/index.ts +3 -0
  200. package/expo-plugin/src/ios/infoPlist.ts +33 -5
  201. package/expo-plugin/src/ios/serverUpdate.ts +29 -0
  202. package/expo-plugin/src/ios-widget/clientRendered.ts +9 -5
  203. package/expo-plugin/src/ios-widget/clientRenderedPrerender.ts +11 -6
  204. package/expo-plugin/src/ios-widget/dynamic-live-activity/swift.ts +138 -0
  205. package/expo-plugin/src/ios-widget/files/index.ts +15 -4
  206. package/expo-plugin/src/ios-widget/files/infoPlist.ts +17 -3
  207. package/expo-plugin/src/ios-widget/files/manifest.ts +42 -2
  208. package/expo-plugin/src/ios-widget/files/swift-utils.ts +3 -0
  209. package/expo-plugin/src/ios-widget/files/swift.ts +69 -22
  210. package/expo-plugin/src/ios-widget/index.ts +13 -3
  211. package/expo-plugin/src/ios-widget/widgetPlist.ts +37 -23
  212. package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +34 -28
  213. package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +59 -12
  214. package/expo-plugin/src/ios-widget/xcode/index.ts +23 -6
  215. package/expo-plugin/src/types.ts +28 -4
  216. package/expo-plugin/src/utils/urlScheme.ts +47 -17
  217. package/expo-plugin/src/validation.ts +76 -1
  218. package/ios/Package.swift +32 -3
  219. package/ios/VoltraWidget.podspec +1 -0
  220. package/ios/app/NativeVoltra.mm +107 -7
  221. package/ios/app/VoltraLiveActivityManager.swift +30 -0
  222. package/ios/app/VoltraLiveActivityService.swift +77 -5
  223. package/ios/app/VoltraModule.swift +99 -1
  224. package/ios/app/VoltraModuleImpl.swift +100 -2
  225. package/ios/app/VoltraViewComponentView.mm +3 -3
  226. package/ios/app/VoltraWidgetService.swift +147 -12
  227. package/ios/app/dynamic-live-activity/VoltraDynamicLiveActivityService.swift +97 -0
  228. package/ios/app/dynamic-live-activity/VoltraLiveActivityChronology.swift +68 -0
  229. package/ios/shared/DynamicWidgetPropsStore.swift +73 -6
  230. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift +34 -0
  231. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift +55 -0
  232. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift +146 -0
  233. package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift +193 -0
  234. package/ios/shared/ServerWidgetContentResolver.swift +60 -0
  235. package/ios/shared/ServerWidgetResponseStore.swift +25 -0
  236. package/ios/shared/ShortNames.swift +6 -0
  237. package/ios/shared/VoltraConfig.swift +41 -0
  238. package/ios/shared/VoltraConstants.swift +15 -0
  239. package/ios/shared/VoltraElement.swift +127 -43
  240. package/ios/shared/VoltraEventBus.swift +52 -7
  241. package/ios/shared/VoltraJSRenderer.swift +90 -22
  242. package/ios/shared/VoltraNode.swift +1 -109
  243. package/ios/shared/VoltraRefreshIntent.swift +1 -1
  244. package/ios/shared/VoltraWidgetKind.swift +41 -0
  245. package/ios/shared/VoltraWidgetServerFetcher.swift +52 -107
  246. package/ios/shared/WidgetServer/VoltraWidgetAppearance.swift +17 -0
  247. package/ios/shared/WidgetServer/VoltraWidgetServer.swift +138 -0
  248. package/ios/shared/WidgetServer/WidgetCanonicalConfiguration.swift +77 -0
  249. package/ios/shared/WidgetServer/WidgetScope.swift +55 -0
  250. package/ios/shared/WidgetServer/WidgetServerEtagStore.swift +55 -0
  251. package/ios/shared/WidgetServer/WidgetServerFetcher.swift +179 -0
  252. package/ios/shared/WidgetServer/WidgetServerRequestBuilder.swift +114 -0
  253. package/ios/shared/WidgetServer/WidgetServerSettingsCodec.swift +47 -0
  254. package/ios/shared/WidgetServer/WidgetServerSettingsResolver.swift +129 -0
  255. package/ios/shared/WidgetServer/WidgetServerSettingsStore.swift +170 -0
  256. package/ios/shared/WidgetServer/WidgetServerSettingsValidator.swift +73 -0
  257. package/ios/shared/WidgetServer/WidgetServerUpdateSettings.swift +123 -0
  258. package/ios/shared/WidgetServer/WidgetServerUpdateSettingsJson.swift +115 -0
  259. package/ios/shared/dynamic-live-activity/DynamicLiveActivityPropsCodec.swift +13 -0
  260. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityBundleSource.swift +156 -0
  261. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityObserver.swift +97 -0
  262. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityOperations.swift +105 -0
  263. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift +55 -0
  264. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRegistry.swift +143 -0
  265. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift +158 -0
  266. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureReporter.swift +120 -0
  267. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderer.swift +370 -0
  268. package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift +87 -0
  269. package/ios/shared/dynamic-live-activity/VoltraLiveActivityOrder.swift +19 -0
  270. package/ios/target/VoltraClientWidgetRuntime.swift +44 -6
  271. package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
  272. package/ios/target/VoltraHomeWidget.swift +85 -90
  273. package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
  274. package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
  275. package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +2 -2
  276. package/ios/ui/Views/VoltraChart.swift +108 -0
  277. package/package.json +4 -4
  278. package/src/events.ts +34 -0
  279. package/src/index.ts +22 -0
  280. package/src/live-activity/api.ts +2 -2
  281. package/src/live-activity/dynamic-api.ts +240 -0
  282. package/src/native/NativeVoltra.ts +21 -0
  283. package/src/types.ts +4 -0
  284. package/src/utils/enableDynamicLiveActivityHotReload.ts +50 -0
  285. package/src/utils/enableWidgetHotReload.ts +9 -16
  286. package/src/utils/useUpdateOnHMR.ts +3 -12
  287. package/src/widgets/server-credentials.ts +8 -0
  288. package/src/widgets/server-update.ts +84 -0
  289. /package/ios/shared/{VoltraKeychainHelper.swift → WidgetServer/VoltraKeychainHelper.swift} +0 -0
@@ -0,0 +1,55 @@
1
+ import Foundation
2
+
3
+ /// Checks the portion of a Dynamic Live Activity payload ActivityKit stores.
4
+ /// This is intentionally uncompressed: ActivityKit's 4 KB limit applies to its
5
+ /// encoded attributes and content state, not Voltra's legacy rendered payload.
6
+ public enum VoltraDynamicLiveActivityPayloadValidator {
7
+ private struct Attributes: Encodable {
8
+ let name: String
9
+ let deepLinkUrl: String?
10
+ }
11
+
12
+ public static func decodeProps(_ jsonString: String) throws -> [String: VoltraDynamicLiveActivityJSONValue] {
13
+ let data = Data(jsonString.utf8)
14
+ return try JSONDecoder().decode([String: VoltraDynamicLiveActivityJSONValue].self, from: data)
15
+ }
16
+
17
+ public static func validate(
18
+ name: String,
19
+ deepLinkUrl: String?,
20
+ props: [String: VoltraDynamicLiveActivityJSONValue]
21
+ ) throws {
22
+ let attributesSize = try encodedSize(Attributes(name: name, deepLinkUrl: deepLinkUrl))
23
+ let contentStateSize = try encodedSize(VoltraDynamicLiveActivityContentState(props: props))
24
+ try validateSize(attributesSize + contentStateSize)
25
+ }
26
+
27
+ public static func validateContentState(_ props: [String: VoltraDynamicLiveActivityJSONValue]) throws {
28
+ try validateSize(encodedSize(VoltraDynamicLiveActivityContentState(props: props)))
29
+ }
30
+
31
+ public static func encodedContentStateSize(
32
+ _ props: [String: VoltraDynamicLiveActivityJSONValue]
33
+ ) throws -> Int {
34
+ try encodedSize(VoltraDynamicLiveActivityContentState(props: props))
35
+ }
36
+
37
+ public static func encodedStartSize(
38
+ name: String,
39
+ deepLinkUrl: String?,
40
+ props: [String: VoltraDynamicLiveActivityJSONValue]
41
+ ) throws -> Int {
42
+ try encodedSize(Attributes(name: name, deepLinkUrl: deepLinkUrl)) +
43
+ encodedSize(VoltraDynamicLiveActivityContentState(props: props))
44
+ }
45
+
46
+ private static func encodedSize(_ value: some Encodable) throws -> Int {
47
+ try JSONEncoder().encode(value).count
48
+ }
49
+
50
+ private static func validateSize(_ size: Int) throws {
51
+ guard size <= VoltraConstants.maxPayloadSizeBytes else {
52
+ throw VoltraDynamicLiveActivityError.payloadTooLarge(size: size)
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,143 @@
1
+ import ActivityKit
2
+ import Foundation
3
+
4
+ /// Type-erased operations for one generated ActivityKit attributes type.
5
+ ///
6
+ /// Generated source registers descriptors at runtime. Pod-owned code only
7
+ /// depends on this registry and never names a type compiled into the host app.
8
+ private struct VoltraDynamicLiveActivityDescriptor {
9
+ let definitionId: String
10
+ let create: (VoltraDynamicLiveActivityCreateRequest) async throws -> VoltraDynamicLiveActivityReference
11
+ let update: (String, VoltraDynamicLiveActivityUpdateRequest) async -> Bool
12
+ let end: (String, ActivityUIDismissalPolicy) async -> Bool
13
+ let endAll: (ActivityUIDismissalPolicy) async -> Void
14
+ let activities: () -> [VoltraDynamicLiveActivityReference]
15
+ let observe: (VoltraDynamicLiveActivityObserver) async -> Void
16
+ let reload: () async -> Void
17
+
18
+ init<Attributes: VoltraDynamicLiveActivityDefinition>(_ attributes: Attributes.Type) {
19
+ definitionId = Attributes.definitionId
20
+ create = { try await VoltraDynamicLiveActivityOperations.create(attributes, request: $0) }
21
+ update = { await VoltraDynamicLiveActivityOperations.update(attributes, byName: $0, request: $1) }
22
+ end = { await VoltraDynamicLiveActivityOperations.end(attributes, byName: $0, dismissalPolicy: $1) }
23
+ endAll = { await VoltraDynamicLiveActivityOperations.endAll(attributes, dismissalPolicy: $0) }
24
+ activities = { VoltraDynamicLiveActivityOperations.activities(attributes) }
25
+ observe = { await $0.observe(attributes) }
26
+ reload = { await VoltraDynamicLiveActivityOperations.reload(attributes) }
27
+ }
28
+ }
29
+
30
+ /// Runtime-owned catalog populated by the generated host source.
31
+ ///
32
+ /// `VoltraGeneratedDynamicLiveActivityRegistration` has a fixed Objective-C
33
+ /// runtime name in both the app and extension products. This lets the pod ask
34
+ /// the host target to register its generated types without creating a reverse
35
+ /// compile-time dependency from the pod to the host.
36
+ public final class VoltraDynamicLiveActivityRegistry {
37
+ public static let shared = VoltraDynamicLiveActivityRegistry()
38
+
39
+ private let lock = NSLock()
40
+ private var descriptors: [String: VoltraDynamicLiveActivityDescriptor] = [:]
41
+ private var attemptedGeneratedRegistration = false
42
+
43
+ private init() {}
44
+
45
+ public func register<Attributes: VoltraDynamicLiveActivityDefinition>(_ attributes: Attributes.Type) {
46
+ lock.lock()
47
+ descriptors[Attributes.definitionId] = VoltraDynamicLiveActivityDescriptor(attributes)
48
+ lock.unlock()
49
+ }
50
+
51
+ public func contains(_ definitionId: String) -> Bool {
52
+ ensureGeneratedRegistration()
53
+ lock.lock()
54
+ defer { lock.unlock() }
55
+ return descriptors[definitionId] != nil
56
+ }
57
+
58
+ public func definitionIds() -> [String] {
59
+ ensureGeneratedRegistration()
60
+ lock.lock()
61
+ defer { lock.unlock() }
62
+ return descriptors.keys.sorted()
63
+ }
64
+
65
+ public func create(_ request: VoltraDynamicLiveActivityCreateRequest) async throws -> VoltraDynamicLiveActivityReference? {
66
+ guard let descriptor = descriptor(for: request.definitionId) else { return nil }
67
+ return try await descriptor.create(request)
68
+ }
69
+
70
+ public func update(byName name: String, request: VoltraDynamicLiveActivityUpdateRequest) async -> Bool {
71
+ for descriptor in descriptorSnapshot() {
72
+ if await descriptor.update(name, request) {
73
+ return true
74
+ }
75
+ }
76
+ return false
77
+ }
78
+
79
+ public func end(byName name: String, dismissalPolicy: ActivityUIDismissalPolicy) async -> Bool {
80
+ var ended = false
81
+ for descriptor in descriptorSnapshot() {
82
+ if await descriptor.end(name, dismissalPolicy) {
83
+ ended = true
84
+ }
85
+ }
86
+ return ended
87
+ }
88
+
89
+ public func endAll(dismissalPolicy: ActivityUIDismissalPolicy) async {
90
+ for descriptor in descriptorSnapshot() {
91
+ await descriptor.endAll(dismissalPolicy)
92
+ }
93
+ }
94
+
95
+ public func activities() -> [VoltraDynamicLiveActivityReference] {
96
+ descriptorSnapshot().flatMap { $0.activities() }
97
+ }
98
+
99
+ public func startObserving(with observer: VoltraDynamicLiveActivityObserver) async {
100
+ for descriptor in descriptorSnapshot() {
101
+ await descriptor.observe(observer)
102
+ }
103
+ }
104
+
105
+ public func reload(definitionIds: Set<String>?) async {
106
+ for descriptor in descriptorSnapshot() where definitionIds?.contains(descriptor.definitionId) != false {
107
+ await descriptor.reload()
108
+ }
109
+ }
110
+
111
+ private func descriptor(for definitionId: String) -> VoltraDynamicLiveActivityDescriptor? {
112
+ ensureGeneratedRegistration()
113
+ lock.lock()
114
+ defer { lock.unlock() }
115
+ return descriptors[definitionId]
116
+ }
117
+
118
+ private func descriptorSnapshot() -> [VoltraDynamicLiveActivityDescriptor] {
119
+ ensureGeneratedRegistration()
120
+ lock.lock()
121
+ defer { lock.unlock() }
122
+ return descriptors.values.sorted { $0.definitionId < $1.definitionId }
123
+ }
124
+
125
+ private func ensureGeneratedRegistration() {
126
+ lock.lock()
127
+ guard !attemptedGeneratedRegistration else {
128
+ lock.unlock()
129
+ return
130
+ }
131
+ attemptedGeneratedRegistration = true
132
+ lock.unlock()
133
+
134
+ let className = "VoltraGeneratedDynamicLiveActivityRegistration"
135
+ let selector = NSSelectorFromString("registerDefinitions")
136
+ guard let registration = NSClassFromString(className) as? NSObject.Type,
137
+ registration.responds(to: selector)
138
+ else {
139
+ return
140
+ }
141
+ registration.perform(selector)
142
+ }
143
+ }
@@ -0,0 +1,158 @@
1
+ import Foundation
2
+
3
+ #if canImport(Darwin)
4
+ import Darwin
5
+ #else
6
+ import Glibc
7
+ #endif
8
+
9
+ /// The intentionally minimal diagnostic record sent from the widget extension
10
+ /// to the app after a Dynamic Live Activity definition cannot be rendered.
11
+ /// Do not add props, tokens, stages, or arbitrary error details here.
12
+ public struct VoltraDynamicLiveActivityRenderFailure: Codable, Equatable {
13
+ public static let eventType = "dynamicLiveActivityRenderFailed"
14
+
15
+ public let type: String
16
+ public let source: String
17
+ public let timestamp: TimeInterval
18
+ public let activityName: String
19
+ public let definitionId: String
20
+ public let message: String
21
+
22
+ public init(activityName: String, definitionId: String, message: String, timestamp: Date = Date()) {
23
+ type = Self.eventType
24
+ source = activityName
25
+ self.timestamp = timestamp.timeIntervalSince1970
26
+ self.activityName = activityName
27
+ self.definitionId = definitionId
28
+ self.message = Self.sanitize(message)
29
+ }
30
+
31
+ public var dictionary: [String: Any] {
32
+ [
33
+ "type": type,
34
+ "source": source,
35
+ "timestamp": timestamp,
36
+ "activityName": activityName,
37
+ "definitionId": definitionId,
38
+ "message": message,
39
+ ]
40
+ }
41
+
42
+ /// Error descriptions can contain line breaks and unbounded implementation
43
+ /// details. Keep the diagnostic useful while making the persisted contract
44
+ /// predictable and safe to expose to JavaScript.
45
+ public static func sanitize(_ message: String) -> String {
46
+ let collapsed = message
47
+ .components(separatedBy: .whitespacesAndNewlines)
48
+ .filter { !$0.isEmpty }
49
+ .joined(separator: " ")
50
+ let fallback = "Dynamic Live Activity rendering failed."
51
+ return String((collapsed.isEmpty ? fallback : collapsed).prefix(500))
52
+ }
53
+ }
54
+
55
+ public protocol VoltraDynamicLiveActivityRenderFailureStorage {
56
+ func append(_ failure: VoltraDynamicLiveActivityRenderFailure, capacity: Int) throws
57
+ func drain() throws -> [VoltraDynamicLiveActivityRenderFailure]
58
+ }
59
+
60
+ /// Cross-process-safe file storage for the dedicated failure queue.
61
+ ///
62
+ /// The app and widget extension take an advisory lock on the same App Group
63
+ /// lock file before every read/modify/write transaction. Atomic replacement of
64
+ /// the JSON data file prevents partial writes. Corrupt data is quarantined and
65
+ /// treated as an empty queue so one damaged record cannot permanently disable
66
+ /// diagnostics.
67
+ public final class VoltraDynamicLiveActivityRenderFailureFileStorage: VoltraDynamicLiveActivityRenderFailureStorage {
68
+ private let directoryURL: URL
69
+ private let fileManager: FileManager
70
+ private let queueFileName = "dynamic-live-activity-render-failures-v1.json"
71
+ private let lockFileName = "dynamic-live-activity-render-failures-v1.lock"
72
+
73
+ public init(directoryURL: URL, fileManager: FileManager = .default) {
74
+ self.directoryURL = directoryURL
75
+ self.fileManager = fileManager
76
+ }
77
+
78
+ public func append(_ failure: VoltraDynamicLiveActivityRenderFailure, capacity: Int) throws {
79
+ try withExclusiveAccess { queueURL in
80
+ var failures = loadRecoveringCorruption(from: queueURL)
81
+ failures.append(failure)
82
+ if failures.count > capacity {
83
+ failures.removeFirst(failures.count - capacity)
84
+ }
85
+ try write(failures, to: queueURL)
86
+ }
87
+ }
88
+
89
+ public func drain() throws -> [VoltraDynamicLiveActivityRenderFailure] {
90
+ try withExclusiveAccess { queueURL in
91
+ let failures = loadRecoveringCorruption(from: queueURL)
92
+ try write([], to: queueURL)
93
+ return failures
94
+ }
95
+ }
96
+
97
+ private func withExclusiveAccess<Result>(
98
+ _ operation: (URL) throws -> Result
99
+ ) throws -> Result {
100
+ try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)
101
+ let lockURL = directoryURL.appendingPathComponent(lockFileName, isDirectory: false)
102
+ let descriptor = open(lockURL.path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)
103
+ guard descriptor >= 0 else { throw POSIXError(.EIO) }
104
+ defer { close(descriptor) }
105
+ guard flock(descriptor, LOCK_EX) == 0 else { throw POSIXError(.EIO) }
106
+ defer { flock(descriptor, LOCK_UN) }
107
+ return try operation(directoryURL.appendingPathComponent(queueFileName, isDirectory: false))
108
+ }
109
+
110
+ private func loadRecoveringCorruption(from url: URL) -> [VoltraDynamicLiveActivityRenderFailure] {
111
+ guard fileManager.fileExists(atPath: url.path) else { return [] }
112
+ do {
113
+ return try JSONDecoder().decode(
114
+ [VoltraDynamicLiveActivityRenderFailure].self,
115
+ from: Data(contentsOf: url)
116
+ )
117
+ } catch {
118
+ let quarantineURL = directoryURL.appendingPathComponent(
119
+ "\(queueFileName).corrupt-\(UUID().uuidString)",
120
+ isDirectory: false
121
+ )
122
+ try? fileManager.moveItem(at: url, to: quarantineURL)
123
+ return []
124
+ }
125
+ }
126
+
127
+ private func write(_ failures: [VoltraDynamicLiveActivityRenderFailure], to url: URL) throws {
128
+ try JSONEncoder().encode(failures).write(to: url, options: .atomic)
129
+ }
130
+ }
131
+
132
+ /// A dedicated, bounded queue whose storage owns the append/drain transaction.
133
+ public final class VoltraDynamicLiveActivityRenderFailureQueue {
134
+ public static let capacity = 100
135
+
136
+ private let storage: VoltraDynamicLiveActivityRenderFailureStorage
137
+ public init(storage: VoltraDynamicLiveActivityRenderFailureStorage) {
138
+ self.storage = storage
139
+ }
140
+
141
+ @discardableResult
142
+ public func record(_ failure: VoltraDynamicLiveActivityRenderFailure) -> Bool {
143
+ do {
144
+ try storage.append(failure, capacity: Self.capacity)
145
+ return true
146
+ } catch {
147
+ return false
148
+ }
149
+ }
150
+
151
+ public func drain() -> [VoltraDynamicLiveActivityRenderFailure] {
152
+ do {
153
+ return try storage.drain()
154
+ } catch {
155
+ return []
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,120 @@
1
+ import CoreFoundation
2
+ import Foundation
3
+
4
+ /// Cross-process notification relay for the App Group backed failure queue.
5
+ /// Darwin notifications carry no payload, so consumers always drain storage.
6
+ private final class VoltraDynamicLiveActivityRenderFailureNotifier {
7
+ static let shared = VoltraDynamicLiveActivityRenderFailureNotifier()
8
+
9
+ private let lock = NSLock()
10
+ private var handlers: [UUID: () -> Void] = [:]
11
+ private let name = "com.voltra.dynamicLiveActivityRenderFailures" as CFString
12
+
13
+ private init() {
14
+ CFNotificationCenterAddObserver(
15
+ CFNotificationCenterGetDarwinNotifyCenter(),
16
+ Unmanaged.passUnretained(self).toOpaque(),
17
+ Self.didReceive,
18
+ name,
19
+ nil,
20
+ .deliverImmediately
21
+ )
22
+ }
23
+
24
+ deinit {
25
+ CFNotificationCenterRemoveObserver(
26
+ CFNotificationCenterGetDarwinNotifyCenter(),
27
+ Unmanaged.passUnretained(self).toOpaque(),
28
+ CFNotificationName(name),
29
+ nil
30
+ )
31
+ }
32
+
33
+ func add(_ handler: @escaping () -> Void) -> UUID {
34
+ let token = UUID()
35
+ lock.lock()
36
+ handlers[token] = handler
37
+ lock.unlock()
38
+ return token
39
+ }
40
+
41
+ func remove(_ token: UUID) {
42
+ lock.lock()
43
+ handlers.removeValue(forKey: token)
44
+ lock.unlock()
45
+ }
46
+
47
+ func post() {
48
+ notifyHandlers()
49
+ CFNotificationCenterPostNotification(
50
+ CFNotificationCenterGetDarwinNotifyCenter(),
51
+ CFNotificationName(name),
52
+ nil,
53
+ nil,
54
+ true
55
+ )
56
+ }
57
+
58
+ private static let didReceive: CFNotificationCallback = { _, observer, _, _, _ in
59
+ guard let observer else { return }
60
+ Unmanaged<VoltraDynamicLiveActivityRenderFailureNotifier>
61
+ .fromOpaque(observer)
62
+ .takeUnretainedValue()
63
+ .notifyHandlers()
64
+ }
65
+
66
+ private func notifyHandlers() {
67
+ lock.lock()
68
+ let currentHandlers = Array(handlers.values)
69
+ lock.unlock()
70
+ currentHandlers.forEach { $0() }
71
+ }
72
+ }
73
+
74
+ /// Owns the production App Group queue and OS logging path. The extension only
75
+ /// records failures; the app's event bus drains them when JavaScript can listen.
76
+ public enum VoltraDynamicLiveActivityRenderFailureReporter {
77
+ private static var queue: VoltraDynamicLiveActivityRenderFailureQueue? {
78
+ guard
79
+ let group = VoltraConfig.groupIdentifier(),
80
+ let directoryURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: group)
81
+ else {
82
+ return nil
83
+ }
84
+ return VoltraDynamicLiveActivityRenderFailureQueue(
85
+ storage: VoltraDynamicLiveActivityRenderFailureFileStorage(directoryURL: directoryURL)
86
+ )
87
+ }
88
+
89
+ @discardableResult
90
+ public static func record(activityName: String, definitionId: String, message: String) -> Bool {
91
+ let failure = VoltraDynamicLiveActivityRenderFailure(
92
+ activityName: activityName,
93
+ definitionId: definitionId,
94
+ message: message
95
+ )
96
+ let persisted = queue?.record(failure) ?? false
97
+ VoltraLogger.activity.error(
98
+ "[DynamicLiveActivity] activity=\(failure.activityName, privacy: .public) definitionId=\(failure.definitionId, privacy: .public) \(failure.message, privacy: .public)"
99
+ )
100
+ if persisted {
101
+ VoltraDynamicLiveActivityRenderFailureNotifier.shared.post()
102
+ } else {
103
+ VoltraLogger.event.error("Failed to persist Dynamic Live Activity render failure")
104
+ }
105
+ return persisted
106
+ }
107
+
108
+ public static func drain() -> [VoltraDynamicLiveActivityRenderFailure] {
109
+ queue?.drain() ?? []
110
+ }
111
+
112
+ /// The returned token must be removed when the JS bridge listener goes away.
113
+ public static func observeChanges(_ handler: @escaping () -> Void) -> UUID {
114
+ VoltraDynamicLiveActivityRenderFailureNotifier.shared.add(handler)
115
+ }
116
+
117
+ public static func removeChangeObserver(_ token: UUID) {
118
+ VoltraDynamicLiveActivityRenderFailureNotifier.shared.remove(token)
119
+ }
120
+ }