expo-widgets 56.0.6 → 56.0.8
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/CHANGELOG.md +17 -0
- package/build/Widgets.d.ts +6 -6
- package/build/Widgets.d.ts.map +1 -1
- package/build/Widgets.js.map +1 -1
- package/build/Widgets.types.d.ts +6 -1
- package/build/Widgets.types.d.ts.map +1 -1
- package/build/Widgets.types.js.map +1 -1
- package/ios/Widgets/DynamicView.swift +2 -0
- package/ios/Widgets/EntryView.swift +4 -5
- package/ios/Widgets/Utils.swift +8 -8
- package/ios/WidgetsStorage.swift +6 -6
- package/package.json +5 -5
- package/plugin/build/types/WidgetConfig.type.d.ts +30 -0
- package/plugin/build/withWidgetSourceFiles.js +173 -2
- package/plugin/src/types/WidgetConfig.type.ts +36 -0
- package/plugin/src/withWidgetSourceFiles.ts +172 -2
- package/src/Widgets.ts +19 -10
- package/src/Widgets.types.ts +6 -1
- package/plugin/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,23 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.8 — 2026-05-13
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Support configurable widgets. ([#45726](https://github.com/expo/expo/pull/45726) by [@jakex7](https://github.com/jakex7))
|
|
18
|
+
- Improve RedBox messages. ([#45732](https://github.com/expo/expo/pull/45732) by [@jakex7](https://github.com/jakex7))
|
|
19
|
+
|
|
20
|
+
### 🐛 Bug fixes
|
|
21
|
+
|
|
22
|
+
- Improve environment configuration types. ([#45734](https://github.com/expo/expo/pull/45734) by [@jakex7](https://github.com/jakex7))
|
|
23
|
+
|
|
24
|
+
## 56.0.7 — 2026-05-13
|
|
25
|
+
|
|
26
|
+
### 🎉 New features
|
|
27
|
+
|
|
28
|
+
- Add `ChartView` support. ([#45674](https://github.com/expo/expo/pull/45674) by [@jakex7](https://github.com/jakex7))
|
|
29
|
+
|
|
13
30
|
## 56.0.6 — 2026-05-11
|
|
14
31
|
|
|
15
32
|
_This version does not introduce any user-facing changes._
|
package/build/Widgets.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import type { ExpoWidgetsEvents, LiveActivityComponent, LiveActivityDismissalPol
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents a widget instance. Provides methods to manage the widget's timeline.
|
|
5
5
|
*/
|
|
6
|
-
export declare class Widget<
|
|
6
|
+
export declare class Widget<PropsType extends object = object, ConfigurationType extends object | undefined = undefined> {
|
|
7
7
|
/** @hidden */
|
|
8
8
|
private nativeWidgetObject;
|
|
9
|
-
constructor(name: string, layout: (props:
|
|
9
|
+
constructor(name: string, layout: (props: PropsType, environment: WidgetEnvironment<ConfigurationType>) => React.JSX.Element);
|
|
10
10
|
/**
|
|
11
11
|
* Force reloads the widget, causing it to refresh its content and timeline.
|
|
12
12
|
*/
|
|
@@ -15,16 +15,16 @@ export declare class Widget<T extends object = object> {
|
|
|
15
15
|
* Schedules a series of updates for the widget's content and reloads the widget.
|
|
16
16
|
* @param entries Timeline entries, each specifying a date and the props to display at that time.
|
|
17
17
|
*/
|
|
18
|
-
updateTimeline(entries: WidgetTimelineEntry<
|
|
18
|
+
updateTimeline(entries: WidgetTimelineEntry<PropsType>[]): void;
|
|
19
19
|
/**
|
|
20
20
|
* Sets the widget's content to the given props immediately, without scheduling a timeline.
|
|
21
21
|
* @param props The properties to display in the widget.
|
|
22
22
|
*/
|
|
23
|
-
updateSnapshot(props:
|
|
23
|
+
updateSnapshot(props: PropsType): void;
|
|
24
24
|
/**
|
|
25
25
|
* Returns the current timeline entries for the widget, including past and future entries.
|
|
26
26
|
*/
|
|
27
|
-
getTimeline(): Promise<WidgetTimelineEntry<
|
|
27
|
+
getTimeline(): Promise<WidgetTimelineEntry<PropsType>[]>;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Represents a Live Activity instance. Provides methods to update its content and end it.
|
|
@@ -91,7 +91,7 @@ export declare function after(date: Date): {
|
|
|
91
91
|
* @param name The widget name. Must match the `'name'` field in your widget configuration in the app config.
|
|
92
92
|
* @param widget The widget component, marked with the `'widget'` directive.
|
|
93
93
|
*/
|
|
94
|
-
export declare function createWidget<
|
|
94
|
+
export declare function createWidget<PropsType extends object = object, ConfigurationType extends object | undefined = undefined>(name: string, widget: (props: PropsType, context: WidgetEnvironment<ConfigurationType>) => React.JSX.Element): Widget<PropsType, ConfigurationType>;
|
|
95
95
|
/**
|
|
96
96
|
* Creates a Live Activity Factory for managing Live Activities of a specific type.
|
|
97
97
|
* @param name The Live Activity name. Must match the `'name'` field in your widget configuration in the app config.
|
package/build/Widgets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Widgets.d.ts","sourceRoot":"","sources":["../src/Widgets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,kBAAkB,EAGlB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,qBAAa,MAAM,
|
|
1
|
+
{"version":3,"file":"Widgets.d.ts","sourceRoot":"","sources":["../src/Widgets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,kBAAkB,EAGlB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,qBAAa,MAAM,CACjB,SAAS,SAAS,MAAM,GAAG,MAAM,EACjC,iBAAiB,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS;IAExD,cAAc;IACd,OAAO,CAAC,kBAAkB,CAAqB;gBAE7C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CACN,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,KAC9C,KAAK,CAAC,GAAG,CAAC,OAAO;IAKxB;;OAEG;IACH,MAAM;IAIN;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,EAAE;IAMxD;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,SAAS;IAI/B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;CAM/D;AAED;;GAEG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACjD,cAAc;IACd,OAAO,CAAC,kBAAkB,CAAqB;gBACnC,kBAAkB,EAAE,kBAAkB;IAIlD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B;;;;;;OAMG;IACH,GAAG,CAAC,eAAe,CAAC,EAAE,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhG;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAItC;;;;;OAKG;IACH,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,iBAAiB;CAGnF;AAED;;GAEG;AACH,qBAAa,mBAAmB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACxD,cAAc;IACd,OAAO,CAAC,yBAAyB,CAA4B;gBACjD,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAO1D;;;;;OAKG;IACH,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM;IAI5B;;OAEG;IACH,YAAY;CAKb;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,SAAS,SAAS,MAAM,GAAG,MAAM,EACjC,iBAAiB,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EAExD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,OAAO,GAC7F,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAEtC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC1D,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACrC,mBAAmB,CAAC,CAAC,CAAC,CAExB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,iBAAiB,CAAC,8BAA8B,CAAC,GAC1D,iBAAiB,CAEnB;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,iBAAiB,CAAC,uCAAuC,CAAC,GACnE,iBAAiB,CAEnB"}
|
package/build/Widgets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Widgets.js","sourceRoot":"","sources":["../src/Widgets.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EventSubscription } from 'expo-modules-core';\n\nimport ExpoWidgetsModule from './ExpoWidgets';\nimport type {\n ExpoWidgetsEvents,\n LiveActivityComponent,\n LiveActivityDismissalPolicy,\n NativeLiveActivity,\n NativeLiveActivityFactory,\n NativeWidgetObject,\n PushTokenEvent,\n WidgetEnvironment,\n WidgetTimelineEntry,\n} from './Widgets.types';\n\n/**\n * Represents a widget instance. Provides methods to manage the widget's timeline.\n */\nexport class Widget<T extends object = object> {\n /** @hidden */\n private nativeWidgetObject: NativeWidgetObject;\n constructor(\n name: string,\n layout: (props: T, environment: WidgetEnvironment) => React.JSX.Element\n ) {\n this.nativeWidgetObject = new ExpoWidgetsModule.Widget(name, layout as unknown as string);\n }\n\n /**\n * Force reloads the widget, causing it to refresh its content and timeline.\n */\n reload() {\n this.nativeWidgetObject.reload();\n }\n\n /**\n * Schedules a series of updates for the widget's content and reloads the widget.\n * @param entries Timeline entries, each specifying a date and the props to display at that time.\n */\n updateTimeline(entries: WidgetTimelineEntry<T>[]) {\n this.nativeWidgetObject.updateTimeline(\n entries.map((entry) => ({ timestamp: entry.date.getTime(), props: entry.props }))\n );\n }\n\n /**\n * Sets the widget's content to the given props immediately, without scheduling a timeline.\n * @param props The properties to display in the widget.\n */\n updateSnapshot(props: T) {\n this.nativeWidgetObject.updateTimeline([{ timestamp: Date.now(), props }]);\n }\n\n /**\n * Returns the current timeline entries for the widget, including past and future entries.\n */\n async getTimeline(): Promise<WidgetTimelineEntry<T>[]> {\n return (await this.nativeWidgetObject.getTimeline()).map((entry) => ({\n date: new Date(entry.timestamp),\n props: entry.props as T,\n }));\n }\n}\n\n/**\n * Represents a Live Activity instance. Provides methods to update its content and end it.\n */\nexport class LiveActivity<T extends object = object> {\n /** @hidden */\n private nativeLiveActivity: NativeLiveActivity;\n constructor(nativeLiveActivity: NativeLiveActivity) {\n this.nativeLiveActivity = nativeLiveActivity;\n }\n\n /**\n * Updates the Live Activity's content. The UI reflects the new properties immediately.\n * @param props The updated content properties.\n */\n update(props: T): Promise<void> {\n return this.nativeLiveActivity.update(JSON.stringify(props));\n }\n\n /**\n * Ends the Live Activity.\n * @param dismissalPolicy Controls when the Live Activity is removed from the Lock Screen after ending.\n * Can be `'default'`, `'immediate'`, or `after(date)`.\n * @param props Final content properties to update after the activity ends.\n * @param contentDate The time the data in the payload was generated. If this is older than a previous update or push payload, the system ignores this update.\n */\n end(dismissalPolicy?: LiveActivityDismissalPolicy, props?: T, contentDate?: Date): Promise<void> {\n const serializedProps = props ? JSON.stringify(props) : undefined;\n if (\n typeof dismissalPolicy === 'object' &&\n dismissalPolicy !== null &&\n 'after' in dismissalPolicy\n ) {\n return this.nativeLiveActivity.end(\n 'after',\n dismissalPolicy.after?.getTime(),\n serializedProps,\n contentDate?.getTime()\n );\n }\n return this.nativeLiveActivity.end(\n dismissalPolicy,\n undefined,\n serializedProps,\n contentDate?.getTime()\n );\n }\n\n /**\n * Returns the push token for this Live Activity, used to send push notification updates via APNs.\n * Returns `null` if push notifications are not enabled or the token is not yet available.\n */\n getPushToken(): Promise<string | null> {\n return this.nativeLiveActivity.getPushToken();\n }\n\n /**\n * Adds a listener for push token update events on this Live Activity instance.\n * The token can be used to send content updates to this specific activity via APNs.\n * @param listener Callback invoked when a new push token is available.\n * @returns An event subscription that can be used to remove the listener.\n */\n addPushTokenListener(listener: (event: PushTokenEvent) => void): EventSubscription {\n return this.nativeLiveActivity.addListener('onExpoWidgetsTokenReceived', listener);\n }\n}\n\n/**\n * Manages Live Activity instances of a specific type. Use it to start new activities and retrieve currently active ones.\n */\nexport class LiveActivityFactory<T extends object = object> {\n /** @hidden */\n private nativeLiveActivityFactory: NativeLiveActivityFactory;\n constructor(name: string, layout: LiveActivityComponent<T>) {\n this.nativeLiveActivityFactory = new ExpoWidgetsModule.LiveActivityFactory(\n name,\n layout as unknown as string\n );\n }\n\n /**\n * Starts a new Live Activity with the given properties.\n * @param props The initial content properties for the Live Activity.\n * @param url An optional URL to associate with the Live Activity, used for deep linking.\n * @returns The new Live Activity instance.\n */\n start(props: T, url?: string) {\n return new LiveActivity<T>(this.nativeLiveActivityFactory.start(JSON.stringify(props), url));\n }\n\n /**\n * Returns all currently active instances of this Live Activity type.\n */\n getInstances() {\n return this.nativeLiveActivityFactory\n .getInstances()\n .map((instance) => new LiveActivity<T>(instance));\n }\n}\n\n/**\n * Creates a dismissal policy that removes the Live Activity at the specified time within a four-hour window.\n * @param date The date after which the Live Activity should be removed from the Lock Screen.\n * @hidden\n */\nexport function after(date: Date): { after: Date } {\n return { after: date };\n}\n\n/**\n * Creates a Widget instance.\n * @param name The widget name. Must match the `'name'` field in your widget configuration in the app config.\n * @param widget The widget component, marked with the `'widget'` directive.\n */\nexport function createWidget<T extends object = object>(\n name: string,\n widget: (props: T, context: WidgetEnvironment) => React.JSX.Element\n): Widget<T> {\n return new Widget<T>(name, widget);\n}\n\n/**\n * Creates a Live Activity Factory for managing Live Activities of a specific type.\n * @param name The Live Activity name. Must match the `'name'` field in your widget configuration in the app config.\n * @param liveActivity The Live Activity component, marked with the `'widget'` directive.\n */\nexport function createLiveActivity<T extends object = object>(\n name: string,\n liveActivity: LiveActivityComponent<T>\n): LiveActivityFactory<T> {\n return new LiveActivityFactory<T>(name, liveActivity);\n}\n\n/**\n * Adds a listener for widget interaction events (for example, button taps).\n * @param listener Callback function to handle user interaction events.\n * @return An event subscription that can be used to remove the listener.\n */\nexport function addUserInteractionListener(\n listener: ExpoWidgetsEvents['onExpoWidgetsUserInteraction']\n): EventSubscription {\n return ExpoWidgetsModule.addListener('onExpoWidgetsUserInteraction', listener);\n}\n\n/**\n * Adds a listener for push-to-start token events.\n * This token can be used to start live activities remotely via APNs.\n * @param listener Callback function to handle push-to-start token events.\n * @return An event subscription that can be used to remove the listener.\n */\nexport function addPushToStartTokenListener(\n listener: ExpoWidgetsEvents['onExpoWidgetsPushToStartTokenReceived']\n): EventSubscription {\n return ExpoWidgetsModule.addListener('onExpoWidgetsPushToStartTokenReceived', listener);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Widgets.js","sourceRoot":"","sources":["../src/Widgets.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EventSubscription } from 'expo-modules-core';\n\nimport ExpoWidgetsModule from './ExpoWidgets';\nimport type {\n ExpoWidgetsEvents,\n LiveActivityComponent,\n LiveActivityDismissalPolicy,\n NativeLiveActivity,\n NativeLiveActivityFactory,\n NativeWidgetObject,\n PushTokenEvent,\n WidgetEnvironment,\n WidgetTimelineEntry,\n} from './Widgets.types';\n\n/**\n * Represents a widget instance. Provides methods to manage the widget's timeline.\n */\nexport class Widget<\n PropsType extends object = object,\n ConfigurationType extends object | undefined = undefined,\n> {\n /** @hidden */\n private nativeWidgetObject: NativeWidgetObject;\n constructor(\n name: string,\n layout: (\n props: PropsType,\n environment: WidgetEnvironment<ConfigurationType>\n ) => React.JSX.Element\n ) {\n this.nativeWidgetObject = new ExpoWidgetsModule.Widget(name, layout as unknown as string);\n }\n\n /**\n * Force reloads the widget, causing it to refresh its content and timeline.\n */\n reload() {\n this.nativeWidgetObject.reload();\n }\n\n /**\n * Schedules a series of updates for the widget's content and reloads the widget.\n * @param entries Timeline entries, each specifying a date and the props to display at that time.\n */\n updateTimeline(entries: WidgetTimelineEntry<PropsType>[]) {\n this.nativeWidgetObject.updateTimeline(\n entries.map((entry) => ({ timestamp: entry.date.getTime(), props: entry.props }))\n );\n }\n\n /**\n * Sets the widget's content to the given props immediately, without scheduling a timeline.\n * @param props The properties to display in the widget.\n */\n updateSnapshot(props: PropsType) {\n this.nativeWidgetObject.updateTimeline([{ timestamp: Date.now(), props }]);\n }\n\n /**\n * Returns the current timeline entries for the widget, including past and future entries.\n */\n async getTimeline(): Promise<WidgetTimelineEntry<PropsType>[]> {\n return (await this.nativeWidgetObject.getTimeline()).map((entry) => ({\n date: new Date(entry.timestamp),\n props: entry.props as PropsType,\n }));\n }\n}\n\n/**\n * Represents a Live Activity instance. Provides methods to update its content and end it.\n */\nexport class LiveActivity<T extends object = object> {\n /** @hidden */\n private nativeLiveActivity: NativeLiveActivity;\n constructor(nativeLiveActivity: NativeLiveActivity) {\n this.nativeLiveActivity = nativeLiveActivity;\n }\n\n /**\n * Updates the Live Activity's content. The UI reflects the new properties immediately.\n * @param props The updated content properties.\n */\n update(props: T): Promise<void> {\n return this.nativeLiveActivity.update(JSON.stringify(props));\n }\n\n /**\n * Ends the Live Activity.\n * @param dismissalPolicy Controls when the Live Activity is removed from the Lock Screen after ending.\n * Can be `'default'`, `'immediate'`, or `after(date)`.\n * @param props Final content properties to update after the activity ends.\n * @param contentDate The time the data in the payload was generated. If this is older than a previous update or push payload, the system ignores this update.\n */\n end(dismissalPolicy?: LiveActivityDismissalPolicy, props?: T, contentDate?: Date): Promise<void> {\n const serializedProps = props ? JSON.stringify(props) : undefined;\n if (\n typeof dismissalPolicy === 'object' &&\n dismissalPolicy !== null &&\n 'after' in dismissalPolicy\n ) {\n return this.nativeLiveActivity.end(\n 'after',\n dismissalPolicy.after?.getTime(),\n serializedProps,\n contentDate?.getTime()\n );\n }\n return this.nativeLiveActivity.end(\n dismissalPolicy,\n undefined,\n serializedProps,\n contentDate?.getTime()\n );\n }\n\n /**\n * Returns the push token for this Live Activity, used to send push notification updates via APNs.\n * Returns `null` if push notifications are not enabled or the token is not yet available.\n */\n getPushToken(): Promise<string | null> {\n return this.nativeLiveActivity.getPushToken();\n }\n\n /**\n * Adds a listener for push token update events on this Live Activity instance.\n * The token can be used to send content updates to this specific activity via APNs.\n * @param listener Callback invoked when a new push token is available.\n * @returns An event subscription that can be used to remove the listener.\n */\n addPushTokenListener(listener: (event: PushTokenEvent) => void): EventSubscription {\n return this.nativeLiveActivity.addListener('onExpoWidgetsTokenReceived', listener);\n }\n}\n\n/**\n * Manages Live Activity instances of a specific type. Use it to start new activities and retrieve currently active ones.\n */\nexport class LiveActivityFactory<T extends object = object> {\n /** @hidden */\n private nativeLiveActivityFactory: NativeLiveActivityFactory;\n constructor(name: string, layout: LiveActivityComponent<T>) {\n this.nativeLiveActivityFactory = new ExpoWidgetsModule.LiveActivityFactory(\n name,\n layout as unknown as string\n );\n }\n\n /**\n * Starts a new Live Activity with the given properties.\n * @param props The initial content properties for the Live Activity.\n * @param url An optional URL to associate with the Live Activity, used for deep linking.\n * @returns The new Live Activity instance.\n */\n start(props: T, url?: string) {\n return new LiveActivity<T>(this.nativeLiveActivityFactory.start(JSON.stringify(props), url));\n }\n\n /**\n * Returns all currently active instances of this Live Activity type.\n */\n getInstances() {\n return this.nativeLiveActivityFactory\n .getInstances()\n .map((instance) => new LiveActivity<T>(instance));\n }\n}\n\n/**\n * Creates a dismissal policy that removes the Live Activity at the specified time within a four-hour window.\n * @param date The date after which the Live Activity should be removed from the Lock Screen.\n * @hidden\n */\nexport function after(date: Date): { after: Date } {\n return { after: date };\n}\n\n/**\n * Creates a Widget instance.\n * @param name The widget name. Must match the `'name'` field in your widget configuration in the app config.\n * @param widget The widget component, marked with the `'widget'` directive.\n */\nexport function createWidget<\n PropsType extends object = object,\n ConfigurationType extends object | undefined = undefined,\n>(\n name: string,\n widget: (props: PropsType, context: WidgetEnvironment<ConfigurationType>) => React.JSX.Element\n): Widget<PropsType, ConfigurationType> {\n return new Widget<PropsType, ConfigurationType>(name, widget);\n}\n\n/**\n * Creates a Live Activity Factory for managing Live Activities of a specific type.\n * @param name The Live Activity name. Must match the `'name'` field in your widget configuration in the app config.\n * @param liveActivity The Live Activity component, marked with the `'widget'` directive.\n */\nexport function createLiveActivity<T extends object = object>(\n name: string,\n liveActivity: LiveActivityComponent<T>\n): LiveActivityFactory<T> {\n return new LiveActivityFactory<T>(name, liveActivity);\n}\n\n/**\n * Adds a listener for widget interaction events (for example, button taps).\n * @param listener Callback function to handle user interaction events.\n * @return An event subscription that can be used to remove the listener.\n */\nexport function addUserInteractionListener(\n listener: ExpoWidgetsEvents['onExpoWidgetsUserInteraction']\n): EventSubscription {\n return ExpoWidgetsModule.addListener('onExpoWidgetsUserInteraction', listener);\n}\n\n/**\n * Adds a listener for push-to-start token events.\n * This token can be used to start live activities remotely via APNs.\n * @param listener Callback function to handle push-to-start token events.\n * @return An event subscription that can be used to remove the listener.\n */\nexport function addPushToStartTokenListener(\n listener: ExpoWidgetsEvents['onExpoWidgetsPushToStartTokenReceived']\n): EventSubscription {\n return ExpoWidgetsModule.addListener('onExpoWidgetsPushToStartTokenReceived', listener);\n}\n"]}
|
package/build/Widgets.types.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export type ActivityFamily = 'small' | 'medium';
|
|
|
33
33
|
* - `accessoryInline` - Inline accessory widget for the Lock Screen.
|
|
34
34
|
*/
|
|
35
35
|
export type WidgetFamily = 'systemSmall' | 'systemMedium' | 'systemLarge' | 'systemExtraLarge' | 'accessoryCircular' | 'accessoryRectangular' | 'accessoryInline';
|
|
36
|
-
export type WidgetEnvironment = {
|
|
36
|
+
export type WidgetEnvironment<T extends object | undefined = undefined> = {
|
|
37
37
|
/**
|
|
38
38
|
* The date of this timeline entry.
|
|
39
39
|
*/
|
|
@@ -79,6 +79,11 @@ export type WidgetEnvironment = {
|
|
|
79
79
|
* @platform iOS 26+
|
|
80
80
|
*/
|
|
81
81
|
levelOfDetail?: LevelOfDetail;
|
|
82
|
+
/**
|
|
83
|
+
* Widget configuration parameters.
|
|
84
|
+
* @platform iOS 17+
|
|
85
|
+
*/
|
|
86
|
+
configuration: T;
|
|
82
87
|
};
|
|
83
88
|
export type LiveActivityEnvironment = {
|
|
84
89
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Widgets.types.d.ts","sourceRoot":"","sources":["../src/Widgets.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,SAAS,CAAC;AAErD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,cAAc,GACd,aAAa,GACb,kBAAkB,GAClB,mBAAmB,GACnB,sBAAsB,GACtB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,iBAAiB,GAAG;
|
|
1
|
+
{"version":3,"file":"Widgets.types.d.ts","sourceRoot":"","sources":["../src/Widgets.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Widgets.types.js","sourceRoot":"","sources":["../src/Widgets.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC","sourcesContent":["import { SharedObject } from 'expo';\nimport type { ReactNode } from 'react';\n\nimport type { after } from './Widgets';\n\n/**\n * The rendering mode of the widget as provided by WidgetKit.\n * - `fullColor` — Home screen widgets (default).\n * - `accented` — Tinted widgets (iOS 18+) and watchOS.\n * - `vibrant` — Lock screen widgets.\n */\nexport type WidgetRenderingMode = 'fullColor' | 'accented' | 'vibrant';\n\n/**\n * The level of detail the view is recommended to have.\n * The system can update the levelOfDetail value based on user proximity or other system specific factors and allow content customization adapting to show different levels of details.\n * - `simplified` — The system recommends showing a simplified view with less details.\n * - `default` — The system has no specific recommendation for the level of detail.\n * @platform iOS 26+\n */\nexport type LevelOfDetail = 'simplified' | 'default';\n\n/**\n * The size family of the current Live Activity.\n * A Live Activity you initiate on one device can also appear on a remote device that renders the Live Activity in a different family size. As a result, it renders for a specific family, depending on both the device and the location in which it appears.\n * @platform iOS 18+\n */\nexport type ActivityFamily = 'small' | 'medium';\n\n/**\n * The widget family (size).\n * - `systemSmall` - Small square widget (2x2 grid).\n * - `systemMedium` - Medium widget (4x2 grid).\n * - `systemLarge` - Large widget (4x4 grid).\n * - `systemExtraLarge` - Extra large widget (iPad only, 6x4 grid).\n * - `accessoryCircular` - Circular accessory widget for the Lock Screen.\n * - `accessoryRectangular` - Rectangular accessory widget for the Lock Screen.\n * - `accessoryInline` - Inline accessory widget for the Lock Screen.\n */\nexport type WidgetFamily =\n | 'systemSmall'\n | 'systemMedium'\n | 'systemLarge'\n | 'systemExtraLarge'\n | 'accessoryCircular'\n | 'accessoryRectangular'\n | 'accessoryInline';\n\nexport type WidgetEnvironment = {\n /**\n * The date of this timeline entry.\n */\n date: Date;\n /**\n * The widget family.\n */\n widgetFamily: WidgetFamily;\n /**\n * The color scheme of the widget's environment.\n */\n colorScheme?: 'light' | 'dark';\n /**\n * A Boolean value that indicates whether the display or environment currently requires reduced luminance.\n *\n * When you detect this condition, lower the overall brightness of your view.\n * For example, you can change large, filled shapes to be stroked, and choose less bright colors.\n * @platform iOS 16+\n */\n isLuminanceReduced?: boolean;\n /**\n * The widget's rendering mode, based on where the system is displaying it.\n * @platform iOS 16+\n */\n widgetRenderingMode?: WidgetRenderingMode;\n /**\n * A Boolean value that indicates whether an accessory family widget can display an accessory label.\n * @platform iOS 16+\n */\n showsWidgetLabel?: boolean;\n /**\n * The content margins for the widget.\n * @platform iOS 17+\n */\n widgetContentMargins?: {\n top: number;\n bottom: number;\n leading: number;\n trailing: number;\n };\n /**\n * The level of detail the view is recommended to have.\n * @platform iOS 26+\n */\n levelOfDetail?: LevelOfDetail;\n};\n\nexport type LiveActivityEnvironment = {\n /**\n * The color scheme of the activity's environment.\n */\n colorScheme: 'light' | 'dark';\n /**\n * Whether the activity is displayed in a context with reduced luminance.\n * @platform iOS 16+\n */\n isLuminanceReduced?: boolean;\n /**\n * Whether the activity is currently displayed in fullscreen.\n * @platform iOS 16.1+\n */\n isActivityFullscreen?: boolean;\n /**\n * A Boolean value that indicates whether the Live Activity update synchronization rate is reduced.\n * @platform iOS 18+\n */\n isActivityUpdateReduced?: boolean;\n /**\n * The size family of the current Live Activity.\n * @platform iOS 18+\n */\n activityFamily?: ActivityFamily;\n /**\n * The level of detail the view is recommended to have.\n * @platform iOS 26+\n */\n levelOfDetail?: LevelOfDetail;\n};\n\nexport type WidgetTimelineEntry<T extends object = object> = {\n /**\n * Date when widget should update.\n */\n date: Date;\n /**\n * Props to be passed to the widget.\n */\n props: T;\n};\n\nexport type ExpoTimelineEntry = {\n timestamp: number;\n props: Record<string, any>;\n};\n\n/**\n * Defines the layout sections for an iOS Live Activity.\n */\nexport type LiveActivityLayout = {\n /**\n * The main banner content displayed in Notifications Center.\n */\n banner: ReactNode;\n /**\n * The small banner content displayed in CarPlay and WatchOS. Falls back to `banner` if not provided.\n */\n bannerSmall?: ReactNode;\n /**\n * The leading content in the compact Dynamic Island presentation.\n */\n compactLeading?: ReactNode;\n /**\n * The trailing content in the compact Dynamic Island presentation.\n */\n compactTrailing?: ReactNode;\n /**\n * The minimal content shown when the Dynamic Island is in its smallest form.\n */\n minimal?: ReactNode;\n /**\n * The center content in the expanded Dynamic Island presentation.\n */\n expandedCenter?: ReactNode;\n /**\n * The leading content in the expanded Dynamic Island presentation.\n */\n expandedLeading?: ReactNode;\n /**\n * The trailing content in the expanded Dynamic Island presentation.\n */\n expandedTrailing?: ReactNode;\n /**\n * The bottom content in the expanded Dynamic Island presentation.\n */\n expandedBottom?: ReactNode;\n};\n\n/**\n * A function that returns the layout for a Live Activity.\n */\nexport type LiveActivityComponent<T extends object = object> = (\n props: T,\n environment: LiveActivityEnvironment\n) => LiveActivityLayout;\n\n/**\n * Event emitted when a user interacts with a widget.\n */\nexport type UserInteractionEvent = {\n /**\n * Widget that triggered the interaction.\n */\n source: string;\n /**\n * Button/toggle that was pressed.\n */\n target: string;\n /**\n * Timestamp of the event.\n */\n timestamp: number;\n /**\n * The event type identifier.\n */\n type: 'ExpoWidgetsUserInteraction';\n};\n\n/**\n * Event emitted when a push token is received for a live activity.\n */\nexport type PushTokenEvent = {\n /**\n * The ID of the live activity.\n */\n activityId: string;\n /**\n * The push token for the live activity.\n */\n pushToken: string;\n};\n\n/**\n * Event emitted when a push-to-start token is received.\n */\nexport type PushToStartTokenEvent = {\n /**\n * The push-to-start token for starting live activities remotely.\n */\n activityPushToStartToken: string;\n};\n\n/**\n * Dismissal policy for ending a live activity.\n * - `'default'` - The system’s default dismissal policy for the Live Activity.\n * - `'immediate'` - The system immediately removes the Live Activity that ended.\n * - `after(date)` - The system removes the Live Activity that ended at the specified time within a four-hour window.\n */\nexport type LiveActivityDismissalPolicy = 'default' | 'immediate' | ReturnType<typeof after>;\n\nexport type ExpoWidgetsEvents = {\n /**\n * Function that is invoked when user interacts with a widget.\n * @param event Interaction event details.\n */\n onExpoWidgetsUserInteraction: (event: UserInteractionEvent) => void;\n /**\n * Function that is invoked when a push-to-start token is received.\n * @param event Token event details.\n */\n onExpoWidgetsPushToStartTokenReceived: (event: PushToStartTokenEvent) => void;\n};\n\nexport type LiveActivityEvents = {\n /**\n * Function that is invoked when a push token is received for a live activity.\n * @param event Token event details.\n */\n onExpoWidgetsTokenReceived: (event: PushTokenEvent) => void;\n};\n\nexport declare class NativeWidgetObject extends SharedObject {\n constructor(name: string, layout: string);\n reload(): void;\n updateTimeline(entries: ExpoTimelineEntry[]): void;\n getTimeline(): Promise<ExpoTimelineEntry[]>;\n}\n\nexport declare class NativeLiveActivityFactory extends SharedObject {\n constructor(name: string, layout: string);\n start(props: string, url?: string): NativeLiveActivity;\n getInstances(): NativeLiveActivity[];\n}\n\nexport declare class NativeLiveActivity extends SharedObject<LiveActivityEvents> {\n update(props: string): Promise<void>;\n end(\n dismissalPolicy?: string,\n afterDate?: number,\n state?: string,\n contentDate?: number\n ): Promise<void>;\n getPushToken(): Promise<string | null>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Widgets.types.js","sourceRoot":"","sources":["../src/Widgets.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC","sourcesContent":["import { SharedObject } from 'expo';\nimport type { ReactNode } from 'react';\n\nimport type { after } from './Widgets';\n\n/**\n * The rendering mode of the widget as provided by WidgetKit.\n * - `fullColor` — Home screen widgets (default).\n * - `accented` — Tinted widgets (iOS 18+) and watchOS.\n * - `vibrant` — Lock screen widgets.\n */\nexport type WidgetRenderingMode = 'fullColor' | 'accented' | 'vibrant';\n\n/**\n * The level of detail the view is recommended to have.\n * The system can update the levelOfDetail value based on user proximity or other system specific factors and allow content customization adapting to show different levels of details.\n * - `simplified` — The system recommends showing a simplified view with less details.\n * - `default` — The system has no specific recommendation for the level of detail.\n * @platform iOS 26+\n */\nexport type LevelOfDetail = 'simplified' | 'default';\n\n/**\n * The size family of the current Live Activity.\n * A Live Activity you initiate on one device can also appear on a remote device that renders the Live Activity in a different family size. As a result, it renders for a specific family, depending on both the device and the location in which it appears.\n * @platform iOS 18+\n */\nexport type ActivityFamily = 'small' | 'medium';\n\n/**\n * The widget family (size).\n * - `systemSmall` - Small square widget (2x2 grid).\n * - `systemMedium` - Medium widget (4x2 grid).\n * - `systemLarge` - Large widget (4x4 grid).\n * - `systemExtraLarge` - Extra large widget (iPad only, 6x4 grid).\n * - `accessoryCircular` - Circular accessory widget for the Lock Screen.\n * - `accessoryRectangular` - Rectangular accessory widget for the Lock Screen.\n * - `accessoryInline` - Inline accessory widget for the Lock Screen.\n */\nexport type WidgetFamily =\n | 'systemSmall'\n | 'systemMedium'\n | 'systemLarge'\n | 'systemExtraLarge'\n | 'accessoryCircular'\n | 'accessoryRectangular'\n | 'accessoryInline';\n\nexport type WidgetEnvironment<T extends object | undefined = undefined> = {\n /**\n * The date of this timeline entry.\n */\n date: Date;\n /**\n * The widget family.\n */\n widgetFamily: WidgetFamily;\n /**\n * The color scheme of the widget's environment.\n */\n colorScheme?: 'light' | 'dark';\n /**\n * A Boolean value that indicates whether the display or environment currently requires reduced luminance.\n *\n * When you detect this condition, lower the overall brightness of your view.\n * For example, you can change large, filled shapes to be stroked, and choose less bright colors.\n * @platform iOS 16+\n */\n isLuminanceReduced?: boolean;\n /**\n * The widget's rendering mode, based on where the system is displaying it.\n * @platform iOS 16+\n */\n widgetRenderingMode?: WidgetRenderingMode;\n /**\n * A Boolean value that indicates whether an accessory family widget can display an accessory label.\n * @platform iOS 16+\n */\n showsWidgetLabel?: boolean;\n /**\n * The content margins for the widget.\n * @platform iOS 17+\n */\n widgetContentMargins?: {\n top: number;\n bottom: number;\n leading: number;\n trailing: number;\n };\n /**\n * The level of detail the view is recommended to have.\n * @platform iOS 26+\n */\n levelOfDetail?: LevelOfDetail;\n /**\n * Widget configuration parameters.\n * @platform iOS 17+\n */\n configuration: T;\n};\n\nexport type LiveActivityEnvironment = {\n /**\n * The color scheme of the activity's environment.\n */\n colorScheme: 'light' | 'dark';\n /**\n * Whether the activity is displayed in a context with reduced luminance.\n * @platform iOS 16+\n */\n isLuminanceReduced?: boolean;\n /**\n * Whether the activity is currently displayed in fullscreen.\n * @platform iOS 16.1+\n */\n isActivityFullscreen?: boolean;\n /**\n * A Boolean value that indicates whether the Live Activity update synchronization rate is reduced.\n * @platform iOS 18+\n */\n isActivityUpdateReduced?: boolean;\n /**\n * The size family of the current Live Activity.\n * @platform iOS 18+\n */\n activityFamily?: ActivityFamily;\n /**\n * The level of detail the view is recommended to have.\n * @platform iOS 26+\n */\n levelOfDetail?: LevelOfDetail;\n};\n\nexport type WidgetTimelineEntry<T extends object = object> = {\n /**\n * Date when widget should update.\n */\n date: Date;\n /**\n * Props to be passed to the widget.\n */\n props: T;\n};\n\nexport type ExpoTimelineEntry = {\n timestamp: number;\n props: Record<string, any>;\n};\n\n/**\n * Defines the layout sections for an iOS Live Activity.\n */\nexport type LiveActivityLayout = {\n /**\n * The main banner content displayed in Notifications Center.\n */\n banner: ReactNode;\n /**\n * The small banner content displayed in CarPlay and WatchOS. Falls back to `banner` if not provided.\n */\n bannerSmall?: ReactNode;\n /**\n * The leading content in the compact Dynamic Island presentation.\n */\n compactLeading?: ReactNode;\n /**\n * The trailing content in the compact Dynamic Island presentation.\n */\n compactTrailing?: ReactNode;\n /**\n * The minimal content shown when the Dynamic Island is in its smallest form.\n */\n minimal?: ReactNode;\n /**\n * The center content in the expanded Dynamic Island presentation.\n */\n expandedCenter?: ReactNode;\n /**\n * The leading content in the expanded Dynamic Island presentation.\n */\n expandedLeading?: ReactNode;\n /**\n * The trailing content in the expanded Dynamic Island presentation.\n */\n expandedTrailing?: ReactNode;\n /**\n * The bottom content in the expanded Dynamic Island presentation.\n */\n expandedBottom?: ReactNode;\n};\n\n/**\n * A function that returns the layout for a Live Activity.\n */\nexport type LiveActivityComponent<T extends object = object> = (\n props: T,\n environment: LiveActivityEnvironment\n) => LiveActivityLayout;\n\n/**\n * Event emitted when a user interacts with a widget.\n */\nexport type UserInteractionEvent = {\n /**\n * Widget that triggered the interaction.\n */\n source: string;\n /**\n * Button/toggle that was pressed.\n */\n target: string;\n /**\n * Timestamp of the event.\n */\n timestamp: number;\n /**\n * The event type identifier.\n */\n type: 'ExpoWidgetsUserInteraction';\n};\n\n/**\n * Event emitted when a push token is received for a live activity.\n */\nexport type PushTokenEvent = {\n /**\n * The ID of the live activity.\n */\n activityId: string;\n /**\n * The push token for the live activity.\n */\n pushToken: string;\n};\n\n/**\n * Event emitted when a push-to-start token is received.\n */\nexport type PushToStartTokenEvent = {\n /**\n * The push-to-start token for starting live activities remotely.\n */\n activityPushToStartToken: string;\n};\n\n/**\n * Dismissal policy for ending a live activity.\n * - `'default'` - The system’s default dismissal policy for the Live Activity.\n * - `'immediate'` - The system immediately removes the Live Activity that ended.\n * - `after(date)` - The system removes the Live Activity that ended at the specified time within a four-hour window.\n */\nexport type LiveActivityDismissalPolicy = 'default' | 'immediate' | ReturnType<typeof after>;\n\nexport type ExpoWidgetsEvents = {\n /**\n * Function that is invoked when user interacts with a widget.\n * @param event Interaction event details.\n */\n onExpoWidgetsUserInteraction: (event: UserInteractionEvent) => void;\n /**\n * Function that is invoked when a push-to-start token is received.\n * @param event Token event details.\n */\n onExpoWidgetsPushToStartTokenReceived: (event: PushToStartTokenEvent) => void;\n};\n\nexport type LiveActivityEvents = {\n /**\n * Function that is invoked when a push token is received for a live activity.\n * @param event Token event details.\n */\n onExpoWidgetsTokenReceived: (event: PushTokenEvent) => void;\n};\n\nexport declare class NativeWidgetObject extends SharedObject {\n constructor(name: string, layout: string);\n reload(): void;\n updateTimeline(entries: ExpoTimelineEntry[]): void;\n getTimeline(): Promise<ExpoTimelineEntry[]>;\n}\n\nexport declare class NativeLiveActivityFactory extends SharedObject {\n constructor(name: string, layout: string);\n start(props: string, url?: string): NativeLiveActivity;\n getInstances(): NativeLiveActivity[];\n}\n\nexport declare class NativeLiveActivity extends SharedObject<LiveActivityEvents> {\n update(props: string): Promise<void>;\n end(\n dismissalPolicy?: string,\n afterDate?: number,\n state?: string,\n contentDate?: number\n ): Promise<void>;\n getPushToken(): Promise<string | null>;\n}\n"]}
|
|
@@ -81,6 +81,8 @@ public struct WidgetsDynamicView: View, ExpoSwiftUI.AnyChild {
|
|
|
81
81
|
render(UnevenRoundedRectangleView.self, UnevenRoundedRectangleViewProps.self)
|
|
82
82
|
case "GaugeView":
|
|
83
83
|
render(GaugeView.self, GaugeProps.self)
|
|
84
|
+
case "ChartView":
|
|
85
|
+
render(ChartView.self, ChartProps.self)
|
|
84
86
|
case "Button":
|
|
85
87
|
if #available(iOS 17.0, *) {
|
|
86
88
|
switch kind {
|
|
@@ -25,13 +25,12 @@ public struct WidgetsEntryView: View {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
public var body: some View {
|
|
28
|
-
let layout = WidgetsStorage.getString(forKey: "__expo_widgets_\(entry.name)_layout")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if let node {
|
|
28
|
+
if let layout = WidgetsStorage.getString(forKey: "__expo_widgets_\(entry.name)_layout"),
|
|
29
|
+
!layout.isEmpty {
|
|
30
|
+
let node = evaluateLayout(layout: layout, props: entry.props ?? [:], environment: widgetEnvironment)
|
|
32
31
|
WidgetsDynamicView(name: entry.name, kind: .widget, node: node, entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
33
32
|
} else {
|
|
34
|
-
|
|
33
|
+
WidgetsDynamicView(name: entry.name, kind: .widget, node: createRedBox(message: "No layout found for \(WidgetsStorage.appGroupIdentifier ?? "")::\(entry.name)"), entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
}
|
package/ios/Widgets/Utils.swift
CHANGED
|
@@ -20,7 +20,7 @@ func parseTimeline(identifier: String, name: String, family: WidgetFamily) -> [W
|
|
|
20
20
|
return entries.compactMap(\.self)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
func createRedBox(message: String, stack: String? = nil) -> [String: Any] {
|
|
23
|
+
public func createRedBox(message: String, stack: String? = nil) -> [String: Any] {
|
|
24
24
|
var props: [String: Any] = ["message": message]
|
|
25
25
|
if let stack {
|
|
26
26
|
props["stack"] = stack
|
|
@@ -28,13 +28,13 @@ func createRedBox(message: String, stack: String? = nil) -> [String: Any] {
|
|
|
28
28
|
return ["type": "RedBoxView", "props": props]
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
func evaluateLayout(
|
|
31
|
+
public func evaluateLayout(
|
|
32
32
|
layout: String,
|
|
33
33
|
props: [String: Any],
|
|
34
34
|
environment: [String: Any]
|
|
35
|
-
) -> [String: Any]
|
|
35
|
+
) -> [String: Any] {
|
|
36
36
|
guard let context = createWidgetContext(layout: layout) else {
|
|
37
|
-
return
|
|
37
|
+
return createRedBox(message: "Could not create context for layout evaluation.")
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
let result = context.objectForKeyedSubscript("__expoWidgetRender")?.call(
|
|
@@ -44,7 +44,7 @@ func evaluateLayout(
|
|
|
44
44
|
print("[ExpoWidgets] Layout evaluation failed: \(exception)")
|
|
45
45
|
return createRedBox(message: exception.toString())
|
|
46
46
|
}
|
|
47
|
-
return result?.toObject() as? [String: Any]
|
|
47
|
+
return result?.toObject() as? [String: Any] ?? createRedBox(message: "Expo widget render did not produce any results.")
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
func getLiveActivityNodes(forName name: String, props: String = "{}", environment: [String: Any]) -> [String: Any] {
|
|
@@ -52,7 +52,7 @@ func getLiveActivityNodes(forName name: String, props: String = "{}", environmen
|
|
|
52
52
|
let propsData = props.data(using: .utf8)
|
|
53
53
|
let propsDict = propsData.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) as? [String: Any] } ?? [:]
|
|
54
54
|
guard let context = createWidgetContext(layout: layout) else {
|
|
55
|
-
return [:]
|
|
55
|
+
return ["banner": createRedBox(message: "Could not create context for layout evaluation.")]
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
var widgetEnvironment = environment
|
|
@@ -67,7 +67,7 @@ func getLiveActivityNodes(forName name: String, props: String = "{}", environmen
|
|
|
67
67
|
return ["banner": createRedBox(message: exception.toString())]
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
return result?.toObject() as? [String: Any] ?? [:]
|
|
70
|
+
return result?.toObject() as? [String: Any] ?? ["banner": createRedBox(message: "Expo widget render did not produce any results.")]
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
func getLiveActivityUrl(forName name: String) -> URL? {
|
|
@@ -77,7 +77,7 @@ func getLiveActivityUrl(forName name: String) -> URL? {
|
|
|
77
77
|
return URL(string: urlString)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
func getWidgetEnvironment(environment: EnvironmentValues) -> [String: Any] {
|
|
80
|
+
public func getWidgetEnvironment(environment: EnvironmentValues) -> [String: Any] {
|
|
81
81
|
var env: [String: Any] = [
|
|
82
82
|
"showsContainerBackground": environment.showsWidgetContainerBackground,
|
|
83
83
|
"widgetFamily": environment.widgetFamily.description,
|
package/ios/WidgetsStorage.swift
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
enum WidgetsStorage {
|
|
2
|
-
static var appGroupIdentifier: String? = Bundle.main.object(forInfoDictionaryKey: "ExpoWidgetsAppGroupIdentifier") as? String
|
|
1
|
+
public enum WidgetsStorage {
|
|
2
|
+
public static var appGroupIdentifier: String? = Bundle.main.object(forInfoDictionaryKey: "ExpoWidgetsAppGroupIdentifier") as? String
|
|
3
3
|
static let defaults = UserDefaults(suiteName: appGroupIdentifier)
|
|
4
4
|
|
|
5
5
|
static func set(_ value: [String: Any], forKey key: String) {
|
|
@@ -26,25 +26,25 @@ enum WidgetsStorage {
|
|
|
26
26
|
defaults.set(value, forKey: key)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
static func getDictionary(forKey key: String) -> [String: Any]? {
|
|
29
|
+
public static func getDictionary(forKey key: String) -> [String: Any]? {
|
|
30
30
|
guard let defaults else { return nil }
|
|
31
31
|
|
|
32
32
|
return defaults.dictionary(forKey: key)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
static func getArray(forKey key: String) -> [Any]? {
|
|
35
|
+
public static func getArray(forKey key: String) -> [Any]? {
|
|
36
36
|
guard let defaults else { return nil }
|
|
37
37
|
|
|
38
38
|
return defaults.array(forKey: key)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
static func getData(forKey key: String) -> Data? {
|
|
41
|
+
public static func getData(forKey key: String) -> Data? {
|
|
42
42
|
guard let defaults else { return nil }
|
|
43
43
|
|
|
44
44
|
return defaults.data(forKey: key)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
static func getString(forKey key: String) -> String? {
|
|
47
|
+
public static func getString(forKey key: String) -> String? {
|
|
48
48
|
guard let defaults else { return nil }
|
|
49
49
|
|
|
50
50
|
return defaults.string(forKey: key)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-widgets",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.8",
|
|
4
4
|
"description": "Widgets.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"homepage": "https://docs.expo.dev/versions/latest/sdk/widgets/",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@expo/plist": "^0.6.0",
|
|
25
|
-
"@expo/ui": "~56.0.
|
|
25
|
+
"@expo/ui": "~56.0.7"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@expo/spawn-async": "^1.7.2",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@types/react": "~19.2.0",
|
|
31
31
|
"react-native": "0.85.3",
|
|
32
32
|
"resolve-workspace-root": "^2.0.0",
|
|
33
|
-
"expo
|
|
34
|
-
"expo": "56.0.
|
|
33
|
+
"expo": "56.0.0-preview.11",
|
|
34
|
+
"expo-module-scripts": "56.0.2"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"expo": "*",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"./scripts/xcode-build-bundle.sh"
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "51c27fce31a5b3a877a4b05d832dabf4a99db5e1",
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "expo-module build",
|
|
50
50
|
"build:plugin": "expo-module build plugin",
|
|
@@ -5,4 +5,34 @@ export type WidgetConfig = {
|
|
|
5
5
|
displayName: string;
|
|
6
6
|
description: string;
|
|
7
7
|
contentMarginsDisabled: boolean;
|
|
8
|
+
configuration?: {
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
parameters: Record<string, WidgetParameter>;
|
|
12
|
+
};
|
|
8
13
|
};
|
|
14
|
+
export type WidgetParameterString = {
|
|
15
|
+
title: string;
|
|
16
|
+
type: 'string';
|
|
17
|
+
default: string;
|
|
18
|
+
};
|
|
19
|
+
export type WidgetParameterNumber = {
|
|
20
|
+
title: string;
|
|
21
|
+
type: 'number';
|
|
22
|
+
default: number;
|
|
23
|
+
};
|
|
24
|
+
export type WidgetParameterBoolean = {
|
|
25
|
+
title: string;
|
|
26
|
+
type: 'boolean';
|
|
27
|
+
default: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type WidgetParameterEnum = {
|
|
30
|
+
title: string;
|
|
31
|
+
type: 'enum';
|
|
32
|
+
values: {
|
|
33
|
+
name: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}[];
|
|
36
|
+
default: string;
|
|
37
|
+
};
|
|
38
|
+
export type WidgetParameter = WidgetParameterString | WidgetParameterNumber | WidgetParameterBoolean | WidgetParameterEnum;
|
|
@@ -112,11 +112,22 @@ const addIndexSwiftChunk = (widgets, index, isLastChunk) => `
|
|
|
112
112
|
${index === 0 ? '@main' : ''}
|
|
113
113
|
struct ExportWidgets${index}: WidgetBundle {
|
|
114
114
|
var body: some Widget {
|
|
115
|
-
${widgets
|
|
115
|
+
${widgets
|
|
116
|
+
.map((widget) => {
|
|
117
|
+
if (widget.configuration) {
|
|
118
|
+
return `if #available(iOS 17.0, *) {
|
|
119
|
+
${widget.name}()
|
|
120
|
+
}`;
|
|
121
|
+
}
|
|
122
|
+
return `${widget.name}()`;
|
|
123
|
+
})
|
|
124
|
+
.join('\n ')}
|
|
116
125
|
${!isLastChunk ? `ExportWidgets${index + 1}().body` : `WidgetLiveActivity()`}
|
|
117
126
|
}
|
|
118
127
|
}`;
|
|
119
|
-
const widgetSwift = (widget) =>
|
|
128
|
+
const widgetSwift = (widget) => {
|
|
129
|
+
if (!widget.configuration)
|
|
130
|
+
return `import WidgetKit
|
|
120
131
|
import SwiftUI
|
|
121
132
|
internal import ExpoWidgets
|
|
122
133
|
|
|
@@ -132,6 +143,166 @@ struct ${widget.name}: Widget {
|
|
|
132
143
|
.supportedFamilies([.${widget.supportedFamilies.join(', .')}])${widget.contentMarginsDisabled ? '\n .contentMarginsDisabled()' : ''}
|
|
133
144
|
}
|
|
134
145
|
}`;
|
|
146
|
+
return `import WidgetKit
|
|
147
|
+
import SwiftUI
|
|
148
|
+
import AppIntents
|
|
149
|
+
internal import ExpoWidgets
|
|
150
|
+
|
|
151
|
+
// AppIntent
|
|
152
|
+
struct ${widget.name}ConfigurationAppIntent: WidgetConfigurationIntent {
|
|
153
|
+
static var title: LocalizedStringResource = "${widget.configuration?.title ?? widget.displayName} Configuration"
|
|
154
|
+
${widget.configuration?.description ? ` static var description: LocalizedStringResource = "${widget.configuration?.description}"\n` : ''}
|
|
155
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
156
|
+
.map(([name, param]) => {
|
|
157
|
+
let paramType;
|
|
158
|
+
switch (param.type) {
|
|
159
|
+
case 'string':
|
|
160
|
+
paramType = 'String';
|
|
161
|
+
break;
|
|
162
|
+
case 'number':
|
|
163
|
+
paramType = 'Double';
|
|
164
|
+
break;
|
|
165
|
+
case 'boolean':
|
|
166
|
+
paramType = 'Bool';
|
|
167
|
+
break;
|
|
168
|
+
case 'enum':
|
|
169
|
+
paramType = `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum`;
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
paramType = 'String';
|
|
173
|
+
}
|
|
174
|
+
return ` @Parameter(title: "${param.title}", default: ${param.type === 'string' ? `"${param.default}"` : param.type === 'number' ? param.default : param.type === 'boolean' ? param.default : `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum.${param.default}`})\n var ${name}: ${paramType}`;
|
|
175
|
+
})
|
|
176
|
+
.join('\n')}
|
|
177
|
+
|
|
178
|
+
func perform() async throws -> some IntentResult {
|
|
179
|
+
return .result()
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
183
|
+
.map(([name, param]) => {
|
|
184
|
+
if (param.type !== 'enum')
|
|
185
|
+
return '';
|
|
186
|
+
const paramTypeName = `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum`;
|
|
187
|
+
return `
|
|
188
|
+
enum ${paramTypeName}: String, CaseIterable, AppEnum {
|
|
189
|
+
${param.values
|
|
190
|
+
.map((value) => {
|
|
191
|
+
return `case ${value.value}`;
|
|
192
|
+
})
|
|
193
|
+
.join('\n ')}
|
|
194
|
+
|
|
195
|
+
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "${param.title}")
|
|
196
|
+
|
|
197
|
+
static var caseDisplayRepresentations: [${paramTypeName}: DisplayRepresentation] = [
|
|
198
|
+
${param.values
|
|
199
|
+
.map((value) => {
|
|
200
|
+
return `.${value.value}: DisplayRepresentation(title: "${value.name}")`;
|
|
201
|
+
})
|
|
202
|
+
.join(',\n ')}
|
|
203
|
+
]
|
|
204
|
+
}`;
|
|
205
|
+
})
|
|
206
|
+
.join('\n')}
|
|
207
|
+
|
|
208
|
+
struct ${widget.name}TimelineEntry: TimelineEntry {
|
|
209
|
+
let date: Date
|
|
210
|
+
public let name: String
|
|
211
|
+
public let props: [String: Any]?
|
|
212
|
+
public let entryIndex: Int?
|
|
213
|
+
let configuration: ${widget.name}ConfigurationAppIntent
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
struct ${widget.name}TimelineProvider: AppIntentTimelineProvider {
|
|
217
|
+
func placeholder(in context: Context) -> ${widget.name}TimelineEntry {
|
|
218
|
+
${widget.name}TimelineEntry(date: Date(), name: "${widget.name}", props: nil, entryIndex: nil, configuration: ${widget.name}ConfigurationAppIntent())
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func snapshot(for configuration: ${widget.name}ConfigurationAppIntent, in context: Context) async -> ${widget.name}TimelineEntry {
|
|
222
|
+
let entries = parseTimeline(configuration: configuration)
|
|
223
|
+
return entries.first ?? ${widget.name}TimelineEntry(date: Date(), name: "${widget.name}", props: nil, entryIndex: nil, configuration: configuration)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
func timeline(for configuration: ${widget.name}ConfigurationAppIntent, in context: Context) async -> Timeline<${widget.name}TimelineEntry> {
|
|
227
|
+
let entries = self.parseTimeline(configuration: configuration)
|
|
228
|
+
let timeline = Timeline<${widget.name}TimelineEntry>(entries: entries, policy: .atEnd)
|
|
229
|
+
return timeline
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
func parseTimeline(configuration: ${widget.name}ConfigurationAppIntent) -> [${widget.name}TimelineEntry] {
|
|
233
|
+
let timeline = WidgetsStorage.getArray(forKey: "__expo_widgets_${widget.name}_timeline") ?? []
|
|
234
|
+
let entries: [${widget.name}TimelineEntry?] = timeline.enumerated().map { index, entry in
|
|
235
|
+
guard let entry = entry as? [String: Any], let timestamp = entry["timestamp"] as? Int, let props = entry["props"] as? [String: Any] else {
|
|
236
|
+
return nil
|
|
237
|
+
}
|
|
238
|
+
return ${widget.name}TimelineEntry(
|
|
239
|
+
date: Date(timeIntervalSince1970: Double(timestamp) / 1000),
|
|
240
|
+
name: "${widget.name}",
|
|
241
|
+
props: props,
|
|
242
|
+
entryIndex: index,
|
|
243
|
+
configuration: configuration
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return entries.compactMap(\\.self)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
struct ${widget.name}EntryView: View {
|
|
252
|
+
@Environment(\\.self) var environment
|
|
253
|
+
var entry: ${widget.name}TimelineProvider.Entry
|
|
254
|
+
|
|
255
|
+
init(entry: ${widget.name}TimelineProvider.Entry) {
|
|
256
|
+
self.entry = entry
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private var widgetEnvironment: [String: Any] {
|
|
260
|
+
var env: [String: Any] = getWidgetEnvironment(environment: environment)
|
|
261
|
+
env["timestamp"] = Int(entry.date.timeIntervalSince1970 * 1000)
|
|
262
|
+
env["configuration"] = [
|
|
263
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
264
|
+
.map(([name, param]) => {
|
|
265
|
+
return ` "${name}": entry.configuration.${name}${param.type === 'enum' ? '.rawValue' : ''}`;
|
|
266
|
+
})
|
|
267
|
+
.join(',\n')}
|
|
268
|
+
]
|
|
269
|
+
return env
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private var widgetEnvironmentString: String? {
|
|
273
|
+
guard let data = try? JSONSerialization.data(withJSONObject: widgetEnvironment),
|
|
274
|
+
let jsonString = String(data: data, encoding: .utf8) else {
|
|
275
|
+
return nil
|
|
276
|
+
}
|
|
277
|
+
return jsonString
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
public var body: some View {
|
|
281
|
+
if let layout = WidgetsStorage.getString(forKey: "__expo_widgets_\\(entry.name)_layout"),
|
|
282
|
+
!layout.isEmpty {
|
|
283
|
+
let node = evaluateLayout(layout: layout, props: entry.props ?? [:], environment: widgetEnvironment)
|
|
284
|
+
WidgetsDynamicView(name: entry.name, kind: .widget, node: node, entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
285
|
+
} else {
|
|
286
|
+
WidgetsDynamicView(name: entry.name, kind: .widget, node: createRedBox(message: "No layout found for \\(WidgetsStorage.appGroupIdentifier ?? "")::\\(entry.name)"), entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@available(iOS 17.0, *)
|
|
293
|
+
struct ${widget.name}: Widget {
|
|
294
|
+
let name: String = "${widget.name}"
|
|
295
|
+
|
|
296
|
+
var body: some WidgetConfiguration {
|
|
297
|
+
return AppIntentConfiguration(kind: name, intent: ${widget.name}ConfigurationAppIntent.self, provider: ${widget.name}TimelineProvider()) { entry in
|
|
298
|
+
${widget.name}EntryView(entry: entry)
|
|
299
|
+
}
|
|
300
|
+
.configurationDisplayName("${widget.displayName}")
|
|
301
|
+
.description("${widget.description}")
|
|
302
|
+
.supportedFamilies([.${widget.supportedFamilies.join(', .')}])${widget.contentMarginsDisabled ? '\n .contentMarginsDisabled()' : ''}
|
|
303
|
+
}
|
|
304
|
+
}`;
|
|
305
|
+
};
|
|
135
306
|
const infoPlist = (groupIdentifier, marketingVersion, bundleVersion) => `<?xml version="1.0" encoding="UTF-8"?>
|
|
136
307
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
137
308
|
<plist version="1.0">
|
|
@@ -6,4 +6,40 @@ export type WidgetConfig = {
|
|
|
6
6
|
displayName: string;
|
|
7
7
|
description: string;
|
|
8
8
|
contentMarginsDisabled: boolean;
|
|
9
|
+
configuration?: {
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
parameters: Record<string, WidgetParameter>;
|
|
13
|
+
};
|
|
9
14
|
};
|
|
15
|
+
|
|
16
|
+
export type WidgetParameterString = {
|
|
17
|
+
title: string;
|
|
18
|
+
type: 'string';
|
|
19
|
+
default: string;
|
|
20
|
+
};
|
|
21
|
+
export type WidgetParameterNumber = {
|
|
22
|
+
title: string;
|
|
23
|
+
type: 'number';
|
|
24
|
+
default: number;
|
|
25
|
+
};
|
|
26
|
+
export type WidgetParameterBoolean = {
|
|
27
|
+
title: string;
|
|
28
|
+
type: 'boolean';
|
|
29
|
+
default: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type WidgetParameterEnum = {
|
|
32
|
+
title: string;
|
|
33
|
+
type: 'enum';
|
|
34
|
+
values: {
|
|
35
|
+
name: string;
|
|
36
|
+
value: string;
|
|
37
|
+
}[];
|
|
38
|
+
default: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type WidgetParameter =
|
|
42
|
+
| WidgetParameterString
|
|
43
|
+
| WidgetParameterNumber
|
|
44
|
+
| WidgetParameterBoolean
|
|
45
|
+
| WidgetParameterEnum;
|
|
@@ -116,12 +116,23 @@ const addIndexSwiftChunk = (
|
|
|
116
116
|
${index === 0 ? '@main' : ''}
|
|
117
117
|
struct ExportWidgets${index}: WidgetBundle {
|
|
118
118
|
var body: some Widget {
|
|
119
|
-
${widgets
|
|
119
|
+
${widgets
|
|
120
|
+
.map((widget) => {
|
|
121
|
+
if (widget.configuration) {
|
|
122
|
+
return `if #available(iOS 17.0, *) {
|
|
123
|
+
${widget.name}()
|
|
124
|
+
}`;
|
|
125
|
+
}
|
|
126
|
+
return `${widget.name}()`;
|
|
127
|
+
})
|
|
128
|
+
.join('\n ')}
|
|
120
129
|
${!isLastChunk ? `ExportWidgets${index + 1}().body` : `WidgetLiveActivity()`}
|
|
121
130
|
}
|
|
122
131
|
}`;
|
|
123
132
|
|
|
124
|
-
const widgetSwift = (widget: WidgetConfig): string =>
|
|
133
|
+
const widgetSwift = (widget: WidgetConfig): string => {
|
|
134
|
+
if (!widget.configuration)
|
|
135
|
+
return `import WidgetKit
|
|
125
136
|
import SwiftUI
|
|
126
137
|
internal import ExpoWidgets
|
|
127
138
|
|
|
@@ -137,6 +148,165 @@ struct ${widget.name}: Widget {
|
|
|
137
148
|
.supportedFamilies([.${widget.supportedFamilies.join(', .')}])${widget.contentMarginsDisabled ? '\n .contentMarginsDisabled()' : ''}
|
|
138
149
|
}
|
|
139
150
|
}`;
|
|
151
|
+
return `import WidgetKit
|
|
152
|
+
import SwiftUI
|
|
153
|
+
import AppIntents
|
|
154
|
+
internal import ExpoWidgets
|
|
155
|
+
|
|
156
|
+
// AppIntent
|
|
157
|
+
struct ${widget.name}ConfigurationAppIntent: WidgetConfigurationIntent {
|
|
158
|
+
static var title: LocalizedStringResource = "${widget.configuration?.title ?? widget.displayName} Configuration"
|
|
159
|
+
${widget.configuration?.description ? ` static var description: LocalizedStringResource = "${widget.configuration?.description}"\n` : ''}
|
|
160
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
161
|
+
.map(([name, param]) => {
|
|
162
|
+
let paramType: string;
|
|
163
|
+
switch (param.type) {
|
|
164
|
+
case 'string':
|
|
165
|
+
paramType = 'String';
|
|
166
|
+
break;
|
|
167
|
+
case 'number':
|
|
168
|
+
paramType = 'Double';
|
|
169
|
+
break;
|
|
170
|
+
case 'boolean':
|
|
171
|
+
paramType = 'Bool';
|
|
172
|
+
break;
|
|
173
|
+
case 'enum':
|
|
174
|
+
paramType = `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum`;
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
paramType = 'String';
|
|
178
|
+
}
|
|
179
|
+
return ` @Parameter(title: "${param.title}", default: ${param.type === 'string' ? `"${param.default}"` : param.type === 'number' ? param.default : param.type === 'boolean' ? param.default : `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum.${param.default}`})\n var ${name}: ${paramType}`;
|
|
180
|
+
})
|
|
181
|
+
.join('\n')}
|
|
182
|
+
|
|
183
|
+
func perform() async throws -> some IntentResult {
|
|
184
|
+
return .result()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
188
|
+
.map(([name, param]) => {
|
|
189
|
+
if (param.type !== 'enum') return '';
|
|
190
|
+
const paramTypeName = `${widget.name}${name[0]?.toUpperCase() + name.slice(1)}Enum`;
|
|
191
|
+
return `
|
|
192
|
+
enum ${paramTypeName}: String, CaseIterable, AppEnum {
|
|
193
|
+
${param.values
|
|
194
|
+
.map((value) => {
|
|
195
|
+
return `case ${value.value}`;
|
|
196
|
+
})
|
|
197
|
+
.join('\n ')}
|
|
198
|
+
|
|
199
|
+
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "${param.title}")
|
|
200
|
+
|
|
201
|
+
static var caseDisplayRepresentations: [${paramTypeName}: DisplayRepresentation] = [
|
|
202
|
+
${param.values
|
|
203
|
+
.map((value) => {
|
|
204
|
+
return `.${value.value}: DisplayRepresentation(title: "${value.name}")`;
|
|
205
|
+
})
|
|
206
|
+
.join(',\n ')}
|
|
207
|
+
]
|
|
208
|
+
}`;
|
|
209
|
+
})
|
|
210
|
+
.join('\n')}
|
|
211
|
+
|
|
212
|
+
struct ${widget.name}TimelineEntry: TimelineEntry {
|
|
213
|
+
let date: Date
|
|
214
|
+
public let name: String
|
|
215
|
+
public let props: [String: Any]?
|
|
216
|
+
public let entryIndex: Int?
|
|
217
|
+
let configuration: ${widget.name}ConfigurationAppIntent
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
struct ${widget.name}TimelineProvider: AppIntentTimelineProvider {
|
|
221
|
+
func placeholder(in context: Context) -> ${widget.name}TimelineEntry {
|
|
222
|
+
${widget.name}TimelineEntry(date: Date(), name: "${widget.name}", props: nil, entryIndex: nil, configuration: ${widget.name}ConfigurationAppIntent())
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
func snapshot(for configuration: ${widget.name}ConfigurationAppIntent, in context: Context) async -> ${widget.name}TimelineEntry {
|
|
226
|
+
let entries = parseTimeline(configuration: configuration)
|
|
227
|
+
return entries.first ?? ${widget.name}TimelineEntry(date: Date(), name: "${widget.name}", props: nil, entryIndex: nil, configuration: configuration)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func timeline(for configuration: ${widget.name}ConfigurationAppIntent, in context: Context) async -> Timeline<${widget.name}TimelineEntry> {
|
|
231
|
+
let entries = self.parseTimeline(configuration: configuration)
|
|
232
|
+
let timeline = Timeline<${widget.name}TimelineEntry>(entries: entries, policy: .atEnd)
|
|
233
|
+
return timeline
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
func parseTimeline(configuration: ${widget.name}ConfigurationAppIntent) -> [${widget.name}TimelineEntry] {
|
|
237
|
+
let timeline = WidgetsStorage.getArray(forKey: "__expo_widgets_${widget.name}_timeline") ?? []
|
|
238
|
+
let entries: [${widget.name}TimelineEntry?] = timeline.enumerated().map { index, entry in
|
|
239
|
+
guard let entry = entry as? [String: Any], let timestamp = entry["timestamp"] as? Int, let props = entry["props"] as? [String: Any] else {
|
|
240
|
+
return nil
|
|
241
|
+
}
|
|
242
|
+
return ${widget.name}TimelineEntry(
|
|
243
|
+
date: Date(timeIntervalSince1970: Double(timestamp) / 1000),
|
|
244
|
+
name: "${widget.name}",
|
|
245
|
+
props: props,
|
|
246
|
+
entryIndex: index,
|
|
247
|
+
configuration: configuration
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return entries.compactMap(\\.self)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
struct ${widget.name}EntryView: View {
|
|
256
|
+
@Environment(\\.self) var environment
|
|
257
|
+
var entry: ${widget.name}TimelineProvider.Entry
|
|
258
|
+
|
|
259
|
+
init(entry: ${widget.name}TimelineProvider.Entry) {
|
|
260
|
+
self.entry = entry
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private var widgetEnvironment: [String: Any] {
|
|
264
|
+
var env: [String: Any] = getWidgetEnvironment(environment: environment)
|
|
265
|
+
env["timestamp"] = Int(entry.date.timeIntervalSince1970 * 1000)
|
|
266
|
+
env["configuration"] = [
|
|
267
|
+
${Object.entries(widget.configuration?.parameters ?? {})
|
|
268
|
+
.map(([name, param]) => {
|
|
269
|
+
return ` "${name}": entry.configuration.${name}${param.type === 'enum' ? '.rawValue' : ''}`;
|
|
270
|
+
})
|
|
271
|
+
.join(',\n')}
|
|
272
|
+
]
|
|
273
|
+
return env
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private var widgetEnvironmentString: String? {
|
|
277
|
+
guard let data = try? JSONSerialization.data(withJSONObject: widgetEnvironment),
|
|
278
|
+
let jsonString = String(data: data, encoding: .utf8) else {
|
|
279
|
+
return nil
|
|
280
|
+
}
|
|
281
|
+
return jsonString
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
public var body: some View {
|
|
285
|
+
if let layout = WidgetsStorage.getString(forKey: "__expo_widgets_\\(entry.name)_layout"),
|
|
286
|
+
!layout.isEmpty {
|
|
287
|
+
let node = evaluateLayout(layout: layout, props: entry.props ?? [:], environment: widgetEnvironment)
|
|
288
|
+
WidgetsDynamicView(name: entry.name, kind: .widget, node: node, entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
289
|
+
} else {
|
|
290
|
+
WidgetsDynamicView(name: entry.name, kind: .widget, node: createRedBox(message: "No layout found for \\(WidgetsStorage.appGroupIdentifier ?? "")::\\(entry.name)"), entryIndex: entry.entryIndex, environmentString: widgetEnvironmentString)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@available(iOS 17.0, *)
|
|
297
|
+
struct ${widget.name}: Widget {
|
|
298
|
+
let name: String = "${widget.name}"
|
|
299
|
+
|
|
300
|
+
var body: some WidgetConfiguration {
|
|
301
|
+
return AppIntentConfiguration(kind: name, intent: ${widget.name}ConfigurationAppIntent.self, provider: ${widget.name}TimelineProvider()) { entry in
|
|
302
|
+
${widget.name}EntryView(entry: entry)
|
|
303
|
+
}
|
|
304
|
+
.configurationDisplayName("${widget.displayName}")
|
|
305
|
+
.description("${widget.description}")
|
|
306
|
+
.supportedFamilies([.${widget.supportedFamilies.join(', .')}])${widget.contentMarginsDisabled ? '\n .contentMarginsDisabled()' : ''}
|
|
307
|
+
}
|
|
308
|
+
}`;
|
|
309
|
+
};
|
|
140
310
|
|
|
141
311
|
const infoPlist = (
|
|
142
312
|
groupIdentifier: string,
|
package/src/Widgets.ts
CHANGED
|
@@ -16,12 +16,18 @@ import type {
|
|
|
16
16
|
/**
|
|
17
17
|
* Represents a widget instance. Provides methods to manage the widget's timeline.
|
|
18
18
|
*/
|
|
19
|
-
export class Widget<
|
|
19
|
+
export class Widget<
|
|
20
|
+
PropsType extends object = object,
|
|
21
|
+
ConfigurationType extends object | undefined = undefined,
|
|
22
|
+
> {
|
|
20
23
|
/** @hidden */
|
|
21
24
|
private nativeWidgetObject: NativeWidgetObject;
|
|
22
25
|
constructor(
|
|
23
26
|
name: string,
|
|
24
|
-
layout: (
|
|
27
|
+
layout: (
|
|
28
|
+
props: PropsType,
|
|
29
|
+
environment: WidgetEnvironment<ConfigurationType>
|
|
30
|
+
) => React.JSX.Element
|
|
25
31
|
) {
|
|
26
32
|
this.nativeWidgetObject = new ExpoWidgetsModule.Widget(name, layout as unknown as string);
|
|
27
33
|
}
|
|
@@ -37,7 +43,7 @@ export class Widget<T extends object = object> {
|
|
|
37
43
|
* Schedules a series of updates for the widget's content and reloads the widget.
|
|
38
44
|
* @param entries Timeline entries, each specifying a date and the props to display at that time.
|
|
39
45
|
*/
|
|
40
|
-
updateTimeline(entries: WidgetTimelineEntry<
|
|
46
|
+
updateTimeline(entries: WidgetTimelineEntry<PropsType>[]) {
|
|
41
47
|
this.nativeWidgetObject.updateTimeline(
|
|
42
48
|
entries.map((entry) => ({ timestamp: entry.date.getTime(), props: entry.props }))
|
|
43
49
|
);
|
|
@@ -47,17 +53,17 @@ export class Widget<T extends object = object> {
|
|
|
47
53
|
* Sets the widget's content to the given props immediately, without scheduling a timeline.
|
|
48
54
|
* @param props The properties to display in the widget.
|
|
49
55
|
*/
|
|
50
|
-
updateSnapshot(props:
|
|
56
|
+
updateSnapshot(props: PropsType) {
|
|
51
57
|
this.nativeWidgetObject.updateTimeline([{ timestamp: Date.now(), props }]);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
/**
|
|
55
61
|
* Returns the current timeline entries for the widget, including past and future entries.
|
|
56
62
|
*/
|
|
57
|
-
async getTimeline(): Promise<WidgetTimelineEntry<
|
|
63
|
+
async getTimeline(): Promise<WidgetTimelineEntry<PropsType>[]> {
|
|
58
64
|
return (await this.nativeWidgetObject.getTimeline()).map((entry) => ({
|
|
59
65
|
date: new Date(entry.timestamp),
|
|
60
|
-
props: entry.props as
|
|
66
|
+
props: entry.props as PropsType,
|
|
61
67
|
}));
|
|
62
68
|
}
|
|
63
69
|
}
|
|
@@ -175,11 +181,14 @@ export function after(date: Date): { after: Date } {
|
|
|
175
181
|
* @param name The widget name. Must match the `'name'` field in your widget configuration in the app config.
|
|
176
182
|
* @param widget The widget component, marked with the `'widget'` directive.
|
|
177
183
|
*/
|
|
178
|
-
export function createWidget<
|
|
184
|
+
export function createWidget<
|
|
185
|
+
PropsType extends object = object,
|
|
186
|
+
ConfigurationType extends object | undefined = undefined,
|
|
187
|
+
>(
|
|
179
188
|
name: string,
|
|
180
|
-
widget: (props:
|
|
181
|
-
): Widget<
|
|
182
|
-
return new Widget<
|
|
189
|
+
widget: (props: PropsType, context: WidgetEnvironment<ConfigurationType>) => React.JSX.Element
|
|
190
|
+
): Widget<PropsType, ConfigurationType> {
|
|
191
|
+
return new Widget<PropsType, ConfigurationType>(name, widget);
|
|
183
192
|
}
|
|
184
193
|
|
|
185
194
|
/**
|
package/src/Widgets.types.ts
CHANGED
|
@@ -46,7 +46,7 @@ export type WidgetFamily =
|
|
|
46
46
|
| 'accessoryRectangular'
|
|
47
47
|
| 'accessoryInline';
|
|
48
48
|
|
|
49
|
-
export type WidgetEnvironment = {
|
|
49
|
+
export type WidgetEnvironment<T extends object | undefined = undefined> = {
|
|
50
50
|
/**
|
|
51
51
|
* The date of this timeline entry.
|
|
52
52
|
*/
|
|
@@ -92,6 +92,11 @@ export type WidgetEnvironment = {
|
|
|
92
92
|
* @platform iOS 26+
|
|
93
93
|
*/
|
|
94
94
|
levelOfDetail?: LevelOfDetail;
|
|
95
|
+
/**
|
|
96
|
+
* Widget configuration parameters.
|
|
97
|
+
* @platform iOS 17+
|
|
98
|
+
*/
|
|
99
|
+
configuration: T;
|
|
95
100
|
};
|
|
96
101
|
|
|
97
102
|
export type LiveActivityEnvironment = {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"root":["./src/index.ts","./src/withappgroupentitlements.ts","./src/withappinfoplist.ts","./src/witheasconfig.ts","./src/withioswarning.ts","./src/withpodslinking.ts","./src/withpushnotifications.ts","./src/withwidgetsourcefiles.ts","./src/types/widgetconfig.type.ts","./src/types/widgetfamily.type.ts","./src/xcode/addbuildphases.ts","./src/xcode/addpbxgroup.ts","./src/xcode/addproductfile.ts","./src/xcode/addtargetdependency.ts","./src/xcode/addtopbxnativetargetsection.ts","./src/xcode/addtopbxprojectsection.ts","./src/xcode/addxcconfigurationlist.ts","./src/xcode/withtargetxcodeproject.ts"],"version":"5.9.2"}
|