@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.
- package/Voltra.podspec +1 -0
- package/build/commonjs/events.js +23 -0
- package/build/commonjs/events.js.map +1 -1
- package/build/commonjs/index.js +59 -0
- 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/events.js +23 -0
- package/build/module/events.js.map +1 -1
- package/build/module/index.js +3 -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/events.d.ts +7 -0
- package/build/typescript/commonjs/events.d.ts.map +1 -1
- package/build/typescript/commonjs/index.d.ts +3 -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 +20 -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/events.d.ts +7 -0
- package/build/typescript/module/events.d.ts.map +1 -1
- package/build/typescript/module/index.d.ts +3 -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 +20 -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 +6 -0
- 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/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 +51 -11
- package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/cjs/ios-widget/xcode/index.js +21 -5
- package/expo-plugin/build/cjs/ios-widget/xcode/index.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 +6 -0
- 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/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 +51 -11
- package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
- package/expo-plugin/build/esm/ios-widget/xcode/index.js +21 -5
- package/expo-plugin/build/esm/ios-widget/xcode/index.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/widgetPlist.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +3 -1
- package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
- package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +2 -1
- package/expo-plugin/build/types/ios-widget/xcode/index.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 +9 -0
- 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/widgetPlist.ts +37 -23
- package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +34 -28
- package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +59 -12
- package/expo-plugin/src/ios-widget/xcode/index.ts +23 -6
- 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 +32 -3
- package/ios/VoltraWidget.podspec +1 -0
- package/ios/app/NativeVoltra.mm +107 -7
- package/ios/app/VoltraLiveActivityManager.swift +30 -0
- package/ios/app/VoltraLiveActivityService.swift +77 -5
- package/ios/app/VoltraModule.swift +99 -1
- package/ios/app/VoltraModuleImpl.swift +100 -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/DynamicWidgetPropsStore.swift +73 -6
- 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/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 +15 -0
- package/ios/shared/VoltraElement.swift +127 -43
- package/ios/shared/VoltraEventBus.swift +52 -7
- package/ios/shared/VoltraJSRenderer.swift +90 -22
- package/ios/shared/VoltraNode.swift +1 -109
- package/ios/shared/VoltraRefreshIntent.swift +1 -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 +44 -6
- package/ios/target/VoltraDynamicWidgetServerUpdateProvider.swift +204 -0
- package/ios/target/VoltraHomeWidget.swift +85 -90
- package/ios/ui/Extensions/VoltraNode+View.swift +113 -0
- package/ios/ui/Generated/Parameters/ChartParameters.swift +3 -0
- package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +2 -2
- package/ios/ui/Views/VoltraChart.swift +108 -0
- package/package.json +4 -4
- package/src/events.ts +34 -0
- package/src/index.ts +22 -0
- package/src/live-activity/api.ts +2 -2
- package/src/live-activity/dynamic-api.ts +240 -0
- package/src/native/NativeVoltra.ts +21 -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
|
@@ -6,7 +6,8 @@ import { ensureWidgetFileReference, normalizeRef } from './fileReferences'
|
|
|
6
6
|
|
|
7
7
|
const pbxFile = require('xcode/lib/pbxFile')
|
|
8
8
|
|
|
9
|
-
const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic
|
|
9
|
+
const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Content'
|
|
10
|
+
const LEGACY_WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
|
|
10
11
|
|
|
11
12
|
// Release-only build phase that bakes each Dynamic Widget's production JS bundle into the
|
|
12
13
|
// extension's resources. Debug builds fetch from Metro (and hot-reload), so this no-ops there. Runs
|
|
@@ -14,7 +15,8 @@ const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
|
|
|
14
15
|
// voltra-widget-<id>.bundle lands in the .appex (Bundle.main) where the runtime's release loader
|
|
15
16
|
// reads it. SRCROOT is the ios/ dir; the project root is one level up, matching how Expo's main
|
|
16
17
|
// "Bundle React Native code and images" phase resolves things.
|
|
17
|
-
|
|
18
|
+
function bundleShellScript(content: 'all' | 'live-activities'): string {
|
|
19
|
+
return `if [[ "$CONFIGURATION" == *Debug* ]]; then
|
|
18
20
|
echo "Voltra: Debug build — Dynamic Widgets load from Metro, skipping bundling"
|
|
19
21
|
exit 0
|
|
20
22
|
fi
|
|
@@ -48,6 +50,7 @@ try {
|
|
|
48
50
|
console.error(error && error.message ? error.message : String(error))
|
|
49
51
|
process.exit(1)
|
|
50
52
|
}
|
|
53
|
+
|
|
51
54
|
NODE
|
|
52
55
|
)"
|
|
53
56
|
if [[ -z "$BUNDLER" ]]; then
|
|
@@ -56,14 +59,23 @@ if [[ -z "$BUNDLER" ]]; then
|
|
|
56
59
|
fi
|
|
57
60
|
|
|
58
61
|
echo "Voltra: widget bundler resolved to $BUNDLER"
|
|
59
|
-
"$NODE_BINARY" "$BUNDLER" --out-dir "$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH" --platform ios --project-root "$PROJECT_ROOT"
|
|
62
|
+
"$NODE_BINARY" "$BUNDLER" --out-dir "$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH" --platform ios --project-root "$PROJECT_ROOT" --content ${content}
|
|
60
63
|
`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pbxQuotedShellScript(script: string): string {
|
|
67
|
+
return `"${script.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`
|
|
68
|
+
}
|
|
61
69
|
|
|
62
70
|
/**
|
|
63
71
|
* Adds (idempotently) the release-only shell-script phase that bakes Dynamic Widget
|
|
64
72
|
* bundles into the extension. Safe to call on every prebuild; only added when absent.
|
|
65
73
|
*/
|
|
66
|
-
export function ensureWidgetBundleScriptPhase(
|
|
74
|
+
export function ensureWidgetBundleScriptPhase(
|
|
75
|
+
xcodeProject: XcodeProject,
|
|
76
|
+
targetUuid: string,
|
|
77
|
+
content: 'all' | 'live-activities' = 'all'
|
|
78
|
+
): void {
|
|
67
79
|
const nativeTargets = xcodeProject.pbxNativeTargetSection()
|
|
68
80
|
const target = nativeTargets[targetUuid]
|
|
69
81
|
if (!target) {
|
|
@@ -75,15 +87,37 @@ export function ensureWidgetBundleScriptPhase(xcodeProject: XcodeProject, target
|
|
|
75
87
|
|
|
76
88
|
const shellPhases = xcodeProject.hash.project.objects.PBXShellScriptBuildPhase || {}
|
|
77
89
|
const quotedName = `"${WIDGET_BUNDLE_PHASE_NAME}"`
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', WIDGET_BUNDLE_PHASE_NAME, targetUuid, {
|
|
84
|
-
shellPath: '/bin/sh',
|
|
85
|
-
shellScript: WIDGET_BUNDLE_SHELL_SCRIPT,
|
|
90
|
+
const quotedLegacyName = `"${LEGACY_WIDGET_BUNDLE_PHASE_NAME}"`
|
|
91
|
+
const matchingEntries = target.buildPhases.filter((entry: any) => {
|
|
92
|
+
const name = shellPhases[entry.value]?.name
|
|
93
|
+
return name === quotedName || name === quotedLegacyName
|
|
86
94
|
})
|
|
95
|
+
let phase: any | undefined
|
|
96
|
+
if (matchingEntries.length > 0) {
|
|
97
|
+
const retained = matchingEntries[0]
|
|
98
|
+
phase = shellPhases[retained.value]
|
|
99
|
+
phase.name = quotedName
|
|
100
|
+
target.buildPhases = target.buildPhases.filter((entry: any) => {
|
|
101
|
+
if (entry === retained || !matchingEntries.includes(entry)) return true
|
|
102
|
+
delete shellPhases[entry.value]
|
|
103
|
+
delete shellPhases[`${entry.value}_comment`]
|
|
104
|
+
return false
|
|
105
|
+
})
|
|
106
|
+
} else {
|
|
107
|
+
xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', WIDGET_BUNDLE_PHASE_NAME, targetUuid, {
|
|
108
|
+
shellPath: '/bin/sh',
|
|
109
|
+
shellScript: bundleShellScript(content),
|
|
110
|
+
})
|
|
111
|
+
phase = Object.values(xcodeProject.hash.project.objects.PBXShellScriptBuildPhase || {}).find(
|
|
112
|
+
(candidate: any) => candidate?.name === quotedName
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
if (phase) {
|
|
116
|
+
// xcode's serializer expects an already PBX-quoted value when an existing
|
|
117
|
+
// phase is reconciled directly (addBuildPhase performs this for new ones).
|
|
118
|
+
phase.shellScript = pbxQuotedShellScript(bundleShellScript(content))
|
|
119
|
+
phase.alwaysOutOfDate = 1
|
|
120
|
+
}
|
|
87
121
|
|
|
88
122
|
// The phase intentionally re-bakes on every release build (it can't statically enumerate every
|
|
89
123
|
// widget source as an input). Mark it always-out-of-date so Xcode doesn't warn about the missing
|
|
@@ -112,6 +146,8 @@ export interface EnsureBuildPhasesOptions {
|
|
|
112
146
|
}
|
|
113
147
|
widgetFiles: IOSWidgetExtensionFiles
|
|
114
148
|
mainTargetUuid?: string
|
|
149
|
+
/** Generated ActivityKit types compiled into both the extension and the app. */
|
|
150
|
+
mainSwiftFiles?: string[]
|
|
115
151
|
}
|
|
116
152
|
|
|
117
153
|
/**
|
|
@@ -191,6 +227,17 @@ export function ensureBuildPhases(xcodeProject: XcodeProject, options: EnsureBui
|
|
|
191
227
|
ensureBuildPhaseFiles(xcodeProject, sourcesPhase, [...swiftFiles, ...intentFiles], targetName)
|
|
192
228
|
}
|
|
193
229
|
|
|
230
|
+
if (options.mainSwiftFiles && options.mainSwiftFiles.length > 0) {
|
|
231
|
+
let mainSourcesPhase = findTargetPhaseByType(xcodeProject, mainTargetUuid, 'PBXSourcesBuildPhase')
|
|
232
|
+
if (!mainSourcesPhase) {
|
|
233
|
+
xcodeProject.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', mainTargetUuid, folderType, buildPath)
|
|
234
|
+
mainSourcesPhase = findTargetPhaseByType(xcodeProject, mainTargetUuid, 'PBXSourcesBuildPhase')
|
|
235
|
+
}
|
|
236
|
+
if (mainSourcesPhase) {
|
|
237
|
+
ensureBuildPhaseFiles(xcodeProject, mainSourcesPhase, options.mainSwiftFiles, targetName)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
194
241
|
// Copy files build phase (embed extension into main app) — the embed phase is matched purely by
|
|
195
242
|
// its `dstSubfolderSpec == 13` semantics, both before and after creation
|
|
196
243
|
let copyFilesPhase = findExistingEmbedExtensionsPhase(xcodeProject, mainTargetUuid)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConfigPlugin, withXcodeProject, XcodeProject } from '@expo/config-plugins'
|
|
2
2
|
import * as path from 'path'
|
|
3
3
|
|
|
4
|
-
import type { IOSWidgetConfig, IOSWidgetExtensionFiles } from '../../types'
|
|
4
|
+
import type { IOSDynamicLiveActivityConfig, IOSWidgetConfig, IOSWidgetExtensionFiles } from '../../types'
|
|
5
5
|
import { getIOSWidgetExtensionFiles } from '../../utils/fileDiscovery'
|
|
6
6
|
import { detectClientRenderedWidgets } from '../clientRendered'
|
|
7
7
|
import { ensureBuildPhases, ensureWidgetBundleScriptPhase } from './buildPhases'
|
|
@@ -20,6 +20,7 @@ export interface ConfigureXcodeProjectProps {
|
|
|
20
20
|
/** App build number; becomes the widget's CURRENT_PROJECT_VERSION. */
|
|
21
21
|
buildNumber?: string
|
|
22
22
|
widgets?: IOSWidgetConfig[]
|
|
23
|
+
liveActivities?: IOSDynamicLiveActivityConfig[]
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -42,6 +43,13 @@ export function applyXcodeChanges(
|
|
|
42
43
|
): void {
|
|
43
44
|
const { targetName, bundleIdentifier, deploymentTarget, version, buildNumber } = props
|
|
44
45
|
const groupName = 'Embed Foundation Extensions'
|
|
46
|
+
const mainTargetUuid = xcodeProject.getFirstTarget().uuid
|
|
47
|
+
// The catalog is compiled into the app even for a legacy-only configuration.
|
|
48
|
+
// Keep it in the extension group too so both targets share one PBX file reference.
|
|
49
|
+
const effectiveWidgetFiles: IOSWidgetExtensionFiles = {
|
|
50
|
+
...widgetFiles,
|
|
51
|
+
swiftFiles: Array.from(new Set([...widgetFiles.swiftFiles, 'VoltraDynamicLiveActivityTypes.swift'])),
|
|
52
|
+
}
|
|
45
53
|
|
|
46
54
|
// Read main app target settings to synchronize code signing (per configuration).
|
|
47
55
|
const mainAppSettings = getMainAppTargetSettings(xcodeProject)
|
|
@@ -92,7 +100,7 @@ export function applyXcodeChanges(
|
|
|
92
100
|
// reference them (the phases resolve files through the widget-scoped group).
|
|
93
101
|
ensurePbxGroup(xcodeProject, {
|
|
94
102
|
targetName,
|
|
95
|
-
widgetFiles,
|
|
103
|
+
widgetFiles: effectiveWidgetFiles,
|
|
96
104
|
})
|
|
97
105
|
|
|
98
106
|
// Ensure build phases and their files.
|
|
@@ -101,13 +109,22 @@ export function applyXcodeChanges(
|
|
|
101
109
|
targetName,
|
|
102
110
|
groupName,
|
|
103
111
|
productFile,
|
|
104
|
-
widgetFiles,
|
|
112
|
+
widgetFiles: effectiveWidgetFiles,
|
|
105
113
|
mainTargetUuid: xcodeProject.getFirstTarget().uuid,
|
|
114
|
+
// The app-side lifecycle service always compiles against the generated catalog.
|
|
115
|
+
// An empty catalog keeps legacy-only apps source-compatible.
|
|
116
|
+
mainSwiftFiles: ['VoltraDynamicLiveActivityTypes.swift'],
|
|
106
117
|
})
|
|
107
118
|
|
|
108
|
-
if (hasClientRenderedWidgets) {
|
|
119
|
+
if (hasClientRenderedWidgets || (props.liveActivities?.length ?? 0) > 0) {
|
|
109
120
|
ensureWidgetBundleScriptPhase(xcodeProject, targetUuid)
|
|
110
121
|
}
|
|
122
|
+
if ((props.liveActivities?.length ?? 0) > 0) {
|
|
123
|
+
// Local Dynamic Live Activity starts preflight the baked definition from
|
|
124
|
+
// the app process, while WidgetKit renders from the extension process.
|
|
125
|
+
// Bake the same manifest into both products.
|
|
126
|
+
ensureWidgetBundleScriptPhase(xcodeProject, mainTargetUuid, 'live-activities')
|
|
127
|
+
}
|
|
111
128
|
|
|
112
129
|
ensureTargetAttributes(xcodeProject, targetUuid)
|
|
113
130
|
ensureTargetDependency(xcodeProject, targetUuid)
|
|
@@ -128,7 +145,7 @@ export function applyXcodeChanges(
|
|
|
128
145
|
* mutation to {@link applyXcodeChanges}.
|
|
129
146
|
*/
|
|
130
147
|
export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (config, props) => {
|
|
131
|
-
const { targetName, widgets } = props
|
|
148
|
+
const { targetName, widgets, liveActivities } = props
|
|
132
149
|
|
|
133
150
|
return withXcodeProject(config, (config) => {
|
|
134
151
|
if (config.modRequest.introspect) {
|
|
@@ -147,7 +164,7 @@ export const configureXcodeProject: ConfigPlugin<ConfigureXcodeProjectProps> = (
|
|
|
147
164
|
const targetPath = path.join(platformProjectRoot, targetName)
|
|
148
165
|
const widgetFiles = getIOSWidgetExtensionFiles(targetPath, targetName)
|
|
149
166
|
|
|
150
|
-
applyXcodeChanges(xcodeProject, props, widgetFiles, hasClientRenderedWidgets)
|
|
167
|
+
applyXcodeChanges(xcodeProject, props, widgetFiles, hasClientRenderedWidgets || (liveActivities?.length ?? 0) > 0)
|
|
151
168
|
|
|
152
169
|
return config
|
|
153
170
|
})
|
package/expo-plugin/src/types.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ConfigPlugin } from '@expo/config-plugins'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
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
|
|
48
|
+
const existingInfoPlist = config.ios?.infoPlist
|
|
49
|
+
const existingTypes = existingInfoPlist?.CFBundleURLTypes as URLType[] | undefined
|
|
12
50
|
|
|
13
|
-
|
|
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
|
|
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 (
|
|
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
|
|
9
|
+
import { iosServerUpdateRules } from './ios/serverUpdate'
|
|
10
|
+
import { getDynamicLiveActivityAttributesType } from '@use-voltra/expo-plugin'
|
|
11
|
+
|
|
12
|
+
import { widgetKind } from './constants'
|
|
13
|
+
import type { IOSConfigPluginProps, IOSDynamicLiveActivityConfig, IOSWidgetConfig, IOSWidgetFamily } from './types'
|
|
9
14
|
|
|
10
15
|
const VALID_FAMILIES: Set<IOSWidgetFamily> = new Set([
|
|
11
16
|
'systemSmall',
|
|
@@ -16,9 +21,31 @@ const VALID_FAMILIES: Set<IOSWidgetFamily> = new Set([
|
|
|
16
21
|
'accessoryRectangular',
|
|
17
22
|
'accessoryInline',
|
|
18
23
|
])
|
|
24
|
+
const DYNAMIC_LIVE_ACTIVITY_ID_PATTERN = /^[a-zA-Z0-9_]+$/
|
|
25
|
+
|
|
26
|
+
export function validateIOSDynamicLiveActivityConfig(
|
|
27
|
+
liveActivity: IOSDynamicLiveActivityConfig,
|
|
28
|
+
projectRoot?: string
|
|
29
|
+
): void {
|
|
30
|
+
if (!liveActivity.id || typeof liveActivity.id !== 'string') {
|
|
31
|
+
throw new Error('Dynamic Live Activity ID is required and must be a string')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!DYNAMIC_LIVE_ACTIVITY_ID_PATTERN.test(liveActivity.id)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Dynamic Live Activity ID '${liveActivity.id}' is invalid. ` +
|
|
37
|
+
'It must be non-empty and contain only alphanumeric characters and underscores.'
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
validateWidgetEntry(liveActivity.entry, liveActivity.id, projectRoot)
|
|
42
|
+
}
|
|
19
43
|
|
|
20
44
|
export function validateIOSWidgetConfig(widget: IOSWidgetConfig, projectRoot?: string): void {
|
|
21
45
|
validateHomeScreenWidgetId(widget.id)
|
|
46
|
+
if (widget.kind !== undefined && (typeof widget.kind !== 'string' || widget.kind.trim() === '')) {
|
|
47
|
+
throw new Error(`Widget '${widget.id}': kind must be a non-empty string`)
|
|
48
|
+
}
|
|
22
49
|
validateWidgetLabel(widget.displayName, widget.id, 'displayName')
|
|
23
50
|
validateWidgetLabel(widget.description, widget.id, 'description')
|
|
24
51
|
validateInitialStatePath(widget.initialStatePath, widget.id, projectRoot)
|
|
@@ -27,6 +54,8 @@ export function validateIOSWidgetConfig(widget: IOSWidgetConfig, projectRoot?: s
|
|
|
27
54
|
validateWidgetEntry(widget.entry, widget.id, projectRoot)
|
|
28
55
|
}
|
|
29
56
|
|
|
57
|
+
validateWidgetServerUpdate(widget.serverUpdate, widget.id, iosServerUpdateRules(widget))
|
|
58
|
+
|
|
30
59
|
if (widget.supportedFamilies) {
|
|
31
60
|
if (!Array.isArray(widget.supportedFamilies)) {
|
|
32
61
|
throw new Error(`Widget '${widget.id}': supportedFamilies must be an array`)
|
|
@@ -60,13 +89,59 @@ export function validateIOSConfigPluginProps(props: IOSConfigPluginProps, projec
|
|
|
60
89
|
}
|
|
61
90
|
|
|
62
91
|
const seenIds = new Set<string>()
|
|
92
|
+
const seenKinds = new Set<string>()
|
|
63
93
|
for (const widget of props.widgets) {
|
|
64
94
|
validateIOSWidgetConfig(widget, projectRoot)
|
|
65
95
|
|
|
96
|
+
// A server-driven Dynamic Widget commits fetched props to the App Group so the widget
|
|
97
|
+
// extension can read them. Without one it would fetch and have nowhere to put the result.
|
|
98
|
+
if (widget.entry !== undefined && widget.serverUpdate !== undefined && !props.groupIdentifier) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Widget '${widget.id}' has both entry and serverUpdate, which requires groupIdentifier ` +
|
|
101
|
+
'so fetched props can be shared with the widget extension.'
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
66
105
|
if (seenIds.has(widget.id)) {
|
|
67
106
|
throw new Error(`Duplicate widget ID: '${widget.id}'`)
|
|
68
107
|
}
|
|
69
108
|
seenIds.add(widget.id)
|
|
109
|
+
|
|
110
|
+
const kind = widgetKind(widget)
|
|
111
|
+
if (seenKinds.has(kind)) {
|
|
112
|
+
throw new Error(`Duplicate widget kind: '${kind}'`)
|
|
113
|
+
}
|
|
114
|
+
seenKinds.add(kind)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (props.liveActivities !== undefined) {
|
|
119
|
+
if (!Array.isArray(props.liveActivities)) {
|
|
120
|
+
throw new Error('liveActivities must be an array')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (props.liveActivities.length > 0 && !props.groupIdentifier) {
|
|
124
|
+
throw new Error('groupIdentifier is required when liveActivities is non-empty')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const seenIds = new Set<string>()
|
|
128
|
+
const seenAttributesTypes = new Map<string, string>()
|
|
129
|
+
for (const liveActivity of props.liveActivities) {
|
|
130
|
+
validateIOSDynamicLiveActivityConfig(liveActivity, projectRoot)
|
|
131
|
+
|
|
132
|
+
if (seenIds.has(liveActivity.id)) {
|
|
133
|
+
throw new Error(`Duplicate Dynamic Live Activity ID: '${liveActivity.id}'`)
|
|
134
|
+
}
|
|
135
|
+
seenIds.add(liveActivity.id)
|
|
136
|
+
|
|
137
|
+
const attributesType = getDynamicLiveActivityAttributesType(liveActivity.id)
|
|
138
|
+
const conflictingId = seenAttributesTypes.get(attributesType)
|
|
139
|
+
if (conflictingId) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Dynamic Live Activity IDs '${conflictingId}' and '${liveActivity.id}' generate the same ActivityKit attributes type '${attributesType}'`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
seenAttributesTypes.set(attributesType, liveActivity.id)
|
|
70
145
|
}
|
|
71
146
|
}
|
|
72
147
|
}
|
package/ios/Package.swift
CHANGED
|
@@ -25,15 +25,12 @@ let package = Package(
|
|
|
25
25
|
"BrotliCompression.swift",
|
|
26
26
|
"Data+hexString.swift",
|
|
27
27
|
"Date+toTimerInterval.swift",
|
|
28
|
-
"ShortNames.swift",
|
|
29
28
|
"VoltraAttributes.swift",
|
|
30
|
-
"VoltraElement.swift",
|
|
31
29
|
"VoltraEvent.swift",
|
|
32
30
|
"VoltraEventBus.swift",
|
|
33
31
|
"VoltraImageStore.swift",
|
|
34
32
|
"VoltraInteractionIntent.swift",
|
|
35
33
|
"VoltraLiveActivityPayload.swift",
|
|
36
|
-
"VoltraNode.swift",
|
|
37
34
|
"VoltraPersistentEventQueue.swift",
|
|
38
35
|
],
|
|
39
36
|
sources: [
|
|
@@ -42,12 +39,44 @@ let package = Package(
|
|
|
42
39
|
"DynamicWidgetPropsStore.swift",
|
|
43
40
|
"DynamicWidgetRenderCoordinator.swift",
|
|
44
41
|
"DynamicWidgetUpdater.swift",
|
|
42
|
+
"ServerWidgetContentResolver.swift",
|
|
43
|
+
"ServerWidgetResponseStore.swift",
|
|
44
|
+
// The settings stack's pure half. The Keychain-, Bundle- and URLSession-backed files in
|
|
45
|
+
// this folder are compiled only by the podspec, which ships the whole tree.
|
|
46
|
+
"WidgetServer/WidgetScope.swift",
|
|
47
|
+
"WidgetServer/WidgetCanonicalConfiguration.swift",
|
|
48
|
+
"WidgetServer/WidgetServerUpdateSettings.swift",
|
|
49
|
+
"WidgetServer/WidgetServerSettingsResolver.swift",
|
|
50
|
+
"WidgetServer/WidgetServerSettingsCodec.swift",
|
|
51
|
+
"WidgetServer/WidgetServerSettingsValidator.swift",
|
|
52
|
+
"WidgetServer/WidgetServerRequestBuilder.swift",
|
|
53
|
+
"WidgetServer/WidgetServerFetcher.swift",
|
|
54
|
+
"WidgetServer/WidgetServerSettingsStore.swift",
|
|
55
|
+
"WidgetServer/WidgetServerEtagStore.swift",
|
|
56
|
+
"WidgetServer/VoltraKeychainHelper.swift",
|
|
57
|
+
"WidgetServer/VoltraWidgetServer.swift",
|
|
58
|
+
"WidgetServer/WidgetServerUpdateSettingsJson.swift",
|
|
59
|
+
"DynamicWidgetServerUpdate/DynamicWidgetServerProps.swift",
|
|
60
|
+
"DynamicWidgetServerUpdate/DynamicWidgetServerPropsStore.swift",
|
|
61
|
+
"DynamicWidgetServerUpdate/DynamicWidgetServerFetchResolver.swift",
|
|
62
|
+
"DynamicWidgetServerUpdate/DynamicWidgetServerUpdateRunner.swift",
|
|
63
|
+
"VoltraLogger.swift",
|
|
64
|
+
"dynamic-live-activity/VoltraDynamicLiveActivityTypes.swift",
|
|
65
|
+
"dynamic-live-activity/VoltraDynamicLiveActivityPayloadValidator.swift",
|
|
66
|
+
"dynamic-live-activity/VoltraDynamicLiveActivityRenderFailureQueue.swift",
|
|
67
|
+
"dynamic-live-activity/VoltraLiveActivityOrder.swift",
|
|
45
68
|
"JSONValue.swift",
|
|
46
69
|
"VoltraConfig.swift",
|
|
47
70
|
"VoltraConstants.swift",
|
|
71
|
+
"VoltraWidgetKind.swift",
|
|
48
72
|
"VoltraPayloadMigrator.swift",
|
|
49
73
|
"VoltraRegion.swift",
|
|
50
74
|
"ComponentTypeID.swift",
|
|
75
|
+
// The JSON -> AST types. They carry no SwiftUI dependency; rendering is attached
|
|
76
|
+
// by `ui/VoltraNode+View.swift`, which the podspecs compile alongside them.
|
|
77
|
+
"ShortNames.swift",
|
|
78
|
+
"VoltraElement.swift",
|
|
79
|
+
"VoltraNode.swift",
|
|
51
80
|
]
|
|
52
81
|
),
|
|
53
82
|
.testTarget(
|