How to create a mobile app (PWA) with MapTiler SDK JS

This example shows how to use MapTiler SDK to create a native mobile app that can be pushed to the Apple App Store and Google Play.

With this project as a starter (or any Capacitor app) and with a single codebase, you can create:

  • A web app hosted online, which people visit with a regular web browser.
  • A PWA that people can turn into a semi-app (under certain conditions: Android, EU-iPhone, etc.).
  • A Capacitor-powered native web app wrapped to be distributed on Apple AppStore and Google Play.

PWAs or Progressive Web Apps are web apps hosted online, like any website, but they are shipped with a few extra settings in a manifest file. We are not going to cover this here, but it's worth mentioning that your web app does **not** need to comply to the PWA set of rules to be wrapped inside a native mobile app with Capacitor. Those are two very different things, both with their pros and cons.

Get started

For this project, we started from the ViteJS Vanilla TypeScript sample project and then followed the Ionic Capacitor instructions to give our project mobile superpowers.

To make things easier for you, we have created the maptiler-mini-mobile-app repository where we have already configured all the dependencies, configuration files, styles, etc. Here’s how to set it up:

  1. Clone the maptiler-mini-mobile-app repo.
  2. To install project dependencies, run npx npm install.
  3. Rename .env.sample to .env and replace the value of YOUR_MAPTILER_CLOUD_API_KEY with your actual MapTiler API key.
  4. Modify the file capacitor.config.ts: Update the app name, logo, bundleID, etc. For more info, refer to the Capacitor configuration docs.
  5. In terminal, run npx npm run build && npx cap sync.

Modify the source

The project itself reuses the structure of the ViteJS boilerplate, meaning the project source is located in the src/ directory and can be ran in a regular web browser:

  • In dev/watch mode: npm run dev
  • To build a production bundle: npm run build

After building the project for production, you can run it locally with npm run preview.

Update the mobile app

Both mobile projects (XCode workspace and Android Studio Workspace) need to be updated after the web project has been built for production. To do so, run: npm run sync

Back in XCode or Android Studio, you may see a popup asking if you want to refresh the project based on the updates. Select “yes” (or “Read from disk” in XCode).

Then build the projects in XCode or Android Studio again and you should see your latest changes.

Customize the iOS project

First, run your project directly on iOS with the command npx cap run ios or open the iOS project with npm run open-xcode. You can then tune a few things.

Select the target iOS version:

Select the iOS target version

If you plan to distribute your app, set up signing by associating it to a Team:

iOS app signing & capabilities

To change the icon of your app, go to menu App > App > Assets and drag and drop a non-transparent image in the icon frame:

Change the iOS app icon

What’s in this project?

Mobile-friendly geolocation

The geolocation control originally available in MapTiler SDK is web-specific, and even though it will work when encapsulated into a mobile app, using it will trigger two user-agreement panel: one at the web-view level, and the next at system level. A more elegant way is to use @capacitor/geolocation, an official plugin that uses geolocation directly from the system. We had to modify the settings of the native projects (both Android and iOS) to grant the app the permission to use geolocation. You can read more about this on the plugins’s page.

To make it simpler to integrate, we have created a SDK-friendly control that you can find in src/universalgeolocatecontrol.ts. Note that this also works in a regular web page as the plugin provides a complete fallback.

Safe insets

The safe insets are the margins that need to be put so that the content you display on screen does not step on the system display, such as the status bar on top or the bottom menu on iOS. They are defined as CSS environment variables that you can directly leverage in your styling.

For instance, MapTiler SDK and MapLibre stylesheet define some classes that apply to the controls called .maplibregl-ctrl-yyy-xxx. Our mobile app can define more properties to these classes to prevent the visual elements to clash with the system display. You can see the classes with the insets in the file src/style.css.

Example of a class using safe insets for controls positioned in the top-right corner of the map:

.maplibregl-ctrl-top-right {
  top: env(safe-area-inset-top);
}

The app’s viewport must also have the following attributes in the index.html:

<meta
  name="viewport"
  content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>

Here is the result:

Without safe insets With safe insets
Map applicaction controls without safe insets Map applicaction controls with safe insets

Note: The safe insets values will be 0 in a web browser, so it will not impact your layout to include them in all cases.

An extension of MapLibre GL JS
On this page

    Was this helpful?