To access camera, GPS, and push notifications in OutSystems Mobile apps, install Forge plugins (Camera Plugin, Location Plugin, FCM Plugin) and call their Client Actions with proper permission handling. This tutorial covers installing plugins via Forge, capturing photos with TakePicture, getting GPS coordinates with GetLocation, configuring Firebase Cloud Messaging for push notifications, and triggering MABS builds for device testing.
How Device Features Work in OutSystems Mobile
OutSystems Mobile apps run in a native container built by MABS (Mobile Application Build Service), which supports Apache Cordova and Capacitor plugins. Device hardware access — camera, GPS, push notifications — is provided through OutSystems Forge plugins that wrap these native SDKs. Each plugin installs as a module you reference via Manage Dependencies, exposing Client Actions you call to interact with the hardware. This tutorial covers the three most commonly needed device features: camera capture, location/GPS, and push notifications. All three follow the same pattern: install plugin from Forge → add dependency → call plugin actions in Client Actions → handle permissions.
Prerequisites
- An OutSystems Mobile app module open in Service Studio (not Reactive Web — these plugins require native container)
- MABS version 9 or later (check in Service Center → Factory → Modules → [your module] → Mobile Platforms)
- Forge account to install plugins (forge.outsystems.com)
- For push notifications: a Firebase project with FCM configured and your google-services.json / GoogleService-Info.plist files
- A physical iOS or Android device for final testing (device emulators have limited camera/GPS simulation)
Step-by-step guide
Install the Camera Plugin from Forge
Install the Camera Plugin from Forge
Go to **forge.outsystems.com** in your browser. Search for **Camera Plugin** (by OutSystems). Check the compatibility badge shows your MABS version is supported. Click **Install** → select your environment → confirm. Once installed, back in Service Studio: press **Ctrl+Q** (Manage Dependencies). In the Producer search, type **Camera Plugin** → expand it → check **CameraPlugin** under the Client Actions section → expand Logic → check: `TakePicture`, `ChooseFromGallery`, `GetCameraSettings`. Click **Apply**. TrueChange confirms the dependencies are resolved. The Camera Plugin actions appear under Logic tab → Client Actions → CameraPlugin.
Expected result: Logic tab shows TakePicture and ChooseFromGallery as available Client Actions from the CameraPlugin dependency. TrueChange shows no broken references.
Capture a Photo with TakePicture
Capture a Photo with TakePicture
In Service Studio, open the screen where you want camera functionality. Create a **Client Action** named `CapturePhoto` (right-click screen → Add Client Action). Create Local Variables: - `CapturedImageB64` (Text) — stores the base64-encoded image string - `ImageBinary` (Binary Data) — for converting and storing - `CameraError` (Text) Action flow: ``` Start → TakePicture Quality = 80 (integer, 0-100) Width = 1024 (max pixel width) Height = 1024 (max pixel height) CorrectOrientation = True EncodingType = JPEG (JPEG or PNG) → If TakePicture.Success [True] → Assign CapturedImageB64 = TakePicture.ImageCaptured → ConvertB64ToBinary (use TextToBinaryData built-in) → Assign ImageBinary = ConvertB64ToBinary.BinaryData → Call Server Action to save ImageBinary to entity [False] → Assign CameraError = TakePicture.Error.Message → End ``` To display the image immediately: add an Image widget → set Source to `"data:image/jpeg;base64," + CapturedImageB64`
1/* TakePicture Client Action call — in CapturePhoto Client Action */23/* Plugin action parameters */4TakePicture(5 Quality: 80,6 Width: 1024,7 Height: 1024,8 CorrectOrientation: True,9 EncodingType: Entities.CameraEncodingType.JPEG,10 MediaType: Entities.CameraMediaType.PICTURE,11 AllowEdit: False,12 SaveToPhotoAlbum: False13)1415/* Output parameters */16TakePicture.Success /* Boolean */17TakePicture.ImageCaptured /* Text — base64-encoded image data */18TakePicture.Error.Message /* Text — error description */1920/* Display captured image inline */21/* Image widget Source property expression: */22"data:image/jpeg;base64," + CapturedImageB642324/* Convert base64 to Binary Data for storage */25/* Use TextToBinaryData() built-in function: */26TextToBinaryData(CapturedImageB64)Expected result: Tapping the camera button triggers the native camera UI on the device. After capturing a photo, it displays in the Image widget and the base64 data is ready to save to the server entity via a Server Action.
Get GPS Location with the Location Plugin
Get GPS Location with the Location Plugin
Install the **Location Plugin** from Forge (search 'Location Plugin', by OutSystems). Add dependency via Ctrl+Q → Location Plugin → check `GetLocation` Client Action. Create a Client Action `GetCurrentLocation` on your screen: Local Variables: `Latitude` (Decimal), `Longitude` (Decimal), `Accuracy` (Decimal), `LocationError` (Text) Action flow: ``` Start → GetLocation EnableHighAccuracy = True (uses GPS, not just cell towers) Timeout = 10000 (10 seconds in milliseconds) MaximumAge = 0 (always get fresh reading) → If GetLocation.Success [True] → Assign Latitude = GetLocation.Latitude → Assign Longitude = GetLocation.Longitude → Assign Accuracy = GetLocation.Accuracy [False] → Assign LocationError = GetLocation.Error.Message → End ``` After getting coordinates, display them in expression widgets or pass to a Server Action to save them with a record or calculate distance using your own formula.
1/* GetLocation Client Action call */2GetLocation(3 EnableHighAccuracy: True, /* True = GPS, False = network/wifi */4 Timeout: 10000, /* milliseconds before timeout error */5 MaximumAge: 0 /* 0 = always fresh; N = use cached if < N ms old */6)78/* Output parameters */9GetLocation.Success /* Boolean */10GetLocation.Latitude /* Decimal — e.g., 40.712776 */11GetLocation.Longitude /* Decimal — e.g., -74.005974 */12GetLocation.Accuracy /* Decimal — meters radius of accuracy */13GetLocation.Altitude /* Decimal — meters above sea level */14GetLocation.Error.Code /* Integer */15GetLocation.Error.Message /* Text */1617/* Display coordinates with 6 decimal places */18DecimalToText(Round(Latitude, 6)) + ", " + DecimalToText(Round(Longitude, 6))1920/* Calculate approximate distance between two points (Haversine — simplified) */21/* For production use, call a mapping API from a Server Action */22/* Google Maps static link: */23"https://maps.google.com/?q=" + DecimalToText(Latitude) + "," + DecimalToText(Longitude)Expected result: Tapping 'Get Location' shows the native permission dialog. After granting permission, latitude and longitude values appear in the expression widgets. Accuracy shows the radius in meters (lower = more accurate; GPS typically gives <10m, cell tower gives >100m).
Handle Device Permissions Correctly
Handle Device Permissions Correctly
Both camera and location plugins request permissions at runtime (iOS and Android require explicit user consent). Handle the permission flow gracefully: **Check permission status before calling the plugin:** Use the Camera Plugin's `CheckCameraPermission` and the Location Plugin's `CheckLocationPermission` Client Actions. Action flow with permission check: ``` Start → CheckCameraPermission → If CheckCameraPermission.PermissionStatus = "granted" [True] → TakePicture [False] → RequestCameraPermission → If RequestCameraPermission.PermissionStatus = "granted" [True] → TakePicture [False] → Show message explaining permission is needed → End ``` For iOS, once a user denies permission the system does not re-prompt — you must direct them to Settings. Detect this with Error.Code from TakePicture and show a link.
1/* Permission check pattern — Camera */2/* Available in Camera Plugin: CheckCameraPermission, RequestCameraPermission */34CheckCameraPermission() output:5 PermissionStatus: Text6 /* Values: "granted", "denied", "prompt" (not yet asked) */78/* Full permission-aware camera capture flow */9Start10→ CheckCameraPermission11→ Switch on CheckCameraPermission.PermissionStatus12 Case "granted":13 → TakePicture → [handle result]14 Case "prompt":15 → RequestCameraPermission16 → If RequestCameraPermission.PermissionStatus = "granted"17 [True] → TakePicture → [handle result]18 [False] → Assign CameraError = "Camera permission denied"19 Case "denied":20 → Assign CameraError = "Please enable camera access in device Settings"21→ End2223/* iOS — open app settings for permission re-grant */24/* JavaScript node: */25if (cordova && cordova.plugins && cordova.plugins.diagnostic) {26 cordova.plugins.diagnostic.switchToSettings();27}Expected result: On first use, the native permission dialog appears with your app name. Granting permission proceeds to the feature. Denying shows a friendly message. If previously denied on iOS, the error message directs the user to Settings.
Configure Push Notifications with Firebase and the OutSystems Plugin
Configure Push Notifications with Firebase and the OutSystems Plugin
Push notifications require: (1) a Firebase project with FCM configured, (2) the OutSystems Push Notifications Plugin from Forge, (3) Firebase configuration files added to your app build. **Step A — Firebase setup:** 1. Go to console.firebase.google.com → Create project 2. Add Android app → enter your app package name (from Service Studio → app properties → Package Name) → download `google-services.json` 3. Add iOS app → enter Bundle ID → download `GoogleService-Info.plist` **Step B — Install plugin:** Search Forge for **OutSystems Firebase Cloud Messaging Plugin** (or **OneSignal Plugin** as a simpler alternative) → Install. **Step C — Add config files to app:** In Service Studio → Interface tab → Resources → Add Resource: - Add `google-services.json` (Deploy Action: Copy to App Resources) - Add `GoogleService-Info.plist` (Deploy Action: Copy to App Resources) **Step D — Request push permission and get device token:**
1/* Push Notifications Plugin — register and get token */2/* After installing FCM Plugin and adding dependency */34/* Client Action: RegisterForPushNotifications */5/* Call on app start in OnApplicationReady */67Start8→ RegisterDevice9 /* FCM Plugin action: registers with FCM and returns token */10→ If RegisterDevice.Success11 [True]12 → SaveDeviceToken (Server Action)13 /* Save RegisterDevice.DeviceToken to PushToken entity */14 /* Associate with current UserId: GetUserId() */15 UserId = GetUserId()16 Token = RegisterDevice.DeviceToken17 Platform = GetDevicePlatform() /* "Android" or "iOS" */18 UpdatedOn = CurrDateTime()19 [False]20 → Log error (FeedbackMessage or Logger)21→ End2223/* Server Action: SendPushNotification */24/* Called from Timer or Server Action when notification needed */25/* Uses FCM API via REST — Consume REST API in Logic tab */2627/* FCM v1 API call */28/* POST https://fcm.googleapis.com/v1/projects/{projectId}/messages:send */29/* Body: */30{31 "message": {32 "token": "<DeviceToken>",33 "notification": {34 "title": "Order #123 Shipped",35 "body": "Your order has been dispatched"36 }37 }38}Expected result: App requests notification permission on first launch. After granting, a FCM device token is saved to your PushToken entity. From the server, calling your SendPushNotification Server Action delivers a push notification to the device.
Trigger a MABS Build and Test on Device
Trigger a MABS Build and Test on Device
After adding Forge plugins and configuration files, you must trigger a new MABS build to compile the native app with the new plugins included. **In Service Studio:** Click **1-Click Publish** (top center). After publishing, go to your app in the browser → click **Generate Android App** or **Generate iOS App** button → MABS builds the native package (APK/IPA). **Alternatively:** In **Service Center** → Factory → Applications → [your app] → Mobile → Generate → select platform → Submit. MABS build times: 5-15 minutes typically. Monitor progress in Service Center → Factory → Applications → your app → Mobile Versions. **Test:** Download and install the generated APK on an Android device (or use TestFlight for iOS). Open the app → test camera, GPS, and push notification flows. **Debug crashes:** If the app crashes on device, check Service Center → Monitoring → Error Logs → filter by application name for JavaScript and native errors.
Expected result: MABS build completes successfully in Service Center. Installing the APK on an Android device and opening the app triggers the native permission dialogs for camera, location, and notifications. All three features work as implemented.
Complete working example
1/* ============================================================2 OutSystems Mobile Device Features — Reference Patterns3 ============================================================ */456/* === CAMERA === */78/* CapturePhoto Client Action */9Start10→ CheckCameraPermission11→ If PermissionStatus = "granted" or PermissionStatus = "prompt"12 ["prompt"] → RequestCameraPermission13 ["granted" or after request]14 → TakePicture15 Quality: 8016 Width: 102417 Height: 102418 CorrectOrientation: True19 EncodingType: JPEG20 SaveToPhotoAlbum: False21 → If TakePicture.Success22 [True] → Assign PhotoB64 = TakePicture.ImageCaptured23 Assign PhotoBinary = TextToBinaryData(PhotoB64)24 [False] → Assign CameraError = TakePicture.Error.Message25→ End2627/* Image widget Source — display captured photo */28"data:image/jpeg;base64," + PhotoB64293031/* === GPS === */3233/* GetCurrentLocation Client Action */34Start35→ GetLocation36 EnableHighAccuracy: True37 Timeout: 1000038 MaximumAge: 039→ If GetLocation.Success40 [True]41 → Assign Latitude = GetLocation.Latitude42 → Assign Longitude = GetLocation.Longitude43 → Assign Accuracy = GetLocation.Accuracy44 [False]45 → If GetLocation.Error.Code = 1 /* PERMISSION_DENIED */46 [True] → Show 'Enable location in Settings'47 [False] → Show GetLocation.Error.Message48→ End4950/* Coordinate display expression */51DecimalToText(Round(Latitude, 6)) + ", " + DecimalToText(Round(Longitude, 6))5253/* Google Maps link */54"https://maps.google.com/?q=" + DecimalToText(Latitude) + "," + DecimalToText(Longitude)555657/* === PUSH NOTIFICATIONS === */5859/* RegisterForPush Client Action — call from OnApplicationReady */60Start61→ RegisterDevice /* FCM Plugin action */62→ If RegisterDevice.Success63 [True] → SaveDeviceToken (Server Action)64 UserId: GetUserId()65 Token: RegisterDevice.DeviceToken66 Platform: GetDevicePlatform()67 [False] → Log error68→ End6970/* GetDevicePlatform() returns "Android" or "iOS" */71/* GetUserId() returns current user's Id */Common mistakes
Why it's a problem: Calling TakePicture or GetLocation from a Server Action — these are device-hardware plugins and can only be called from Client Actions running on the device.
How to avoid: Camera and GPS plugin actions are Client Actions only. Call them from Client Actions on the screen, capture the output (image bytes, coordinates), then pass the data to a Server Action for storage.
Why it's a problem: Adding the google-services.json file to the module as a regular Resource without setting Deploy Action to 'Copy to App Resources', causing the MABS build to fail with a Firebase configuration error.
How to avoid: In Interface tab → Resources → select google-services.json → Properties → set Deploy Action to 'Copy to App Resources' (not 'Copy to Output Directory'). This places the file in the correct location for MABS to include it in the native build.
Why it's a problem: Testing camera and GPS functionality in the browser preview in Service Studio — the browser is not a native container and plugin calls fail silently or throw console errors.
How to avoid: Device feature testing requires a real MABS-built app on a physical device. For faster iteration, use Chrome for Android remote debugging (USB) or TestFlight for iOS to deploy and test without full app store submission.
Why it's a problem: Saving the FCM device token once and never updating it, causing push notifications to stop working after app reinstallation (tokens change on reinstall).
How to avoid: Call RegisterDevice on every app startup in OnApplicationReady and upsert the token — if the token for the UserId already exists, update it with the new value. Delete tokens that receive FCM 404 (not registered) error responses.
Best practices
- Always check and request permissions explicitly before calling plugin actions — do not assume the user has granted permission from a previous session.
- Handle plugin errors gracefully with user-friendly messages — Error.Code = 1 means permission denied, which requires different handling (Settings redirect) than Error.Code = 3 (timeout, which is worth retrying).
- Store captured images as Binary Data in your server entity rather than as base64 text — Binary Data is stored efficiently in the database while base64 text is ~33% larger.
- Compress images before upload using the Quality parameter (80 is a good default) — a 12MP phone photo at full quality can be 5-8MB; compressed to 1024px/Quality 80 it is typically under 500KB.
- Rotate device tokens periodically in your PushToken entity — FCM tokens can change when the app is reinstalled or the device is reset. Always handle 404 FCM responses by deleting the stale token.
- Test on both iOS and Android physical devices — plugin behavior, permission dialog text, and camera output quality differ between platforms and cannot be fully simulated in an emulator.
- In ODC mobile apps, the plugin installation and MABS build process is identical — install from Forge, add resources in ODC Studio, build via the ODC Portal.
- Check the MABS compatibility matrix before upgrading your platform version — some Forge plugins have minimum and maximum MABS version constraints.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building an OutSystems Mobile app that needs camera, GPS, and push notifications. Explain: (1) how to install Camera Plugin and Location Plugin from Forge and add dependencies in Service Studio, (2) how to call TakePicture with permission checking and display the captured image, (3) how to call GetLocation with EnableHighAccuracy and handle permission denied errors, (4) how to set up Firebase FCM for push notifications, add google-services.json as an App Resource, and save the device token to a server entity, and (5) how to trigger a MABS build and test on a real device. Use OutSystems expression syntax and exact Service Studio paths.
In my OutSystems Mobile app in Service Studio, I need to add a 'Capture Inspection Photo' feature. Help me: install Camera Plugin from Forge, add TakePicture dependency via Manage Dependencies, create a CapturePhoto Client Action that checks camera permission first then calls TakePicture with Quality 80 and 1024px max dimensions, display the photo immediately in an Image widget using the base64 output, and save the Binary Data to my Inspection entity via a Server Action. Show the complete action flow with If nodes for permission and success checks.
Frequently asked questions
What is MABS and why does it matter for device feature plugins?
MABS (Mobile Application Build Service) is OutSystems' cloud-based native app builder. It takes your OutSystems module and Forge plugins and compiles them into a native iOS (IPA) or Android (APK) app. Device feature plugins provide native code (Objective-C/Swift for iOS, Java/Kotlin for Android) that MABS compiles into the app binary. Without a MABS build, plugins are not included in the app and their Client Actions throw errors.
Can I use device features in an OutSystems Reactive Web app, or only in Mobile modules?
Cordova/Capacitor plugins from Forge are Mobile module-only — they require the native container that MABS builds. Reactive Web apps can access some device features via web standard APIs (Geolocation API, MediaDevices for camera, Web Push for notifications) using JavaScript nodes, but these have more limited capabilities and browser permission models than native plugins.
How do I send push notifications from my OutSystems server to a specific user?
Save each device's FCM token to a server entity (with UserId) when the user registers for push. To send a notification, look up the user's token(s) from that entity, then call the FCM API (consume it as a REST API in OutSystems) passing the token and notification content. Handle FCM 404 responses by deleting the stale token. You can call this from a Timer for scheduled notifications or from a Server Action for event-triggered notifications.
Do device feature plugins work the same in ODC as in O11?
Yes — the plugin installation, Manage Dependencies workflow, Client Action calls, and MABS build process are identical in ODC Studio for Mobile apps. The difference is that ODC Portal handles MABS build submissions (instead of Service Center in O11), and ODC has a separate Asset Library for Forge components. Plugin Client Actions, permission handling patterns, and output parameters are unchanged.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation