In this article we will guide you through the steps to build a Capacitor application with our advanced indoor positioning and tracking technologies. In case you have already integrated our plugin in your project, take a look at our Situm Cordova SDK documentation to learn how to use our api more in depth.
Pre-requisites: configure Capacitor #
- Install the Ionic framework:
In this guide, we will be using Ionic with Angular as a reference. However, our plugin can be easily integrated into apps built with other frameworks, such as Vue.js, by following a similar approach. To illustrate this, you’ll find equivalent code snippets for both Ionic + Angular and Vue.js throughout the guide. While Ionic is not mandatory, we use it alongside Capacitor to take advantage of its pre-built UI components and project structure templates.
To install it follow the installation guide by CLI. In this guide we are going to build an Angular application with tabs, so pick the Angular framework, the tabs template and the use of Standalone components when executing “ionic start”.
NOTE! Make sure you are using at least Node 20.0 and @ionic/angular 8.4.X to ensure the project builds correctly. If you are using Vue.js, ensure you have the latest compatible versions of Vue and Capacitor. - (iOS only) Fill out the code signing of your project:
Inside Xcode go to your target and select the team that will be used to sign your app. Follow this guide to learn how to. - Run your project:
Once you have created your first project with the ionic CLI with the configuration mentioned before, you can now try the pre-built application with the following commands:
# For Android ionic cap run android # For iOS ionic cap run ios
npm run build npx cap sync #For Android npx cap run android #For iOS npx cap run ios
This two steps should be enough to run the default ionic application. In case you face some unexpected issue, try to check the configuration steps on both Android and iOS platforms.
Congratulations! You have created a Capacitor + Ionic application. Now lets integrate our plugin in a few steps.
Installing the plugin #
Add our plugin to your project by executing the following command in your terminal:
npm install @situm/cordova
Setup native projects #
You will also need to make some changes to the native projects of your Capacitor application:
Android #
Add the ACCESS_FINE_LOCATION permission declaration to your AndroidManifest.xml (you can omit this step if you have configured Situm SDK not to use GPS):
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Finally, set the minSdkVersion to 22 at least on your app’s build.gradle file (android/app/build.gradle).
iOS #
First, you need to configure the required permissions (more info here), so include the following permissions in your app’s Info.plist file (modify the permission’s informative texts as you wish):
<key>NSLocationWhenInUseUsageDescription</key> <string>Location is required to find out where you are</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>Location is required to find out where you are</string> <key>NSMotionUsageDescription</key> <string>We use your phone sensors (giroscope, accelerometer and altimeter) to improve location quality</string>
Modify your project’s Podfile (ios/App/Podfile) so our plugin works fine on iOS :
use_frameworks! :linkage => :static
Finally, if you want the our MapViewer (built-in Wayfinding UI) to work offline, you will need to add the underlying web application’s domain inside the entry WKAppBoundDomains, also on Info.plist as follows:
<key>WKAppBoundDomains</key> <array> <string>map-viewer.situm.com</string> </array>
Warning! If you have other Webviews in your app this might affect them. Learn more about this here.
Request permission #
Starting in Situm Cordova plugin version 3.15.0 the SDK can automatically request the necessary permissions for positioning.
You can enable this powerful feature by simply calling the Situm.enableUserHelper method:
cordova.plugins.Situm.setApiKey(Constants.API_USER, Constants.API_KEY); ... // Tell the native SDK to automatically request permissions and manage // sensor (BLE/Location) issues. cordova.plugins.Situm.enableUserHelper();
This method acts as a convenient shortcut, but if you prefer a more tailored setup, you can customize the behavior through configureUserHelper.
By default, this feature is disabled and entirely optional. If you choose not to use it, you can continue managing permissions just as you have until now, for example using cordova.plugins.diagnostic:
npm install cordova.plugins.diagnostic
Run your app! #
There are many things you can do with the @situm/cordova plugin. In this guide, we will show you how to do the basic stuff. For more info, take a look at our sample app and at the plugin documentation.
Code snippets #
- Copy & paste this file into your tab1.page.html (src/app/tab1/tab1.page.html):
<ion-header [translucent]="true"> <ion-toolbar> <ion-title> Map Viewer </ion-title> </ion-toolbar> </ion-header> <ion-content [fullscreen]="true"> <map-view viewer-domain="https://map-viewer.situm.com" situm-api-key="YOUR_API_KEY" building-identifier="YOUR_BUILDING_IDENTIFIER" profile="YOUR_MAPVIEW_PROFILE" /> </ion-content>
// If you're using Vue.js, skip this step as you're likely integrating the code within a Single File Component (.vue file).
- Copy & paste the following snippet into your tab1.page.ts (src/app/tab1/tab1.page.ts). If you’re using Vue.js, add the equivalent code to your chosen .vue Component. Remember to add cordova.plugins.diagnostic as we will be requesting the app permissions to the user with this package. Also fill out the blank variables with your email, api-key, the identifier of your building and the identifier of a POI from your building:
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core'; import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone'; import { Platform } from '@ionic/angular/standalone'; // Declare a cordova variable to avoid typescript errors declare let cordova: any; const YOUR_SITUM_EMAIL = ''; const YOUR_SITUM_API_KEY = ''; const YOUR_POI_IDENTIFIER = ''; const YOUR_BUILDING_IDENTIFIER = ''; @Component({ selector: 'app-tab1', templateUrl: 'tab1.page.html', // Let the component know that we will use a custom HTMLElement. schemas: [CUSTOM_ELEMENTS_SCHEMA], styleUrls: ['tab1.page.scss'], standalone: true, imports: [IonHeader, IonToolbar, IonTitle, IonContent], }) export class Tab1Page { constructor(public platform: Platform) {} ionViewDidEnter() { // 1. Authenticate in our SDK. cordova.plugins.Situm.setApiKey(YOUR_SITUM_EMAIL, YOUR_SITUM_API_KEY); // 2. Use the remote configuration of your situm account. cordova.plugins.Situm.setUseRemoteConfig(true); // With this flag activated, you can modify your location request without any code changes. // See all the parameters you can modify in https://dashboard.situm.com/settings. // 3. Tell the native SDK to automatically request permissions and manage // sensor (BLE/Location) issues. cordova.plugins.Situm.enableUserHelper(); // 4. Start positioning. // You may want to specify a buildingIdentifier to locate the user inside a specific building. // When positioning starts, the map will automatically draw the user's position. cordova.plugins.Situm.onLocationUpdate((location) => { console.log("EXAMPLE> location update: ", location.position); }); cordova.plugins.Situm.onLocationStatus((status) => { console.log("EXAMPLE> location status: ", status); }); cordova.plugins.Situm.onLocationError((err) => { // Something went wrong while trying to start positioning on the device. // Handle the possible errors and notify the user so he can fix them. console.error("EXAMPLE> location error :", err); }); // Start positioning in the building specified at /src/constants.ts: cordova.plugins.Situm.requestLocationUpdates( // In case you have multiple buildings that the user could visit, // you might want to start positioning in all your buildings using global mode // by specifying an empty identifier. // Visit https://situm.com/docs/sdk-cartography/ to learn how to obtain your identifier. { buildingIdentifier: YOUR_BUILDING_IDENTIFIER } ); // 5. Send actions to our viewer and handle events happening inside it. cordova.plugins.MapView.onLoad((controller: any) => { // MapView was loaded correctly, // now you can use our controller to manage our visual component. controller.selectPoi(YOUR_POI_IDENTIFIER); // In this case we are selecting some POI of a building. // You can find out what identifier has a certain POI of your building // by previously retrieving them with cordova.plugins.Situm.fetchIndoorPOIsFromBuilding(), // learn to use this method in our documentation (https://developers.situm.com/sdk_documentation/cordova/jsdoc/latest/situm#.fetchIndoorPOIsFromBuilding) }); } }
<script setup> import { ref, onMounted } from 'vue'; const YOUR_SITUM_EMAIL = ""; const YOUR_SITUM_API_KEY = ""; const YOUR_BUILDING_IDENTIFIER = ""; onMounted(() => { // Verify Situm plugin is installed: if (!window.cordova || !window.cordova.plugins || !window.cordova.plugins.Situm) { console.warn("EXAMPLE> Cordova or Situm not available!"); return; } const situm = window.cordova.plugins.Situm; // Setup Situm: situm.setApiKey(YOUR_SITUM_EMAIL, YOUR_SITUM_API_KEY); situm.setUseRemoteConfig(true); // Tell the native SDK to automatically request permissions and manage // sensor (BLE/Location) issues. situm.enableUserHelper(); // Register location, status and error callbacks: situm.onLocationUpdate((location) => { console.log("EXAMPLE> Location update: ", location?.position); }); situm.onLocationStatus((status) => { console.log("EXAMPLE> Location status: ", status); }); situm.onLocationError((err) => { console.error("EXAMPLE> Location error:", err); }); // Start positioning: You may want to specify a buildingIdentifier to locate the user inside a specific building. // When positioning starts, the map will automatically draw the user's position. situm.requestLocationUpdates({ buildingIdentifier: YOUR_BUILDING_IDENTIFIER }); }); </script> <template> <map-view viewer-domain="https://maps.situm.com" :situm-api-key="YOUR_SITUM_API_KEY" :building-identifier="YOUR_BUILDING_IDENTIFIER" :profile="YOUR_MAPVIEW_PROFILE"> </map-view> </template> <style scoped> map-view { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } </style>
Finally, run your app with this command to launch the app on the device:
#For Android ionic cap run android #For iOS ionic cap run ios
npm run build npx cap sync #For Android npx cap run android #For iOS npx cap run ios
If your followed step by step this guide your app should be asking for the permissions needed right after launching, and then start positioning in the building specified and painting positions in the map. You might face common configuration issues, so troubleshoot them by checking the logs with the web inspector.
Also make sure you have calibrated correctly your building to be able to draw indoor positions in the map view.
For those who still prefer to handle runtime permission requests manually, we provide below the legacy permission request function, based on cordova.plugins.diagnostic:
requestPermissions(successCb: Function, errorCb: Function) { var isAndroid = this.platform.is("android"); var isIOS = this.platform.is("ios"); if (isAndroid) { cordova.plugins.diagnostic.requestRuntimePermissions( function (permissions: any) { console.log("EXAMPLE> permissions statuses: ", permissions); successCb(); }, function (error: any) { errorCb(JSON.stringify(error)); }, [ cordova.plugins.diagnostic.permission.ACCESS_FINE_LOCATION, cordova.plugins.diagnostic.permission.BLUETOOTH_CONNECT, cordova.plugins.diagnostic.permission.BLUETOOTH_SCAN, ] ); } else if (isIOS) { cordova.plugins.diagnostic.getLocationAuthorizationStatus( (status: any) => { if (status == "authorized") { successCb(); } }, () => { // Do nothing } ); cordova.plugins.diagnostic.requestLocationAuthorization( function (status: any) { switch (status) { case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED: errorCb("Permission not requested"); break; case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS: errorCb("Permission denied"); break; case cordova.plugins.diagnostic.permissionStatus.GRANTED: console.log("Permission granted always"); successCb(); break; case cordova.plugins.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE: console.log("Permission granted only when in use"); successCb(); break; } }, function (error: any) { errorCb(JSON.stringify(error)); }, cordova.plugins.diagnostic.locationAuthorizationMode.ALWAYS ); } }
const requestPermissions = (successCb, errorCb) => { if (!window.cordova || !window.cordova.plugins || !window.cordova.plugins.Situm) { console.warn("EXAMPLE> Cordova or Diagnostic not available!"); return; } const diagnostic = window.cordova.plugins.diagnostic; const isAndroid = /Android/i.test(navigator.userAgent); const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); if (isAndroid) { diagnostic.requestRuntimePermissions( (permissions) => { console.log("EXAMPLE> permissions statuses: ", permissions); successCb(); }, (error) => { errorCb(JSON.stringify(error)); }, [ diagnostic.permission.ACCESS_FINE_LOCATION, diagnostic.permission.BLUETOOTH_CONNECT, diagnostic.permission.BLUETOOTH_SCAN, ] ); } else if (isIOS) { diagnostic.getLocationAuthorizationStatus( (status) => { if (status === diagnostic.permissionStatus.GRANTED || status === diagnostic.permissionStatus.GRANTED_WHEN_IN_USE) { successCb(); } }, () => { // Do nothing } ); diagnostic.requestLocationAuthorization( (status) => { switch (status) { case diagnostic.permissionStatus.NOT_REQUESTED: errorCb("Permission not requested"); break; case diagnostic.permissionStatus.DENIED_ALWAYS: errorCb("Permission denied"); break; case diagnostic.permissionStatus.GRANTED: console.log("EXAMPLE> Permission granted always"); successCb(); break; case diagnostic.permissionStatus.GRANTED_WHEN_IN_USE: console.log("EXAMPLE> Permission granted only when in use"); successCb(); break; } }, (error) => { errorCb(JSON.stringify(error)); }, diagnostic.locationAuthorizationMode.ALWAYS ); } };