Skip to content

Mobile Integration (iOS & Android)

SmartMaps styles work natively with MapLibre Native on iOS and Android. Since SmartMaps uses the open MapLibre Style Specification, you can load any SmartMaps style.json directly, no proprietary SDK required.

Style URLs

All SmartMaps styles are available as standard style.json endpoints. Replace [INSERT API-KEY] with your actual API key:

Style URL
Light https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app
Dark https://tiles.smartmaps.cloud/styles/v1/smartmaps/dark/style.json?apiKey=[INSERT API-KEY]&channel=app
Grey https://tiles.smartmaps.cloud/styles/v1/smartmaps/grey/style.json?apiKey=[INSERT API-KEY]&channel=app
Essential https://tiles.smartmaps.cloud/styles/v1/smartmaps/essential/style.json?apiKey=[INSERT API-KEY]&channel=app
Accessible https://tiles.smartmaps.cloud/styles/v1/smartmaps/accessible/style.json?apiKey=[INSERT API-KEY]&channel=app
Satellite https://tiles.smartmaps.cloud/styles/v1/smartmaps/satellite/style.json?apiKey=[INSERT API-KEY]&channel=app

For more details on each style, see the Style definition page.


MapSession (required)

Important

When using MapLibre Native directly (without the SmartMaps GL JS SDK), you must register a MapSession manually. The SmartMaps GL JS SDK handles this automatically, but native apps need to call the MapSession endpoint themselves.

SmartMaps uses MapSessions to track tile usage for billing. Your app must send a GET request to the MapSession endpoint periodically every 10 minutes while the map is active.

Endpoint:

GET https://www.yellowmap.de/api_rst/api/mapsessiongl?apiKey={API-KEY}&provider=smartmaps&channel=app
Parameter Required Value
apiKey Yes Your SmartMaps API key (URL-encoded)
provider Yes smartmaps
import java.net.URL
import java.net.URLEncoder
import java.util.Timer
import kotlin.concurrent.fixedRateTimer

class MapSessionManager(private val apiKey: String) {
    private var timer: Timer? = null

    fun start() {
        // Send initial session request, then repeat every 10 minutes
        timer = fixedRateTimer("mapsession", daemon = true, period = 600_000L) {
            sendSession()
        }
    }

    fun stop() {
        timer?.cancel()
        timer = null
    }

    private fun sendSession() {
        Thread {
            try {
                val encodedKey = URLEncoder.encode(apiKey, "UTF-8")
                val url = URL(
                    "https://www.yellowmap.de/api_rst/api/mapsessiongl?apiKey=$encodedKey&provider=smartmaps&channel=app"
                )
                val connection = url.openConnection() as java.net.HttpURLConnection
                connection.requestMethod = "GET"
                connection.responseCode // execute request
                connection.disconnect()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }.start()
    }
}

Usage in your Activity:

class MainActivity : AppCompatActivity() {
    private val mapSession = MapSessionManager("[INSERT API-KEY]")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ... map setup ...
        mapSession.start()
    }

    override fun onDestroy() {
        mapSession.stop()
        super.onDestroy()
    }
}
import Foundation

class MapSessionManager {
    private let apiKey: String
    private var timer: Timer?

    init(apiKey: String) {
        self.apiKey = apiKey
    }

    func start() {
        // Send initial session request
        sendSession()
        // Repeat every 10 minutes
        timer = Timer.scheduledTimer(withTimeInterval: 600, repeats: true) { [weak self] _ in
            self?.sendSession()
        }
    }

    func stop() {
        timer?.invalidate()
        timer = nil
    }

    private func sendSession() {
        guard let encodedKey = apiKey.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
              let url = URL(string:
                "https://www.yellowmap.de/api_rst/api/mapsessiongl?apiKey=\(encodedKey)&provider=smartmaps&channel=app"
              ) else { return }

        URLSession.shared.dataTask(with: url) { _, _, _ in }.resume()
    }
}

Usage in UIKit:

class MapViewController: UIViewController {
    private let mapSession = MapSessionManager(apiKey: "[INSERT API-KEY]")

    override func viewDidLoad() {
        super.viewDidLoad()
        // ... map setup ...
        mapSession.start()
    }

    deinit {
        mapSession.stop()
    }
}

Usage in SwiftUI:

struct SmartMapsMapView: View {
    @StateObject private var mapSession = MapSessionObservable(apiKey: "[INSERT API-KEY]")

    var body: some View {
        MapView(styleURL: styleURL)
            .onAppear { mapSession.start() }
            .onDisappear { mapSession.stop() }
    }
}

class MapSessionObservable: ObservableObject {
    private let manager: MapSessionManager
    init(apiKey: String) { manager = MapSessionManager(apiKey: apiKey) }
    func start() { manager.start() }
    func stop() { manager.stop() }
}

Android

Prerequisites

  • Android Studio
  • Min SDK 21 (Android 5.0) or higher
  • A SmartMaps API key (register for free)

Setup

Add the MapLibre Native dependency to your build.gradle.kts (module-level):

dependencies {
    implementation("org.maplibre.gl:android-sdk:11.8.5")
}

