Skip to content
Developers Docs

Mapsted Maps - Mobile Docs



What is the Mapsted Maps Mobile Framework?

The Mapsted Maps mobile framework is a cross-platform development toolkit which allows you to quickly integrate Mapsted's core location technology into your own applications. The framework is organized into several modules for easy integration.

Core Module: This module contains Mapsted's core location positioning technology, analytics data collection, and various data retrieval and wayfinding logic functionality.

Map Module: Use the Map Module to integrate our interactive map and corresponding customizable map UI/UX into your application. The interface includes views for the map, wayfinding, itinerary management, and more.

Prebuilt UI/UX Components: A collection of beautiful, modern prebuilt UI/UX components, which are optimized for, and include, built-in integration with the Mapsted Maps mobile framework. You can import these components directly for use in your own application.

App Template: The App Template is a fully functional application right out-of-the-box, which includes some basic customization.

Mapsted Key Terms

This section will go over some key terminology that is unique to our products, to make it easier for you to get started with the Mapsted Maps Mobile Framework.

Properties, Buildings, and Floors

Each venue is referred to as a Property, which consists of one or more Building structures. Each of these Building structures contains one or more Floor structures. The Property establishes a relationship between related buildings. For reference, a visualization of the Property, Building, and Floor structures can be seen in the Figure below.

Each Property is identified by a PropertyId, each Building is identified by a BuildingId, and each Floor is identified by a FloorId. The PropertyId, BuildingId, and FloorId are unique across the Mapsted system.


An Entity is a fundamental element within a Property or Building. An Entity consists of a geometry type (i.e., MapPolygon, MapPolyline, MapPoint), a physical interpretation (e.g., Structure-Building, Structure-Room, or Obstacle-Wall), and at least one Category (e.g., Restaurant). One Entity can be associated with multiple categories, but each Entity will have one main Category.

Please see the figure below for a visualization of the Entity structure. Note that an Entity which is on the property-level (e.g., a building, parking garage, park, pond) is found in the PropertyEntities, and an Entity which is on the building-level (e.g., a room, elevator, wall) is found in the BuildingEntities.

Each Entity is identified by an EntityId, and depending on the geometry type, will have an associated PolygonId, PolylineLineId, or PointId. Note that EntityId and corresponding geometry type id are only unique within their scope. This means that for PropertyEntities, the EntityId will be unique to that particular Property. In the case of BuildingEntities, the EntityId will be unique to that Building.

Interactive Map

Map interactions are a fundamental component of Mapsted's mobile map SDK. This section will demonstrate some sample usages.

Map Interactions

The map view supports two types of map interactions, map gestures and map clicks. Map gestures allow for pan, zoom, tilt, and rotations, while map clicks allow for customizable actions (e.g., selection of entities).

Map Gestures

All common map gestures are supported by default. Learn more about the available map gestures and their effects below.

A Map Event listener can be registered to be notified when the map state changes. There are three types of map event notifications available.

  • Idle Map: A user is notified when the map view is finished rendering
  • Stable Map: A notification is sent out when all map animations have finished, and the user has lifted their fingers off the screen
  • Map Moved: This type of notification occurs when a map gesture is in progress, such as when a map is being panned, rotated, tilted, or zoomed

Below is some sample code to register a listener to be notified of map state changes.

