Skip to main content
View as Markdown

Install skill

Give your AI coding agent the applicaster-preference-selector skill so it can build this integration on its own. Read it first to see exactly what it instructs your agent to do.

1. Install it

Run this in your terminal, from your project root:

npx skills add https://docs.applicaster.com --skill applicaster-preference-selector

In the agent picker, select your agent.

2. Use it

In your agent, paste this prompt (or just ask it to build the integration using the installed skill):

Use the applicaster-preference-selector skill to help me implement the Applicaster "Preference Selector Feeds" guide. Ask me about the requirements first, then build.

Preference Selector Feeds

Introduction

Preference Selector is a versatile feature that enables app managers to enhance user experiences in Zapp apps. By integrating this feature, app managers can create dynamic and personalized interfaces, allowing users to select preferences, configure settings, and tailor app content to their needs. Key high-level use cases may include:

  • For genre preferences, create a selector component that enables the user to choose their preference, save it as a local or remote context key, and adjust the context feeds or the curation feeds based on the user context keys.
  • Other use cases include configuring language preferences and managing multi-select options for customized content delivery.

Technical Explanation

Key Components

  1. Actions:

    Actions are predefined operations that enable specific functionalities within Zapp apps. They serve as the building blocks for implementing dynamic user interactions and application behaviors. Each action is executed based on user input or specific triggers within the app’s workflow. Key examples include:

    • sendCloudEvent: Sends structured events to cloud endpoints for preference updates. When using cloud events, make sure to create an End-point in Zapp, including relevant CTX if needed.
    • setUILanguage: Changes the app's UI language.
    • appRestart: Restarts the app after preference changes.
    • localStorageSet: Saves user preferences locally for persistence.
  2. Roles:

    Roles define the behavior and functionality of specific components within the app, enabling efficient implementation of dynamic features. They work as feed extensions and are automatically applied to app feeds to inject predefined configurations. Examples include:

    • preference_editor: Configures a component to handle user preferences through single or multi-select options. It supports behaviors such as tagging and linking user preferences to local storage keys.
    • push_topic: Enables components to manage push notification subscriptions. It dynamically assigns topics based on entry identifiers or preconfigured paths.
    • language_selector: Enables components to manage the app UI language.

    Roles ensure modularity and reusability by standardizing functionality across different parts of the app.

Actions VS Roles

Roles can be efficient and easier to implement when creating feeds for simple one-action feeds. For example, creating only a Language Selector component or a Preference Selector for local storage only.

Actions, on the other hand, should be used for more complex multi-action scenarios. For example, creating a component for user selection that will save the user preferences as local storage, send a cloud event, and save a push topic, in one click.

Feed Extensions

  • Behavior attributes (singleSelect, multiSelect) determine how user inputs are handled.
  • current_selection allows highlighting of pre-selected options for a better user experience.

current_selection reads from one source — never both

The cell highlight is computed from extensions.behavior.current_selection, which resolves to exactly one source:

  • Local storage token"@{ctx/<key>}" (e.g. "@{ctx/user_preferences.genres}"). The highlight reads the value stored on the device. A localStorageToggleFlag / localStorageSet write repaints the cell instantly, but the state is device-local: selections made on another device (or written server-side) never appear, and a fresh install shows nothing selected.
  • Inline value(s) — a literal such as "us_en" (single) or ["horror", "action"] (multi). The highlight reads the value baked into the feed by the backend, so a server can pre-select options per user (cross-device). The value is a static snapshot: a tap cannot repaint it live, so the feed must be re-fetched to reflect a change.

sendCloudEvent is always the write path (it records the selection server-side); the two options above are the mutually exclusive read paths for the highlight. Because they are mutually exclusive, a single feed cannot provide both instant local feedback and server-truth cross-device highlight at the same time.

Choosing state authority: local vs. remote

  • Local authority (storage token): instant toggle feedback, offline-friendly, single-device. Use the preference_editor role, or the localStorageSet / localStorageToggleFlag actions.
  • Remote authority (inline value from the backend): cross-device / server-driven pre-selection. The backend receives the sendCloudEvent, stores the selection, and injects the stored value into current_selection when it builds the feed. To reflect a change after a tap, refresh the component (Zapp refresh configuration, or a refreshComponent action in the tap chain) so the feed re-hydrates — this is eventual, not instant.
  • Both at once (instant and cross-device) is not expressible from the feed alone. It requires a client-side action plugin that owns the selection state — loading it from the backend on mount, applying an optimistic toggle on tap, then reconciling with the server — the same mechanism the favorites action uses.

