@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
|
@@ -2,10 +2,12 @@ import { XcodeProject } from '@expo/config-plugins'
|
|
|
2
2
|
import * as util from 'util'
|
|
3
3
|
|
|
4
4
|
import type { IOSWidgetExtensionFiles } from '../../types'
|
|
5
|
+
import { ensureWidgetFileReference, normalizeRef } from './fileReferences'
|
|
5
6
|
|
|
6
7
|
const pbxFile = require('xcode/lib/pbxFile')
|
|
7
8
|
|
|
8
|
-
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'
|
|
9
11
|
|
|
10
12
|
// Release-only build phase that bakes each Dynamic Widget's production JS bundle into the
|
|
11
13
|
// extension's resources. Debug builds fetch from Metro (and hot-reload), so this no-ops there. Runs
|
|
@@ -13,7 +15,8 @@ const WIDGET_BUNDLE_PHASE_NAME = 'Bundle Voltra Dynamic Widgets'
|
|
|
13
15
|
// voltra-widget-<id>.bundle lands in the .appex (Bundle.main) where the runtime's release loader
|
|
14
16
|
// reads it. SRCROOT is the ios/ dir; the project root is one level up, matching how Expo's main
|
|
15
17
|
// "Bundle React Native code and images" phase resolves things.
|
|
16
|
-
|
|
18
|
+
function bundleShellScript(content: 'all' | 'live-activities'): string {
|
|
19
|
+
return `if [[ "$CONFIGURATION" == *Debug* ]]; then
|
|
17
20
|
echo "Voltra: Debug build — Dynamic Widgets load from Metro, skipping bundling"
|
|
18
21
|
exit 0
|
|
19
22
|
fi
|
|
@@ -47,6 +50,7 @@ try {
|
|
|
47
50
|
console.error(error && error.message ? error.message : String(error))
|
|
48
51
|
process.exit(1)
|
|
49
52
|
}
|
|
53
|
+
|
|
50
54
|
NODE
|
|
51
55
|
)"
|
|
52
56
|
if [[ -z "$BUNDLER" ]]; then
|
|
@@ -55,14 +59,23 @@ if [[ -z "$BUNDLER" ]]; then
|
|
|
55
59
|
fi
|
|
56
60
|
|
|
57
61
|
echo "Voltra: widget bundler resolved to $BUNDLER"
|
|
58
|
-
"$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}
|
|
59
63
|
`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pbxQuotedShellScript(script: string): string {
|
|
67
|
+
return `"${script.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`
|
|
68
|
+
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
62
71
|
* Adds (idempotently) the release-only shell-script phase that bakes Dynamic Widget
|
|
63
72
|
* bundles into the extension. Safe to call on every prebuild; only added when absent.
|
|
64
73
|
*/
|
|
65
|
-
export function ensureWidgetBundleScriptPhase(
|
|
74
|
+
export function ensureWidgetBundleScriptPhase(
|
|
75
|
+
xcodeProject: XcodeProject,
|
|
76
|
+
targetUuid: string,
|
|
77
|
+
content: 'all' | 'live-activities' = 'all'
|
|
78
|
+
): void {
|
|
66
79
|
const nativeTargets = xcodeProject.pbxNativeTargetSection()
|
|
67
80
|
const target = nativeTargets[targetUuid]
|
|
68
81
|
if (!target) {
|
|
@@ -74,15 +87,37 @@ export function ensureWidgetBundleScriptPhase(xcodeProject: XcodeProject, target
|
|
|
74
87
|
|
|
75
88
|
const shellPhases = xcodeProject.hash.project.objects.PBXShellScriptBuildPhase || {}
|
|
76
89
|
const quotedName = `"${WIDGET_BUNDLE_PHASE_NAME}"`
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
xcodeProject.addBuildPhase([], 'PBXShellScriptBuildPhase', WIDGET_BUNDLE_PHASE_NAME, targetUuid, {
|
|
83
|
-
shellPath: '/bin/sh',
|
|
84
|
-
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
|
|
85
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
|
+
}
|
|
86
121
|
|
|
87
122
|
// The phase intentionally re-bakes on every release build (it can't statically enumerate every
|
|
88
123
|
// widget source as an input). Mark it always-out-of-date so Xcode doesn't warn about the missing
|
|
@@ -98,71 +133,79 @@ export function ensureWidgetBundleScriptPhase(xcodeProject: XcodeProject, target
|
|
|
98
133
|
}
|
|
99
134
|
}
|
|
100
135
|
|
|
101
|
-
export interface
|
|
136
|
+
export interface EnsureBuildPhasesOptions {
|
|
102
137
|
targetUuid: string
|
|
138
|
+
targetName: string
|
|
103
139
|
groupName: string
|
|
104
140
|
productFile: {
|
|
105
141
|
uuid: string
|
|
142
|
+
fileRef?: string
|
|
106
143
|
target?: string
|
|
107
144
|
basename: string
|
|
108
145
|
group: string
|
|
109
146
|
}
|
|
110
147
|
widgetFiles: IOSWidgetExtensionFiles
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface EnsureBuildPhasesOptions extends AddBuildPhasesOptions {
|
|
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
|
/**
|
|
118
|
-
*
|
|
154
|
+
* Finds a `PBXCopyFilesBuildPhase` on the given target that embeds app extensions
|
|
155
|
+
* (`dstSubfolderSpec == 13`), regardless of its display name. Returns the phase object or null.
|
|
156
|
+
*
|
|
157
|
+
* A project that already contains another app extension (Share/Watch/expo-live-activity) will have
|
|
158
|
+
* such a phase — typically named "Embed App Extensions" — and the widget's product must be added to
|
|
159
|
+
* it rather than to a second, duplicate embed phase (issue #87).
|
|
119
160
|
*/
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const resourcePaths = [...assetDirectories, ...localizedStringResources]
|
|
127
|
-
|
|
128
|
-
// Sources build phase
|
|
129
|
-
xcodeProject.addBuildPhase(
|
|
130
|
-
[...swiftFiles, ...intentFiles],
|
|
131
|
-
'PBXSourcesBuildPhase',
|
|
132
|
-
'Sources',
|
|
133
|
-
targetUuid,
|
|
134
|
-
folderType,
|
|
135
|
-
buildPath
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
// Copy files build phase
|
|
139
|
-
xcodeProject.addBuildPhase(
|
|
140
|
-
[],
|
|
141
|
-
'PBXCopyFilesBuildPhase',
|
|
142
|
-
groupName,
|
|
143
|
-
xcodeProject.getFirstTarget().uuid,
|
|
144
|
-
folderType,
|
|
145
|
-
buildPath
|
|
146
|
-
)
|
|
161
|
+
function findExistingEmbedExtensionsPhase(xcodeProject: XcodeProject, targetUuid: string): any | null {
|
|
162
|
+
const nativeTargets = xcodeProject.pbxNativeTargetSection()
|
|
163
|
+
const target = nativeTargets[targetUuid]
|
|
164
|
+
if (!target?.buildPhases) {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
147
167
|
|
|
148
|
-
xcodeProject.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
const copyFilesSection = xcodeProject.hash.project.objects['PBXCopyFilesBuildPhase'] || {}
|
|
169
|
+
for (const entry of target.buildPhases) {
|
|
170
|
+
const phase = copyFilesSection[normalizeRef(entry.value)]
|
|
171
|
+
if (phase && String(phase.dstSubfolderSpec) === '13') {
|
|
172
|
+
return phase
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
153
177
|
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Finds a target's build phase of the given pbx section type by walking the target's own
|
|
180
|
+
* `buildPhases` list and checking membership in the type's object section; the first match wins.
|
|
181
|
+
*
|
|
182
|
+
* This is deliberately semantic: pbxproj comments are decorative and other tools strip them, and
|
|
183
|
+
* the xcode lib's `buildPhaseObject` both relies on comments and silently falls back to a
|
|
184
|
+
* project-wide search that can return another target's phase (e.g. the main app's "Sources").
|
|
185
|
+
* The embed phase is the one exception with extra semantics — see
|
|
186
|
+
* {@link findExistingEmbedExtensionsPhase} (`dstSubfolderSpec == 13`).
|
|
187
|
+
*/
|
|
188
|
+
function findTargetPhaseByType(xcodeProject: XcodeProject, targetUuid: string, phaseType: string): any | null {
|
|
189
|
+
const target = xcodeProject.pbxNativeTargetSection()[targetUuid]
|
|
190
|
+
if (!target?.buildPhases) {
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
const phaseSection = xcodeProject.hash.project.objects[phaseType] || {}
|
|
195
|
+
for (const entry of target.buildPhases) {
|
|
196
|
+
const phase = phaseSection[normalizeRef(entry.value)]
|
|
197
|
+
if (phase) {
|
|
198
|
+
return phase
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null
|
|
159
202
|
}
|
|
160
203
|
|
|
161
204
|
/**
|
|
162
205
|
* Ensures all required build phases and files exist for the widget extension target.
|
|
163
206
|
*/
|
|
164
207
|
export function ensureBuildPhases(xcodeProject: XcodeProject, options: EnsureBuildPhasesOptions): void {
|
|
165
|
-
const { targetUuid, groupName, productFile, widgetFiles } = options
|
|
208
|
+
const { targetUuid, targetName, groupName, productFile, widgetFiles } = options
|
|
166
209
|
const buildPath = `""`
|
|
167
210
|
const folderType = 'app_extension'
|
|
168
211
|
const mainTargetUuid = options.mainTargetUuid ?? xcodeProject.getFirstTarget().uuid
|
|
@@ -170,60 +213,66 @@ export function ensureBuildPhases(xcodeProject: XcodeProject, options: EnsureBui
|
|
|
170
213
|
const { swiftFiles, intentFiles, assetDirectories, localizedStringResources } = widgetFiles
|
|
171
214
|
const resourcePaths = [...assetDirectories, ...localizedStringResources]
|
|
172
215
|
|
|
173
|
-
dedupeBuildPhasesForTarget(xcodeProject, targetUuid, 'PBXSourcesBuildPhase'
|
|
174
|
-
dedupeBuildPhasesForTarget(xcodeProject, targetUuid, 'PBXFrameworksBuildPhase'
|
|
175
|
-
|
|
216
|
+
dedupeBuildPhasesForTarget(xcodeProject, targetUuid, 'PBXSourcesBuildPhase')
|
|
217
|
+
dedupeBuildPhasesForTarget(xcodeProject, targetUuid, 'PBXFrameworksBuildPhase')
|
|
218
|
+
dedupeEmbedPhasesForTarget(xcodeProject, mainTargetUuid)
|
|
176
219
|
|
|
177
220
|
// Sources build phase
|
|
178
|
-
let sourcesPhase = xcodeProject
|
|
221
|
+
let sourcesPhase = findTargetPhaseByType(xcodeProject, targetUuid, 'PBXSourcesBuildPhase')
|
|
179
222
|
if (!sourcesPhase) {
|
|
180
|
-
xcodeProject.addBuildPhase(
|
|
181
|
-
|
|
182
|
-
'PBXSourcesBuildPhase',
|
|
183
|
-
'Sources',
|
|
184
|
-
targetUuid,
|
|
185
|
-
folderType,
|
|
186
|
-
buildPath
|
|
187
|
-
)
|
|
188
|
-
sourcesPhase = xcodeProject.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', targetUuid)
|
|
223
|
+
xcodeProject.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', targetUuid, folderType, buildPath)
|
|
224
|
+
sourcesPhase = findTargetPhaseByType(xcodeProject, targetUuid, 'PBXSourcesBuildPhase')
|
|
189
225
|
}
|
|
190
226
|
if (sourcesPhase) {
|
|
191
|
-
ensureBuildPhaseFiles(xcodeProject, sourcesPhase, [...swiftFiles, ...intentFiles])
|
|
227
|
+
ensureBuildPhaseFiles(xcodeProject, sourcesPhase, [...swiftFiles, ...intentFiles], targetName)
|
|
228
|
+
}
|
|
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
|
+
}
|
|
192
239
|
}
|
|
193
240
|
|
|
194
|
-
// Copy files build phase (embed extension into main app)
|
|
195
|
-
|
|
241
|
+
// Copy files build phase (embed extension into main app) — the embed phase is matched purely by
|
|
242
|
+
// its `dstSubfolderSpec == 13` semantics, both before and after creation
|
|
243
|
+
let copyFilesPhase = findExistingEmbedExtensionsPhase(xcodeProject, mainTargetUuid)
|
|
196
244
|
if (!copyFilesPhase) {
|
|
197
245
|
xcodeProject.addBuildPhase([], 'PBXCopyFilesBuildPhase', groupName, mainTargetUuid, folderType, buildPath)
|
|
198
|
-
copyFilesPhase = xcodeProject
|
|
246
|
+
copyFilesPhase = findExistingEmbedExtensionsPhase(xcodeProject, mainTargetUuid)
|
|
199
247
|
}
|
|
200
248
|
if (copyFilesPhase) {
|
|
201
249
|
ensureCopyFilesPhaseProduct(xcodeProject, copyFilesPhase, productFile)
|
|
202
250
|
}
|
|
203
251
|
|
|
204
252
|
// Frameworks build phase
|
|
205
|
-
const frameworksPhase = xcodeProject
|
|
253
|
+
const frameworksPhase = findTargetPhaseByType(xcodeProject, targetUuid, 'PBXFrameworksBuildPhase')
|
|
206
254
|
if (!frameworksPhase) {
|
|
207
255
|
xcodeProject.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', targetUuid, folderType, buildPath)
|
|
208
256
|
}
|
|
209
257
|
|
|
210
258
|
// Resources build phase
|
|
211
|
-
let resourcesPhase = xcodeProject
|
|
259
|
+
let resourcesPhase = findTargetPhaseByType(xcodeProject, targetUuid, 'PBXResourcesBuildPhase')
|
|
212
260
|
if (!resourcesPhase) {
|
|
213
|
-
xcodeProject.addBuildPhase([
|
|
214
|
-
resourcesPhase = xcodeProject
|
|
261
|
+
xcodeProject.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', targetUuid)
|
|
262
|
+
resourcesPhase = findTargetPhaseByType(xcodeProject, targetUuid, 'PBXResourcesBuildPhase')
|
|
215
263
|
}
|
|
216
264
|
if (resourcesPhase) {
|
|
217
|
-
ensureBuildPhaseFiles(xcodeProject, resourcesPhase,
|
|
265
|
+
ensureBuildPhaseFiles(xcodeProject, resourcesPhase, resourcePaths, targetName)
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
268
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
269
|
+
/**
|
|
270
|
+
* Drops duplicate build phases of one pbx section type from a target, keeping the first phase in
|
|
271
|
+
* the target's `buildPhases` order. Membership in the type's object section is the only criterion —
|
|
272
|
+
* comments are decorative and other tools strip them. De-referenced phase objects are deleted so no
|
|
273
|
+
* orphans remain.
|
|
274
|
+
*/
|
|
275
|
+
function dedupeBuildPhasesForTarget(xcodeProject: XcodeProject, targetUuid: string, phaseType: string): void {
|
|
227
276
|
const nativeTargets = xcodeProject.pbxNativeTargetSection()
|
|
228
277
|
const target = nativeTargets[targetUuid]
|
|
229
278
|
if (!target?.buildPhases) {
|
|
@@ -231,114 +280,97 @@ function dedupeBuildPhasesForTarget(
|
|
|
231
280
|
}
|
|
232
281
|
|
|
233
282
|
const phaseSection = xcodeProject.hash.project.objects[phaseType] || {}
|
|
234
|
-
const matching = target.buildPhases.filter((entry: any) => phaseSection[entry.value])
|
|
283
|
+
const matching = target.buildPhases.filter((entry: any) => phaseSection[normalizeRef(entry.value)])
|
|
235
284
|
if (matching.length <= 1) {
|
|
236
285
|
return
|
|
237
286
|
}
|
|
238
287
|
|
|
239
|
-
const keep = matching
|
|
240
|
-
|
|
241
|
-
|
|
288
|
+
const keep = matching[0]
|
|
289
|
+
const removed = matching.filter((entry: any) => entry.value !== keep.value)
|
|
242
290
|
target.buildPhases = target.buildPhases.filter((entry: any) => {
|
|
243
|
-
if (!phaseSection[entry.value]) {
|
|
291
|
+
if (!phaseSection[normalizeRef(entry.value)]) {
|
|
244
292
|
return true
|
|
245
293
|
}
|
|
246
294
|
return entry.value === keep.value
|
|
247
295
|
})
|
|
296
|
+
deleteOrphanPhaseObjects(phaseSection, removed)
|
|
248
297
|
}
|
|
249
298
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Drops duplicate embed-app-extensions copy-files phases (`dstSubfolderSpec == 13`) from a target,
|
|
301
|
+
* keeping the first one in the target's `buildPhases` order. Other copy-files phases (different
|
|
302
|
+
* `dstSubfolderSpec`) are left untouched. De-referenced phase objects are deleted so no orphans
|
|
303
|
+
* remain.
|
|
304
|
+
*/
|
|
305
|
+
function dedupeEmbedPhasesForTarget(xcodeProject: XcodeProject, targetUuid: string): void {
|
|
256
306
|
const nativeTargets = xcodeProject.pbxNativeTargetSection()
|
|
257
307
|
const target = nativeTargets[targetUuid]
|
|
258
308
|
if (!target?.buildPhases) {
|
|
259
309
|
return
|
|
260
310
|
}
|
|
261
311
|
|
|
262
|
-
const phaseSection = xcodeProject.hash.project.objects[
|
|
263
|
-
const
|
|
312
|
+
const phaseSection = xcodeProject.hash.project.objects['PBXCopyFilesBuildPhase'] || {}
|
|
313
|
+
const isEmbedEntry = (entry: any) => {
|
|
314
|
+
const phase = phaseSection[normalizeRef(entry.value)]
|
|
315
|
+
return phase && String(phase.dstSubfolderSpec) === '13'
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const matching = target.buildPhases.filter(isEmbedEntry)
|
|
264
319
|
if (matching.length <= 1) {
|
|
265
320
|
return
|
|
266
321
|
}
|
|
267
322
|
|
|
268
323
|
const keep = matching[0]
|
|
324
|
+
const removed = matching.filter((entry: any) => entry.value !== keep.value)
|
|
269
325
|
target.buildPhases = target.buildPhases.filter((entry: any) => {
|
|
270
|
-
if (!
|
|
326
|
+
if (!isEmbedEntry(entry)) {
|
|
271
327
|
return true
|
|
272
328
|
}
|
|
273
329
|
return entry.value === keep.value
|
|
274
330
|
})
|
|
331
|
+
deleteOrphanPhaseObjects(phaseSection, removed)
|
|
275
332
|
}
|
|
276
333
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
for (const key of Object.keys(fileReferenceSection)) {
|
|
289
|
-
if (/_comment$/.test(key)) {
|
|
290
|
-
continue
|
|
291
|
-
}
|
|
292
|
-
const entry = fileReferenceSection[key]
|
|
293
|
-
const entryPath = normalizePath(entry?.path)
|
|
294
|
-
if (entryPath === normalizedPath) {
|
|
295
|
-
return key
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return null
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function findBuildFileKeyByFileRef(xcodeProject: XcodeProject, fileRef: string): string | null {
|
|
303
|
-
const buildFileSection = xcodeProject.pbxBuildFileSection()
|
|
304
|
-
for (const key of Object.keys(buildFileSection)) {
|
|
305
|
-
if (/_comment$/.test(key)) {
|
|
306
|
-
continue
|
|
307
|
-
}
|
|
308
|
-
const entry = buildFileSection[key]
|
|
309
|
-
if (entry?.fileRef === fileRef) {
|
|
310
|
-
return key
|
|
311
|
-
}
|
|
334
|
+
/**
|
|
335
|
+
* Deletes de-referenced build-phase objects (and their `_comment` keys) from a phase section, so
|
|
336
|
+
* dropping duplicate phase refs from a target does not leave orphan objects that fail the
|
|
337
|
+
* `[Xcodeproj]` consistency check.
|
|
338
|
+
*/
|
|
339
|
+
function deleteOrphanPhaseObjects(phaseSection: Record<string, any>, removedEntries: any[]): void {
|
|
340
|
+
for (const entry of removedEntries) {
|
|
341
|
+
const key = normalizeRef(entry.value)
|
|
342
|
+
delete phaseSection[key]
|
|
343
|
+
delete phaseSection[`${key}_comment`]
|
|
312
344
|
}
|
|
313
|
-
return null
|
|
314
345
|
}
|
|
315
346
|
|
|
316
|
-
|
|
317
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Resolves (or creates) the PBXBuildFile for a widget file within a specific build phase.
|
|
349
|
+
*
|
|
350
|
+
* The lookup is scoped to the given phase's `files`: a PBXBuildFile is only reused when it is
|
|
351
|
+
* already part of THIS phase. This prevents adopting a build file that belongs to another target's
|
|
352
|
+
* phase — the same PBXFileReference (e.g. an asset catalog) may legitimately need its own build
|
|
353
|
+
* file per phase, and sharing one produces the "no parent for object" corruption (issue #87). When
|
|
354
|
+
* no match exists a new PBXBuildFile with a fresh UUID is created.
|
|
355
|
+
*/
|
|
356
|
+
function ensureBuildFile(xcodeProject: XcodeProject, filePath: string, buildPhase: any, targetName: string) {
|
|
357
|
+
const fileRef = ensureWidgetFileReference(xcodeProject, filePath, targetName)
|
|
318
358
|
const file = new pbxFile(filePath)
|
|
319
359
|
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function ensureBuildFile(xcodeProject: XcodeProject, filePath: string) {
|
|
330
|
-
const fileReference = ensureFileReference(xcodeProject, filePath)
|
|
331
|
-
const existingBuildFile = findBuildFileKeyByFileRef(xcodeProject, fileReference.fileRef)
|
|
332
|
-
|
|
333
|
-
if (existingBuildFile) {
|
|
334
|
-
return { uuid: existingBuildFile, ...fileReference }
|
|
360
|
+
if (buildPhase?.files) {
|
|
361
|
+
const buildFileSection = xcodeProject.pbxBuildFileSection()
|
|
362
|
+
const existingInPhase = buildPhase.files.find((entry: any) => {
|
|
363
|
+
const buildFile = buildFileSection[normalizeRef(entry.value)]
|
|
364
|
+
return buildFile?.fileRef === fileRef
|
|
365
|
+
})
|
|
366
|
+
if (existingInPhase) {
|
|
367
|
+
return { uuid: normalizeRef(existingInPhase.value), fileRef, basename: file.basename, group: file.group }
|
|
368
|
+
}
|
|
335
369
|
}
|
|
336
370
|
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
file.
|
|
340
|
-
xcodeProject.addToPbxBuildFileSection(file)
|
|
341
|
-
return { uuid: file.uuid, ...fileReference, group: file.group }
|
|
371
|
+
const uuid = xcodeProject.generateUuid()
|
|
372
|
+
xcodeProject.addToPbxBuildFileSection({ uuid, fileRef, basename: file.basename, group: file.group } as any)
|
|
373
|
+
return { uuid, fileRef, basename: file.basename, group: file.group }
|
|
342
374
|
}
|
|
343
375
|
|
|
344
376
|
function buildPhaseHasFile(xcodeProject: XcodeProject, buildPhase: any, fileRef: string): boolean {
|
|
@@ -348,23 +380,28 @@ function buildPhaseHasFile(xcodeProject: XcodeProject, buildPhase: any, fileRef:
|
|
|
348
380
|
const buildFileSection = xcodeProject.pbxBuildFileSection()
|
|
349
381
|
|
|
350
382
|
return buildPhase.files.some((entry: any) => {
|
|
351
|
-
const buildFile = buildFileSection[entry.value]
|
|
383
|
+
const buildFile = buildFileSection[normalizeRef(entry.value)]
|
|
352
384
|
return buildFile?.fileRef === fileRef
|
|
353
385
|
})
|
|
354
386
|
}
|
|
355
387
|
|
|
356
|
-
function ensureBuildPhaseFiles(
|
|
388
|
+
function ensureBuildPhaseFiles(
|
|
389
|
+
xcodeProject: XcodeProject,
|
|
390
|
+
buildPhase: any,
|
|
391
|
+
filePaths: string[],
|
|
392
|
+
targetName: string
|
|
393
|
+
): void {
|
|
357
394
|
if (!buildPhase.files) {
|
|
358
395
|
buildPhase.files = []
|
|
359
396
|
}
|
|
360
397
|
|
|
361
398
|
for (const filePath of filePaths) {
|
|
362
|
-
const
|
|
363
|
-
if (buildPhaseHasFile(xcodeProject, buildPhase,
|
|
399
|
+
const fileRef = ensureWidgetFileReference(xcodeProject, filePath, targetName)
|
|
400
|
+
if (buildPhaseHasFile(xcodeProject, buildPhase, fileRef)) {
|
|
364
401
|
continue
|
|
365
402
|
}
|
|
366
403
|
|
|
367
|
-
const buildFile = ensureBuildFile(xcodeProject, filePath)
|
|
404
|
+
const buildFile = ensureBuildFile(xcodeProject, filePath, buildPhase, targetName)
|
|
368
405
|
buildPhase.files.push({
|
|
369
406
|
value: buildFile.uuid,
|
|
370
407
|
comment: util.format('%s in %s', buildFile.basename, buildFile.group),
|
|
@@ -377,18 +414,27 @@ function ensureCopyFilesPhaseProduct(xcodeProject: XcodeProject, buildPhase: any
|
|
|
377
414
|
buildPhase.files = []
|
|
378
415
|
}
|
|
379
416
|
|
|
380
|
-
const
|
|
381
|
-
|
|
417
|
+
const buildFileSection = xcodeProject.pbxBuildFileSection()
|
|
418
|
+
const alreadyInPhase = buildPhase.files.some((entry: any) => {
|
|
419
|
+
const buildFile = buildFileSection[normalizeRef(entry.value)]
|
|
420
|
+
return buildFile?.fileRef === productFile.fileRef
|
|
421
|
+
})
|
|
422
|
+
if (alreadyInPhase) {
|
|
382
423
|
return
|
|
383
424
|
}
|
|
384
425
|
|
|
385
|
-
|
|
386
|
-
|
|
426
|
+
// Reuse the product's build-file UUID only if it isn't already registered by another phase;
|
|
427
|
+
// otherwise mint a fresh UUID so the two phases never share one PBXBuildFile (issue #87).
|
|
428
|
+
let useUuid = productFile.uuid
|
|
429
|
+
if (buildFileSection[productFile.uuid]) {
|
|
430
|
+
useUuid = xcodeProject.generateUuid()
|
|
431
|
+
xcodeProject.addToPbxBuildFileSection({ ...productFile, uuid: useUuid })
|
|
432
|
+
} else {
|
|
387
433
|
xcodeProject.addToPbxBuildFileSection(productFile)
|
|
388
434
|
}
|
|
389
435
|
|
|
390
436
|
buildPhase.files.push({
|
|
391
|
-
value:
|
|
437
|
+
value: useUuid,
|
|
392
438
|
comment: util.format('%s in %s', productFile.basename, productFile.group),
|
|
393
439
|
})
|
|
394
440
|
}
|