// Handle Map's event types
mapApi.getMapEvents().observe(this, mapEvent -> {
    switch (mapEvent.getEventType()) {
        case EventType.MAP_IDLE:
            // handle idle event
        case EventType.MAP_STABLE:
            // handle stable event
        case EventType.MAP_MOVED:
            // handle map moved event
extension YourViewController: MNMapListenerDelegate {
    func onMapMoved() {

    func onMapStable() {

    func onMapIdle(){

Map Clicks

Map click events indicate when the user has clicked on the map. A map click event consists of a click type, a click location, and a click entity. See below for definitions of the various types of clicks, including Single Tap, Double Tap, Long Press, and Dual Tap.

// Register map click listener
mapApi.getMapClickEvents().observe(this, mapClickEvent -> {
    ClickType clickType = mapClickEvent.getClickType();
    IMercatorZone clickLocation = mapClickEvent.getClickLocation();
    Entity clickEntity = mapClickEvent.getClickEntity();

    // Handle event
/*Your view controller hosting the Mapsted map viewcontroller will 
automatically receive map event notifications if it conforms to the 
MNMapListenerDelegate delegate. When user taps on the map outside any 
vector elements, outsideBuildingTapped gets called with the tap position 
and the tap type.*/

extension YourViewController: MNMapListenerDelegate {
    func outsideBuildingTapped(tapPos: MNMercator, tapType: MapstedMapApi.TapType) {
        DispatchQueue.main.async {
            if tapType == .eSingle {
                //handle single tap
            else if tapType == .eLong {
            //handle long tap
            else if tapType == .eDouble {
                //handle double tap

/*To receive map vector element notifications, your viewcontroller 
needs to conform to MNMapVectorElementListenerDelegate. The 
onPolygonTapped gets called when user taps on a vector element 
on map. The onBalloonClicked gets called when user taps a pop up.*/

extension YourViewController: MNMapVectorElementListenerDelegate {
    func onPolygonTapped(polygon: MNMapPolygon, tapType: MapstedMapApi.TapType, tapPos: MNMercator) {
        //From polygon clicked, get info such as name, centroid etc.

        let centroid = polygon.centroid()
        let name =

    func onBalloonClicked(searchEntity: MNSearchEntity) {

Automatic Map Actions

The below code sample demonstrates how to enable or disable automatic pan, zoom, tilt, and rotation functionalities. These actions are controlled automatically in Mapsted’s UI SDK.

// Disable auto-follow

// Enable auto-follow

// Check auto-follow Status
boolean isCameraFollowUser = mapApi.isCameraFollowUser();
// Currently, automatically handled in iOS SDK
// Programmatic control will be implemented in an upcoming release

Programmatic Map Actions

You can also programmatically adjust the map to control zoom, tilt, and rotation actions.

Add custom parameters for map view events like zoom, tilt and rotate. 
Create a list of UpdateMapEvent. 
Add individual map view events with the necessary values. 
Set the map view events on UpdateMapEvent object.
Call onUpdateMapEvent passing updateMapEvent object

ArrayList<UpdateMapViewEvent> updateMapViewEvents = new ArrayList<>();
// Add the required map view events
// Specify the type of event, value for that event and the speed at which you want the event to occur
updateMapViewEvents.add(new UpdateMapViewEvent(UpdateMapViewEvent.TILT, 45.0f, 1.0f));
updateMapViewEvents.add(new UpdateMapViewEvent(UpdateMapViewEvent.ZOOM, 20.0f, 0.5f));
updateMapViewEvents.add(new UpdateMapViewEvent(UpdateMapViewEvent.RECENTER, 0f, 0.5f));
updateMapViewEvents.add(new UpdateMapViewEvent(UpdateMapViewEvent.ROTATE, 0f, 0.5f));

UpdateMapEvent updateMapEvent = new UpdateMapEvent();
//To simply center the map at a specific location at existing zoom, use:
MapstedMapApi.shared.mapView()?.moveToLocation(mercator: MNMercator)

//To center and update zoom use:
MapstedMapApi.shared.mapView()?.moveToLocation(mercator: MNMercator, zoom: Float, duration: Float)

//To rotate map, use:
MapstedMapApi.shared.mapView()?.setRotation(angle: Float, durationSeconds: Float)

//To tilt map, use:
MapstedMapApi.shared.mapView()?.tiltMap(angle: Float, duration: Float)

Offline Maps

All downloaded map data is automatically stored in a local database and available for use offline. The SDK will automatically download map data, as needed, based on what is nearby. Map data downloads can also be managed programmatically, triggered by UI interactions. See below for sample code.

// start downloading map data for a given propertyId
coreApi.propertyManager().startPropertyDownload(propertyId, new PropertyDownloadManager.Listener() {
    public void onProgress(int propertyId, int current, int total) {

    public void onComplete(int propertyId) {

    void onFail(int propertyId, Exception e){

// When using the Map SDK, you can select and draw the property on the map
// Note that this will internally trigger a map download if it has not previously been downloaded 
mapApi.selectPropertyAndDrawIfNeeded(propertyId, new SelectPropertyListener() {
        public void onCached(boolean isSuccess, int propertyId){
            // If successful, property data has been loaded into memory and property is selected

        public void onPlotted(boolean isSuccess, int propertyId){
            // If successful, property has completed being drawn on the map

// Properties can also be undrawn from the map as follows:
let myPropertyId = 504

//Get the property manager from CoreApi
let propertyManager = CoreApi.Propertymanager

// To download property data, provide a delegate to 
propertyManager.startDownload(propertyId: myPropertyId, propertyDownloadListener: self)

extension myViewController; PropertyDownloadListener {
    public func onSuccess(propertyId: Int) {

    public func onFailureWithProperty(propertyId: Int) {


    public func onProgress(propertyId: Int, percentage: Float) {


// To check the download status of a property 
let downloadStatus = propertyManager.getDownloadStatus(propertyId: myPropertyId)

// You can retrieve property data using the property identifier
let propertyData = propertyManager.getCached(propertyId: myPropertyId)

// When using the Map SDK, you can select and draw the property on the map
MapstedMapApi.shared.drawProperty(isSelected, propertyData)

// To delete downloaded property data
propertyManager.delete(propertyId: myPropertyId, listener: self)

// To undraw a property from the map using MapSDK

Prebuilt Map View

You have the option to customize the Prebuilt Map View in a variety of different ways. A few examples are detailed below.

// Set custom params prior to initializing the Mapsted SDK
                .setBaseMapStyle(BaseMapStyle.DARK) // Set theme to dark
                .setMapPanType(MapPanType.RESTRICT_TO_SELECTED_PROPERTY) // restrict map panning to property
                .setMapZoomRange(new MapstedMapRange(6.0f, 24.0f)) // restrict map zoom range

// ...

// Note: For runtime map parameter adjustments, use the following api
//Set the map theme to dark
MapstedMapApi.shared.setBaseMapStyle(style: .DARK)

//Set map panning to restricted which limits panning to a region around the property
MapstedMapApi.shared.setMapPanType(type: MapstedMapMeta.MapPanType.eRestrictToProperty)

Custom Map View

You can power your application using our position location technology, while using your own MapView. The Location Positioning Technology guide shows you how to access user location information. Mapsted uses Open Street Maps as the georefence for user locations, buildings, and other map elements. If your system uses a basemap such as Google maps or Apple maps, please contact us, so we can make sure that our user locations, buildings, and other map elements are appropriately georeferenced for your basemap.

Mapsted's prebuilt UI/UX includes intelligent search. This type of search finds matches based on names, and keywords or categories, and offers suggestions which can handle various typos or spelling errors.

Prebuilt UI/UX

Prebuilt UI/UX automatically uses the intelligent search functionality.

Search via Names/Auto Suggestions

As the user types, auto suggestions are also provided, as shown below.

Search via Categories/Keywords

Search is supported by category and keyword.

Search via Reverse Geocoding (Coming Soon)

Reverse geocoding is supported within properties and buildings. Using reverse geocoding, a MercatorZone object, like a ClickLocation from a ClickEvent, can be passed in and an Entity object will be returned. If the specified MercatorZone corresponds to a specific Entity, that entity will be returned, otherwise it will return null.

Customizable UI/UX

If Mapsted's prebuilt UI/UX is not used, the intelligent search functionality can also be programmatically accessed, as shown in the sample code below.

// Any class that inherits from ISearchable can be used
List<ISearchable> mySearchableList = new ArrayList<>();

// For example, populate ISearchable list with BuildingSearchEntities
SelectedLocation selectedLocation = mapApi.getSelectedLocation();
BuildingData buildingData = selectedLocation.getBuildingData();

if (buildingData != null) { // null if no building is selected
    BuildingSearchEntities buildingSearchEntities = getBuildingSearchEntities();
    if (buildingSearchEntities != null) {
        SearchEntityMapIterator buildingEntityIt = buildingSearchEntities.getSearchEntityMap().getIterator();
        while (buildingEntityIt.hasNext()) {
            SearchEntity searchEntity = buildingEntityIt.getValue();

            if (searchEntity == null || searchEntity.getName().trim().equals("")) {
                continue; // skip empty names


// Apply filter and sort based on input string query (e.g., query = "ga" would provide a good match for "Gap")
// filterAndSortSearchables method will filter and re-order mySearchableList
// When complete, UI can now be updated
coreApi.utilities().filterAndSortSearchables(query, mySearchableList);
let myPropertyId = 512 
// Get a list of searchables for the property
let searchables = CoreApi.PropertyManager.getSearchEntities(propertyId: myPropertyId)

//Get ordered list of indices into list of searchables based on intelligent search
let indices = CoreApi.UtilsManager.filterAndSortSearchables(searchables: searchables, propertyInfo: PropertyInfo(propertyId: myPropertyId), searchString: "foo")
let searchResults = [Searchable]()
for index in indices {

Location Positioning Technology

Our Location positioning technology allows for user position updates in location-enabled indoor venues and outdoor spaces. Mapsted’s technology relies on innovative, adaptive, data-fusion, and self-learning algorithms to deliver accurate, scalable indoor and outdoor positioning technology using any off-the-shelf smart-phone. The technology is forward-thinking in nature. It uses disturbances (e.g., magnetic, wireless) in the environment to learn and determine location. Basically, it approaches the problem by converting the interference, or noise, in the environment into useful information. The Figure below will give you a high-level overview of how this location positioning technology works.

User Positioning

A user's position is defined by a Position object. The Position object includes the user's IZone, which includes a propertyId, buildingId, and floorId, representing where the user is located. If any of these Ids show a value of -1, then it means they are not available. Some examples are detailed below.

Outdoor Positioning

The location positioning technology engine is seamless. It uses the same mechanism to return positions whether the user is indoors or outdoors. Based on the returned Position object, you can determine whether a user is outdoors, for example.

  • propertyId = -1 means the user is not on any premises,
  • propertyId > 0 and buildingId = -1 means the user is on the premises of the specified propertyId, but not inside any of its buildings.

Indoor Positioning

Similarly to outdoor positioning, when the user is indoors, this can be identified based on the returned Position object, as follows.

  • propertyId > 0, buildingId > 0, and floorId > 0 means that the user is on the propertyId within the buildingId, and on the floorId.

Programmatically Handling User Positions

There are two types of Position events that you can register as a listener for. The first type is a standard Position event. Position events are continuously updated by the positioning SDK, depending on the user's current activity and the type of data sources available. The second event type is PositionAnimation, which occurs when the user is in motion. PositionAnimation events dynamically update the user position at 30 frames per second, creating a smooth UI/UX experience when the user marker is in motion.

The Position event listener should be used for business logic processing based on a position update, and the PositionAnimation event listener should be used for displaying the user marker on a map UI to ensure the best user experience. The Position event listener is typically updated roughly every second, when the user is in motion. The PositionAnimation event listener is updated approximately 30 times per second when the user is in motion, to ensure smooth animation for enhanced UI/UX.

// Observe Position change events
// Updated roughly every 1 second
PositionChangeListener positionChangeListener = position -> {
    int propertyId = position.getPropertyId();
    int buildingId = position.getBuildingId();
    int floorId = position.getFloorId();
    double x = position.getX();
    double y = position.getY();
//when no longer needed, remove the listener

// Observe Position Animation change events
// Updated roughly 30 fps when user is in motion
PositionAnimationListener positionAnimationListener = position -> {
    int propertyId = position.getPropertyId();
    int buildingId = position.getBuildingId();
    int floorId = position.getFloorId();
    double x = position.getX();
    double y = position.getY();
//when no longer needed, remove the listener
// Observe Position change events
// Updated roughly every 1 second
//Your viewcontroller needs to register for position updates as follows:
CoreApi.LocationManager.addPositionChangeListener(listener: self)

//You can satisfy the protocol requirements by implementing the required method 
extension YourViewController: PositionChangeListener {

    // Updated roughly every 1 second
    public func onPositionChange(position: MNPosition) {
        let propertyId =
        let buildingId =
        let floorId =
        let x = position.loc.x
        let y = position.loc.y


/ Observe Position Animation change events
// Updated roughly 30 fps when user is in motion
//Your viewcontroller needs to register for position animation updates as follows:
CoreApi.LocationManager.addPositionAnimationListener(listener: self)

//You can satisfy the protocol requirements by implementing the required method 
extension YourViewController: PositionAnimationListener {
    func onPositionAnimation(position: MNPosition, animationBegins: Bool) {
        //process new position

User Bearing

The Bearing is a float which represents the direction that the user is currently facing. Bearing is specified in degrees clockwise from North. This listener is only called when the user is on the premises of a property.

BearingChangeListener bearingChangeListener = bearing -> {
    // bearing is specified in degrees clockwise from North
//when no longer needed, remove listener
//Your viewcontroller needs to register for bearing updates as follows:
CoreApi.LocationManager.addBearingChangeListener(listener: self)

//You can satisfy the protocol requirements by implementing the required method 
extension YourViewController : BearingChangeListener {
    func onBearingChanged(degrees: CGFloat) {
        // bearing is specified in degrees clockwise from North

Coordinate Systems

Mapsted uses the Universal Transverse Mercator (UTM, EPSG:3857) coordinate system to repesent the global (x,y) position in meters. Support is also provided for latitude and longitude (WGS84) in degrees, as shown below.

LatLng latLng_1 = new LatLng(someLatitude, someLongitude);
Mercator mercator_1 = MapCalc.toMercator(latLng); // From WGS84 to EPSG:3857

Mercator mercator_2 = new Mercator(someX, someY);
LatLng latLng_2 = MapCalc.toLatLng(mercator.getX(), mercator.getY());  // From EPSG:3857 to Lat, Lng
let pos1 = MNMercator(lat: someLatitude, lng: someLongitude)
print("X: \(pos1.x) Y: \(pos1.y)") // From WGS84 to EPSG:3857

let pos2 = MNMercator(x: someX, y: someY, z: someZ)
let latLng:CLLocationCoordinate2D = MNMercator.latLong(from: pos2)  // From EPSG:3857 to Lat, Lng
print("Lat: \(latLng.latitude) Long: \(latLng.longitude)") 


Wayfinding uses Mapsted's advanced routing technology to generate optimal routes to help users get around in selected properties and buildings. The wayfinding engine can handle property-wide multi-destination routing. Each RouteRequest consists of a start location, such as the user's current location or a point of interest, and a list of destination locations or points of interest. The RouteRequest also consists of several RouteOptions that determine which accessibility measures are necessary, the order the user would like to navigate to the destinations in, and allow for the use of optimized routes.

The RouteResponse consists of a list of Route objects, where each Route consists of a list of RouteSegment objects. Each RouteSegment corresponds to a portion of the route on a specific floor of a building, or property level, for example outdoors, and is linked via a TransitionType enum. A figure showing an illustrative example of a RouteResponse is shown below. In the illustrative example above, the user is navigating from a location on Level 2 of Building A to a location on Level 1 of Building B. As shown, the route from point A to point B involves four RouteSegments and includes three transitions - between floors, to outdoors, and to indoors.

Prebuilt UI/UX

When using the Prebuilt UI/UX, the wayfinding functionality is automatically in use.

Customizable UI/UX

There are a number of customizable routing parameters available, which can be set programmatically, as outlined below.

Programmatic Wayfinding

// Setup desired routing options
// OptimizedRoute will re-order destinations optimally, if set to false will navigate in order
// If Accessibility is true, it may override the behaviour of Stairs/Elevator/Escalator
RouteOptions routingOptions = new RouteOptions();
routingOptions.setFromCurrentLocation(false); // true if routing from user's position
//enable use of emergency exits 

RouteRequest routeRequest = new RouteRequest.Builder()
                    .setStartSearchable(myStartSearchable) // only if not from 'MyLocation'
                    .addDestination(myDestination_1) // add possibly multiple destinations

// Start Async route request
// Response will be passed to the RoutingRequestCallback
// UI should be updated accordingly
coreApi.routingManager().requestRouting(routingRequest, new RoutingRequestCallback() {
    public void onSuccess(RoutingResponse routingResponse) {
        //use routingResponse
        // If user desires to begin navigation, can be started by using:            
        Route route = routingResponse.getRoutes().get(0);
        RoutingStatusCallback routingStatusCallback = new RoutingStatusCallback() {
            public void onRoutingStatus(boolean isRoutingModeOn, RoutingResponse latestRouteResponse) {
                // Indicates a status change for the latest route response
                // For example, if isRoutingModeOn is false, UI can adjust accordingly

            public void onRouteSegmentReached(RouteSegment currentRouteSegment, List<RouteSegment> visitedRouteSegments, List<RouteSegment> upcomingRouteSegments) {
                //Called when a route segment has been reached. 

            public void onRouteInstruction(RouteNode routeNode) {
                // Called when a new instruction is available for the real-time navigating user

            public void onUserProgressAlongRoute(RouteUserProgress routeUserProgress) {
                //This function will be called as user continues progress along route

            public void onRouteRecalculation(RoutingResponse routingResponse) {
                // Called when the user deviates from the route provided
                // and a new route is automatically recalculated

            public void onDestinationReached(int destinationEntityId) {
                //reached destination of this route
        coreApi.routingManager().startNavigation(route, routingStatusCallback);

        // Navigation can be cancelled midway through (e.g., user UI interactions) by calling:

    public void onError(CppRouteResponse.ErrorType errorType, String error, List<String> alertIds) {
// To make a route request, you should first setup your desired routing options

    - optimizedRoute = false will navigate in order
    - optimizedRoute = true will re-order destinations optimally 
let optimizedRoute = true 

//Preferred transition options
let useStairs = false
let useEscalators = true
let useElevators = true

// There is also an accessibility option provided - it may override the behaviour of Stairs/Elevator/Escalator

let calculateFromCurrentLocation = true

let routeOptions = MNRouteOptions.init(useStairs, escalators: useEscalators, elevators: useElevators, current: calculateFromCurrentLocation, optimized: optimizedRoute)

//Build a route request
var routeRequest: MNRouteRequest?

if calculateFromCurrentLocation { //startEntity is passed as nil
    routeRequest = MNRouteRequest.init(routeOptions: routeOptions, destinations: destinations, startEntity: nil)
else { //destinationsMinusStart is an array of destinations excluding the start 
    routeRequest = MNRouteRequest.init(routeOptions: routeOptions, destinations: destinationsMinusStart, startEntity: startEntity)

if let routeRequest = routeRequest {
    CoreApi.RoutingManager.requestRoute(request: routeRequest, routingRequestCallback: self)

var routes : [MNRoute] = []

// To receive the callback after you make the route request, you will need to have implemented the RoutingRequestCallback protocol 
extension YourViewController: RoutingRequestCallback {
    func onSuccess(routeResponse: MNRouteResponse)
        //You can proceed to request using one of the routes returned with routeResponse
        //Check for error
        if routeResponse.errorType == .noError {
            routes = routeResponse.routes

    func onError(errorCode: Int, errorMessage: String, alertIds: [String]) {


//Make sure routes are not empty, and choose a route from the list
let route = routes.first 

//Your can now make a route request with the route. In order to receive route status updates, you also pass a delegate to protocol RouteStatusCallback. 
CoreApi.RoutingManager.startNavigation(route: route, routingStatusCallback: self)

//Your delegate will need to be implement the methods of *RoutingStatusCallback*. 
extension YourViewController: RoutingStatusCallback {

    func onRoutingStatus(isRoutingModeOn: Bool, latestRouteResponse: MNRouteResponse) {
        // Indicates a status change for the latest route response
        // For example, if isRoutingModeOn is false, UI can adjust accordingly

    func onRouteInstructionReceived(routeNode: MNRouteNode) {
        // Called when a new instruction is available for the real-time navigating user

    func onRouteSegmentReached(currentRouteSegment: MNRouteSegment,
                           visitedRouteSegments: [MNRouteSegment],
                           upcomingRouteSegments: [MNRouteSegment]) {
        //Called when a route segment has been reached. 

    func onUserProgressAlongRoute(routeUserProgress: MNRouteUserProgress) {
        //This function will be called as user continues progress along route

    func onRouteRecalculation(newRouteResponse: MNRouteResponse) {
        // Called when the user deviates from the route provided
        // and a new route is automatically recalculated

    func onDestinationReached(destinationEntityId: NSInteger) {
        // Called when the user reaches the destination (specified by the destinationEntityId)

Understanding Route Responses

A RouteResponse contains a boolean which indicates whether or not the RouteRequest was successfully processed. An error may occur for several reasons, such as if an invalid start or data was provided, or a route could not be found.

A requested route may involve single or multiple destinations. A RouteResponse consists of a RouteVector (a list of Route objects), where each Route represents a trip to a specific destination. Each Route consists of a RouteSegmentVector (a list of RouteSegment objects), where each RouteSegment represents a separate segment of the route, such as a different floor inside a building, or an indoor-outdoor segment. Each RouteSegment consists of a RouteNodeVector (a list of RouteNode objects), where each RouteNode represents a specific location, and possibly, a corresponding instruction.

// Note: Do not forget to handle null cases
//when you receive a routingResponse from RouteingRequestCallback
void onRoutingResponseReceived(RoutingResponse routingResponse) {
    boolean success = routingResponse.isSuccessful();
    if(!success) {
        String error = routingResponse.getError();
        ErrorType errorType = routingResponse.getErrorType();
        //check or log the error or errorType

        If multiple destinations are requested, there may be multiple routes.
        Each route represents the trip to a specific destination.
    List<Route> routes = routingResponse.getRoutes();

    // The route index that is currently selected (e.g., in route preview mode)
    Route currentRoute =  mapApi.getCurRoute(); 

    // All route segments for the specifc route (e.g., multiple floors or indoor-outdoor)
    List<RouteSegment> routeSegments = route.getSegments();

    // Loop through all route segments
    for (RouteSegment routeSegment : routeSegments) {
        // Outside property, within property, or within building
        RouteSegmentType segmentType = routeSegment.getSegmentType();

        // get path mercators for UI plotting
        MercatorVector smoothedRoute = routeSegment.getPath();

        // Can iterate over non-smoothed (discretized) route nodes
        for (RouteNode routeNode : routeSegment.getRouteNodes()) {
            // key points will have instructions
            boolean isKeyPoint = routeNode.isKeyPoint();

            if(isKeyPoint) {
                Instruction instruction = routeNode.getInstruction(appContext);

                // instruction will take into account localization, if supplied
                // default is english
                String instructionText = instruction.text;

                // You can customize your own behaviour by using the instruction type
                // e.g., TURN_LEFT, TURN_RIGHT, ENTER_BUILDING, ...
                InstructionType instructionType = instruction.instructionType;

                //drawables for instruction type
                int drawableId = instruction.drawableResId;

// ...
func routeResponseReceived(response: MNRouteResponse) {
    guard response.isSuccessful else {
            // routing failed

    If multiple destinations are requested, there may be multiple routes.
    Each route represents the trip to a specific destination.
    let routes = response.routes
    //Index into routes to access a particular route
    let route = routes[0];

    //You can access individual segments of a route as follows
    let routeSegment = route.segments[1] //size checks are skipped for brevity
    let segmentType = routeSegment.segmentType // Outside property, within property, or within building

    // Smoothed route for UI plotting
    let smoothedRoute = routeSegment.smoothedRouteNodes

    //Get instructions
    for routeSeg in route.segments {
        for routeNode in routeSeg.routeNodes {
            if routeNode.isKeyPoint {
                // instruction will take into account localization, if supplied
                // default is english
                let instruction = routeNode.instruction

                // You can customize your own behaviour by using the instruction type
                // e.g., TURN_LEFT, TURN_RIGHT, ENTER_BUILDING, ...
                let instructionType = routeNode.instructionType

                // EntityId of the instruction's landmark (-1 if doesn't exist)
                let landmarkEntityId = routeNode.landmarkEntityId

Programmatically Add Points-of-Interest

Mapsted Maps supports programmatically adding and wayfinding to your own Points of Interest (POI). You can create a Tag object which holds the necessary location data. Your custom Tag or POI can be added to the user's itinerary and/or be navigated to. You also have the ability to add multiple tags at the same time, or to delete all tags from a property.

// Create MercatorZone object
MercatorZone mercatorZone = new MercatorZone(propertyId, buildingId, floorId, xCoordinate, yCoordinate);

// Create the Tag object which holds the specific location
Tag tag = new Tag(tagName, propertyName, creationTimeStamp, propertyId, buildingId, mercatorZone);

// When using prebuilt UI/UX, 
// This will place a pin on the POI location and provide options for adding to itinerary or navigation

// ...

// When not using prebuild UI/UX, 
// You can also programmatically add your tag to a RouteRequestBuilder (e.g., destination)
RouteRequest request = new RouteRequestBuilder()
                .setStartSearchable(myStartSearchable) // only if not from 'MyLocation'
    let zone = MNZone(propertyId: thePropertyId, buildingId: theBuildingId, floorId: theFloorId)
    let mercator = MNMercator(x: xCoordinate, y: yCoordinate, z: zCoordinate)

    // When using prebuilt UI/UX, 
    // This will place a pin on the POI location and provide options for adding to itinerary or navigation
    if let mapsVC = MapstedMapUiViewController.shared as? MapstedMapUiViewController {
        let newTagName = "My Tag";
        mapsVC.addTag(tagName: newTagName, tagPos: MNPosition(zone: zone, loc: mercator))

        //Use the addTags method to add more than one tag at once. 

        //Initialize and populate your tags
        let multipleTags: [MNTag] = ... 

        //Add the batch at once.
        mapsVC.addTags(tags: multipleTags)

        Use deleteAllTags to delete all tags added to a property 
        let pId = 1234
        mapsVC.deleteAllTags(propertyId: pId)


Accessibility Routing

Mapsted SDK supports accessibility routing. When enabled, the provided routes avoid stairs, steps, and escalators, and use ramps or elevators to help the user navigate to their destination instead. For even finer control, the SDK also allows for users to specify specific transitions which they would like to take, for example if the user would prefer to take the escalator or elevator over stairs.

RouteOptions routeOptions = new RouteOptions();

// If full accessibility is desired, it can be enabled
// Note that this will override all other customizations for stairs, escalators, elevators

// Alternatively, if for example, the user perfers escalators/elevators over stairs
// The following can be used

// Create RouteRequest and process. See Wayfinding section for more information.
//Create an instance of MNRouteOptions. You can configure the first three parameters based on whether you prefer 
//stairs/escalators/elevators. The MNRouteOptions object is used to create a route request.

let useStairs = false
let useEscalators = true
let useElevators = true

let routeOptions = MNRouteOptions.init(useStairs, escalators: useEscalators, elevators: useElevators, current: true, optimized: true)

//There is an accessibility mode that overrides previous settings.


The Mapsted SDK offers multiple language support, including support for both left-to-right and right-to-left languages. Support is automatically provided for most languages, but appropriate translation and font files would be necessary to support non-mainstream languages. This is handled automatically when using the Map UI SDK or prebuilt UI/UX.

Localization Example

The figures below show the same map view in English and Arabic, respectively. As you can see, the map UI automatically handles the left-to-right and right-to-left localizations.

Programmatically Controlling Localization

The Mapsted mobile SDK will automatically capture the localization based on the device's settings, if the selected language is supported. If the language is not available, it will automatically default back to English. The localization can also be modified programmatically, as shown below.

// Sample usage for listening for language events, notifying the Mapsted Map
// Note that when using the Mapsted Map-Ui or prebuilt UI/UX this is handled automatically

 HandlerThread languageHandler = new HandlerThread("LanguageSupport");

    new Handler(Looper.getMainLooper()).post(() -> {
        LocaleManager.getInstance(mActivity.getApplicationContext()).getLanguageMutableLiveData().observe(mActivity, (languageLiveData) -> {
            new Handler(languageHandler.getLooper()).post(() -> {
                mapApi.setLanguageCode(languageLiveData, (language, propertyId, isAvailable) -> {
                    // TODO: Update the rest of the UI

//iOS automatically relaunches the app in use when the the user changes their 
//preferred language from settings. To change the user default language programmatically, 
//call the set method of the system provided UserDefault class by means of its shared standard object.

UserDefaults.standard.set(["en"], forKey: "AppleLanguages")

//You will need to provide the two letter language code to the method. 
//Standard language code values include "en" for English, "fr" for French, "ar" for Arabic, etc.

Mapsted SDK supports parsing deep links for various sdk features.

Deep links must be in following format


  • your_host : your host/domain name
  • your_app_name : an identifier for your app
  • feature_name : feature identifier which may be feeds or map
  • optional_sub_feature : optional sub feature are available if feature_name is map. They include select (for selecting an entity) or routing (routing feature)
  • related_parameters : additional parameters like property, building, floor or entity for their respective ids. It also includes destinations which is list of buildingId:entityId items.
Description Deep link
Campaign feeds for all properties
Campaign feeds for a particular property
Select entity on map
Open route preview,111:444

Note: destinations are in buildingId:entityId format.

<activity android:name=".activities.MainActivity"
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:host="" />
        <data android:pathPrefix="/deeplink/appname"/>
        <data android:scheme="https" />
        <data android:scheme="http" />
//in your activity
protected void onNewIntent(Intent intent) {
    Uri intent.getData(); 
    boolean handled = CoreApi().processDeeplink(uri, deeplinkData -> {
        Log.d("processDeeplink: deeplinkData=" + deeplinkData);
        String feature = deeplinkData.getFeaturePath();
            case DeeplinkProcessor.FEATURE_FEEDS: 
                //feeds page was requested. use deeplinkData to get propertyId.
            case DeeplinkProcessor.FEATURE_MAP_ROUTING:
                //routing feature was requested. use deeplinkData to get propertyId and destinations.
            case DeeplinkProcessor.FEATURE_MAP_SELECT:
                //an entity was requested to be selected. use deeplinkData to get propertyId, buildingId, floorId, entityId
                //could not determine the feature requested. 

    if(!handled) {
        //if it was not handled, deeplink is not mapsted related. 
guard let url = URL(string: ",111:444") else {
    //Not valid URL
CoreApi.processDeepLink(uri: url, completion: { (handled, deepLinkFeature, deepLinkData)  in

    //Check whether it was handled
    guard handled else {
        //Not Mapsted deeplink. Handle via your own methods

    //Was handled as a Mapsted deeplink, will be processed accordingly

    switch (deepLinkFeature, deepLinkData) {

    case (.MapSelect, .MapSelect(propertyId: let p, buildingId: let b, entityId: let e)):
        //handle map selection
        //print("Select \(p)-\(b)-\(e)")

    case (.MapRouting, .MapRouting(searchEntities: let entities)):
        //handle destinations
        //print("Map Routing to \(entities.count) destinations")

    /* Handle other cases */ 

        //handle other cases



Location Marketing

The Location Marketing module allows you to incorporate precision location-based Triggers, Events, or Feeds. Campaigns can be easily created, configured, managed, and published using the Mapsted Notify software. When using the Location Marketing module, these campaigns will be automatically synced with the Mapsted Maps - Mobile SDK.

Campaigns created via the Mapsted Notify Software can displayed as Feed items. Campaigns can also be associated with various Triggers. A Trigger is a combination of Location Criteria (e.g., geofence), Demographic Criteria (e.g., device type), and/or Behaviour Criteria (e.g., keyword searches).

When enabled, the Mapsted Maps - Mobile SDK will provide a callback to identify that a particular campaign and trigger has occurred, referred to as a Marketing Event. When using the Prebuilt UI/UX, the Marketing Event may be displayed as a Notification, a Dialog, or in a Feed Bar (a rectangular pop-up at the top of the screen). On the other hand, for Customizable UI/UX, the Marketing Event may be handled however you see fit.

Campaign Feeds

The Location Marketing module provides an API to retrieve the currently active campaigns. The campaign feeds may be retrieved for a single or multiple properties.


LocMarketing locMarketing = ...

// ------------------------
// Option 1: Customized UI/UX 
// ------------------------
// get Feeds and show in your own UI
LocMarketing.Callback callback = new LocationMarketing.Callback() {
    public void onFeedsResult(List<Feed> feedsList) {
        Logger.d("onFeedsResult: %d", feedsList.size());
        //show feeds to UI

//you can retrieve propertyId from mapApi or coreApi. Alternatively, if you need
//feeds for multiple properties, you can pass in an arraylist of propertyIds as well.
locMarketing.getFeedsAsync(propertyId, callback);

// ------------------------
// Option 2: Prebuilt UI/UX
// ------------------------
// use a FeedFragment. A FeedFragment shows the feed in a list. You can provide the campaignId argument to
// pre-scroll to that campaign. Campaign ids can be retrieved from LocMarketingListener
FeedFragment feedFragment = FeedFragment.newInstance(propertyId, null);
//use fragment manager to show the above feedFragment. The parent activity should implement FeedFragmentListener

//Parent activity
public MyActivity extends AppCompatActivity implements LocationMarketApiProvider, FeedFragmentListener{
    public LocMarketing getLocationMarketingSdk() {
        Logger.d("getLocationMarketingSdk: ");
        if (locMarketing == null) {
            locMarketing = setupLocMarketingSdk(coreApi);
        return locMarketing;

    void onFeedSelected(Feed feedItem){}

    void onFeedShareClicked(Feed feedItem){}

    void onFeedViewed(Feed feed){}

    void onFeedFragmentViewCreated(int propertyId){}
//Import the Location Marketing SDK first
import LocationMarketing 

//Instantiate Location Marketing API
let locMarketingManager = LocMarketingApi.shared

//If you have a variable for identifying the property you want to fetch feeds for 
let myPropertyId = 123 

//Define the callback method to handle feeds fetched for that property of your interest
func myCallbackMethod(fetchedCampaigns: [Campaign]) {

    //extract the feeds from campaigns
    for campaign in fetchedCampaign { 
        //do something

//Fetch feeds and pass them to the callback method which will know how to process them.
locMarketingManager.fetchCampaignsForProperty(propertyId: myPropertyId, completion: myCallbackMethod);

Campaign Triggers/Events

Marketing Events occur whenever the necessary Trigger Criteria have occurred. The Marketing Event can be handled in a number of different ways. For example, the campaign can be displayed using a Dialog or Feeds Bar (a rectangular pop-up at the top of the screen) when the app is foreground or a Notification when app is in background. Campaigns and Triggers can be configured using the Mapsted Notify software.

//activity onCreate
//When the user interacts with the notification or dialog, LocMarketingListener will
//receive callback to open the map or open the website.
LocMarketing.LocMarketingListener locMarketingListener = new LocMarketing.LocMarketingListener() {
        public void navigateToMap(String campaignId, List<HomeEntity> homeEntityList) {
            //use the mapApi to show the map

        public void openWebsite(String campaignId, String websiteURL) {
            //open browser or webview with the websiteUrl

        public void showInFeedsBar(Campaign campaign) {
            //add this campaign to FeedsBarFragment

//initialize the sdk. Note, to initialize pass in a initialized coreApi instance. you can share this instance of LocMarketing through out your app.
LocMarketing locMarketing = new LocMarketing(context, coreApi, locMarketingListener);

//activity onNewIntent
public void onNewIntent(Intent intent) {
    if (LocaMarketing.canHandle(intent)){
    } else {
//Import the Location Marketing SDK first
import LocationMarketing 

//Assuming you have already initialized, you can use its shared instance
let locMarketingManager = LocMarketingApi.shared

//To respond to notifications from the Mapsted Location Marketing API, set your delegate as listener 
locMarketingManager.setListener(listener: self)

//For this to work, you delegate needs to have implemented the LocMarketingListener protocol
extension MyViewController : LocMarketingListener {

    //Mandatory method
    //Implementation is required for this method  
    public func navigateToMap(homeEntities: [EntityInfo]) {
        //Choose from home entities and show on the map

    //Optional method
    //Provide an alternate implementation for the dismiss() method 
    //to perform additional actions after the notification popup is dismissed
    func dismiss(action: Action?) {}

    //Optional method
    //override the openWebsite() method or return false to modify the default behavior 
    //By default, this method opens the websiteURL in an embedded webview. 
    func openWebsite(websiteURL: String) -> Bool { return false }



The Alerts module allows you to incorporate precision location-based Alerts. These can be easily created, configured, managed, and published using the Mapsted Notify software. When using the Alerts module, these alerts will be automatically synced with the Mapsted Maps - Mobile SDK.

Scheduled Alerts

Scheduled alerts are one time or recurring alerts that are pre scheduled. Similar to location marketing campaigns, scheduled alerts also has triggers that causes the alerts to activate when the location criteria are met. Scheduled alerts can be created and set by signing into the Mapsted Hub Web Software.

//After the coreApi is initialized, you can initialize AlertManager
AlertsManager alertsManager = AlertsManagerImpl.getInstance(getApplicationContext(), coreApi);

//setup callback for user location. When an scheduled alert is triggered at the location. 
alertsManager.setUserAtLocationCallback((scheduledAlert, autoDismiss) -> {
    ScheduledAlertNotification alertNotification = new ScheduledAlertNotification(scheduledAlert, autoDismiss);
    //If you want to show the scheduled alert in the InAppNotification, you can use the InAppNotificationApi
    if (inAppNotificationApi == null) {
        inAppNotificationApi = new MapstedInAppNotificationsApi(getSupportFragmentManager(), fragmentContainerView, new MapstedInAppNotificationsApi.Listener(){
            public void onInAppNotificationClick(InAppNoticiation inAppNotification) {
                if(inAppNotification instanceof ScheduledAlertNotification){
                    //in app notification was clicked

            public void onViewed(InAppNotification inAppNotification) {
                if(inAppNotification instanceof ScheduledAlertNotification){
                    //in app notification shown
    inAppNotificationApi.showInInAppNotificationBar(alertNotification, true);

//To get list of ongoing scheduled alerts
int propertyId = 504;
List<ScheduledAlert> ongoingAlertsForEntity = alertsManager.getOngoingAlertsForProperty(propertyId);
showList(ongoingAlertsForEntity); //showList being your own implementation

//to get changes in ongoing scheduled alerts, register a listener
AlertsOnChangeListener alertsOnChangeListener = new AlertsOnChangeListener(propertyId) {
    public void onChangeInOngoingAlerts(Set<ScheduledAlert> before, Set<ScheduledAlert> after) {
        //the two sets provide the scheduled alerts before and after/now.
//unregister when not needed
let myPropertyId = 504;

//to get list of ongoing scheduled alerts for a property
let alertList = AlertsApi.shared.getOngoingAlertsForProperty(propertyId)
for scheduled in alertList {
    //do something with each

Emergency Alerts

To receive emergency alerts, first send your firebase token. Emergency alerts can be created and sent by signing into the Mapsted Hub Web software. Emergency alerts are sent via Firebase messaging service.

alertsManager = AlertsManagerImpl.getInstance(getApplicationContext(), coreApi);
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
    if (!task.isSuccessful()) {
    String token = task.getResult();
    alertsManager.sendFirebaseTokenForEmergencyAlerts(getApplicationContext(), token);

let identifier = "com.myCompany.myApp" //Usually the Bundle identifier from your app
AlertsApi.shared.sendFirebaseToken(token: fcmToken, appIdentifier: identifier);


Then, configure your app to receive the firebase messages

//create a service by extending EmergencyAlertsFirebaseMessagingService.
//Observe {@link InAppNotificationLiveDatas.DeactivatedLiveData} and 
//{@link InAppNotificationLiveDatas.ActivatedLiveData} to get 
//activated or deactivated emergency alerts when the app is in foreground. 
//When the app is in background, android notification is created. When the 
//notification is opened, the app will open with a deeplink in following format.
//You can use the AlertManager to get details about the alert with id.
public class MyEmergencyFirebaseMessagingService extends EmergencyAlertsFirebaseMessagingService {
    public void onNewToken(@NonNull String token) {
        alertsManager.sendFirebaseTokenForEmergencyAlerts(getApplicationContext(), token);
<-- Register your service in the AndroidManifest -->
<application ..>
            <action android:name="" />
In a typical scenario, you integrate Firebase messaging AppDelegate via an extension 
to the Firebase `MessagingDelegate` protocol

extension AppDelegate : MessagingDelegate {

    //implement this method
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {

        /** Other code .... */

        let appIdentifier = "com.myCompany.myApp" //Usually the Bundle identifier from your app
        AlertsApi.shared.sendFirebaseToken(token: fcmToken, appIdentifier: identifier);

        /** Other code .... */



 If your application was launched or resumed because of the remote notification, you will get a callback 
 in the AppDelegate's `application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method
func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    //Ask the Alerts API to process the notification if applicable
    AlertsApi.shared.processNotification(userInfo: userInfo, completion: { (handled, error)  in
        if handled {
            //Alert Found
            print("Alert Handled")
        if let error = error {


 The `application(_:didReceiveNotificationResponse:withCompletionHandler:) method will be called by 
 your app on the Application Delegate when the user responded to the notification
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    /** ... Some code **/

  //If you are using Alerts SDK, ask it to process any alerts
  AlertsApi.shared.processNotification(response: response, window: self.window, completion:{ handled, error in
        if handled {

        if let error = error {


    //If you are using LocMarketing SDK, ask it to process any campaign events
    LocMarketingApi.shared.processNotification(response: response, window: self.window, completion:{ success, error in
        if handled {

        if let error = error {



    /** ... More code **/

//to get list of emergency alerts for a property
let myPropertyId = 504

AlertsApi.shared.fetchEmergencyAlerts(propertyId: myPropertyId, callback: { alertList in 
    for emergency in alertList {
        // do something with each

Prebuilt UI/UX

We offer a wide variety of Prebuilt app templates and UI components which have been designed and optimized for use with the complete Mapsted Maps Mobile framework.

Depending on your desired level of customization, you can choose from several Prebuilt UI/UX options. Our Prebuilt app templates provide a fully immersive mobile application that includes basic customizations such as colour changes. For more advanced customization, you can use our Prebuilt UI components to build your own application. These components include a suite of Prebuilt UI views, such as wayfinding UI/UX, itinerary management, and category lists.

Prebuilt App Templates

Our Prebuilt app templates allow you to experience the look and feel of a full mobile application within minutes, while providing basic customization options to give the app your own look and feel. See below for examples of a few app template UI screens.

To learn how to enable a Prebuilt app template, see the sample code below.

// Extending Mapsted App Template enabled Mapsted's Prebuilt app templte
// Note that this could also be achieved using an Intent to launch
// MapstedAppTemplateMainActivity
public class MainActivity extends MapstedAppTemplateMainActivity {

    protected void onCreate(Bundle savedInstanceState) {

        // Anything you want...

// Go to the AppDelegate.swift file in the main bundle of your application.
// Inside the application(_:didFinishLaunchingWithOptions:) method, add the relevant lines as per the example below

internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 

    //Compute copyright text OR use from localizable strings
    let copyRightText = NSLocalizedString("CopyrightText", comment: "Main App")

    //Set the name of the animation gif image, without the extension
    let startupAnimationGif = "Mall_mApp_GIF_Slower";

    //Instantiate the main screen
    let mainScreen = LaunchViewController.splashScreen(copyrightText: copyRightText, animationGif: startupAnimationGif);

    //Set the launch screen as the root view controller
    self.window?.rootViewController = mainScreen

    //Show the window and make it the key window

    return true

Prebuilt UI Components

The Prebuilt app templates outlined in the previous section use a variety of Prebuilt UI components. Our UI components are specific UI/UX views which are designed and optimized to interact with the Mapsted Maps Mobile framework. This allows you to custom pick and choose which aspects of our Prebuilt UI/UX you want to use in your application.

The Prebuilt UI components are an excellent choice when you already have your own application and want to easily add some additional functionality based on the Mapsted Maps Mobile framework. For example, one common usage is to trigger a Mapsted Map view and use various Prebuilt UI components, like wayfinding UI/UX or itinerary management, when a maps button is clicked within your application. In other scenarios, various Prebuilt UI components can be reused in other areas of your application, for example category lists.

Customizable UI/UX

The customizable UI/UX allows you to use various prebuilt UI/UX components, while also allowing you the option of adding your own customizable UI. As an example, you can use your own UI views on the Mapsted Map. This section explains how you can create and display custom views on the map. The two examples below show how custom UI elements, such as the blue "Explore More" button and the horizontal floating category list, were added to the Mapsted Map component.

Programmatically Add, Update, and Remove Custom Views

Any view can be programmatically added, modified, and removd from the Mapsted Map. Each view that is passed in should be given a custom view tag (a string to uniquely identify the view), which can be used for retrieving/modify, and/or removing the view. The sample code below shows how a view can be added, modified, and removed.

// Note that if you want to add customizable UI views you need to have initialized your SDK as follows:

// the mapContainerView is the container for the MapView
// the uiContainerView is the container for your custom views that you wish to insert into the MapView
FrameLayout mapContainerView = findViewById(; 
FrameLayout uiContainerView = findViewById(;

// When you initialize your SDK, you must provide both uiContainerView and mapContainerView
mapUiApi.initializeMapstedSDK(activity, uiContainerView, mapViewContainer, new MapstedInitCallback() {
    void onCoreInitialized(){
        //Internally core api has initialized

    void onMapInitialized(){
        //Internally map api has initialized

    void onSuccess(){
        //MapUiApi has initialized successfully

    void onFailure(SdkError sdkError){
        //MapUiApi initialization failed

    void onMessage(MessageType messageType, String message){
        //sdk message during sdk initialization

// ...

// Create Custom View (e.g., contains a button named with id custom_button)
View inflate = LayoutInflater.from(this).inflate(R.layout.custom_layout, null, false);
Button customButton = inflate.findViewById(;

// For example, set click listener
customButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        // Handle click event

// Display custom View on the map
mapUiApi.addViewToMap("my_custom_tag", myView);

// Get custom View on the map
View myView = mapUiApi.getViewOnMap("my_custom_tag");

// Can now modify view, as desired (e.g., change visibility)
// ...

// Remove custom View from the map
//To access UI components, you need to add the following import
import appuikit

//To facilitate adding UI components to your app, a ContainerViewController class is provided 
//which allows easy swapping in/out of viewcontrollers. To use this, first drag and drop a container 
//view to your viewcontroller and set the embedded viewcontroller class in IB to ContainerViewController. 
//Assuming you have named the embedded segue "containerSegue", you can obtain a reference to the 
//ContainerViewController as follows.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "containerSegue" {
        if let containerVC = segue.destination as? ContainerViewController {
            self.containerVC = containerVC

//Now to add a viewcontroller, simply call addController. Parameter isNew should be 
//set to true in case you want to maintain a stack of the viewcontrollers in memory 
//for fast swapping. If set to false, only one viewcontroller will be retained.

containerVC.addController(controller: categoryListVC, yOffset: 0, isNew: false)

//To display a floating category list, access the CategoryCarouselViewController. 
//Pass categoryUIDs which is list of category uids to display. The alignment can 
//be horizontal/vertical. Your viewcontroller needs to conform to CategorySelectionDelegate
//to get notified when user selects a category.

if let categoryListVC = CategoryCarouselViewController.instantiateViewController(propertyInfo: propertyInfo, categoryUIDs: categoryUIDs, alignmentIsVertical: false, delegate: self) as? CategoryCarouselViewController {
    categoryListVC.setCategoryTextColor(color: .white)
    containerVC.addController(controller: categoryListVC, yOffset: 0, isNew: false)

extension YourViewController : CategorySelectionDelegate {
    func selectedCategory(category: MNCategory) {
        //handle the selection...

Location Sharing (Coming Soon)

The Mapsted Maps Mobile framework offers several location-sharing options which use the power of our location positioning technology to help users interact with friends or colleagues.

Using location tags, users can easily share a specific location and/or message to one another for easy coordination. For example, a user could tag the food court on level 3 of a shopping centre, and share the location tag with several friends, along with a message that reads, "Meet me at 1 pm for lunch."

Using location sharing, users can also display their locations on each other's maps. This can be especially useful for couples or family members that wish to visit different places in a large venue, while still being aware of each other's location.

Location Tags

When location tagging is enabled in your application, users can use a long press gesture on the map to set a tag. This tag can be labelled, saved, navigated to, or shared with another user. Users also have the option to attach a message to the tag. Once the location tag is shared and received by another user, they can easily view and navigate to it. This provides a simple and easy-to-use approach for sharing information about a specific location with a friend or colleague.

To learn how to enable or disable a location tag, see the sample code below.

// For example, to enable tag UI when initializing
                .setEnableTagUI(true) // enable tag UI

// Note that this can also be adjusted in runtime, as shown below 

// Enable location tagging
mapUiApi.setEnableTagUI(true); // for the UI views
mapApi.setPlacePinOnSelectedLocation(true); // for pin on map

// Disable location tagging
mapUiApi.setEnableTagUI(false); // for the UI views
mapApi.setPlacePinOnSelectedLocation(false); // for pin on map
//Enable location tagging
MapstedMapApi.shared.allowTagsOnMap(enable: true)

//Disable location tagging
MapstedMapApi.shared.allowTagsOnMap(enable: false)

Programmatic Location Sharing

The location sharing framework was designed for maximum flexibility. You can use the Prebuilt option or choose to customize based on your specific needs. The location sharing framework requires a string key identifier to represent different users. It is important that the application is able to interpret this string identifier to provide meaningful UI to the user to connect and share locations with friends, colleagues, or family.

For example, our Prebuilt location sharing UI/UX offers location sharing based on the user's contact list and uses their phone number as the string key identifier. This allows for our UI to provide the user with a recognizable name or description of the friends or colleagues they can connect to, based on how they labelled them in their own contact list. Depending on your application, a phone number may not always be the best choice of string identifier. If your application maintains its own user ID system, which links to a user profile or friend list, then that option may work better for you as a string identifier. In that case, the customizable location sharing option will still allow you to take advantage of our location sharing functionality.

Prebuilt Location Sharing

Our Prebuilt location sharing UI/UX has been designed to use each user's phone number as the string key identifier. The Prebuilt UI/UX includes views for viewing the contact list, selecting which users you want to share your location with, and managing the location sharing. Once enabled, the map will automatically display the locations of other users and will also provide views for easily locating them on the map.

Customizable Location Sharing

The customizable location sharing option requires that you manage your own string key identifiers. These could still be the user's phone number, or, you can use any type of identifier that works for you. The location sharing map UI is still included in this case, but as the developer, you will be required to specify the properties for each user that will be displayed on the map, for example, string key identifier, name, or image.