Map with XML Layout

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <org.maplibre.android.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.MapLibre
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.Style
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng

class MainActivity : AppCompatActivity() {
    private lateinit var mapView: MapView

    private val styleUrl =
        "https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        MapLibre.getInstance(this)
        setContentView(R.layout.activity_main)

        mapView = findViewById(R.id.mapView)
        mapView.onCreate(savedInstanceState)
        mapView.getMapAsync { map ->
            map.setStyle(styleUrl)
            map.cameraPosition = CameraPosition.Builder()
                .target(LatLng(51.1657, 10.4515))
                .zoom(6.0)
                .build()
        }
    }

    override fun onStart() { super.onStart(); mapView.onStart() }
    override fun onResume() { super.onResume(); mapView.onResume() }
    override fun onPause() { super.onPause(); mapView.onPause() }
    override fun onStop() { super.onStop(); mapView.onStop() }
    override fun onDestroy() { super.onDestroy(); mapView.onDestroy() }
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        mapView.onSaveInstanceState(outState)
    }
    override fun onLowMemory() { super.onLowMemory(); mapView.onLowMemory() }
}

Jetpack Compose

MapLibre Native also provides a Jetpack Compose extension:

dependencies {
    implementation("org.maplibre.gl:android-sdk:11.8.5")
    implementation("org.maplibre.gl:android-sdk-compose:0.2.0")
}
import org.maplibre.android.compose.MapView
import org.maplibre.android.compose.CameraPosition
import org.maplibre.android.geometry.LatLng

@Composable
fun SmartMapsMap() {
    MapView(
        styleUri = "https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app",
        cameraPosition = CameraPosition(
            target = LatLng(51.1657, 10.4515),
            zoom = 6.0
        )
    )
}

Adding a Marker

mapView.getMapAsync { map ->
    map.setStyle(styleUrl) { style ->
        // Add a marker at Karlsruhe
        map.addMarker(
            MarkerOptions()
                .position(LatLng(49.0069, 8.4037))
                .title("Karlsruhe")
                .snippet("SmartMaps Headquarters")
        )
    }
}

For the full Android API reference, see the MapLibre Native Android documentation.


iOS

Prerequisites

  • Xcode 15 or later
  • iOS 14.0+ deployment target
  • A SmartMaps API key (register for free)

Setup with Swift Package Manager

In Xcode, go to File > Add Package Dependencies and add:

https://github.com/maplibre/maplibre-gl-native-distribution

For SwiftUI support, also add:

https://github.com/maplibre/swiftui-dsl

UIKit

import UIKit
import MapLibre

class MapViewController: UIViewController {
    private var mapView: MLNMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let styleURL = URL(string:
            "https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app"
        )!

        mapView = MLNMapView(frame: view.bounds, styleURL: styleURL)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.setCenter(
            CLLocationCoordinate2D(latitude: 51.1657, longitude: 10.4515),
            zoomLevel: 6,
            animated: false
        )
        view.addSubview(mapView)
    }
}

SwiftUI

import SwiftUI
import MapLibre
import MapLibreSwiftUI

struct SmartMapsMapView: View {
    let styleURL = URL(string:
        "https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app"
    )!

    var body: some View {
        MapView(styleURL: styleURL)
            .initialViewport(
                .centerAndZoom(
                    center: CLLocationCoordinate2D(
                        latitude: 51.1657,
                        longitude: 10.4515
                    ),
                    zoom: 6
                )
            )
    }
}

Adding an Annotation

func mapView(_ mapView: MLNMapView, didFinishLoading style: MLNStyle) {
    let annotation = MLNPointAnnotation()
    annotation.coordinate = CLLocationCoordinate2D(
        latitude: 49.0069,
        longitude: 8.4037
    )
    annotation.title = "Karlsruhe"
    annotation.subtitle = "SmartMaps Headquarters"
    mapView.addAnnotation(annotation)
}

For the full iOS API reference, see the MapLibre Native iOS documentation.


Using SmartMaps REST APIs on Mobile

All SmartMaps geo services are available as REST APIs and can be called directly from native apps using standard HTTP clients. This includes:

These APIs return standard JSON responses and work with any HTTP client (URLSession on iOS, OkHttp/Retrofit on Android).


Switching Styles at Runtime

You can switch between SmartMaps styles dynamically, for example to support light/dark mode:

// React to system dark mode
val isDarkMode = resources.configuration.uiMode and
    Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES

val styleUrl = if (isDarkMode) {
    "https://tiles.smartmaps.cloud/styles/v1/smartmaps/dark/style.json?apiKey=[INSERT API-KEY]&channel=app"
} else {
    "https://tiles.smartmaps.cloud/styles/v1/smartmaps/light/style.json?apiKey=[INSERT API-KEY]&channel=app"
}

map.setStyle(styleUrl)
// React to system dark mode
let styleName: String = {
    switch UITraitCollection.current.userInterfaceStyle {
    case .dark: return "dark"
    default: return "light"
    }
}()

let styleURL = URL(string:
    "https://tiles.smartmaps.cloud/styles/v1/smartmaps/\(styleName)/style.json?apiKey=[INSERT API-KEY]&channel=app"
)!

mapView.styleURL = styleURL