diff --git a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/_index.md b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/_index.md index dc825c17624..90b56ab8566 100644 --- a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/_index.md +++ b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/_index.md @@ -30,6 +30,9 @@ This PWA Wrapper documentation section includes the following topics: * Signing reference: [Sign PWA Wrapper Apps](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/sign-pwa-wrapper-apps/) explains the signing step in the build flow * Capabilities guide: [PWA Wrapper Capabilities](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities/) * Limitations: [PWA Wrapper Limitations](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-limitations/) +* Deep linking guide: [PWA Wrapper Deep Linking](/refguide/mobile/pwa-wrapper/pwa-wrapper-deep-linking/) explains how to handle inbound and outbound deep links +* Security and network access reference: [PWA Wrapper Security and Network Access](/refguide/mobile/pwa-wrapper/pwa-wrapper-security/) describes what the extension downloads, what local paths it scans, and which binaries it bundles +* Troubleshooting: [Troubleshoot PWA Wrapper](/refguide/mobile/pwa-wrapper/pwa-wrapper-troubleshooting/) lists known issues and solutions ## Typical Use Cases diff --git a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities.md b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities.md index 766c4a0c05f..34934b38b57 100644 --- a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities.md +++ b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities.md @@ -31,6 +31,7 @@ Verify the final packaged app on representative physical devices before release. | Upload videos from the camera or gallery | Lets the user capture a video or select an existing video for upload. | Yes | No | | Geolocation | Retrieves the device location for map, tracking, or location-aware flows. | Yes | Yes | | Local notification | Schedules or shows notifications generated by the app on the device. | No | Yes | +| Push notification (remote) | Receives remote push notifications sent from a server via Firebase Cloud Messaging (FCM). | No | Yes | | Permissions | Checks or requests supported runtime permissions from the app flow. | No | Yes | | Biometric authentication | Checks biometric availability and prompts the user for biometric authentication. This depends on device hardware and biometric enrollment. | Yes | No | @@ -60,9 +61,14 @@ This mapping is guidance, not a fixed contract. Exact permission prompts can var | Upload videos from the camera or gallery | `Camera`, `Record Audio` | `Camera Usage`, `Microphone Usage` | | Geolocation | `Fine Location`, `Coarse Location` | `Location - When In Use Usage` | | Local notification | `Notifications` | No explicit builder permission message required | +| Push notification (remote) | `Notifications` | No explicit builder permission message required | When a capability relies on camera, location, or notifications, align the configured builder permissions with the JavaScript actions your app actually calls. For video capture flows, also verify whether the target platform requires microphone permission. +{{% alert color="info" %}} +Push notifications require Firebase Cloud Messaging (FCM) integration through the Push Notifications Connector module. For detailed setup instructions for wrapped PWAs, see [PWA Wrapper: Push Notification Setup](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper/). +{{% /alert %}} + ## Designing with Capabilities When you build a Mendix app with PWA Wrapper capabilities, follow these guidelines: diff --git a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-deep-linking.md b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-deep-linking.md new file mode 100644 index 00000000000..246806dd432 --- /dev/null +++ b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-deep-linking.md @@ -0,0 +1,171 @@ +--- +title: "PWA Wrapper Deep Linking" +url: /refguide/mobile/pwa-wrapper/pwa-wrapper-deep-linking/ +weight: 40 +description: "How to implement deep linking in your PWA Wrapper app to enable inbound and outbound URL handling." +--- + +## Introduction + +Deep linking enables users to jump directly to specific content in your app via URLs or schemes. PWA Wrapper supports two independent deep linking flows: + +* **Outbound** — your Mendix app opens a URL or deep link (e.g., jumping to another app) +* **Inbound** — the operating system launches your wrapped app via a registered deep link scheme, and the wrapper routes it into your app content + +This guide covers both flows. + +## Part 1: Outbound — Opening URLs and Deep Links + +Use the **OpenDeepLink** JavaScript action to open external URLs or deep links from your nanoflows: + +``` +OpenDeepLink(url: string): Promise +``` + +### Behavior + +**iOS and Android:** Returns `true` or `false` based on success. Catches errors and returns `false` on failure. + +### Important Caveat + +On both Android and iOS, the result of `OpenDeepLink` (true/false) indicates only that the call succeeded—not whether the target app could actually handle the link. Always test with the actual target apps you expect to open. + +## Part 2: Inbound — Registering Deep Link Schemes and Hosts + +Configure deep link schemes and hosts in the PWA Wrapper Builder during the app info step. The builder includes a **Deep Link editor** where you define which schemes and hosts your app should respond to. + +### Adding a Deep Link Entry + +Each deep link entry requires: + +* **scheme** (required) — e.g., `myapp`, `pwa`, or `https`/`http` for universal/app links +* **host** (required) — domain or authority to match, e.g., `example.com` +* **path** fields (optional, pick at most one): +* **path** — exact path to match (e.g., `/profile`) +* **pathStartWith** — path prefix to match (e.g., `/jump` catches `/jump/profile`) +* **linkFeature** — free-text label (metadata only, affects HarmonyOS builds) +* **platform** — `Android`, `iOS`, `HarmonyOS`, `all`, or unset (unset or `all` applies to every platform) + +### Common Deep Link Patterns + +**Custom scheme (works everywhere, no domain ownership needed):** + +* scheme: `myapp` +* host: `example.com` +* path fields: empty +* Result: catches `myapp://example.com/...` + +**Universal Link / App Link (requires domain ownership):** + +* scheme: `https` +* host: `yourapp.com` +* pathStartWith: `/jump` +* Result: catches `https://yourapp.com/jump/*` from any context that supports it + +## Part 3: What the Builder Configures Per Platform + +The PWA Wrapper builder automatically registers your configured deep links with the operating system. You do not need to manually edit any native configuration files — the builder handles everything behind the scenes. + +## Part 4: Domain Verification Files (Manual Setup Required) + +{{% alert color="warning" %}} +For `https` scheme links (Android App Links and iOS Universal Links), the operating system requires verification files hosted on your own domain. The PWA Wrapper builder does NOT generate or host these files—you must create and publish them yourself. + +Without these verification files, `https` links will open in the browser instead of the app. Android may display a disambiguation dialog; iOS Universal Links will fail silently. +{{% /alert %}} + +### Android: assetlinks.json + +Create and host `https://yourdomain.com/.well-known/assetlinks.json` on your web server. The file must be: + +* Valid JSON +* Served with `Content-Type: application/json` +* Accessible via `https` only (not `http`) + +**Example structure:** + +```json +[ + { + "relation": ["delegate_permission/common.handle_all_urls"], + "target": { + "namespace": "android_app", + "package_name": "com.yourcompany.yourapp", + "sha256_cert_fingerprints": ["AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78"] + } + } +] +``` + +To find your app's SHA-256 fingerprint, use `jarsigner` or the Android Studio Build Variants panel after signing your APK. + +### iOS: apple-app-site-association + +Create and host `https://yourdomain.com/.well-known/apple-app-site-association` on your web server. The file must be: + +* Valid JSON (no `.json` extension in the path) +* Served with `Content-Type: application/json` +* Accessible via `https` only (not `http`) + +**Example structure:** + +```json +{ + "applinks": { + "apps": [], + "details": [ + { + "appID": "TEAM_ID.com.yourcompany.yourapp", + "paths": ["/jump/*"] + } + ] + } +} +``` + +The `appID` format is `TEAM_ID.BUNDLE_ID`. The `paths` array should match the paths you configured in the PWA Wrapper Builder. + +### Hosting the Files + +The `.well-known` directory must be accessible at the root of your domain via `https`. The typical approach is: + +* **Mendix Cloud:** Upload the verification files to your Mendix Cloud environment's static file directory. Refer to [Mendix Cloud documentation](/developerportal/deploy/) for details on serving static files, or contact Mendix Support. +* **Custom Domain or External Hosting:** If your Mendix app is behind a reverse proxy or hosted on a custom domain, ensure the `.well-known` directory is properly configured to serve these files at the domain root. + +### Testing File Accessibility + +Verify your files are correctly hosted: + +```bash +curl -I https://yourdomain.com/.well-known/assetlinks.json +curl -I https://yourdomain.com/.well-known/apple-app-site-association +``` + +Both should return HTTP 200 with `Content-Type: application/json`. + +## Part 5: How Inbound Links Reach Your App Content + +Once a deep link is caught by the wrapper, it is routed into your app's runtime. The exact process depends on the platform: + +**Android:** + +The wrapper loads the incoming deep link in the webview. + +**iOS:** + +The wrapper loads the incoming deep link in the webview. + +**Implication for your app:** Your Mendix app's routing (pages, deep-link microflows, etc.) must be configured to handle the deep link URL. + +## Common Deep Linking Pitfalls + +* **Missing verification files** — forgetting to create and host `assetlinks.json` (Android) or `apple-app-site-association` (iOS) for `https` scheme links is the most common integration failure. +* **Using https without domain ownership** — `https` scheme links require that you own and control the domain; do not use `https` for domains you do not own. +* **Misinterpreting the OpenDeepLink return value** — the boolean result only indicates the call succeeded, not whether the target app successfully opened or handled the link. +* **Mismatched path configuration** — ensure your `path` and `pathStartWith` values match the actual Mendix page URLs or deep-link microflow entry points your app exposes. + +## Read More + +* [PWA Wrapper](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/) +* [PWA Wrapper Capabilities](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities/) +* [Build PWA Wrapper Apps](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/build-pwa-wrapper-apps/) diff --git a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-security.md b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-security.md new file mode 100644 index 00000000000..ec339f82e30 --- /dev/null +++ b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-security.md @@ -0,0 +1,88 @@ +--- +title: "PWA Wrapper Security and Network Access" +url: /refguide/mobile/pwa-wrapper/pwa-wrapper-security/ +weight: 50 +description: "Describes the network access and local filesystem scanning behavior of PWA Wrapper for security and IT reviewers." +--- + +## Introduction + +This page describes two behaviors of PWA Wrapper that are relevant for security reviews and IT governance: what the extension downloads from the network during a build, and what local paths and environment variables it inspects to detect existing developer tool installations. + +This information is intended for security engineers, IT administrators, and other reviewers who need a precise account of what the extension does outside its own process boundary. It does not cover general build usage; for that, see [Build PWA Wrapper Apps](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/build-pwa-wrapper-apps/). + +## Network Downloads During Build + +PWA Wrapper itself does not download anything when it is installed or when Studio Pro starts. Network access happens at build time, through platform-standard build tooling, not through custom code in the extension. The following two downloads can occur on the first build for each respective platform. + +### Android: Gradle Wrapper Distribution + +Android builds are run via the Gradle Wrapper, which is standard for Android and Gradle-based projects. On the first Android build, the Gradle Wrapper checks whether the required Gradle distribution is already cached locally. If it is not, the wrapper downloads the following archive and caches it for all subsequent builds: + +| Property | Value | +| --- | --- | +| File | `gradle-8.9-bin.zip` | +| URL | `https://services.gradle.org/distributions/gradle-8.9-bin.zip` | +| Host | `services.gradle.org` (Gradle's official distribution server, operated by Gradle, Inc.) | +| Cache location | `~/.gradle/wrapper/dists/gradle-8.9-bin/` | +| Trigger | First Android build on a machine; not re-downloaded on subsequent builds or on extension install | + +This is standard Gradle Wrapper behavior, identical to any Gradle-based Android project. It is not custom logic added by PWA Wrapper. + +To avoid this download in air-gapped environments, pre-populate the Gradle wrapper cache directory before running the first build. + +## Bundled Binaries Executed Locally + +In addition to the runtime downloads above, PWA Wrapper ships the following binaries inside its own extension package. These are not downloaded at build time — they are included in the extension at install time and are invoked as local subprocesses during Android and HarmonyOS build, signing, and packaging steps. They run with the same operating-system privileges as the Studio Pro process. + +| Binary | Purpose | Platform | +| --- | --- | --- | +| `apktool.jar` | Decoding and rebuilding Android APKs | Android | +| `bundletool.jar` | Building and manipulating Android App Bundles | Android | +| `aapt2` / `aapt2.exe` | Android Asset Packaging Tool | Android | +| `apksigner.jar` | APK signing | Android | +| `android.jar` | Android SDK stub library used during compilation | Android | + +These binaries are third-party open-source tools redistributed with the extension. They are not fetched from the network at runtime. + +## Local Filesystem and Environment Scanning + +When the builder opens, PWA Wrapper inspects the local machine to auto-detect existing developer tool installations. This is a read-only discovery process: no data leaves the machine, and no files are created or modified. The scan is performed so users do not have to manually configure tool paths. + +### JDK Discovery + +PWA Wrapper needs a Java runtime to execute the build tools listed above. It searches for an installed JDK in the following order and uses the first usable result it finds. + +**On macOS:** + +| Step | Method | +| --- | --- | +| 1 | Read the `JAVA_HOME` environment variable, if set | +| 2 | Run `/usr/libexec/java_home` — the operating-system JDK registry | +| 3 | Resolve `java` on the shell `PATH` (equivalent to `which java`) | +| 4 | Scan `/Library/Java/JavaVirtualMachines/` — the standard directory where installers such as Adoptium/Temurin, Oracle, and Azul place JDKs | + +**On Windows:** + +On Windows, JDK discovery does not scan directories. Instead it reads the output of `mx show-java-version` and inspects the Mendix Studio Pro settings database (`Settings.sqlite`) to find the JDK path configured in Studio Pro. + +If no usable JDK is found, the build fails with an error that instructs the user to install a JDK from [Adoptium](https://adoptium.net) or set `JAVA_HOME` manually. PWA Wrapper does not download a JDK on the user's behalf. + +### Mendix Studio Pro Discovery + +PWA Wrapper needs to locate the Mendix Studio Pro installation to access the runtime and SDK files it uses during the build. + +**On macOS:** The extension scans `/Applications/` and `~/Applications/` for app bundles whose names match "Mendix Studio Pro" or "Studio Pro". Among all matches that contain a valid `runtime/` subdirectory, it selects the one with the highest version number. + +**On Windows:** Auto-detection is not performed. The Studio Pro path must be provided explicitly via a CLI argument, a `.env` file, or a `.launch` file. + +## Summary for Reviewers + +| Category | What happens | When | Data leaves machine? | +| --- | --- | --- | --- | +| Gradle distribution download | Downloads `gradle-8.9-bin.zip` from `services.gradle.org` | First Android build only | No (download only; no data sent) | +| Bundled binary execution | Runs `apktool.jar`, `bundletool.jar`, `aapt2`, `apksigner.jar`, `android.jar` locally | Every applicable build | No | +| JDK filesystem scan | Reads `JAVA_HOME`, `/Library/Java/JavaVirtualMachines/`, version manager directories, or Windows Studio Pro settings | When the builder opens | No | +| Studio Pro filesystem scan | Scans `/Applications/` and `~/Applications/` for Studio Pro app bundles (macOS only) | When the builder opens | No | + +PWA Wrapper makes no telemetry calls, analytics requests, or other network connections beyond the two build-time package downloads described above. diff --git a/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-troubleshooting.md b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-troubleshooting.md new file mode 100644 index 00000000000..6234f7bec19 --- /dev/null +++ b/content/en/docs/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-troubleshooting.md @@ -0,0 +1,52 @@ +--- +title: "Troubleshoot PWA Wrapper" +url: /refguide/mobile/pwa-wrapper/pwa-wrapper-troubleshooting/ +weight: 60 +description: "Known issues and solutions for PWA Wrapper." +--- + +## Introduction + +This page describes known issues you may encounter when building or testing PWA Wrapper apps, along with the steps to resolve them. + +## Android + +### App Cannot Connect to localhost on a Physical Device{#android-localhost} + +**Symptom**: When you set the runtime URL to a localhost address (for example, `http://localhost:8080`) and install the app on a physical Android device, the app fails to load and shows the following error: + +``` +Page Load Error +net::ERR_CACHE_MISS +``` + +{{< figure src="/attachments/refguide/mobile/pwa-wrapper/localhost-cache-miss-error.png" alt="Page Load Error showing net::ERR_CACHE_MISS on Android" max-width="400px" >}} + +**Cause**: Two things are required for a physical Android device to reach a localhost server on your machine: + +1. The app must have the `INTERNET` permission. Without it, the Android OS blocks all outbound network traffic. +2. The device must have a port forwarding rule from its own localhost to your machine's localhost. Android devices do not forward ports automatically. + +**Solution**: + +1. In the PWA Wrapper builder, go to the **Permissions** step and enable the **INTERNET** permission. + + For general guidance on permissions, see [Build PWA Wrapper Apps](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/build-pwa-wrapper-apps/). + +1. Connect the Android device to your machine via USB and make sure [USB debugging](https://developer.android.com/studio/debug/dev-options) is enabled on the device. + +1. Run the following ADB command to forward port 8080 from the device to your machine: + + ```shell + adb reverse tcp:8080 tcp:8080 + ``` + + If your Mendix runtime uses a different port, replace `8080` with the correct port number on both sides. + +1. Rebuild and reinstall the app with the localhost runtime URL. + +The app can now reach the Mendix runtime running on your development machine. + +{{% alert color="info" %}} +The `adb reverse` rule applies only to the current USB session. You must re-run the command each time you reconnect the device or restart ADB. +{{% /alert %}} diff --git a/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/_index.md b/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/_index.md index dbf4ec5359a..1103f841e96 100644 --- a/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/_index.md +++ b/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/_index.md @@ -17,6 +17,7 @@ These guides teach you how to configure push notifications (also known as remote * [Configuring Push Notifications](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-config-push/) – Learn to configure push notifications in the runtime. * [Implementing Push Notifications in Your Native App](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-implement-native/) – Implement push notifications for a native app. * [Implementing Push Notifications in Your Progressive Web App](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-implement-pwa/) – Implement push notifications for a progressive web app. +* [PWA Wrapper Push Notification Setup](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper/) – Configure notification-related settings when packaging your progressive web app with PWA Wrapper. * [Sending Your First Test Push Notification](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-send-test/) – Send a test notification to confirm your app is working properly. After you complete the tasks above, you may wish to do more advanced tasks with notifications. For information on **sending push notifications to multiple devices**, use this documentation: diff --git a/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper.md b/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper.md new file mode 100644 index 00000000000..f782a834953 --- /dev/null +++ b/content/en/docs/refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper.md @@ -0,0 +1,219 @@ +--- +title: "PWA Wrapper: Push Notification Setup" +linktitle: "PWA Wrapper Push Setup" +url: /refguide/mobile/using-mobile-capabilities/push-notifications/notif-pwa-wrapper/ +weight: 75 +description: "How to configure push notification prerequisites when your progressive web app is packaged with PWA Wrapper." +--- + +## Introduction + +This guide explains how to set up push notifications for a Mendix PWA packaged with PWA Wrapper. Unlike standard PWAs, wrapped PWAs behave like native applications, which changes how you integrate push notifications. + +In a wrapped PWA, push notifications are handled natively through the device's operating system (Android or iOS) rather than through the browser. This guide takes you through Firebase configuration, PWA Wrapper integration, Push Notifications Connector setup, and app-level implementation. + +## Prerequisites + +Before starting this guide, make sure you have completed the following: + +* [Install PWA Wrapper](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/install-pwa-wrapper/) +* Have a Firebase account (https://firebase.google.com) + +## Setting Up Firebase and PWA Wrapper + +Follow these steps to configure Firebase and integrate it with PWA Wrapper: + +### Step 1: Create and Configure Firebase Project + +1. Follow the steps in [Setting Up the Google Firebase Cloud Messaging Server](/refguide/mobile/using-mobile-capabilities/push-notifications/setting-up-google-firebase-cloud-messaging-server/) to create a Firebase project and set up a service account. +2. In Firebase, add your Android and iOS apps to the project by following the same guide. +3. Download and securely store the configuration files: + * For Android: `google-services.json` + * For iOS: `GoogleService-Info.plist` + +### Step 2: Configure PWA Wrapper for Push Notifications + +When building your wrapped app with PWA Wrapper, enable push notification infrastructure: + +1. Open the PWA Wrapper builder and select **Build** for your target platform (Android or iOS). +1. Navigate to the **App Packing** section. +1. Enable **Enable push notification infrastructure**. +1. Upload the appropriate Firebase configuration file: + * For Android: select and upload `google-services.json` + * For iOS: select and upload `GoogleService-Info.plist` +1. Complete the remaining builder steps as described in [Build PWA Wrapper Apps](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/build-pwa-wrapper-apps/). + +This configures the native layer to receive push notifications from Firebase Cloud Messaging (FCM). +{{% alert color="warning" %}} +Push notifications only work on physical devices. Emulators and simulators do not have the necessary Firebase Cloud Messaging infrastructure to receive push notifications. Always test push notifications on real Android and iOS devices. +{{% /alert %}} +## Installing Push Notifications Connector + +Add the Push Notifications Connector module to your app: + +1. In Mendix Studio Pro, download the [Push Notifications Connector](https://marketplace.mendix.com/link/component/3003) from the Marketplace. +1. Add it to your app following the standard module import process. +1. Complete the steps in [Adding Module Dependencies](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-add-module-depends/) and [Implementing the Push Notifications Module](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-implement-module/). + +## Implementing Push Notification Registration + +In your wrapped PWA application, create a nanoflow to handle device registration for push notifications. This nanoflow uses AppBuilderClient JavaScript actions to request permissions and retrieve device information. + +### Creating the Registration Nanoflow + +Create a new nanoflow in your app (for example, `ACT_RequestPushNotifications`) and add the following steps: + +#### Step 1: Request Permission + +1. Add a **Call JavaScript Action** activity. +1. Select the **RequestPermission** action from **AppBuilderClient**. +1. Set the parameter: + * **Permission**: `notifications` +1. Assign the return value to a variable (for example, `$PermissionState`). + +#### Step 2: Check Permission Status + +1. Add a **Decision** activity to check if permission was granted. +1. Verify: `$PermissionState = 'granted'` +1. If the condition is false (permission denied), end the flow and optionally show a message to the user. + +#### Step 3: Retrieve Device Information + +Add three **Call JavaScript Action** activities to retrieve device details: + +1. **GetDeviceInfo** from **AppBuilderClient** + * Assign return value to `$DeviceInfo` + +2. **GetPushToken** from **AppBuilderClient** + * Assign return value to `$PushToken` + +3. **GetOperatingSystem** from **AppBuilderClient** + * Assign return value to `$OperatingSystem` + +#### Step 4: Create Device Registration Object + +1. Add a **Create Object** activity to create a new `PushNotifications.DeviceRegistration` object. +2. Assign the following attributes: + * **DeviceType**: Use a decision based on the operating system: + ``` + if $OperatingSystem = getKey(PushNotifications.DeviceSystemType.Android) then + PushNotifications.DeviceType.Android + else if $OperatingSystem = getKey(PushNotifications.DeviceSystemType.iOS) then + PushNotifications.DeviceType.iOS + else if $OperatingSystem = getKey(PushNotifications.DeviceSystemType.iPadOS) then + PushNotifications.DeviceType.iOS + else + empty + ``` + * **RegistrationID**: `$PushToken` + * **DeviceId**: `$DeviceInfo/UniqueId` + +3. Assign the created object to a variable (for example, `$NewDeviceRegistration`). + +#### Step 5: Synchronize Device Registration + +1. Add a **Synchronize** activity to commit the device registration object to the database. +1. Select `$NewDeviceRegistration` as the object to synchronize. + +The complete nanoflow flow is shown below: + +{{< figure src="/attachments/refguide/mobile/push-notifications/notif-pwa-wrapper/pwa-wrapper-push-nanoflow.png" alt="Push notification registration nanoflow for PWA Wrapper" max-width="80%" >}} + +{{% alert color="info" %}} +The nanoflow requests the `notifications` permission, retrieves the device token and unique ID, and registers the device in the Push Notifications Connector database. This allows the runtime to send push notifications to this device. +{{% /alert %}} + +### Calling the Registration Nanoflow + +The registration nanoflow should run automatically each time the app opens — both on first launch and when the user returns to the app from the background. This ensures the FCM token stays current. The next section explains how to implement this using app lifecycle events. + +## Running Push Notification Registration on App Lifecycle Events + +To keep device registration up to date, run the registration nanoflow automatically whenever the app becomes active. Use the [Events](https://marketplace.mendix.com/link/component/224259) module from the Mendix Marketplace together with a custom JavaScript action that listens for app lifecycle changes. + +### Step 1: Install the Events Module + +Download and install the [Events](https://marketplace.mendix.com/link/component/224259) module from the Mendix Marketplace. + +### Step 2: Create the ListenToAppLifecycle JavaScript Action + +Create a new JavaScript action named `ListenToAppLifecycle` in your app with the following configuration: + +* Add one parameter of type **Nanoflow**: + * `onResume` + +Use the following as the action's implementation: + +```js +// BEGIN EXTRA CODE +let appLifecycleListenerInitialized = false; +// END EXTRA CODE + +export async function ListenToAppLifecycle(onResume) { + // BEGIN USER CODE + + if (appLifecycleListenerInitialized) { + return; + } + + appLifecycleListenerInitialized = true; + + const handleVisibilityChange = async () => { + try { + if (document.visibilityState === "visible") { + if (onResume) { + await onResume(); + } + } + } catch (error) { + console.error("Failed to handle app lifecycle event:", error); + } + }; + + document.addEventListener("visibilitychange", handleVisibilityChange); + + if (document.visibilityState === "visible" && onResume) { + await onResume(); + } + + // END USER CODE +} +``` + +The `appLifecycleListenerInitialized` guard prevents duplicate event listeners from being registered if the action is called more than once. + +### Step 3: Create the Lifecycle Nanoflow + +Create a new nanoflow (for example, `ACT_InitAppLifecycle`) and add the following: + +1. Add a **Call JavaScript Action** activity. +1. Select the **ListenToAppLifecycle** action you created. +1. Set the parameters: + * **onResume**: select the `ACT_RequestPushNotifications` nanoflow you created earlier + +### Step 4: Add the Events Widget to the Home Page + +1. Open your app's default home page in Studio Pro. +1. Add the **Events** widget from the installed Events module to the page. +1. In the widget properties, locate the **Component Load** event. +1. Set the action to **Call a nanoflow**. +1. Select the `ACT_InitAppLifecycle` nanoflow. + +This ensures that every time the page loads — which happens when the app opens — the lifecycle listener is registered and the push notification registration nanoflow runs immediately. If the FCM token has changed since the last session, the updated token is saved automatically. + +## Sending Push Notifications + +After you have built and deployed your wrapped app with device registrations in place, use the standard Push Notifications Connector workflow to send notifications: + +1. Follow [Configuring Push Notifications](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-config-push/) to configure Firebase credentials in your app's administration page. +2. Use [Sending Your First Test Push Notification](/refguide/mobile/using-mobile-capabilities/push-notifications/notif-send-test/) to test sending a notification to a registered device. + +When testing, verify behavior on physical Android and iOS devices to ensure notifications are received correctly. + +## Read More + +* [Push Notifications](/refguide/mobile/using-mobile-capabilities/push-notifications/) +* [PWA Wrapper](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/) +* [PWA Wrapper Capabilities](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-capabilities/) +* [PWA Wrapper Limitations](/refguide/mobile/distributing-mobile-apps/pwa-wrapper/pwa-wrapper-limitations/) +* [PWA Wrapper Deep Linking](/refguide/mobile/pwa-wrapper/pwa-wrapper-deep-linking/) diff --git a/static/attachments/refguide/mobile/push-notifications/notif-pwa-wrapper/pwa-wrapper-push-nanoflow.png b/static/attachments/refguide/mobile/push-notifications/notif-pwa-wrapper/pwa-wrapper-push-nanoflow.png new file mode 100644 index 00000000000..2e7a5f96c53 Binary files /dev/null and b/static/attachments/refguide/mobile/push-notifications/notif-pwa-wrapper/pwa-wrapper-push-nanoflow.png differ diff --git a/static/attachments/refguide/mobile/pwa-wrapper/localhost-cache-miss-error.png b/static/attachments/refguide/mobile/pwa-wrapper/localhost-cache-miss-error.png new file mode 100644 index 00000000000..cbb1c842afc Binary files /dev/null and b/static/attachments/refguide/mobile/pwa-wrapper/localhost-cache-miss-error.png differ