Use Case Examples

1. Roles Example: Language Selector Feed

A simple roles feed for managing language selection:

{
"entry": [
{
"extensions": {
"tag": "en"
},
"id": "5eab8590-c82d-48cc-b9de-e6a127ddf9cc",
"summary": "code: en",
"title": "English",
"type": {
"value": "action"
}
},
{
"extensions": {
"tag": "fr"
},
"id": "209d654b-4701-41b9-9be0-cea88ac8fc8c",
"summary": "code: fr",
"title": "French",
"type": {
"value": "action"
}
}
],
"extensions": {
"role": "language_selector"
},
"id": "63d5c312-cdff-48ae-b4be-06f335f811a7",
"title": "Selector Example - UI Language",
"type": {
"value": "feed"
}
}

2. Roles Example: Multi Local Preference Selector Feed

A simple roles feed for managing user preferences selection:

{
"entry": [
{
"extensions": {
"tag": "horror"
},
"id": "e7d1e195-68a7-428a-9b98-21cfee989753",
"title": "horror",
"type": {
"value": "video"
}
},
{
"extensions": {
"tag": "action"
},
"id": "94902c6e-efa6-4d23-97ce-5604db5df715",
"title": "action",
"type": {
"value": "action"
}
}
],
"extensions": {
"behavior": {
"current_selection": "@{ctx/user_preferences.genres}",
"select_mode": "multi"
},
"preference_editor_options": {
"key": "user_preferences.genres"
},
"role": "preference_editor"
},
"id": "a4408444-8732-4df0-a88c-9f50853d32d6",
"title": "Selector Example: Genre Context",
"type": {
"value": "feed"
}
}

3. Multi-Action Feed Example

A more complex feed with multiple actions for a single selection:

{
"id": "feed-region-datasource",
"type": {
"value": "feed"
},
"title": "Select a region",
"extensions": {
"behavior": {
"select_mode": "single",
"current_selection": "us_en"
}
},
"entry": [
{
"id": "en",
"type": {
"value": "action"
},
"title": "UK (English)",
"summary": "UK Region",
"extensions": {
"tap_actions": {
"actions": [
{
"type": "confirmDialog",
"options": {
"message": "Region will be changed to UK. Are you sure you want to continue?",
"title": "Region change",
"okButtonText": "Yes",
"cancelButtonText": "No"
}
},
{
"type": "sendCloudEvent",
"options": {
"url": "https://my-domain.com/save_region",
"type": "com.applicaster.selector.action.v1",
"subject": "preference_selection",
"data": {
"networkSelection": "region_uk"
}
}
},
{
"type": "localStorageSet",
"options": {
"content": {
"user_preferences": {
"region": "uk"
}
}
}
},
{
"type": "setUILanguage",
"options": {
"languageCode": "en-UK",
"noConfirmation": true
}
},
{
"type": "appRestart"
}
]
}
}
}
]
}

Step-by-Step Integration Guide

  1. Set Up Feed Extensions:

    • Define behavior (e.g., singleSelect or multiSelect).
    • Configure current_selection to highlight default options for remote if needed.
  2. Create Actions:

    • Use sendCloudEvent for server-side updates.
    • Leverage localStorageSet for local persistence.
  3. Integrate UI Components:

    • Embed selectors into the app’s UI using predefined roles like preference_editor.
    • Enable real-time feedback for toggle actions.
  4. Test Integration:

    • Verify actions trigger correctly.
    • Ensure preference data persists across app sessions.
  5. Define Cell-Style:

    • Define a component using a cell style with "selected-state" configured.

Best Practices and Tips

  • Highlight Defaults: Always specify current_selection to guide users intuitively.
  • Choose one authority per selector: current_selection reads either the local storage token or an inline (remote-provided) value — not both. Pick local for instant single-device toggles, or remote for cross-device pre-selection with a feed refresh; combining instant and cross-device requires a client-side action plugin.
  • Optimize Performance: Avoid unnecessary app restarts by batching updates.
  • Adopt Modular Design: Keep actions and components loosely coupled for maintainability.
  • Enable Feedback: Provide visual cues for user actions, especially in multi-select scenarios.