## version-6.0
### Additional Guides
# Additional Guides
>
Additional guides for Mappedin SDK for iOS v6 are coming soon.
### API Reference
# API Reference
## Latest Version
Mappedin SDK for iOS v6.1.0-alpha.1
## Previous Versions
v6.0.0-alpha.0
### Building & Floor Selection
# Building & Floor Selection
>
## Floor Selection
When mapping a building with multiple floors, each floor has its own unique map. These are represented by the Floor class, which are accessed using MapData.getByType()/). The currently displayed Floor can be accessed using MapView.currentFloor/).
:::tip
A complete example demonstrating Building and Floor Selection can be found in the Mappedin iOS Github repo: BuildingFloorSelectionDemoViewController.kt
:::
### Changing Floors
When initialized, MapView displays the Floor with the elevation that's closest to 0. This can be overridden by setting Show3DMapOptions.initialFloor to a Floor.id and passing it to MapView.show3dMap()/).
```swift
// Setting the initial floor to Floor.id 'm_123456789'.
self.mapView.show3dMap(options: Show3DMapOptions(initialFloor: "m_123456789")) { r2 in
if case .success = r2 {
// Successfully initialized the map view.
} else if case .failure(let error) = r2 {
// Failed to initialize the map view.
}
}
```
The Floor displayed in a MapView can also be changed during runtime by calling MapView.setFloor()/) and passing in a Floor.id.
```swift
// Set the floor to Floor.id 'm_987654321'.
mapView.setFloor(floorId: "m_987654321") { result in
switch result {
case .success:
print("Floor changed successfully")
case .failure(let error):
print("Error: \(error)")
}
}
```
### Listening for Floor Changes
Mappedin SDK for iOS provides the ability to listen for floor changes on a MapView. The code below listens for the `floor-change` event and logs the new floor and building name to the console.
```swift
mapView.on(Events.floorChange) { [weak self] data in
guard let self = self else { return }
if let payload = data as? FloorChangePayload {
print("Floor changed to: \(payload.floor.name) in building: \(payload.floor.floorStack?.name ?? "unknown")")
}
}
```
## Building Selection
Mappedin Maps can contain one to many buildings with each building having one to many floors. Each building has its floors organized into a FloorStack object. When multiple buildings are present, there are multiple FloorStack objects, one for each building. A FloorStack contains one to many Floor objects, each representing the map of each level of the building.
The FloorStack object is accessed by using MapData.getByType()/).
```swift
// Get all floor stacks
mapView.mapData.getByType(.floorStack) { [weak self] (result: Result<[FloorStack], Error>) in
guard let self = self else { return }
if case .success(let stacks) = result {
self.floorStacks = stacks.sorted { $0.name.localizedCompare($1.name) == .orderedAscending }
// Get all floors in the floor stack
self.mapView.mapData.getByType(.floor) { [weak self] (floorsResult: Result<[Floor], Error>) in
guard let self = self else { return }
if case .success(let floors) = floorsResult {
print("Floors: \(floors)")
}
}
}
}
```
:::tip
A complete example demonstrating Building and Floor Selection can be found in the Mappedin iOS Github repo: BuildingFloorSelectionDemoViewController.kt
:::
### Enterprise Data
# Enterprise Data
>
:::note
> The Enterprise Data classes described in this guide are populated in Mappedin CMS, which requires a Solutions Tier subscription.
:::
## Enterprise Locations
An EnterpriseLocation contains metadata about a location, such as its name, description, logo, phone number, social medial links, hours of operation and more. They can be accessed using the MapData.getByType()/) method as shown below.
```swift
mapView.mapData.getByType(MapDataType.enterpriseLocation) { [weak self] (result: Result<[EnterpriseLocation], Error>) in
switch result {
case .success(let enterpriseLocations):
print("getByType success: \(enterpriseLocations)")
case .failure(let e):
print("getByType error: \(e)")
}
}
```
## Enterprise Categories
An EnterpriseCategory groups one or more EnterpriseLocation. These allow similar locations to be sorted in a logical fashion. For example a mall may group locations into Food Court, Footwear and Women's Fashion. They can be accessed using the MapData.getByType()/) method as shown below.
```swift
mapView.mapData.getByType(MapDataType.enterpriseCategory) { [weak self] (result: Result<[EnterpriseCategory], Error>) in
switch result {
case .success(let enterpriseCategories):
print("getByType success: \(enterpriseCategories)")
case .failure(let e):
print("getByType error: \(e)")
}
}
```
EnterpriseCategory can contain one or more sub EnterpriseCategory that are accessed from its `children` member.
## Enterprise Venue
The EnterpriseVenue class holds metadata about the map, which includes the map name, supported languages, default language, top locations and more. It can be accessed using the MapData.getByType()/) method as shown below.
```swift
mapView.mapData.getByType(MapDataType.enterpriseVenue) { [weak self] (result: Result<[EnterpriseVenue], Error>) in
switch result {
case .success(let enterpriseVenue):
print("getByType success: \(enterpriseVenue)")
case .failure(let e):
print("getByType error: \(e)")
}
}
```
## Search
:::tip
Note that the MapData class instantiates the Search class and exposes it as MapView.mapData.search. Use MapView.mapData.search to utilize Search' methods.
:::
The Search functionality allows users to search for locations, categories, and other points of interest within the venue.
:::tip
A complete example demonstrating Search can be found in the Mappedin iOS Github repo: SearchDemoViewController.swift
:::
Here are two ways to enable search:
1. Enable Search on map initialization:
```swift
// See Trial API key Terms and Conditions
// https://developer.mappedin.com/docs/demo-keys-and-maps
let options = GetMapDataWithCredentialsOptions(
key: "5eab30aa91b055001a68e996",
secret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1",
mapId: "mappedin-demo-mall",
search: SearchOptions(enabled: true)
)
mapView.getMapData(options: options) { [weak self] r in
```
2. Enable Search via method:
```swift
mapView.mapData.search.enable { [weak self] result in
guard let self = self else { return }
if case .success = result {
print("Search enabled")
self.loadAllLocations()
}
}
```
### Search Query
Use Search.query/) to search for locations based on a string input:
- EnterpriseLocationOptions: Specific places such as stores, restaurants, or washrooms.
- EnterpriseCategoryOptions: Groups of locations, such as "Food Court" or "Electronics."
- PlaceOptions: Any main objects that can be searched for such as Space, Door, PointOfInterest
Search query returns a list of matching SearchResult based on the input string.
SearchResult include information about the type of match, the score (relevance), and detailed metadata about the matching items.
#### Example Search Query
```swift
mapView.mapData.search.query(term: suggestion.suggestion) { [weak self] result in
guard let self = self else { return }
if case .success(let searchResult) = result {
print("Search result: \(searchResult)")
}
}
```
### Search Suggestions
Use Search.suggest/) to fetch real-time suggestions based on partial input. This is useful for creating an autocomplete feature for a search bar.
#### Example Code
```swift
mapView.mapData.search.suggest(term: searchText) { [weak self] result in
guard let self = self else { return }
if case .success(let suggestions) = result {
print("Suggestions: \(suggestions)")
}
}
```
:::tip
A complete example demonstrating Search can be found in the Mappedin iOS Github repo: SearchDemoViewController.swift
:::
### Getting Started
# Getting Started
>
Mappedin SDK for iOS helps to deliver the rich indoor mapping experience of a venue, inside iOS apps.
The Mappedin SDK for iOS is a native interface to Mappedin JS. The SDK is a dependency built using Swift, and it automatically handles any authentication, network communication, fetching of map data, its display, and basic user interaction, such as panning, tapping, and zooming. The SDK allows a developer to build their own interactions. Additional layers can be rendered on top of the map.
:::info
Mappedin SDK for iOS is supported on iOS versions 13.0 and above.
:::
## Coding with AI
Mappedin SDK for iOS provides an llms-mappedin-ios.txt file that can be used to help with coding when using Large Language Models (LLMs).
## Xcode Project Setup
### Add Mappedin SDK Using Swift Package Manager
Add the Mappedin SDK to your Xcode project using Swift Package Manager:
1. In Xcode, go to **File > Add Package Dependencies**
2. Enter the repository URL: `https://github.com/MappedIn/ios.git`
3. Select version `6.1.0-alpha.1`
4. Click **Add Package**
Alternatively, you can add the package dependency to your `Package.swift` file:
```swift
dependencies: [
.package(url: "https://github.com/MappedIn/ios.git", from: "6.1.0-alpha.1")
]
```
The latest version can be found in the iOS GitHub repository releases.
### Add Permissions
If you plan to display user location, add the following permissions to your `Info.plist` file:
```xml
NSLocationWhenInUseUsageDescriptionThis app needs access to your location to show you on the mapNSLocationAlwaysAndWhenInUseUsageDescriptionThis app needs access to your location to show you on the mapNSBluetoothAlwaysUsageDescriptionThis app uses Bluetooth to determine your location inside buildings
```
Update the description strings to match your app's specific use case.
:::tip
The Mappedin iOS Github repository contains a reference project that demonstrates how to use the Mappedin SDK for iOS.
:::
### Display a Map
The core class of the Mappedin SDK for iOS is MapView, which is responsible for instantiating an WKWebView that loads Mappedin JS. MapView is the core class of which all other views and data models can be accessed.
#### Load Map Data
Call MapView.getMapData/) to load map data from Mappedin servers. This function must be called first and map data must be loaded before any other Mappedin functions can be called.
```kotlin
// See Trial API key Terms and Conditions
// https://developer.mappedin.com/docs/demo-keys-and-maps
let options = GetMapDataWithCredentialsOptions(
key: "mik_yeBk0Vf0nNJtpesfu560e07e5",
secret: "mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022",
mapId: "660c0c6e7c0c4fe5b4cc484c"
)
// Load the map data.
mapView.getMapData(options: options) { [weak self] r in
guard let self = self else { return }
if case .success = r {
print("getMapData success")
} else if case .failure(let error) = r {
print("getMapData error: \(error)")
}
}
```
#### Show a Map
Call MapView.show3dMap/) to display the map.
```swift
self.mapView.show3dMap(options: Show3DMapOptions()) { r2 in
if case .success = r2 {
print("show3dMap success")
}
}
```
The following sample code combines the loading of map data and the display of the map and includes a function to be called when the map is ready.
```swift
import UIKit
import Mappedin
final class DisplayMapDemoViewController: UIViewController {
private let mapView = MapView()
private let loadingIndicator = UIActivityIndicatorView(style: .large)
override func viewDidLoad() {
super.viewDidLoad()
title = "Display a Map"
view.backgroundColor = .systemBackground
let container = mapView.view
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
// Add loading indicator
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
loadingIndicator.startAnimating()
view.addSubview(loadingIndicator)
NSLayoutConstraint.activate([
container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
container.trailingAnchor.constraint(equalTo: view.trailingAnchor),
container.topAnchor.constraint(equalTo: view.topAnchor),
container.bottomAnchor.constraint(equalTo: view.bottomAnchor),
loadingIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loadingIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// See Trial API key Terms and Conditions
// https://developer.mappedin.com/docs/demo-keys-and-maps
let options = GetMapDataWithCredentialsOptions(
key: "mik_yeBk0Vf0nNJtpesfu560e07e5",
secret: "mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022",
mapId: "64ef49e662fd90fe020bee61"
)
// Load the map data.
mapView.getMapData(options: options) { [weak self] r in
guard let self = self else { return }
if case .success = r {
print("getMapData success")
// Display the map.
self.mapView.show3dMap(options: Show3DMapOptions()) { r2 in
if case .success = r2 {
DispatchQueue.main.async {
self.loadingIndicator.stopAnimating()
}
self.onMapReady()
} else if case .failure(let error) = r2 {
DispatchQueue.main.async {
self.loadingIndicator.stopAnimating()
}
print("show3dMap error: \(error)")
}
}
} else if case .failure(let error) = r {
print("getMapData error: \(error)")
}
}
}
// Place your code to be called when the map is ready here.
private func onMapReady() {
print("show3dMap success - Map displayed")
}
}
```
#### Result
The app should display something that looks like this in the iPhone Emulator:
And zooming in to have a closer look:
## Create a Key & Secret
## Debug Mode
Use Debug Mode to get a closer look at how various map components behave and interact. Here's how to enable it:
### 1. Enable Debug Mode
To activate the debug interface, call the following function in your code:
```swift
mapView.enableDebug()
```
MapView.enableDebug/) displays a panel on the right side of the map, revealing several tools and controls for direct interaction with the map's elements.
### 2. Navigating the Debug Panel
The debug panel provides access to a variety of controls:
**Geometry Controls**
- Adjust individual geometry settings, such as color, opacity, visibility. These controls make it easy to see how different elements respond to changes.
**Interactivity**
- Use toggles to turn interactivity on/off
- Change colors and hover effects to highlight specific areas
**Scene Controls**
- Manage the overall scene settings, such as scaling, positioning, and group management. Toggle the visibility of groups or containers within the scene
- Adjust padding, scale, and watermark positioning
- Markers & Labels — Add, remove, or edit markers and labels directly through the panel
**Camera Controls**
- Fine-tune camera settings, including zoom levels, pitch, and center position
- Set minimum and maximum zoom levels
- Adjust the focus to a specific area or level
!enable debug
### Interactivity
# Interactivity
>
This guide explains how to enhance a map's interactivity by making components clickable. Interactivity can greatly improve the user experience and user engagement with a map.
:::tip
A complete example demonstrating interactivity can be found in the Mappedin iOS Github repo: InteractivityDemoViewController.swift
:::
!Mappedin v6 Interactivity
## Interactive Spaces
A Space can be set to interactive to allow a user to click on it. When interactivity is enabled for a space, it enables a hover effect that highlights the space when the cursor is moved over the space.
The following code enables interactivity for all spaces:
```swift
// Set all spaces to be interactive so they can be clicked
mapView.mapData.getByType(.space) { [weak self] (result: Result<[Space], Error>) in
guard let self = self else { return }
if case .success(let spaces) = result {
spaces.forEach { space in
self.mapView.updateState(target: space, state: ["interactive": true]) { _ in }
}
}
}
```
## Interactive Labels
A Label added to the map can be set to interactive to allow users to click on it and have the click event captured by an app. The code sample below adds a label to each space with a name and sets the label's interactivity to true.
```swift
// Add interactive labels to all spaces with names.
self.mapView.mapData.getByType(.space) { (spacesResult: Result<[Space], Error>) in
if case .success(let spaces) = spacesResult {
spaces.forEach { space in
guard !space.name.isEmpty else { return }
self.mapView.labels.add(
target: space,
text: space.name,
options: AddLabelOptions(interactive: true)
)
}
}
}
```
After enabling interactivity, click events on the label can be captured using Mapview.on('click')/). The Events object passed into the `on` method contains an array of labels that were clicked.
The following code sample captures the click event and checks if the user clicked on a label. If they did, it logs the id of the label that was clicked and removes it from the map.
```swift
self?.mapView.on("click") { payload in
if let click = payload as? ClickPayload, let label = click.labels?.first {
print("removing label: \(label.text)")
self?.mapView.labels.remove(label: label)
}
}
```
## Interactive Markers
A Marker can be set to interactive, allowing it to be clickable and have an app act on the click event. The `interactive` property of a Marker can be set to `true`, `false` or `pointer-events-auto`.
- `false` - The Marker is not interactive.
- `true` - The Marker is interactive and the click event is captured by the `mapView.on('click')` event handler.
- `pointer-events-auto` - The Marker is interactive and mouse events are passed to the Marker's HTML content.
The code sample below adds a marker for each annotation and sets the marker's interactivity to true.
```swift
mapView.mapData.getByType(.annotation) { [weak self] (result: Result<[Annotation], Error>) in
guard let self = self else { return }
if case .success(let annotations) = result {
let opts = AddMarkerOptions(
interactive: .True,
placement: .single(.center),
rank: .tier(.high)
)
// Add markers for all annotations that have icons
annotations.forEach { annotation in
let iconUrl = annotation.icon?.url ?? ""
let markerHtml = """
"""
self.mapView.markers.add(target: annotation, html: markerHtml, options: opts) { _ in }
}
}
}
```
After enabling interactivity, click events on the label can be captured using Mapview.on('click')/). The Events object passed into the `on` method contains an array of markers that were clicked.
The following code sample captures the click event and checks if the user clicked on a marker. If they did, it logs the id of the marker that was clicked and removes it from the map.
```swift
// Remove markers that are clicked on
self.mapView.on(Events.click) { [weak self] payload in
guard let self = self,
let click = payload as? ClickPayload,
let clickedMarker = click.markers?.first else { return }
self.mapView.markers.remove(marker: clickedMarker) { _ in }
}
```
## Handling Click Events
Mappedin SDK for iOS enables capturing and responding to click events through its event handling system. After enabling interactivity, click events can be captured using Mapview.on('click')/).
The Events `click` event passes ClickPayload that contains the objects the user clicked on. It allows developers to obtain information about user interactions with various elements on the map, such as:
1. **coordinate** - A Coordinate object that contains the latitude and longitude of the point where the user clicked.
2. **facades** - An array of Facade objects that were clicked.
3. **floors** - An array of Floor objects. If the map contains multiple floors, floors under the click point are included.
4. **labels** - An array of Label objects that were clicked.
5. **markers** - An array of Marker objects that were clicked.
6. **models** - An array of Model objects that were clicked.
7. **objects** - An array of MapObject objects that were clicked.
8. **paths** - An array of Path objects that were clicked.
9. **pointerEvent** - A PointerEvent object that contains the pointer event data.
10. **shapes** - An array of Shape objects that were clicked.
11. **spaces** - An array of Space objects that were clicked.
Use `Mapview.on('click')` to capture click as shown below.
```swift
// Set up click listener
mapView.on("click") { [weak self] payload in
guard let self = self else { return }
if let clickPayload = payload as? ClickPayload {
self.handleClick(clickPayload)
}
}
private func handleClick(_ clickPayload: ClickPayload) {
var message = ""
// Use the map name as the title (from floors)
let title = clickPayload.floors?.first?.name ?? "Map Click"
// If a label was clicked, add its text to the message
if let labels = clickPayload.labels, !labels.isEmpty {
message.append("Label Clicked: ")
message.append(labels.first?.text ?? "")
message.append("\n")
}
// If a space was clicked, add its location name to the message
if let spaces = clickPayload.spaces, !spaces.isEmpty {
message.append("Space clicked: ")
message.append(spaces.first?.name ?? "")
message.append("\n")
}
// If a path was clicked, add it to the message
if let paths = clickPayload.paths, !paths.isEmpty {
message.append("You clicked a path.\n")
}
// Add the coordinates clicked to the message
message.append("Coordinate Clicked: \nLatitude: ")
message.append(clickPayload.coordinate.latitude.description)
message.append("\nLongitude: ")
message.append(clickPayload.coordinate.longitude.description)
print("title: \(title), message: \(message)")
}
```
:::tip
A complete example demonstrating interactivity can be found in the Mappedin iOS Github repo: InteractivityDemoViewController.swift
:::
### Labels
# Labels
>
Labels display text and images anchored to specific points on a map. They rotate, show, or hide based on priority and zoom level, providing information about location names, points of interest, and more.
Effective labels help apps convey additional information, such as room names, points of interest, main entrances, or other useful contextual details.
:::tip
A complete example demonstrating Labels can be found in the Mappedin iOS Github repo: LabelsDemoViewController.swift
:::
!Mappedin v6 Labels
:::tip
Note that the MapView class instantiates the Labels class and exposes it as MapView.labels. Use MapView.labels to utilize Labels' methods.
:::
## Adding & Removing Individual Labels
Labels can be added individually to a map by calling Labels.add()/). A label can be added to any Anchorable target. Refer to the Labels.add()/) documentation for the complete list of targets. The following code sample adds an interactive (clickable) label to each space that has a name:
```swift
mapView.mapData.getByType(MapDataType.space) { [weak self] (result: Result<[Space], Error>) in
switch result {
case .success(let spaces):
spaces.forEach { space in
guard !space.name.isEmpty else { return }
let color = self?.colors.randomElement()
let appearance = LabelAppearance(color: color, icon: space.images.first?.url ?? self?.svgIcon)
self?.mapView.labels.add(target: space, text: space.name, options: AddLabelOptions(labelAppearance: appearance, interactive: true))
}
case .failure(let e):
print("getByType error: \(e)")
}
}
```
Labels can be removed by using the Labels.remove(label)/) method, passing in the label to be removed as shown below:
```swift
mapView.labels.remove(label: label)
```
## Interactive Labels
Labels added to the map can be set to interactive to allow users to click on them. For more information, refer to the Interactive Labels section of the Interactivity Guide.
## Label Icons
Icons can be added to labels in `SVG`, `PNG`, `JPEG` and `WebP` formats. Icons are clipped in a circle to prevent overflow. Three clipping methods of `contain`, `fill` and `cover` can be set in the LabelAppearance.iconFit parameter with `contain` being the default.
| Fill | Contain | Cover (default) |
| ----------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------- |
| !Floating Label fill | !Floating Label contain | !Floating Label cover |
The LabelAppearance.iconPadding property sets the amount of space between the icon and the border. The icon may shrink based on this value.
| `padding: 0` | `padding: 10` |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| !Floating Label fill with 0 padding | !Floating Label fill with 10 padding |
Label icons can be configured to be shown or hidden based on the current zoom level using LabelAppearance.iconVisibleAtZoomLevel. A value below 0 will result in the icon always being hidden. Setting this value to 0 ensures icons show up at maxZoom (fully zoomed in) and 1 configures them to always be displayed.
## Label Appearance
Labels can have their appearance styled to match the visual theme of an app or to make groups of labels easily distinguishable from one another. The following declaration of LabelAppearance describes these customisable attributes.
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `margin` | `number` | Margin around the label text and pin in pixels. This will affect label density. Minimum is 6px. | 6 |
| `maxLines` | `number` | Number of lines to display when text spans multiple lines. | 2 |
| `textSize` | `number` | Text size in pixels | 11.5 |
| `maxWidth` | `number` | Maximum width of text in pixels. | 150 |
| `lineHeight` | `number` | Line height sets the height of a line box. It's commonly used to set the distance between lines of text. | 1.2 |
| `color` | ColorString | A ColorString for the label text and pin. | `#333` |
| `outlineColor` | ColorString | A ColorString for the outline around the label text and pin. | `white` |
| `textColor` | ColorString | A ColorString representing just the text color. Defaults to the same as `color`. | - |
| `textOutlineColor` | ColorString | A ColorString representing just the text outline. Defaults to the same as `outlineColor`. | - |
| `pinColor` | ColorString | A ColorString representing just the pin color. Defaults to the same as `color`. | - |
| `pinOutlineColor` | ColorString | A ColorString representing just the pin outline. Defaults to the same as `outlineColor`. | - |
| `pinColorInactive` | ColorString | A ColorString representing just the pin color when the label is inactive. Defaults to the same as `pinColor`. | - |
| `pinOutlineColorInactive` | ColorString | A
| `icon` | `string` | An icon to be placed inside the label pin. Can be an SVG string or a path to a PNG or JPEG. | - |
| `iconSize` | `number` | Size of the icon in pixels. Requires `icon` to be set. | 20 |
| `iconScale` | `number` \| [Interpolation | Scale the icon uniformly. Specify a number or an Interpolation object. | 1 |
| `iconPadding` | `number` | Padding between the icon and the marker's border in pixels. | 2 |
| `iconFit` | `'fill'` \| `'contain'` \| `'cover'` | How the icon should fit inside the marker. Options: `fill` (stretch to fill), `cover` (maintain aspect ratio and fill), `contain` (maintain aspect ratio and fit inside). | `cover` |
| `iconOverflow` | `'visible'` \| `'hidden'` | Whether the icon should overflow the circle of the marker. Options: `visible`, `hidden`. | `hidden` |
| `iconVisibleAtZoomLevel` | `number` | Defines the zoom level at which the icon becomes visible. Infinity ensures never visible, -Infinity ensures always visible. | -Infinity |
The code sample below demonstrates how to use some label styling options. Labels are added to each space with a name using the space's image if one is set, or a default SVG icon if one is not. It also sets the label text color to a random color from a list of colors.
```swift
let svgIcon = """
"""
let colors = ["#FF610A", "#4248ff", "#891244", "#219ED4"]
mapView.mapData.getByType(MapDataType.space) { [weak self] (result: Result<[Space], Error>) in
switch result {
case .success(let spaces):
spaces.forEach { space in
guard !space.name.isEmpty else { return }
let color = self?.colors.randomElement()
let appearance = LabelAppearance(color: color, icon: space.images.first?.url ?? self?.svgIcon)
self?.mapView.labels.add(target: space, text: space.name, options: AddLabelOptions(labelAppearance: appearance, interactive: true))
}
case .failure(let e):
print("getByType error: \(e)")
}
}
```
## Label Ranking
Ranking can be added to labels to control which label will be shown when more than one label occupies the same space. The label with the highest rank will be shown. If labels do not overlap, ranking will have no effect. Rank values are low, medium, high, and always-visible and are defined in CollisionRankingTier.
The code below add a label with a high ranking where a user clicks on the map.
```swift
mapView.on("click") { [weak self] payload in
guard let self = self else { return }
if let clickPayload = payload as? ClickPayload {
mapView.labels.add(target: clickPayload.coordinate, text: "I'm a high ranking label!", options: AddLabelOptions(rank: CollisionRankingTier.high))
}
}
```
## Enabling and Disabling Labels
Labels can be dynamically enabled or disabled using `mapView.updateState()`/). When a label is disabled, it will be hidden from view but remain in the map's memory. This is useful for managing label visibility based on conditions like zoom level or user interaction, without the overhead of repeatedly adding and removing labels.
For Mappedin JS, use `mapView.getState()`/) to check a label's current state, which returns the label's current properties including its enabled status.
Here's an example on how to enable/disable labels on click:
```swift
mapView.on("click") { [weak self] payload in
guard let self = self else { return }
guard let clickPayload = payload as? ClickPayload else { return }
if self.label == nil {
// Add the label if it doesn't exist.
mapView.labels.add(target: clickPayload.coordinate, text: "Click to Toggle") { result in
if case .success(let createdLabel) = result {
self.label = createdLabel
}
}
} else if let currentLabel = self.label {
// Toggle the label.
mapView.getState(label: currentLabel) { result in
if case .success(let state) = result, let labelState = state {
print(labelState)
self.mapView.updateState(
label: currentLabel,
state: LabelUpdateState(enabled: !labelState.enabled)
)
}
}
}
}
```
:::tip
A complete example demonstrating Labels can be found in the Mappedin iOS Github repo: LabelsDemoViewController.swift
:::
### Location Profiles & Categories
# Location Profiles & Categories
>
A Location refers to something that represents a physical position on the map, such as an annotation, area, connection (e.g. stairs or elevator), door, point of interest or space. Locations may have a LocationProfile attached to them. The profiles contain information about the location such as its name, description, social media links, opening hours, logo and more. A LocationProfile can have zero to many LocationCategory attached to it. A LocationCategory may have a child or parent category. For example, the parent `Food & Drink` LocationCategory has children such as `Bar`, `Cafe`, `Food Court` and `Mediterranean Cuisine`.
:::tip
A complete example demonstrating location profiles and categories can be found in the Mappedin iOS Github repo: LocationsDemoViewController.swift
:::
## LocationProfile
A LocationProfile can be accessed by using the `locationProfiles` property of a location. For example, use Space.locationProfiles to access every LocationProfile of a space. Every LocationProfile can also be access using the MapData.getByType()/) method as shown below.
```swift
mapView.mapData.getByType(MapDataType.locationProfile) { [weak self] (result: Result<[LocationProfile], Error>) in
switch result {
case .success(let locationProfiles):
locationProfiles.forEach { locationProfile in
print(locationProfile.name ?? "")
}
case .failure(let e):
print("getByType error: \(e)")
}
}
```
## LocationCategory
To access the LocationCategory of a LocationProfile, use the LocationProfile.categories property, which contains a list of IDs of its LocationCategory. Then use the MapData.getById()/) method to get the LocationCategory by its ID.
Note that a LocationCategory can have either parent or child categories. These are accessible using the LocationCategory.parent and LocationCategory.children properties respectively. Every LocationCategory can also be access using the MapData.getByType() method as shown below.
```swift
mapView.mapData.getByType(MapDataType.locationCategory) { [weak self] (result: Result<[LocationCategory], Error>) in
switch result {
case .success(let locationCategories):
locationCategories.forEach { locationCategory in
print(locationCategory.name)
}
case .failure(let e):
print("getByType error: \(e)")
}
}
```
:::tip
A complete example demonstrating location profiles and categories can be found in the Mappedin iOS Github repo: LocationsDemoViewController.swift
:::
### Markers
# Markers
>
Mappedin SDK for iOS allows adding and removing Marker on a map. Markers are elements containing HTML that the Mappedin SDK anchors to any Anchorable target. They are automatically rotated and repositioned when the camera moves.
:::tip
A complete example demonstrating Markers can be found in the Mappedin iOS Github repo: MarkersDemoViewController.swift
:::
!Mappedin JS v6 Markers
:::tip
Note that the MapView class instantiates the Markers class and exposes it as MapView.markers. Use MapView.markers to utilize Markers' methods.
:::
## Creating Markers
Markers are added to the map by referencing a target that can be a Door, Space, Coordinate or any Anchorable target. The following code sample adds a marker to a coordinate.
```swift
let opts = AddMarkerOptions(
interactive: .True,
placement: .single(.center),
rank: .tier(.high)
)
let markerHtml = """
This is a Marker!
"""
self.mapView.markers.add(target: coordinate, html: markerHtml, options: opts) { _ in }
```
## Placement
Marker placement is set using the `options` argument of Markers.add()/). Options accepts a AddMarkerOptions value, which has a member named `placement`. When provided, the point of the Marker described by the Placement is placed next to the target. For example, using `center` will place the Marker's center at the target. Both a single placement and an array of placements can be provided. When an array is provided, the Marker is placed at the first target that has space available to display the Marker. Placement positions are defined in MarkerPlacement which contains 9 values. The placement points are as follows, with the default being center.
### MarkerPlacement
- `center`
- `top`
- `left`
- `bottom`
- `right`
- `topLeft`
- `topRight`
- `bottomLeft`
- `bottomRight`
Marker content can be styled using CSS that references the placement of the marker. This can allow the creation of a tooltip or bubble-style marker that points to its target. This is done by using the CSS Selector:
```css
.mappedin-marker[data-placement=''];
```
Where `` is the placement position. Here is an example of adding a triangle pointer to the Marker's target of `left`:
```CSS
.marker:before {
content: '';
width: 0;
height: 0;
top: calc(50% - 10px);
left: -10px;
z-index: 1;
position: absolute;
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
z-index: -1;
}
.mappedin-marker[data-placement="left"] .marker:before {
left: auto;
right: -5px;
border-left: 10px solid #333333;
}
```
## Removing Markers
Markers can be removed individually by using the Markers.remove(marker)/) method, passing in the marker to be removed as shown below.
```swift
mapView.markers.remove(marker: marker) { _ in }
```
To remove all markers from a map, call Markers.removeAll()/).
```swift
mapView.markers.removeAll() { _ in }
```
## Marker Rank
Ranking can be added to markers to control which marker will be shown when more than one marker occupies the same space. The marker with the highest rank will be shown. If markers do not overlap, ranking will have no effect. Rank values `low`, `medium`, `high` and `always-visible` as defined in CollisionRankingTier.
The code below adds markers where a user clicks on the map. The marker rank is cycled with each click. If the user clicks and adds multiple markers in the same location, the marker with the highest rank is shown and lower ranking markers are hidden.
```swift
// Ranks for marker collision priority - cycles through medium, high, always-visible
private let ranks: [CollisionRankingTier] = [.medium, .high, .alwaysVisible]
private var rankIndex = 0
...
// Act on the click event and add a marker with a cycling rank.
// Observe how higher ranking markers are shown when they collide with lower ranking markers.
mapView.on(Events.click) { [weak self] event in
guard let self = self else { return }
guard let clickPayload = event as? ClickPayload else { return }
let currentRank = self.ranks[self.rankIndex]
let rankName = currentRank.rawValue
let markerTemplate = """
Marker Rank: \(rankName)
"""
// Add a marker with the current rank at the clicked coordinate.
self.mapView.markers.add(
target: clickPayload.coordinate,
html: markerTemplate,
options: AddMarkerOptions(rank: .tier(currentRank))
)
// Cycle to the next rank
self.rankIndex += 1
if self.rankIndex == self.ranks.count {
self.rankIndex = 0
}
}
```
## Moving Markers
Markers can be repositioned after they have been added to a map. There are two ways of doing this:
- Markers.setPosition()/) instantly repositions a Marker
- Markers.animateTo()/) animates a Marker to a new position
The Marker's position can be set to a Coordinate, Space or Door.
The `animateTo` method takes an `options` parameter of AnimationOptions that defines the animation duration and EasingFunction, which can be EasingFunction.linear, EasingFunction.easeIn, EasingFunction.easeOut or EasingFunction.easeInOut.
- **Linear**: This function implies a constant rate of change. It means the animation proceeds at the same speed from start to end. There's no acceleration or deceleration, giving a very mechanical feel.
- **Ease In**: This function causes the animation to start slowly and then speed up as it progresses. Initially, there's gradual acceleration, and as the function moves forward, the rate of change increases.
- **Ease Out**: Contrary to ease-in, ease-out makes the animation start quickly and then slow down towards the end. It begins with a faster rate of change and gradually decelerates.
- **Ease In Out**: This function combines both ease-in and ease-out. The animation starts slowly, speeds up in the middle, and then slows down again towards the end. It offers a balance of acceleration and deceleration.
The code samples below displays a custom Marker when the map is first clicked. When the map is clicked again, the Marker is animated to the clicked location using the default animation options.
```swift
private var marker: Marker?
...
mapView.on(Events.click) { [weak self] payload in
guard let self = self,
let click = payload as? ClickPayload else { return }
let coordinate = click.coordinate
if let existingMarker = self.marker {
// Animate existing marker to new position
self.mapView.markers.animateTo(marker: existingMarker, target: coordinate)
} else {
// Add new marker at clicked position
let options = AddMarkerOptions(
interactive: .True,
placement: .single(.right)
)
self.mapView.markers.add(target: coordinate, html: "Marker", options: options) { result in
if case .success(let newMarker) = result {
self.marker = newMarker
}
}
}
}
```
## Enabling and Disabling Markers
Markers can be dynamically enabled or disabled using MapView.updateState()/). When a marker is disabled, it will be hidden from view but remain in the map's memory. This is useful for managing marker visibility based on conditions like zoom level or user interaction, without the overhead of repeatedly adding and removing markers.
Use MapView.getState()/) to check a marker's current state, which returns the marker's current properties including its enabled status.
Here's an example on how to enable/disable markers on click:
```swift
private var marker: Marker?
...
mapView.on(Events.click) { [weak self] payload in
guard let self = self,
let click = payload as? ClickPayload else { return }
let coordinate = click.coordinate
if self.marker == nil {
// First click - add marker
print("Adding marker at: \(coordinate)")
self.mapView.markers.add(
target: coordinate,
html: "
Enabled Marker!
"
) { result in
if case .success(let newMarker) = result {
self.marker = newMarker
}
}
} else {
// Get current state of marker
self.mapView.getState(marker: self.marker!) { result in
if case .success(let markerState) = result {
print("Current Marker State: \(String(describing: markerState))")
// Toggle enabled state
let newState = !(markerState?.enabled ?? true)
self.mapView.updateState(marker: self.marker!, state: MarkerUpdateState(enabled: newState))
print("Marker is now \(newState ? "enabled" : "disabled")")
}
}
}
}
```
:::tip
A complete example demonstrating Markers can be found in the Mappedin iOS Github repo: MarkersDemoViewController.swift
:::
### Points of Interest
# Points of Interest
>
Points of Interest (POIs) are specific locations or features on a map that users find useful or informative. POIs serve as landmarks or markers, highlighting key places, services, or objects to enhance the map's utility and user experience.
They are contained in the PointOfInterest class, which contains a coordinate, name, description, images, links and the floor the point of interest exists on. All of these elements are configured in Mappedin Maker.
!Mappedin JS v6 POIs
PointOfInterest.name could be used to create labels to show users helpful information. The following sample code demonstrates one possible use for PointOfInterest. It labels each PointOfInterest and uses Camera.animateTo()/) to create a flyby of each one.
```swift
import UIKit
import Mappedin
import Foundation
final class DisplayMapDemoViewController: UIViewController {
private let mapView = MapView()
private let loadingIndicator = UIActivityIndicatorView(style: .large)
private let animationDuration = 4000
override func viewDidLoad() {
super.viewDidLoad()
title = "Display a Map"
view.backgroundColor = .systemBackground
let container = mapView.view
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
// Add loading indicator
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
loadingIndicator.startAnimating()
view.addSubview(loadingIndicator)
NSLayoutConstraint.activate([
container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
container.trailingAnchor.constraint(equalTo: view.trailingAnchor),
container.topAnchor.constraint(equalTo: view.topAnchor),
container.bottomAnchor.constraint(equalTo: view.bottomAnchor),
loadingIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loadingIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// See Trial API key Terms and Conditions
// https://developer.mappedin.com/docs/demo-keys-and-maps
let options = GetMapDataWithCredentialsOptions(
key: "mik_yeBk0Vf0nNJtpesfu560e07e5",
secret: "mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022",
mapId: "65c0ff7430b94e3fabd5bb8c"
)
// Load the map data.
mapView.getMapData(options: options) { [weak self] r in
guard let self = self else { return }
if case .success = r {
print("getMapData success")
// Display the map.
self.mapView.show3dMap(options: Show3DMapOptions()) { r2 in
if case .success = r2 {
DispatchQueue.main.async {
self.loadingIndicator.stopAnimating()
}
self.onMapReady()
} else if case .failure(let error) = r2 {
DispatchQueue.main.async {
self.loadingIndicator.stopAnimating()
}
print("show3dMap error: \(error)")
}
}
} else if case .failure(let error) = r {
print("getMapData error: \(error)")
}
}
}
// Place your code to be called when the map is ready here.
private func onMapReady() {
print("show3dMap success - Map displayed")
// Get the map center as the starting point for bearing calculations.
mapView.mapData.mapCenter { [weak self] centerResult in
guard let self = self else { return }
if case .success(let mapCenter) = centerResult, let mapCenter = mapCenter {
// Get all points of interest.
self.mapView.mapData.getByType(.pointOfInterest) { [weak self] (result: Result<[PointOfInterest], Error>) in
guard let self = self else { return }
if case .success(let pois) = result {
// Start iterating through POIs with initial position from map center.
self.animateThroughPOIs(
pois: pois,
index: 0,
startLat: mapCenter.latitude,
startLon: mapCenter.longitude
)
} else if case .failure(let error) = result {
print("Failed to get POIs: \(error)")
}
}
} else if case .failure(let error) = centerResult {
print("Failed to get map center: \(error)")
}
}
}
/// Recursively animates through each point of interest.
private func animateThroughPOIs(
pois: [PointOfInterest],
index: Int,
startLat: Double,
startLon: Double
) {
guard index < pois.count else {
print("Finished animating through all POIs")
return
}
let poi = pois[index]
// Label the point of interest.
mapView.labels.add(target: poi.coordinate, text: poi.name)
// Calculate the bearing between the current position and the POI.
let bearing = calcBearing(
startLat: startLat,
startLng: startLon,
destLat: poi.coordinate.latitude,
destLng: poi.coordinate.longitude
)
// Animate to the current point of interest.
mapView.camera.animateTo(
target: CameraTarget(
bearing: bearing,
center: poi.coordinate,
pitch: 80.0,
zoomLevel: 50.0
),
options: CameraAnimationOptions(
duration: animationDuration,
easing: .easeOut
)
)
// Wait for the animation to complete before moving to the next POI.
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(animationDuration)) { [weak self] in
self?.animateThroughPOIs(
pois: pois,
index: index + 1,
startLat: poi.coordinate.latitude,
startLon: poi.coordinate.longitude
)
}
}
/// Calculate the bearing between two points.
private func calcBearing(startLat: Double, startLng: Double, destLat: Double, destLng: Double) -> Double {
let startLatRad = toRadians(startLat)
let startLngRad = toRadians(startLng)
let destLatRad = toRadians(destLat)
let destLngRad = toRadians(destLng)
let y = sin(destLngRad - startLngRad) * cos(destLatRad)
let x = cos(startLatRad) * sin(destLatRad) -
sin(startLatRad) * cos(destLatRad) * cos(destLngRad - startLngRad)
var brng = atan2(y, x)
brng = toDegrees(brng)
return (brng + 360).truncatingRemainder(dividingBy: 360)
}
/// Converts from degrees to radians.
private func toRadians(_ degrees: Double) -> Double {
return degrees * .pi / 180
}
/// Converts from radians to degrees.
private func toDegrees(_ radians: Double) -> Double {
return radians * 180 / .pi
}
}
```
### Release Notes
# Release Notes
Mappedin SDK for iOS release notes are posted here and this page will be kept up-to-date as updates are released.
{
// Release notes template:
// https://keepachangelog.com/en/1.0.0/#how
// ## vXX.XX.XX - Month Day, Year
// _ Added
// _ Changed
// _ Deprecated
// _ Removed
// _ Fixed
// _ Security
}
## 6.1.0-alpha.1 - December 19, 2025
- Added - LabelAppearance.iconScale property to control the scale of the icon.
### ⚠️ Breaking Changes
- Fixed - Removed duplicate inline implementation of Interpolation and Width.
```kotlin
// ❌ Before
let opts = AddPathOptions(width: .fixed(1.0))
// ✅ After
let opts = AddPathOptions(width: .value(1.0))
```
- Fixed - `MapView.getState` and `MapView.updateState` should use data classes and not plain objects.
```swift
// ❌ Before
self.mapView.updateState(target: space, state: ["interactive": interactive]) { _ in }
// ✅ After
self.mapView.updateState(space: space, state: GeometryUpdateState(interactive: interactive))
```
## 6.0.0-alpha.0 - December 12, 2025
- Initial release of Mappedin SDK for iOS v6.
### Spaces
# Spaces
>
A Space represents an area enclosed by walls, such as a hall or room. Spaces can be Interactive and have Labels and Markers added to them. Spaces can also be customized with a color and texture.
## Spaces Example
The example below loops through all spaces and makes them interactive.
Refer to the Textures & Colors (coming soon) section for more information on how to set the texture or color of a space.
```swift
// Set all spaces to be interactive so they can be clicked
mapView.mapData.getByType(.space) { [weak self] (result: Result<[Space], Error>) in
guard let self = self else { return }
if case .success(let spaces) = result {
spaces.forEach { space in
self.mapView.updateState(target: space, state: ["interactive": true]) { _ in }
}
}
}
```
## Doors
By default, a Door is not visible and drawn as a gap in a wall. An app can set the `visible` property to `true` to make the door visible. Other properties of a door can also be set, such as color, texture, interactivity and more. Refer to DoorsState for the complete list of properties. Refer to the Textures & Colors (coming soon) section for more information on how to set the texture or color of a door.
Doors are grouped into DOORS.Interior and DOORS.Exterior doors, based on whether they are on an interior or exterior wall.
```ts
// Make interior doors visible and brown.
mapView.updateState(
target: Doors.interior.rawValue,
state: [
"visible": true,
"color": "#5C4033",
"opacity": 0.6
]
) { _ in }
// Make exterior doors visible and black.
mapView.updateState(
target: Doors.exterior.rawValue,
state: [
"visible": true,
"color": "black",
"opacity": 0.6
]
) { _ in }
```
The screenshot below shows brown interior and black exterior doors with labels added to depict the status of the door.