@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.
- package/README.md +61 -8
- package/Voltra.podspec +1 -0
- package/build/commonjs/dynamic-widget/api.js +15 -0
- package/build/commonjs/dynamic-widget/api.js.map +1 -0
- package/build/commonjs/events.js +23 -0
- package/build/commonjs/events.js.map +1 -1
- package/build/commonjs/index.js +74 -7
- package/build/commonjs/index.js.map +1 -1
- package/build/commonjs/live-activity/api.js +3 -1
- package/build/commonjs/live-activity/api.js.map +1 -1
- package/build/commonjs/live-activity/dynamic-api.js +173 -0
- package/build/commonjs/live-activity/dynamic-api.js.map +1 -0
- package/build/commonjs/native/NativeVoltra.js.map +1 -1
- package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js +47 -0
- package/build/commonjs/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
- package/build/commonjs/utils/enableWidgetHotReload.js +8 -11
- package/build/commonjs/utils/enableWidgetHotReload.js.map +1 -1
- package/build/commonjs/utils/useUpdateOnHMR.js +3 -7
- package/build/commonjs/utils/useUpdateOnHMR.js.map +1 -1
- package/build/commonjs/widgets/server-credentials.js +9 -0
- package/build/commonjs/widgets/server-credentials.js.map +1 -1
- package/build/commonjs/widgets/server-update.js +78 -0
- package/build/commonjs/widgets/server-update.js.map +1 -0
- package/build/module/dynamic-widget/api.js +10 -0
- package/build/module/dynamic-widget/api.js.map +1 -0
- package/build/module/events.js +23 -0
- package/build/module/events.js.map +1 -1
- package/build/module/index.js +4 -0
- package/build/module/index.js.map +1 -1
- package/build/module/live-activity/api.js +2 -2
- package/build/module/live-activity/api.js.map +1 -1
- package/build/module/live-activity/dynamic-api.js +167 -0
- package/build/module/live-activity/dynamic-api.js.map +1 -0
- package/build/module/native/NativeVoltra.js.map +1 -1
- package/build/module/utils/enableDynamicLiveActivityHotReload.js +43 -0
- package/build/module/utils/enableDynamicLiveActivityHotReload.js.map +1 -0
- package/build/module/utils/enableWidgetHotReload.js +9 -11
- package/build/module/utils/enableWidgetHotReload.js.map +1 -1
- package/build/module/utils/useUpdateOnHMR.js +3 -7
- package/build/module/utils/useUpdateOnHMR.js.map +1 -1
- package/build/module/widgets/server-credentials.js +9 -0
- package/build/module/widgets/server-credentials.js.map +1 -1
- package/build/module/widgets/server-update.js +72 -0
- package/build/module/widgets/server-update.js.map +1 -0
- package/build/typescript/commonjs/dynamic-widget/api.d.ts +8 -0
- package/build/typescript/commonjs/dynamic-widget/api.d.ts.map +1 -0
- package/build/typescript/commonjs/events.d.ts +7 -0
- package/build/typescript/commonjs/events.d.ts.map +1 -1
- package/build/typescript/commonjs/index.d.ts +4 -0
- package/build/typescript/commonjs/index.d.ts.map +1 -1
- package/build/typescript/commonjs/live-activity/api.d.ts +7 -0
- package/build/typescript/commonjs/live-activity/api.d.ts.map +1 -1
- package/build/typescript/commonjs/live-activity/dynamic-api.d.ts +54 -0
- package/build/typescript/commonjs/live-activity/dynamic-api.d.ts.map +1 -0
- package/build/typescript/commonjs/native/NativeVoltra.d.ts +21 -0
- package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
- package/build/typescript/commonjs/types.d.ts +1 -1
- package/build/typescript/commonjs/types.d.ts.map +1 -1
- package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
- package/build/typescript/commonjs/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
- package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts +5 -7
- package/build/typescript/commonjs/utils/enableWidgetHotReload.d.ts.map +1 -1
- package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts +0 -3
- package/build/typescript/commonjs/utils/useUpdateOnHMR.d.ts.map +1 -1
- package/build/typescript/commonjs/widgets/server-credentials.d.ts +8 -0
- package/build/typescript/commonjs/widgets/server-credentials.d.ts.map +1 -1
- package/build/typescript/commonjs/widgets/server-update.d.ts +64 -0
- package/build/typescript/commonjs/widgets/server-update.d.ts.map +1 -0
- package/build/typescript/module/dynamic-widget/api.d.ts +8 -0
- package/build/typescript/module/dynamic-widget/api.d.ts.map +1 -0
- package/build/typescript/module/events.d.ts +7 -0
- package/build/typescript/module/events.d.ts.map +1 -1
- package/build/typescript/module/index.d.ts +4 -0
- package/build/typescript/module/index.d.ts.map +1 -1
- package/build/typescript/module/live-activity/api.d.ts +7 -0
- package/build/typescript/module/live-activity/api.d.ts.map +1 -1
- package/build/typescript/module/live-activity/dynamic-api.d.ts +54 -0
- package/build/typescript/module/live-activity/dynamic-api.d.ts.map +1 -0
- package/build/typescript/module/native/NativeVoltra.d.ts +21 -0
- package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
- package/build/typescript/module/types.d.ts +1 -1
- package/build/typescript/module/types.d.ts.map +1 -1
- package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts +15 -0
- package/build/typescript/module/utils/enableDynamicLiveActivityHotReload.d.ts.map +1 -0
- package/build/typescript/module/utils/enableWidgetHotReload.d.ts +5 -7
- package/build/typescript/module/utils/enableWidgetHotReload.d.ts.map +1 -1
- package/build/typescript/module/utils/useUpdateOnHMR.d.ts +0 -3
- package/build/typescript/module/utils/useUpdateOnHMR.d.ts.map +1 -1
- package/build/typescript/module/widgets/server-credentials.d.ts +8 -0
- package/build/typescript/module/widgets/server-credentials.d.ts.map +1 -1
- package/build/typescript/module/widgets/server-update.d.ts +64 -0
- package/build/typescript/module/widgets/server-update.d.ts.map +1 -0
- package/expo-plugin/build/cjs/constants.js +20 -1
- package/expo-plugin/build/cjs/constants.js.map +1 -1
- package/expo-plugin/build/cjs/index.js +9 -1
- package/expo-plugin/build/cjs/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios/index.js +1 -0
- package/expo-plugin/build/cjs/ios/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios/infoPlist.js +28 -5
- package/expo-plugin/build/cjs/ios/infoPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios/serverUpdate.js +25 -0
- package/expo-plugin/build/cjs/ios/serverUpdate.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/clientRendered.js +4 -4
- package/expo-plugin/build/cjs/ios-widget/clientRendered.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js +5 -5
- package/expo-plugin/build/cjs/ios-widget/clientRenderedPrerender.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js +130 -0
- package/expo-plugin/build/cjs/ios-widget/dynamic-live-activity/swift.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/files/index.js +9 -2
- package/expo-plugin/build/cjs/ios-widget/files/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js +6 -3
- package/expo-plugin/build/cjs/ios-widget/files/infoPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/manifest.js +24 -0
- package/expo-plugin/build/cjs/ios-widget/files/manifest.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js +7 -0
- package/expo-plugin/build/cjs/ios-widget/files/swift-utils.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/files/swift.js +56 -26
- package/expo-plugin/build/cjs/ios-widget/files/swift.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/index.js +9 -3
- package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/podfile.js +38 -12
- package/expo-plugin/build/cjs/ios-widget/podfile.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/widgetPlist.js +34 -22
- package/expo-plugin/build/cjs/ios-widget/widgetPlist.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +194 -118
- package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js +30 -22
- package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js +50 -0
- package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js.map +1 -0
- package/expo-plugin/build/cjs/ios-widget/xcode/groups.js +45 -43
- package/expo-plugin/build/cjs/ios-widget/xcode/groups.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/index.js +93 -91
- package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js +41 -39
- package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js +2 -2
- package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/target.js +29 -63
- package/expo-plugin/build/cjs/ios-widget/xcode/target.js.map +1 -1
- package/expo-plugin/build/cjs/utils/urlScheme.js +36 -14
- package/expo-plugin/build/cjs/utils/urlScheme.js.map +1 -1
- package/expo-plugin/build/cjs/validation.js +54 -0
- package/expo-plugin/build/cjs/validation.js.map +1 -1
- package/expo-plugin/build/esm/constants.js +17 -0
- package/expo-plugin/build/esm/constants.js.map +1 -1
- package/expo-plugin/build/esm/index.js +9 -1
- package/expo-plugin/build/esm/index.js.map +1 -1
- package/expo-plugin/build/esm/ios/index.js +1 -0
- package/expo-plugin/build/esm/ios/index.js.map +1 -1
- package/expo-plugin/build/esm/ios/infoPlist.js +28 -5
- package/expo-plugin/build/esm/ios/infoPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios/serverUpdate.js +21 -0
- package/expo-plugin/build/esm/ios/serverUpdate.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/clientRendered.js +5 -5
- package/expo-plugin/build/esm/ios-widget/clientRendered.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js +6 -6
- package/expo-plugin/build/esm/ios-widget/clientRenderedPrerender.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js +122 -0
- package/expo-plugin/build/esm/ios-widget/dynamic-live-activity/swift.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/files/index.js +10 -3
- package/expo-plugin/build/esm/ios-widget/files/index.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/infoPlist.js +6 -3
- package/expo-plugin/build/esm/ios-widget/files/infoPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/manifest.js +23 -1
- package/expo-plugin/build/esm/ios-widget/files/manifest.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/files/swift-utils.js +4 -0
- package/expo-plugin/build/esm/ios-widget/files/swift-utils.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/files/swift.js +50 -20
- package/expo-plugin/build/esm/ios-widget/files/swift.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/index.js +9 -3
- package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/podfile.js +36 -11
- package/expo-plugin/build/esm/ios-widget/podfile.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/widgetPlist.js +34 -22
- package/expo-plugin/build/esm/ios-widget/widgetPlist.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +194 -117
- package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js +31 -22
- package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js +45 -0
- package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js.map +1 -0
- package/expo-plugin/build/esm/ios-widget/xcode/groups.js +45 -42
- package/expo-plugin/build/esm/ios-widget/xcode/groups.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/index.js +97 -96
- package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js +41 -39
- package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/productFile.js +3 -2
- package/expo-plugin/build/esm/ios-widget/xcode/productFile.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/target.js +28 -62
- package/expo-plugin/build/esm/ios-widget/xcode/target.js.map +1 -1
- package/expo-plugin/build/esm/utils/urlScheme.js +34 -14
- package/expo-plugin/build/esm/utils/urlScheme.js.map +1 -1
- package/expo-plugin/build/esm/validation.js +54 -1
- package/expo-plugin/build/esm/validation.js.map +1 -1
- package/expo-plugin/build/types/constants.d.ts +10 -1
- package/expo-plugin/build/types/constants.d.ts.map +1 -1
- package/expo-plugin/build/types/index.d.ts +1 -1
- package/expo-plugin/build/types/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/index.d.ts +2 -0
- package/expo-plugin/build/types/ios/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/infoPlist.d.ts +2 -0
- package/expo-plugin/build/types/ios/infoPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios/serverUpdate.d.ts +11 -0
- package/expo-plugin/build/types/ios/serverUpdate.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/clientRendered.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/clientRenderedPrerender.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts +7 -0
- package/expo-plugin/build/types/ios-widget/dynamic-live-activity/swift.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/files/index.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/files/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts +2 -1
- package/expo-plugin/build/types/ios-widget/files/infoPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/manifest.d.ts +9 -2
- package/expo-plugin/build/types/ios-widget/files/manifest.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts +2 -0
- package/expo-plugin/build/types/ios-widget/files/swift-utils.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/files/swift.d.ts +6 -2
- package/expo-plugin/build/types/ios-widget/files/swift.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/index.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/podfile.d.ts +19 -1
- package/expo-plugin/build/types/ios-widget/podfile.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/widgetPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +6 -8
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts +15 -10
- package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts +16 -0
- package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts.map +1 -0
- package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts +0 -4
- package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +23 -3
- package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts +16 -4
- package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts +0 -4
- package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/target.d.ts +11 -26
- package/expo-plugin/build/types/ios-widget/xcode/target.d.ts.map +1 -1
- package/expo-plugin/build/types/types.d.ts +23 -4
- package/expo-plugin/build/types/types.d.ts.map +1 -1
- package/expo-plugin/build/types/utils/urlScheme.d.ts +16 -0
- package/expo-plugin/build/types/utils/urlScheme.d.ts.map +1 -1
- package/expo-plugin/build/types/validation.d.ts +2 -1
- package/expo-plugin/build/types/validation.d.ts.map +1 -1
- package/expo-plugin/src/constants.ts +24 -1
- package/expo-plugin/src/index.ts +14 -1
- package/expo-plugin/src/ios/index.ts +3 -0
- package/expo-plugin/src/ios/infoPlist.ts +33 -5
- package/expo-plugin/src/ios/serverUpdate.ts +29 -0
- package/expo-plugin/src/ios-widget/clientRendered.ts +9 -5
- package/expo-plugin/src/ios-widget/clientRenderedPrerender.ts +11 -6
- package/expo-plugin/src/ios-widget/dynamic-live-activity/swift.ts +138 -0
- package/expo-plugin/src/ios-widget/files/index.ts +15 -4
- package/expo-plugin/src/ios-widget/files/infoPlist.ts +17 -3
- package/expo-plugin/src/ios-widget/files/manifest.ts +42 -2
- package/expo-plugin/src/ios-widget/files/swift-utils.ts +3 -0
- package/expo-plugin/src/ios-widget/files/swift.ts +69 -22
- package/expo-plugin/src/ios-widget/index.ts +13 -3
- package/expo-plugin/src/ios-widget/podfile.ts +46 -13
- package/expo-plugin/src/ios-widget/widgetPlist.ts +37 -23
- package/expo-plugin/src/ios-widget/xcode/__fixtures__/fresh.pbxproj +197 -0
- package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-existing-extension.pbxproj +321 -0
- package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-per-config-signing.pbxproj +203 -0
- package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +363 -0
- package/expo-plugin/src/ios-widget/xcode/__tests__/pbxConsistency.ts +130 -0
- package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +210 -164
- package/expo-plugin/src/ios-widget/xcode/configurationList.ts +52 -35
- package/expo-plugin/src/ios-widget/xcode/fileReferences.ts +51 -0
- package/expo-plugin/src/ios-widget/xcode/groups.ts +52 -54
- package/expo-plugin/src/ios-widget/xcode/index.ts +124 -119
- package/expo-plugin/src/ios-widget/xcode/mainAppSettings.ts +59 -54
- package/expo-plugin/src/ios-widget/xcode/productFile.ts +3 -2
- package/expo-plugin/src/ios-widget/xcode/target.ts +31 -83
- package/expo-plugin/src/types.ts +28 -4
- package/expo-plugin/src/utils/urlScheme.ts +47 -17
- package/expo-plugin/src/validation.ts +76 -1
- package/ios/Package.swift +39 -4
- package/ios/VoltraWidget.podspec +1 -0
- package/ios/app/NativeVoltra.mm +126 -7
- package/ios/app/VoltraLiveActivityManager.swift +30 -0
- package/ios/app/VoltraLiveActivityService.swift +77 -5
- package/ios/app/VoltraModule.swift +117 -1
- package/ios/app/VoltraModuleImpl.swift +116 -2
- package/ios/app/VoltraViewComponentView.mm +3 -3
- package/ios/app/VoltraWidgetService.swift +147 -12
- package/ios/app/dynamic-live-activity/VoltraDynamicLiveActivityService.swift +97 -0
- package/ios/app/dynamic-live-activity/VoltraLiveActivityChronology.swift +68 -0
- package/ios/shared/DynamicWidgetPropsCodec.swift +51 -0
- package/ios/shared/DynamicWidgetPropsStorage.swift +53 -0
- package/ios/shared/DynamicWidgetPropsStore.swift +117 -0
- package/ios/shared/DynamicWidgetRenderCoordinator.swift +31 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift +34 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift +55 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift +146 -0
- package/ios/shared/DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift +193 -0
- package/ios/shared/DynamicWidgetUpdater.swift +30 -0
- package/ios/shared/ServerWidgetContentResolver.swift +60 -0
- package/ios/shared/ServerWidgetResponseStore.swift +25 -0
- package/ios/shared/ShortNames.swift +6 -0
- package/ios/shared/VoltraConfig.swift +41 -0
- package/ios/shared/VoltraConstants.swift +19 -0
- package/ios/shared/VoltraElement.swift +127 -43
- package/ios/shared/VoltraEventBus.swift +52 -7
- package/ios/shared/VoltraJSRenderer.swift +93 -23
- package/ios/shared/VoltraNode.swift +7 -111
- package/ios/shared/VoltraRefreshIntent.swift +1 -1
- package/ios/shared/VoltraWidgetDefaults.swift +9 -1
- package/ios/shared/VoltraWidgetKind.swift +41 -0
- package/ios/shared/VoltraWidgetServerFetcher.swift +52 -107
- package/ios/shared/WidgetServer/VoltraWidgetAppearance.swift +17 -0
- package/ios/shared/WidgetServer/VoltraWidgetServer.swift +138 -0
- package/ios/shared/WidgetServer/WidgetCanonicalConfiguration.swift +77 -0
- package/ios/shared/WidgetServer/WidgetScope.swift +55 -0
- package/ios/shared/WidgetServer/WidgetServerEtagStore.swift +55 -0
- package/ios/shared/WidgetServer/WidgetServerFetcher.swift +179 -0
- package/ios/shared/WidgetServer/WidgetServerRequestBuilder.swift +114 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsCodec.swift +47 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsResolver.swift +129 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsStore.swift +170 -0
- package/ios/shared/WidgetServer/WidgetServerSettingsValidator.swift +73 -0
- package/ios/shared/WidgetServer/WidgetServerUpdateSettings.swift +123 -0
- package/ios/shared/WidgetServer/WidgetServerUpdateSettingsJson.swift +115 -0
- package/ios/shared/dynamic-live-activity/DynamicLiveActivityPropsCodec.swift +13 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityBundleSource.swift +156 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityObserver.swift +97 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityOperations.swift +105 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift +55 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRegistry.swift +143 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift +158 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureReporter.swift +120 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityRenderer.swift +370 -0
- package/ios/shared/dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift +87 -0
- package/ios/shared/dynamic-live-activity/VoltraLiveActivityOrder.swift +19 -0
- package/ios/target/VoltraClientWidgetRuntime.swift +60 -10
- package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
- package/ios/target/VoltraHomeWidget.swift +91 -92
- package/ios/target/VoltraWidget.swift +3 -1
- package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
- package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
- package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +5 -3
- package/ios/ui/Helpers/VoltraProgressDriver.swift +6 -2
- package/ios/ui/Layout/VoltraFlexStackLayout.swift +9 -3
- package/ios/ui/Style/JSColorParser.swift +24 -8
- package/ios/ui/Style/JSGradientParser.swift +36 -12
- package/ios/ui/Style/JSStyleParser.swift +36 -12
- package/ios/ui/Style/LayoutStyle.swift +12 -4
- package/ios/ui/Views/VoltraChart.swift +160 -16
- package/ios/ui/Views/VoltraLinearGradient.swift +6 -2
- package/package.json +5 -5
- package/src/dynamic-widget/api.ts +25 -0
- package/src/events.ts +34 -0
- package/src/index.ts +23 -0
- package/src/live-activity/api.ts +2 -2
- package/src/live-activity/dynamic-api.ts +240 -0
- package/src/native/NativeVoltra.ts +22 -0
- package/src/types.ts +4 -0
- package/src/utils/enableDynamicLiveActivityHotReload.ts +50 -0
- package/src/utils/enableWidgetHotReload.ts +9 -16
- package/src/utils/useUpdateOnHMR.ts +3 -12
- package/src/widgets/server-credentials.ts +8 -0
- package/src/widgets/server-update.ts +84 -0
- /package/ios/shared/{VoltraKeychainHelper.swift → WidgetServer/VoltraKeychainHelper.swift} +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// In-memory record of the last successful server response for each server-driven widget.
|
|
4
|
+
///
|
|
5
|
+
/// Lives for the lifetime of the widget extension process. It is the source of truth for
|
|
6
|
+
/// "the last content the server actually sent", independent of whether an App Group is
|
|
7
|
+
/// configured for on-disk persistence.
|
|
8
|
+
actor ServerWidgetResponseStore {
|
|
9
|
+
struct Response: Equatable {
|
|
10
|
+
let data: Data
|
|
11
|
+
let fetchedAt: Date
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private var responses: [String: Response] = [:]
|
|
15
|
+
|
|
16
|
+
init() {}
|
|
17
|
+
|
|
18
|
+
func response(for widgetId: String) -> Response? {
|
|
19
|
+
responses[widgetId]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func store(_ data: Data, for widgetId: String, fetchedAt: Date) {
|
|
23
|
+
responses[widgetId] = Response(data: data, fetchedAt: fetchedAt)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -77,6 +77,7 @@ public enum ShortNames {
|
|
|
77
77
|
"g": "gap",
|
|
78
78
|
"gs": "gaugeStyle",
|
|
79
79
|
"ge": "glassEffect",
|
|
80
|
+
"gc": "gradientColors",
|
|
80
81
|
"h": "height",
|
|
81
82
|
"halig": "horizontalAlignment",
|
|
82
83
|
"hp": "horizontalPadding",
|
|
@@ -92,6 +93,7 @@ public enum ShortNames {
|
|
|
92
93
|
"l": "left",
|
|
93
94
|
"lgv": "legendVisibility",
|
|
94
95
|
"ls": "letterSpacing",
|
|
96
|
+
"lc": "lineCap",
|
|
95
97
|
"lh": "lineHeight",
|
|
96
98
|
"ll": "lineLimit",
|
|
97
99
|
"lsp": "lineSpacing",
|
|
@@ -149,12 +151,15 @@ public enum ShortNames {
|
|
|
149
151
|
"smc": "smallCaps",
|
|
150
152
|
"src": "source",
|
|
151
153
|
"sp": "spacing",
|
|
154
|
+
"sa": "startAngle",
|
|
152
155
|
"start": "startAtMs",
|
|
153
156
|
"sic": "startIcon",
|
|
154
157
|
"stp": "startPoint",
|
|
155
158
|
"sts": "stops",
|
|
156
159
|
"st": "strikethrough",
|
|
160
|
+
"sw": "strokeWidth",
|
|
157
161
|
"s": "style",
|
|
162
|
+
"swa": "sweepAngle",
|
|
158
163
|
"si": "systemImage",
|
|
159
164
|
"txt": "text",
|
|
160
165
|
"ta": "textAlign",
|
|
@@ -184,6 +189,7 @@ public enum ShortNames {
|
|
|
184
189
|
"xav": "xAxisVisibility",
|
|
185
190
|
"ygs": "yAxisGridStyle",
|
|
186
191
|
"yav": "yAxisVisibility",
|
|
192
|
+
"ysc": "yScale",
|
|
187
193
|
"zi": "zIndex",
|
|
188
194
|
]
|
|
189
195
|
|
|
@@ -15,4 +15,45 @@ public enum VoltraConfig {
|
|
|
15
15
|
public static func keychainGroup() -> String? {
|
|
16
16
|
Bundle.main.object(forInfoDictionaryKey: VoltraStorageKeys.keychainGroup) as? String
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/// Reported when `Voltra_Version` is missing or unusable, mirroring the `appVersion` fallback.
|
|
20
|
+
public static let unknownVoltraVersion = "unknown"
|
|
21
|
+
|
|
22
|
+
/// Get the installed Voltra client package version from Info.plist
|
|
23
|
+
/// Written at prebuild (`expo prebuild`) or apply (`voltra apply`) time from the installed
|
|
24
|
+
/// `@use-voltra/{ios,android}-client` manifest, and surfaced to widgets as `env.build.voltraVersion`.
|
|
25
|
+
public static func voltraVersion(bundle: Bundle = .main) -> String {
|
|
26
|
+
normalizeVoltraVersion(bundle.object(forInfoDictionaryKey: VoltraStorageKeys.voltraVersion))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Value handling for `voltraVersion(bundle:)`, split out so every malformed shape the plist can
|
|
30
|
+
/// hold is covered without building a bundle per case.
|
|
31
|
+
static func normalizeVoltraVersion(_ rawValue: Any?) -> String {
|
|
32
|
+
guard let version = rawValue as? String, !version.isEmpty else {
|
|
33
|
+
return unknownVoltraVersion
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return version
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Get the `{ widgetId: kind }` map of widgets that pin a custom WidgetKit kind from Info.plist
|
|
40
|
+
/// Written at prebuild (`expo prebuild`) or apply (`voltra apply`) time from the `kind` option.
|
|
41
|
+
/// Absent when every widget keeps the default `Voltra_Widget_<id>` kind.
|
|
42
|
+
public static func widgetKinds(bundle: Bundle = .main) -> [String: String] {
|
|
43
|
+
normalizeWidgetKinds(bundle.object(forInfoDictionaryKey: VoltraStorageKeys.widgetKinds))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Value handling for `widgetKinds(bundle:)`, split out so every malformed shape the plist can
|
|
47
|
+
/// hold is covered without building a bundle per case. Entries that are not non-empty strings
|
|
48
|
+
/// are dropped, leaving those widgets on the default kind.
|
|
49
|
+
static func normalizeWidgetKinds(_ rawValue: Any?) -> [String: String] {
|
|
50
|
+
guard let rawMap = rawValue as? [String: Any] else {
|
|
51
|
+
return [:]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return rawMap.compactMapValues { value in
|
|
55
|
+
guard let kind = value as? String, !kind.isEmpty else { return nil }
|
|
56
|
+
return kind
|
|
57
|
+
}
|
|
58
|
+
}
|
|
18
59
|
}
|
|
@@ -28,6 +28,22 @@ public enum VoltraStorageKeys {
|
|
|
28
28
|
"Voltra_Widget_ServerUrl_\(widgetId)"
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
public static func dynamicWidgetPropsV1(_ dynamicWidgetId: String) -> String {
|
|
32
|
+
"Voltra_DynamicWidget_Props_v1_\(dynamicWidgetId)"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// One placement's own props slot (ADR 0007), keyed by `WidgetScope.Instance.storageKey`
|
|
36
|
+
/// (`<widgetId>#<key>`).
|
|
37
|
+
public static func dynamicWidgetInstancePropsV1(_ storageKey: String) -> String {
|
|
38
|
+
"Voltra_DynamicWidget_Props_v1_\(storageKey)"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// The set of instance keys `dynamicWidgetId` has ever committed props into, so a widget-level
|
|
42
|
+
/// clear (or logout) can drop every instance slot without enumerating placements.
|
|
43
|
+
public static func dynamicWidgetPropsInstanceIndexV1(_ dynamicWidgetId: String) -> String {
|
|
44
|
+
"Voltra_DynamicWidget_Props_Instances_v1_\(dynamicWidgetId)"
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
// MARK: - Prefixes / kind identifiers
|
|
32
48
|
|
|
33
49
|
public static let widgetKindPrefix = "Voltra_Widget_"
|
|
@@ -39,6 +55,8 @@ public enum VoltraStorageKeys {
|
|
|
39
55
|
// MARK: - Info.plist keys
|
|
40
56
|
|
|
41
57
|
public static let widgetIds = "Voltra_WidgetIds"
|
|
58
|
+
/// `{ widgetId: kind }` for widgets that pin a custom WidgetKit kind (plugin `kind` option)
|
|
59
|
+
public static let widgetKinds = "Voltra_WidgetKinds"
|
|
42
60
|
public static let enablePushNotifications = "Voltra_EnablePushNotifications"
|
|
43
61
|
public static let widgetServerUrls = "Voltra_WidgetServerUrls"
|
|
44
62
|
public static let widgetServerIntervals = "Voltra_WidgetServerIntervals"
|
|
@@ -46,4 +64,5 @@ public enum VoltraStorageKeys {
|
|
|
46
64
|
public static let appGroupIdentifier = "Voltra_AppGroupIdentifier"
|
|
47
65
|
public static let legacyAppGroupIdentifier = "AppGroupIdentifier"
|
|
48
66
|
public static let keychainGroup = "Voltra_KeychainGroup"
|
|
67
|
+
public static let voltraVersion = "Voltra_Version"
|
|
49
68
|
}
|
|
@@ -11,17 +11,20 @@ public struct VoltraElement: Hashable {
|
|
|
11
11
|
/// Child nodes or text content
|
|
12
12
|
public let children: VoltraNode?
|
|
13
13
|
|
|
14
|
-
/// Raw properties (stored as JSONValue for type safety)
|
|
14
|
+
/// Raw properties (stored as JSONValue for type safety).
|
|
15
|
+
///
|
|
16
|
+
/// Stylesheet indices and shared element references are substituted for the values they point
|
|
17
|
+
/// at while parsing, so this holds no handle into a per-payload lookup table.
|
|
15
18
|
private let _props: [String: JSONValue]?
|
|
16
19
|
|
|
17
|
-
///
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/// Optional shared elements for resolving element references
|
|
21
|
-
public let sharedElements: [JSONValue]?
|
|
20
|
+
/// Style dictionary with expanded keys, resolved once while parsing.
|
|
21
|
+
private let _style: [String: JSONValue]?
|
|
22
22
|
|
|
23
23
|
// MARK: - Hashable
|
|
24
24
|
|
|
25
|
+
/// `_props` carries fully resolved values, so it alone distinguishes elements that render
|
|
26
|
+
/// differently. Identity deliberately omits `_style`, which is derived from `_props` and would
|
|
27
|
+
/// only restate it.
|
|
25
28
|
public func hash(into hasher: inout Hasher) {
|
|
26
29
|
hasher.combine(type)
|
|
27
30
|
hasher.combine(id)
|
|
@@ -52,39 +55,15 @@ public struct VoltraElement: Hashable {
|
|
|
52
55
|
|
|
53
56
|
/// Style dictionary with expanded keys
|
|
54
57
|
public var style: [String: JSONValue]? {
|
|
55
|
-
|
|
56
|
-
return nil
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let styleDict: [String: JSONValue]
|
|
60
|
-
|
|
61
|
-
// Handle stylesheet reference (integer index)
|
|
62
|
-
if let index = styleValue.intValue,
|
|
63
|
-
let stylesheet = stylesheet,
|
|
64
|
-
index >= 0, index < stylesheet.count
|
|
65
|
-
{
|
|
66
|
-
styleDict = stylesheet[index]
|
|
67
|
-
}
|
|
68
|
-
// Handle inline style (object)
|
|
69
|
-
else if let objectValue = styleValue.objectValue {
|
|
70
|
-
styleDict = objectValue
|
|
71
|
-
} else {
|
|
72
|
-
return nil
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
var expanded: [String: JSONValue] = [:]
|
|
76
|
-
for (key, value) in styleDict {
|
|
77
|
-
// Use unified ShortNames mapping for style properties
|
|
78
|
-
let expandedKey = ShortNames.expand(key)
|
|
79
|
-
expanded[expandedKey] = value
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return expanded
|
|
58
|
+
_style
|
|
83
59
|
}
|
|
84
60
|
|
|
85
61
|
// MARK: - Initialization
|
|
86
62
|
|
|
87
63
|
/// Initialize from JSONValue (no serialization roundtrip)
|
|
64
|
+
///
|
|
65
|
+
/// The stylesheet and shared elements are consumed here rather than retained: every reference
|
|
66
|
+
/// into them is replaced by the value it names, leaving a self-contained element.
|
|
88
67
|
/// - Parameters:
|
|
89
68
|
/// - json: The JSON value to parse
|
|
90
69
|
/// - stylesheet: Optional shared stylesheet for style deduplication
|
|
@@ -112,25 +91,28 @@ public struct VoltraElement: Hashable {
|
|
|
112
91
|
children = nil
|
|
113
92
|
}
|
|
114
93
|
|
|
115
|
-
// Extract props
|
|
94
|
+
// Extract props, substituting the values behind every stylesheet index and shared element
|
|
95
|
+
// reference so the element stops depending on lookup tables it does not carry.
|
|
96
|
+
let resolvedProps: [String: JSONValue]?
|
|
116
97
|
if let propsValue = dict["p"], case let .object(propsDict) = propsValue {
|
|
117
|
-
|
|
98
|
+
resolvedProps = Self.resolvingReferences(
|
|
99
|
+
inProps: propsDict,
|
|
100
|
+
stylesheet: stylesheet,
|
|
101
|
+
sharedElements: sharedElements
|
|
102
|
+
)
|
|
118
103
|
} else {
|
|
119
|
-
|
|
104
|
+
resolvedProps = nil
|
|
120
105
|
}
|
|
121
106
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// Store shared elements reference
|
|
126
|
-
self.sharedElements = sharedElements
|
|
107
|
+
_props = resolvedProps
|
|
108
|
+
_style = Self.expandedStyle(from: resolvedProps)
|
|
127
109
|
}
|
|
128
110
|
|
|
129
111
|
/// Get component prop by name - handles both single component and array
|
|
130
112
|
public func componentProp(_ propName: String) -> VoltraNode {
|
|
131
113
|
guard let propValue = props?[propName] else { return .empty }
|
|
132
114
|
|
|
133
|
-
return VoltraNode(from: propValue
|
|
115
|
+
return VoltraNode(from: propValue)
|
|
134
116
|
}
|
|
135
117
|
|
|
136
118
|
/// Decode parameters from props
|
|
@@ -150,4 +132,106 @@ public struct VoltraElement: Hashable {
|
|
|
150
132
|
return try! JSONDecoder().decode(T.self, from: "{}".data(using: .utf8)!)
|
|
151
133
|
}
|
|
152
134
|
}
|
|
135
|
+
|
|
136
|
+
// MARK: - Reference Resolution
|
|
137
|
+
|
|
138
|
+
/// Substitutes the stylesheet entry behind a style index, and the shared element behind a
|
|
139
|
+
/// `$r` reference, throughout a props dictionary.
|
|
140
|
+
///
|
|
141
|
+
/// Element-valued props carry serialized nodes of their own, and those nodes reference the
|
|
142
|
+
/// same payload-level tables. Leaving them unresolved would make two elements with equal
|
|
143
|
+
/// props compare equal while rendering differently.
|
|
144
|
+
/// The renderer registers shared elements only after rendering their contents, so generated
|
|
145
|
+
/// references point to completed entries and cannot form cycles.
|
|
146
|
+
private static func resolvingReferences(
|
|
147
|
+
inProps props: [String: JSONValue],
|
|
148
|
+
stylesheet: [[String: JSONValue]]?,
|
|
149
|
+
sharedElements: [JSONValue]?
|
|
150
|
+
) -> [String: JSONValue] {
|
|
151
|
+
guard stylesheet != nil || sharedElements != nil else {
|
|
152
|
+
return props
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
var resolved = props
|
|
156
|
+
for (key, value) in props {
|
|
157
|
+
if ShortNames.expand(key) == "style" {
|
|
158
|
+
if let index = value.intValue,
|
|
159
|
+
let stylesheet = stylesheet,
|
|
160
|
+
index >= 0, index < stylesheet.count
|
|
161
|
+
{
|
|
162
|
+
resolved[key] = .object(stylesheet[index])
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
resolved[key] = resolvingReferences(
|
|
166
|
+
in: value,
|
|
167
|
+
stylesheet: stylesheet,
|
|
168
|
+
sharedElements: sharedElements
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return resolved
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// Resolves references inside a value that may hold serialized nodes.
|
|
176
|
+
private static func resolvingReferences(
|
|
177
|
+
in value: JSONValue,
|
|
178
|
+
stylesheet: [[String: JSONValue]]?,
|
|
179
|
+
sharedElements: [JSONValue]?
|
|
180
|
+
) -> JSONValue {
|
|
181
|
+
switch value {
|
|
182
|
+
case let .array(items):
|
|
183
|
+
return .array(items.map {
|
|
184
|
+
resolvingReferences(in: $0, stylesheet: stylesheet, sharedElements: sharedElements)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
case let .object(dict):
|
|
188
|
+
// Element reference ($r key) - substitute the shared element it names
|
|
189
|
+
if let refIndex = dict["$r"]?.intValue,
|
|
190
|
+
let sharedElements = sharedElements,
|
|
191
|
+
refIndex >= 0, refIndex < sharedElements.count
|
|
192
|
+
{
|
|
193
|
+
return resolvingReferences(
|
|
194
|
+
in: sharedElements[refIndex],
|
|
195
|
+
stylesheet: stylesheet,
|
|
196
|
+
sharedElements: sharedElements
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var resolved = dict
|
|
201
|
+
if let propsValue = dict["p"], case let .object(propsDict) = propsValue {
|
|
202
|
+
resolved["p"] = .object(resolvingReferences(
|
|
203
|
+
inProps: propsDict,
|
|
204
|
+
stylesheet: stylesheet,
|
|
205
|
+
sharedElements: sharedElements
|
|
206
|
+
))
|
|
207
|
+
}
|
|
208
|
+
if let childrenValue = dict["c"] {
|
|
209
|
+
resolved["c"] = resolvingReferences(
|
|
210
|
+
in: childrenValue,
|
|
211
|
+
stylesheet: stylesheet,
|
|
212
|
+
sharedElements: sharedElements
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
return .object(resolved)
|
|
216
|
+
|
|
217
|
+
default:
|
|
218
|
+
return value
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// Expands the short keys of an already resolved style prop into full property names.
|
|
223
|
+
private static func expandedStyle(from props: [String: JSONValue]?) -> [String: JSONValue]? {
|
|
224
|
+
guard let styleValue = props?["s"] ?? props?["style"],
|
|
225
|
+
let styleDict = styleValue.objectValue
|
|
226
|
+
else {
|
|
227
|
+
return nil
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var expanded: [String: JSONValue] = [:]
|
|
231
|
+
for (key, value) in styleDict {
|
|
232
|
+
// Use unified ShortNames mapping for style properties
|
|
233
|
+
expanded[ShortNames.expand(key)] = value
|
|
234
|
+
}
|
|
235
|
+
return expanded
|
|
236
|
+
}
|
|
153
237
|
}
|
|
@@ -8,6 +8,9 @@ public class VoltraEventBus {
|
|
|
8
8
|
public static let shared = VoltraEventBus()
|
|
9
9
|
|
|
10
10
|
private var observer: NSObjectProtocol?
|
|
11
|
+
private var renderFailureObserver: UUID?
|
|
12
|
+
private var handler: ((String, [String: Any]) -> Void)?
|
|
13
|
+
private var renderFailureListenerCount = 0
|
|
11
14
|
private let lock = NSLock()
|
|
12
15
|
|
|
13
16
|
private init() {}
|
|
@@ -39,20 +42,18 @@ public class VoltraEventBus {
|
|
|
39
42
|
/// - Parameter handler: A closure that receives the event name and event data dictionary
|
|
40
43
|
public func subscribe(handler: @escaping (String, [String: Any]) -> Void) {
|
|
41
44
|
lock.lock()
|
|
42
|
-
defer { lock.unlock() }
|
|
43
|
-
|
|
44
45
|
if let observer {
|
|
45
46
|
NotificationCenter.default.removeObserver(observer)
|
|
46
47
|
self.observer = nil
|
|
47
48
|
}
|
|
49
|
+
if let renderFailureObserver {
|
|
50
|
+
VoltraDynamicLiveActivityRenderFailureReporter.removeChangeObserver(renderFailureObserver)
|
|
51
|
+
self.renderFailureObserver = nil
|
|
52
|
+
}
|
|
53
|
+
self.handler = handler
|
|
48
54
|
|
|
49
55
|
// 1. Replay persisted events from UserDefaults (interactions from widget)
|
|
50
56
|
let persistedEvents = VoltraPersistentEventQueue.popAll()
|
|
51
|
-
for event in persistedEvents {
|
|
52
|
-
handler(event.name, event.data)
|
|
53
|
-
}
|
|
54
|
-
VoltraLogger.event.info("Replayed \(persistedEvents.count) persisted events")
|
|
55
|
-
|
|
56
57
|
// 2. Listen for all events via NotificationCenter (hot delivery)
|
|
57
58
|
observer = NotificationCenter.default.addObserver(
|
|
58
59
|
forName: .voltraEvent,
|
|
@@ -66,6 +67,44 @@ public class VoltraEventBus {
|
|
|
66
67
|
}
|
|
67
68
|
handler(eventName, userInfo)
|
|
68
69
|
}
|
|
70
|
+
renderFailureObserver = VoltraDynamicLiveActivityRenderFailureReporter.observeChanges { [weak self] in
|
|
71
|
+
self?.drainDynamicLiveActivityRenderFailures()
|
|
72
|
+
}
|
|
73
|
+
lock.unlock()
|
|
74
|
+
|
|
75
|
+
for event in persistedEvents {
|
|
76
|
+
handler(event.name, event.data)
|
|
77
|
+
}
|
|
78
|
+
VoltraLogger.event.info("Replayed \(persistedEvents.count) persisted events")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// JS owns the dedicated emitter subscription count. Persisted failures must
|
|
82
|
+
/// remain queued whenever there is no relevant JavaScript listener.
|
|
83
|
+
public func setDynamicLiveActivityRenderFailureListenerActive(_ active: Bool) {
|
|
84
|
+
lock.lock()
|
|
85
|
+
renderFailureListenerCount = active ? 1 : 0
|
|
86
|
+
lock.unlock()
|
|
87
|
+
if active {
|
|
88
|
+
drainDynamicLiveActivityRenderFailures()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Flush only the dedicated Dynamic Live Activity diagnostic queue. This
|
|
93
|
+
/// cannot read, displace, or clear persistent interaction events.
|
|
94
|
+
public func drainDynamicLiveActivityRenderFailures() {
|
|
95
|
+
lock.lock()
|
|
96
|
+
let handler = handler
|
|
97
|
+
let isReady = renderFailureListenerCount > 0
|
|
98
|
+
lock.unlock()
|
|
99
|
+
guard isReady, let handler else { return }
|
|
100
|
+
|
|
101
|
+
let failures = VoltraDynamicLiveActivityRenderFailureReporter.drain()
|
|
102
|
+
for failure in failures {
|
|
103
|
+
handler(failure.type, failure.dictionary)
|
|
104
|
+
}
|
|
105
|
+
if !failures.isEmpty {
|
|
106
|
+
VoltraLogger.event.info("Replayed \(failures.count) Dynamic Live Activity render failures")
|
|
107
|
+
}
|
|
69
108
|
}
|
|
70
109
|
|
|
71
110
|
/// Unsubscribe from Voltra events
|
|
@@ -77,6 +116,12 @@ public class VoltraEventBus {
|
|
|
77
116
|
NotificationCenter.default.removeObserver(observer)
|
|
78
117
|
self.observer = nil
|
|
79
118
|
}
|
|
119
|
+
if let renderFailureObserver {
|
|
120
|
+
VoltraDynamicLiveActivityRenderFailureReporter.removeChangeObserver(renderFailureObserver)
|
|
121
|
+
self.renderFailureObserver = nil
|
|
122
|
+
}
|
|
123
|
+
handler = nil
|
|
124
|
+
renderFailureListenerCount = 0
|
|
80
125
|
}
|
|
81
126
|
|
|
82
127
|
deinit {
|
|
@@ -15,6 +15,7 @@ import JavaScriptCore
|
|
|
15
15
|
/// indefinitely.
|
|
16
16
|
public enum VoltraJSRenderer {
|
|
17
17
|
private static var _context: JSContext?
|
|
18
|
+
private static var liveActivitySources: [String: String] = [:]
|
|
18
19
|
private static let lock = NSLock()
|
|
19
20
|
private static let TAG = "VoltraJSRenderer"
|
|
20
21
|
|
|
@@ -30,6 +31,28 @@ public enum VoltraJSRenderer {
|
|
|
30
31
|
/// Idempotent: re-evaluating the same widget overwrites the captured exports — used
|
|
31
32
|
/// by dev-mode hot-reload (always-refetch policy).
|
|
32
33
|
public static func evaluateBundle(source: String, widgetId: String) -> Bool {
|
|
34
|
+
evaluateBundle(source: source, id: widgetId, registryName: "__voltraWidgets", kind: "widget")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Evaluate a Dynamic Live Activity bundle in the shared JSContext. Its exports
|
|
38
|
+
/// are captured separately from Dynamic Widgets so both collections may use the
|
|
39
|
+
/// same definition ID without overwriting one another.
|
|
40
|
+
public static func evaluateLiveActivityBundle(source: String, definitionId: String) -> Bool {
|
|
41
|
+
let evaluated = evaluateBundle(
|
|
42
|
+
source: source,
|
|
43
|
+
id: definitionId,
|
|
44
|
+
registryName: "__voltraDynamicLiveActivities",
|
|
45
|
+
kind: "live activity"
|
|
46
|
+
)
|
|
47
|
+
if evaluated {
|
|
48
|
+
lock.lock()
|
|
49
|
+
liveActivitySources[definitionId] = source
|
|
50
|
+
lock.unlock()
|
|
51
|
+
}
|
|
52
|
+
return evaluated
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private static func evaluateBundle(source: String, id: String, registryName: String, kind: String) -> Bool {
|
|
33
56
|
lock.lock()
|
|
34
57
|
defer { lock.unlock() }
|
|
35
58
|
|
|
@@ -38,7 +61,7 @@ public enum VoltraJSRenderer {
|
|
|
38
61
|
return false
|
|
39
62
|
}
|
|
40
63
|
|
|
41
|
-
let escapedId = jsStringLiteral(
|
|
64
|
+
let escapedId = jsStringLiteral(id)
|
|
42
65
|
// Metro emits the bundle's entry invocation as `__r(<entryModuleId>);` near the
|
|
43
66
|
// end of the file (before the sourcemap/sourceURL comments). The entry id is NOT
|
|
44
67
|
// always 0 — when Metro serves multiple widget bundles from the same process, it
|
|
@@ -50,34 +73,34 @@ public enum VoltraJSRenderer {
|
|
|
50
73
|
let wrapped = """
|
|
51
74
|
\(source)
|
|
52
75
|
;(function () {
|
|
53
|
-
if (!globalThis
|
|
54
|
-
globalThis
|
|
76
|
+
if (!globalThis.\(registryName)) { globalThis.\(registryName) = {}; }
|
|
77
|
+
globalThis.\(registryName)[\(escapedId)] = __r(\(entryModuleId));
|
|
55
78
|
})();
|
|
56
79
|
"""
|
|
57
80
|
|
|
58
81
|
ctx.exception = nil
|
|
59
82
|
ctx.evaluateScript(wrapped)
|
|
60
83
|
if let message = exceptionMessage(ctx) {
|
|
61
|
-
VoltraLogger.widget.error("[\(TAG)] Bundle eval failed for
|
|
84
|
+
VoltraLogger.widget.error("[\(TAG)] Bundle eval failed for \(kind) id=\(id): \(message)")
|
|
62
85
|
return false
|
|
63
86
|
}
|
|
64
87
|
|
|
65
88
|
// Verify the bootstrap captured the exports correctly
|
|
66
89
|
guard
|
|
67
|
-
let registry = ctx.objectForKeyedSubscript(
|
|
90
|
+
let registry = ctx.objectForKeyedSubscript(registryName),
|
|
68
91
|
!registry.isUndefined,
|
|
69
|
-
let
|
|
70
|
-
!
|
|
71
|
-
let renderFn =
|
|
92
|
+
let entry = registry.objectForKeyedSubscript(id),
|
|
93
|
+
!entry.isUndefined,
|
|
94
|
+
let renderFn = entry.objectForKeyedSubscript("render"),
|
|
72
95
|
renderFn.isObject,
|
|
73
96
|
renderFn.objectForKeyedSubscript("call") != nil
|
|
74
97
|
else {
|
|
75
|
-
VoltraLogger.widget.error("[\(TAG)] Bundle evaluated but did not expose render() for
|
|
98
|
+
VoltraLogger.widget.error("[\(TAG)] Bundle evaluated but did not expose render() for \(kind) id=\(id)")
|
|
76
99
|
return false
|
|
77
100
|
}
|
|
78
101
|
_ = renderFn
|
|
79
102
|
|
|
80
|
-
VoltraLogger.widget.info("[\(TAG)] Bundle evaluated for
|
|
103
|
+
VoltraLogger.widget.info("[\(TAG)] Bundle evaluated for \(kind) id=\(id) (\(source.count) chars)")
|
|
81
104
|
return true
|
|
82
105
|
}
|
|
83
106
|
|
|
@@ -90,11 +113,31 @@ public enum VoltraJSRenderer {
|
|
|
90
113
|
/// evaluation never ran. The View calls this before `render()` so rendering never depends on
|
|
91
114
|
/// which process evaluated the bundle.
|
|
92
115
|
public static func ensureEvaluated(widgetId: String, source: String) -> Bool {
|
|
116
|
+
ensureEvaluated(source: source, id: widgetId, registryName: "__voltraWidgets", kind: "widget")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public static func ensureLiveActivityEvaluated(definitionId: String, source: String) -> Bool {
|
|
120
|
+
lock.lock()
|
|
121
|
+
let currentSource = liveActivitySources[definitionId]
|
|
122
|
+
let hasRender =
|
|
123
|
+
_context?
|
|
124
|
+
.objectForKeyedSubscript("__voltraDynamicLiveActivities")?
|
|
125
|
+
.objectForKeyedSubscript(definitionId)?
|
|
126
|
+
.objectForKeyedSubscript("render")?
|
|
127
|
+
.isObject ?? false
|
|
128
|
+
lock.unlock()
|
|
129
|
+
if hasRender, currentSource == source {
|
|
130
|
+
return true
|
|
131
|
+
}
|
|
132
|
+
return evaluateLiveActivityBundle(source: source, definitionId: definitionId)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private static func ensureEvaluated(source: String, id: String, registryName: String, kind: String) -> Bool {
|
|
93
136
|
lock.lock()
|
|
94
137
|
let alreadyEvaluated =
|
|
95
138
|
_context?
|
|
96
|
-
.objectForKeyedSubscript(
|
|
97
|
-
.objectForKeyedSubscript(
|
|
139
|
+
.objectForKeyedSubscript(registryName)?
|
|
140
|
+
.objectForKeyedSubscript(id)?
|
|
98
141
|
.objectForKeyedSubscript("render")?
|
|
99
142
|
.isObject ?? false
|
|
100
143
|
lock.unlock()
|
|
@@ -102,7 +145,7 @@ public enum VoltraJSRenderer {
|
|
|
102
145
|
if alreadyEvaluated {
|
|
103
146
|
return true
|
|
104
147
|
}
|
|
105
|
-
return evaluateBundle(source: source,
|
|
148
|
+
return evaluateBundle(source: source, id: id, registryName: registryName, kind: kind)
|
|
106
149
|
}
|
|
107
150
|
|
|
108
151
|
/// Invoke the previously-evaluated widget's `render(propsJSON, envJSON)` function and
|
|
@@ -114,39 +157,64 @@ public enum VoltraJSRenderer {
|
|
|
114
157
|
widgetId: String,
|
|
115
158
|
propsJSON: String,
|
|
116
159
|
envJSON: String
|
|
160
|
+
) -> String? {
|
|
161
|
+
render(id: widgetId, propsJSON: propsJSON, envJSON: envJSON, registryName: "__voltraWidgets", kind: "widget")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Render a Dynamic Live Activity from its separately registered definition.
|
|
165
|
+
public static func renderLiveActivity(
|
|
166
|
+
definitionId: String,
|
|
167
|
+
propsJSON: String,
|
|
168
|
+
envJSON: String
|
|
169
|
+
) -> String? {
|
|
170
|
+
render(
|
|
171
|
+
id: definitionId,
|
|
172
|
+
propsJSON: propsJSON,
|
|
173
|
+
envJSON: envJSON,
|
|
174
|
+
registryName: "__voltraDynamicLiveActivities",
|
|
175
|
+
kind: "live activity"
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private static func render(
|
|
180
|
+
id: String,
|
|
181
|
+
propsJSON: String,
|
|
182
|
+
envJSON: String,
|
|
183
|
+
registryName: String,
|
|
184
|
+
kind: String
|
|
117
185
|
) -> String? {
|
|
118
186
|
lock.lock()
|
|
119
187
|
defer { lock.unlock() }
|
|
120
188
|
|
|
121
189
|
guard let ctx = _context else {
|
|
122
|
-
VoltraLogger.widget.error("[\(TAG)] render
|
|
190
|
+
VoltraLogger.widget.error("[\(TAG)] render \(kind) id=\(id): no JSContext (call evaluateBundle first)")
|
|
123
191
|
return nil
|
|
124
192
|
}
|
|
125
193
|
|
|
126
194
|
guard
|
|
127
|
-
let registry = ctx.objectForKeyedSubscript(
|
|
195
|
+
let registry = ctx.objectForKeyedSubscript(registryName),
|
|
128
196
|
!registry.isUndefined,
|
|
129
|
-
let
|
|
130
|
-
!
|
|
131
|
-
let renderFn =
|
|
197
|
+
let entry = registry.objectForKeyedSubscript(id),
|
|
198
|
+
!entry.isUndefined,
|
|
199
|
+
let renderFn = entry.objectForKeyedSubscript("render"),
|
|
132
200
|
renderFn.isObject
|
|
133
201
|
else {
|
|
134
|
-
VoltraLogger.widget.error("[\(TAG)] render
|
|
202
|
+
VoltraLogger.widget.error("[\(TAG)] render \(kind) id=\(id): no captured render() — bundle not evaluated?")
|
|
135
203
|
return nil
|
|
136
204
|
}
|
|
137
205
|
|
|
138
206
|
ctx.exception = nil
|
|
139
207
|
guard let result = renderFn.call(withArguments: [propsJSON, envJSON]) else {
|
|
140
|
-
VoltraLogger.widget.error("[\(TAG)] render
|
|
208
|
+
VoltraLogger.widget.error("[\(TAG)] render \(kind) id=\(id): call returned nil")
|
|
141
209
|
return nil
|
|
142
210
|
}
|
|
143
211
|
if let message = exceptionMessage(ctx) {
|
|
144
|
-
VoltraLogger.widget.error("[\(TAG)] render
|
|
212
|
+
VoltraLogger.widget.error("[\(TAG)] render \(kind) id=\(id) threw: \(message)")
|
|
145
213
|
return nil
|
|
146
214
|
}
|
|
147
215
|
|
|
148
216
|
guard result.isString else {
|
|
149
|
-
VoltraLogger.widget.error("[\(TAG)] render
|
|
217
|
+
VoltraLogger.widget.error("[\(TAG)] render \(kind) id=\(id) did not return a string")
|
|
150
218
|
return nil
|
|
151
219
|
}
|
|
152
220
|
return result.toString()
|
|
@@ -155,7 +223,9 @@ public enum VoltraJSRenderer {
|
|
|
155
223
|
// MARK: - Lifecycle
|
|
156
224
|
|
|
157
225
|
private static func context() -> JSContext? {
|
|
158
|
-
if let existing = _context {
|
|
226
|
+
if let existing = _context {
|
|
227
|
+
return existing
|
|
228
|
+
}
|
|
159
229
|
guard let ctx = JSContext() else {
|
|
160
230
|
VoltraLogger.widget.error("[\(TAG)] Failed to create JSContext")
|
|
161
231
|
return nil
|