## version-5.0 ### API Reference # API Reference ## Latest Version Mappedin SDK for iOS v5.8.2 ## Previous Versions v5.8.1v5.8.0v5.7.0v5.6.1v5.6.0v5.5.0v5.4.0v5.3.1v5.3.0v5.2.1v5.2.0v5.1.9v5.1.8v5.1.7v5.1.6v5.1.5v5.1.4v5.1.3v5.1.2v5.1.1v5.1.0v5.0.4v5.0.3v5.0.0v5.0.0-beta.7v5.0.0-beta.0 ### Blue Dot # Blue Dot > Displaying user position inside a building is not accurate with just GPS. However, there are a multitude of Indoor Positioning Systems utilizing different technological approaches to providing an accurate and reliable positioning inside. Mappedin works with any IPS capable of providing the SDK with latitude, longitude and floor level information. > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: BlueDotVC.swift ## Enabling Blue Dot You can initialize and enable the Blue Dot when the map first loads by using the onFirstMapLoaded/>) function implemented by MPIMapViewDelegate. The MPIBlueDotManager can be easily enabled with default settings by calling `mapView.blueDotManager.enable(MPIOptions.BlueDot())`. There are two useful events to listen to: position update and state change. For debugging purposes, you could add print statements to the following functions. Position updates could be useful for reacting to a user entering or leaving a certain radius of a store. Status updates help handle situations where the position is no longer available. ```swift func onFirstMapLoaded() { mapView?.blueDotManager.enable(MPIOptions.BlueDot()) } func onBlueDotPositionUpdate(update: Mappedin.MPIBlueDotPositionUpdate) { print(update.position) } func onBlueDotStateChange(stateChange: Mappedin.MPIBlueDotStateChange) { print(stateChange.name) print(stateChange.reason) } ``` ## Simulating Blue Dot To display a Blue Dot with the SDK, you'll need the position information in the form of MPICoordinates. The following is a point in the fictional Mappedin Demo Mall with an accuracy of 2 meter radius on the floor level `0`. ```swift let coords = MPICoordinates(latitude: 43.52012478635707, longitude: -80.53951744629536, accuracy: 2.0, floorLevel: 0) ``` Using our sample coordinate, we can draw the Blue Dot on the map by creating an `MPIPosition` and passing it to `blueDotManager.updatePosition()`. ```swift func onFirstMapLoaded() { mapView?.blueDotManager.enable(options: MPIOptions.BlueDot()) let coords = MPICoordinates(latitude: 43.52012478635707, longitude: -80.53951744629536, accuracy: 2.0, floorLevel: 0) let position = MPIPosition(timestamp: 1.0, coords: coords) mapView?.blueDotManager.updatePosition(position: position) } ``` !iOS Blue Dot > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: BlueDotVC.swift ## Blue Dot Display States - When the Blue Dot position given to Mappedin JS has a high accuracy (within a few meters) and is on the floor currently displayed, it is displayed as bright blue. !Blue Dot - Accurate - If the Mappedin Blue Dot is able to detect that a given position update is inaccurate, a semi-transparent blue shadow is displayed underneath the Blue Dot to indicate uncertainty range. !Bluedot Inaccurate - When the BlueDot is on another floor it will look semi-transparent. !Blue Dot - Accurate on another floor - The uncertainty range is shown also when displaying the Blue Dot on another floor than the currently viewed one. !Blue Dot Inaccurate on another floor - 30 seconds after the latest position update, the BlueDot is displayed as greyed out as it's possible that the user has moved but no updates have been received. !Blue Dot - Outdated ## Wayfinding From Blue Dot A common map feature is to provide a user with directions from their current location. Mappedin JS is able to provide the nearest node to the user's location, which can be used as the starting point for wayfinding. Pass the user's destination to the `getDirections` method to create a directions object that can be used to draw the path. ```swift func onBlueDotPositionUpdate(update: Mappedin.MPIBlueDotPositionUpdate) { let destination = mapView?.venueData?.locations.first(where: { $0.name == "Destination Name" }) mapView?.getDirections(to: destination!, from: update.nearestNode!) { directions in self.mapView?.journeyManager.draw(directions: directions!) } } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: BlueDotVC.swift ## Camera Follow Mode In some instances, a user may pan the camera away and lose track of their own Blue Dot position. An app may want to snap the camera to the user's location to reorient themselves. While this could also be done using camera controls, an app can also leverage the map STATE to pin the camera to the Blue Dot's location. By default, the map starts in EXPLORE mode allowing the user to freely control the camera position. To pin the camera to the Blue Dot, set the camera state to FOLLOW with Blue Dot enabled. ```swift mapView?.blueDotManager.enable(options: MPIOptions.BlueDot(smoothing: true)) mapView?.blueDotManager.setState(state: MPIState.FOLLOW) ``` The camera will animate to the Blue Dot position whenever it is updated, however, it doesn't zoom or rotate. When providing wayfinding directions, it may be beneficial to orient the camera along the route. Enable this by setting useRotationMode of MPIOptions.BlueDot to true to when calling MPIBlueDotManager.enable()/>). > Rotation mode only works when using the MPIJourneyManager. The camera will not rotate with a path created using MPIPathManager. You can read more about path differences in the Wayfinding guide. ```swift mapView?.blueDotManager.enable( options: MPIOptions.BlueDot(smoothing: true, useRotationMode: true)) mapView?.blueDotManager.setState(state: MPIState.FOLLOW) ``` ## Additional Reading - Indoor Positioning Made Easy with Apple's Core Location for iOS ### Building & Level Selection # Building & Level Selection > This guide demonstrates how to populate menus with Mappedin venue data to create a building and level selector. This will allow listing of all buildings, the levels of each building and the ability to switch between them. !Building & Level Selection - iOS v5 > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: LevelSelectorVC.swift The `populateBuildingMenu` below populates a menu with the names of MPIMapGroups (buildings) used by the `buildingButton`. `populateBuildingMenu` can be called after the MPIMapViewListener.onDataLoaded/>) method has fired, which indicates the venue data has been loaded and is ready to be used. The UIActions update the `buildingButton` title with the chosen MPIMapGroup and call populateLevelMenu to load all of the MPIMap.names (levels) in the `levelButton` menu. ```swift // Populates the Building Selection menu with all Map Groups. func populateBuildingMenu() -> UIMenu { var menuActions: [UIAction] = [] // Loop through all map groups and add their name to the building selection menu. mapView?.venueData?.mapGroups.forEach{mapGroup in let buildingAction = UIAction(title: mapGroup.name ?? "Unknown") { (action) in // When a building is selected from the menu, change the button title and trigger loading of all // map names (levels) in the map group into the level selection menu. print(mapGroup.name ?? "Unknown" + " was clicked") self.buildingButton.setTitle(mapGroup.name, for: .normal) self.levelButton.menu = self.populateLevelMenu(selectedBuilding: mapGroup.name ?? "Default") self.mapView?.setMap(map: mapGroup.maps[0]) self.levelButton.setTitle(mapGroup.maps[0].name, for: .normal) } menuActions.append(buildingAction) } return UIMenu(title: "Choose a Building", options: .displayInline, children: menuActions); } ``` The `populateLevelMenu` is called to populate the menu for the `levelButton`. The `levelButton` menu must be populated on first load after MPIMapViewListener.onDataLoaded/>) has fired and updated when a new map group is selected. `populateLevelMenu` is called from within the UIActions of each map group (building). The following code shows using UIActions to create a menu for the `levelButton` with all MPIMap.names (buildings). When a map name is chosen from the menu, the UIAction fires, updating the levelButton title and loading the chosen map. ```swift // Populates the Level Selection menu with all Map names in the selected building (Map Group). func populateLevelMenu (selectedBuilding:String) -> UIMenu { var menuActions: [UIAction] = [] // Loop through all maps and add their name to the level selection menu. mapView?.venueData?.mapGroups.first(where: {$0.name == selectedBuilding})?.maps.forEach{map in let levelAction = UIAction(title: map.name) { (action) in // When a new level is selected, change the button title and display the chosen map. print(map.name + " was clicked") self.levelButton.setTitle(map.name, for: .normal) self.mapView?.setMap(map: map) } menuActions.append(levelAction) } return UIMenu(title: "Choose a Level", options: .displayInline, children: menuActions); } ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: LevelSelectorVC.swift ### Callbacks # Callbacks > The Mappedin Mobile SDKs offer callback functions that developers can use to trigger various events based on the completion of certain actions. These methods are useful for modifying UI elements such as loading views, changing floors, changing polygon colours, adding markers etc. An `MPIMapViewDelegate` will respond to events triggered by the `MPIMapView`. To listen to these events, create or modify a class to fit the `MPIMapViewDelegate` protocol and implement the methods you'd like to use. They will be executed automatically after the corresponding event has occurred. ## onDataLoaded A callback function that is executed when the 3D files have been downloaded for the first map, and are starting to load into memory. The `MPIMapView` is fully functional at this point, but some things will still be popping in. At this point, you can interact with the `MPIMapView`, venue data, call helper functions, call other mapView functions, and set up your application. This function will pass in an `MPIData` parameter, `data`, which allows you to use maps, locations, polygons, nodes, vortexes, and map groups of the venue. For example, you can access the list of a venue’s locations by using the class variable locations of the `data` parameter. You can also call other `mapView` functions here. For example, you can label your polygons by calling `labelPolygon` and passing in both an `MPIPolygon` parameter and various customization options for your labels. ## onFirstMapLoaded A callback that is executed when the first map is fully loaded. This means `onDataLoaded` has already been fired (if specified) and all the textures and other resources have finished popping in. ## onMapChanged A callback function that is executed when the map of your `MPIMapView` changes. A common use of this callback is to add a level selector for your venue. If you are working with a venue that has multiple maps (such as a venue with multiple floors) you may want to create a dropdown to allow the user to select a level. When you change the map, this callback will be executed, and it will pass in an `MPIMap` parameter, `map`, which is the current map. You can access different properties of `map` using this parameter. ## Sample Code ```swift extension ViewController: MPIMapViewDelegate { func onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) { // Called when the blueDot that tracks the user position is updated } func onBlueDotStateChange(stateChange: MPIBlueDotStateChange) { // Called when the state of blueDot is changed } func onMapChanged(map: MPIMap) { // Called when the map is changed } func onPolygonClicked(polygon: MPIPolygon) { // Called when the polygon is clicked } func onNothingClicked() { // Called when a tap doesn't hit any spaces } func onDataLoaded(data: MPIData) { // Called when the mapView has finished loading both the view and venue data } func onFirstMapLoaded() { // Called when the first map is fully loaded } func onStateChanged (state: MPIState) { // Called when the state of the map has changed } } ``` ### Camera Controls # Camera Controls > To create rich experiences on top of the Mappedin SDK for iOS, it's useful to be able to control the map view programmatically. This guide shows how to focus the map view on targets, how to listen to camera events and how to control the camera. > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: CameraControlsVC.swift ## Focus the Camera on Targets To focus on a polygon after it has been touched, we use the `onPolygonClicked()` function in the MPIMapViewDelegate. In this case, we are targeting only the touched polygon. However, we can also give coordinates or nodes as MPIOptions.CameraTargets. ```swift func onPolygonClicked(polygon: MPIPolygon) { mapView?.cameraManager.focusOn(targets: MPIOptions.CameraTargets(polygons: [polygon])) } ``` ## Listening to Camera Events We can use the `onCameraChanged()` function in the MPIMapViewDelegate to monitor changes to the camera position, rotation, tilt, and zoom. ```swift func onCameraChanged(cameraChange: Mappedin.MPICameraTransform) { print("Position \(cameraChange.position)") print("Rotation \(cameraChange.rotation)") print("Tilt \(cameraChange.tilt)") print("Zoom \(cameraChange.zoom)") } ``` ## Controlling the Camera Controlling the camera to set it to a specific tilt, rotation or zoom can be done with `mapView.cameraManager.set()` and configured MPIOptions.CameraConfiguration. Tilt and rotation are set as radians, where as zoom is in the camera distance in meters from the target. ```swift mapView?.cameraManager.set( cameraTransform: MPIOptions.CameraConfiguration( zoom: 1234, tilt: 0.3, rotation: 1.5 ) ) ``` It is possible to limit the tilting of the camera by listening to the change event and setting it to a desired value in `onCameraChanged()`. ```swift func onCameraChanged(cameraChange: Mappedin.MPICameraTransform) { let tilt = 0.0 if(cameraChange.tilt != tilt) { mapView?.cameraManager.set(cameraTransform: MPIOptions.CameraConfiguration(tilt: tilt)) } } ``` If you want to be able to zoom in closer than the default limit, use the setter `mapView?.cameraManager.setMinZoom(zoomLevel: 200.0)`>). ## Animation The camera can also be animated to zoom, tilt and rotate to a MPINode or MPIMap.MPICoordinate. This works similar to the set methods described in the Controlling the Camera section above but instead of jumping directly to a camera position, the camera moves gradually to the new position. The MPICameraManager.animate() methods are use to accomplish this. Animating to the node of a location: ```swift let zoomTarget = mapView?.venueData?.locations.first( where: { $0.name == "Sunglass Hut" }) let cameraTransform = MPIOptions.CameraTransformNode( zoom: 50.0, tilt: 2.0, rotation: 180.0, position: zoomTarget?.nodes?.first) let cameraAnimation = MPIOptions.CameraAnimation( duration: 3000.0, easing: MPIOptions.EASING_MODE.EASE_IN) mapView?.cameraManager.animate( cameraTransform: cameraTransform, options: cameraAnimation) ``` Animating to latitude and longitude coordinates: ```swift let zoomCoordinate = mapView?.currentMap?.createCoordinate( latitude: 43.86147923972817, longitude: -78.94671703394187) let cameraTransform = MPIOptions.CameraConfiguration( zoom: 50.0, tilt: 2.0, rotation: 180.0, position: zoomCoordinate) let cameraAnimation = MPIOptions.CameraAnimation( duration: 3000.0, easing: MPIOptions.EASING_MODE.EASE_IN) mapView?.cameraManager.animate( cameraTransform: cameraTransform, options: cameraAnimation) ``` ## Reset Camera To reset the camera, store the initial camera position in the onFirstMapLoaded()/>) method. Then, at a later time these values can be used to reset the camera. ```swift // Store the default camera values when the map is loaded. func onFirstMapLoaded() { defaultTilt = mapView?.cameraManager.tilt defaultZoom = mapView?.cameraManager.zoom defaultRotation = mapView?.cameraManager.rotation defaultPosition = mapView?.cameraManager.position } // Use the default camera values to reposition the camera. func resetCamera() { mapView?.cameraManager.set(cameraTransform: MPIOptions.CameraConfiguration( zoom: defaultZoom, tilt: defaultTilt, rotation: defaultRotation, position: defaultPosition)) } ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: CameraControlsVC.swift ### Flat Labels # Flat Labels > FlatLabels are painted on the polygon and fill the available space. They automatically flip to always face toward the camera. > Note: A complete class that uses the code snippet in this guide can be found in the Mappedin iOS Github repo: FlatLabelsVC.swift Flat labels can be added with default styles using MPIMapView.flatLabelsManager.labelAllLocations()/>). This call should be made after MPIMapViewListener.onFirstMapLoaded/>) is called, which indicates that the first map is loaded and can be modified. Note that Floating Labels are enabled by default. Floating Labels should be disabled using MPIOptions.ShowVenue options before enabling Flat Labels. Flat Labels can be added and removed all at once as mentioned above or individually using the MPIFlatLabelManager.add()/>) and MPIFlatLabelManager.remove()/>) methods. ## Styling Options Flat Labels are highly customizable, allowing control of size, color and spacing of each label. Flat Label Appearance - MPIOptions.FlatLabelAppearance - `height` is the upper bounds of the Polygon by default. Height can be set if not using a Polygon or a custom height is desired. - `margin` is the amount of space to leave before starting the text from the edge of the bound of the canvas bound. These bounds are set in the Mappedin CMS. The default value is 5 pixels. - `color` is a string format of a hexadecimal color representation, such as `#ff00000` for red. - `fontSize` in pixels, by default 12 - `scaleMin` sets the minimum percentage to shrink the label to if it won't fit within the bounds at 100%. If it doesn't fit at that level, it won't be created. - `scaleStep` describes how much to decrement the scale each time it doesn't fit, so the map doesn't end up with too many different font sizes on screen at once. If only labelling a few Polygons rather than everything, set this and scaleMin to 0.1 to attempt to fit all text. The default value is 0.25. ```swift let flatLabelAppearance = MPIOptions.FlatLabelAppearance(color: "#0a0dbf", fontSize: 8) let flatLabelLocations = MPIOptions.FlatLabelAllLocations(appearance: flatLabelAppearance) mapView?.flatLabelManager.labelAllLocations(options: flatLabelLocations) ``` > Note: A complete class that uses the code snippet in this guide can be found in the Mappedin iOS Github repo: FlatLabelsVC.swift ### Floating Labels # Floating Labels > Map geometry alone is not enough to have a sense of a location. Adding labels to show the names of locations and other points of interest can help users navigate a space. > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: FloatingLabelsVC.swift FloatingLabels anchor to polygon entrance nodes and automatically handle rotation and repositioning when the camera moves. Floating Labels are enabled by default with default styling. When customizing Floating Labels, disable them from appearing on startup in MPIOptions.ShowVenue options. Floating Labels can be added to the map by calling mapView.floatingLabelManager.labelAllLocations()/>) is called, which indicates that the first map is loaded and can be modified. Floating Labels can be added and removed all at once as shown above or individually using the MPIFloatingLabelManager `add` and `remove` methods. ## Styling Options Floating Labels are highly customizable, allowing control of size, color, spacing and the ability to display both a Marker (icon) and text in each label. MPIOptions.FloatingLabelAppearance.Margin - `margin` in pixels around the label and marker. This will affect label density. Marker Appearance - MPIOptions.FloatingLabelAppearance.Marker - `backgroundColor` & `foregroundColor` - The marker takes both active and inactive variants of its foreground and background colors. - `size` of the marker in pixels. - `icon` is the SVG of the icon to place inside the Floating Label. - `iconVisibilityThreshold` defines when the icon becomes visible relative to the current zoom level. Values should be between 0 and 1. 0 appears at maximum zoom and 1 causes the marker to always be shown. Text Appearance - MPIOptions.FloatingLabelAppearance.Text - `foregroundColor` and `backgroundColor` can be set using CSS colors, as Hex strings or the CSS name of a color such as crimson. - `maxWidth` defines the maximum width of text in pixels. - `numLines` sets the maximum number of lines to display if the text spans multiple lines. - `size` is the text size in pixels. The code snippet below creates a MPIOptions.FloatingLabelAppearance.Text to set the text color and the maximum number of lines to 2. ```swift let floatingLabelAppearance = MPIOptions.FloatingLabelAppearance( text: MPIOptions.FloatingLabelAppearance.Text(numLines: 2, foregroundColor: "#DAA520", backgroundColor: "#000000")) let styleOptions = MPIOptions.FloatingLabelAllLocations(appearance: floatingLabelAppearance) mapView?.floatingLabelManager.labelAllLocations(options: styleOptions) ``` ## Adding Icons Icons can be added to Floating Labels to highlight certain categories or to make locations more eye-catching. These icons automatically adjust, becoming visible as the user zooms the map to view more detail. The supplied icon must be an SVG image and is provided to MPIOptions.FloatingLabelAppearance.Marker. Note that the SVG must be encapsulated using triple double quotes (“””SVG”””) to avoid double escaping characters within the string. ```swift let svgIcon: String = """ """ let foreGroundColor = MPIOptions.FloatingLabelAppearance.Color( active: "#BF4320", inactive: "#7E2D16") let backgroundColor = MPIOptions.FloatingLabelAppearance.Color( active: "#FFFFFF", inactive: "#FAFAFA") let markerAppearance = MPIOptions.FloatingLabelAppearance.Marker( foregroundColor: foreGroundColor, backgroundColor: backgroundColor, icon: svgIcon ) let markerTheme = MPIOptions.FloatingLabelAppearance(marker: markerAppearance) let styleOptions = MPIOptions.FloatingLabelAllLocations(appearance: markerTheme) mapView?.floatingLabelManager.labelAllLocations(options: styleOptions) ``` ## Preset Themes The Mappedin SDK for iOS provides two preset Floating Label themes that can be easily applied to a map. They are lightOnDark and darkOnLight. These two contrasting themes provide options for both light and dark maps. _Applying the lightOnDark Theme_ ```swift let styleOptions = MPIOptions.FloatingLabelAllLocations( appearance: MPIOptions.FloatingLabelAppearance.lightOnDark) mapView?.floatingLabelManager.labelAllLocations(options: styleOptions) ``` _Applying the darkOnLight Theme_ ```swift let styleOptions = MPIOptions.FloatingLabelAllLocations( appearance: MPIOptions.FloatingLabelAppearance.darkOnLight) mapView?.floatingLabelManager.labelAllLocations(options: styleOptions) ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: FloatingLabelsVC.swift ### Getting Started # Getting Started > Mappedin SDK for iOS helps you deliver the rich indoor mapping experience of your venue, designed in Mappedin CMS, inside your iOS apps. In this overview, we will go through the basic concepts that will help you understand how the SDK works. In further sections, we'll also go through the setup and usage of the Mappedin SDK for iOS. The Mappedin SDK for iOS is a native interface to Mappedin JS. The SDK is a framework 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 you to build your own interactions. You can render your own additional layers on top of the map that the SDK renders. > Mappedin SDK for iOS is supported on iOS versions 14.0 and above ## Concepts - After the quick initial setup and configuration, the SDK renders the maps associated with the keys provided by you, inside an MPIMapView instance you created. - The SDK allows you to render your venue's maps in 3D. The SDK pulls the most up-to-date data from our mapping CMS for all your points of interest within your venue. - The SDK supports drawing navigation paths and providing instructions to and from any point of interest within the venue. Both accessible and non-accessible optimized routes are supported. You can build more functionality on top of this experience, such as searching or switching venues. Further pieces of this guide give you more details about how to set up the SDK and customize it as per your liking. ## 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). ## Quickstart This quickstart guide is written for a new Storyboard application that has been initialized with Cocoapods. ## Add dependency to CocoaPods Mappedin's SDK is available through CocoaPods. You can find the latest version on Github. To install it, add the dependency to your Podfile in your project's root directory: `pod 'MappedIn', '5.8.2'` > For more information on how to use Cocoapods, see here. In order for your project to recognize the Pods framework, you must be working from the `.xcworkspace` file rather than the individual `.xcproj` file. If you started a new project from scratch in Xcode, you'll need to close it and open the workspace. Make sure `platform :ios, '11.0'` is set to version 11.0 at minimum. Your `Podfile` should now have at least the following information with your own app name replacing `mappedin-ios-sdk-app`: ```ruby title=Podfile platform :ios, '11.0' target 'mappedin-ios-sdk-app' do use_frameworks! pod 'MappedIn', '5.0.3' end ``` In the terminal, run `pod install` in your project's root directory. To import the module into the project, add the following line to your `ViewController`: ```swift import Mappedin ``` > Refer to our Github repository to view the sample app created by Mappedin developers to understand how to embed a map into your app. ## Display the Venue ### Creating the MPIMapView instance Now we are ready to initialize an MPIMapView instance `mapView` in the `viewDidLoad()` method of your `ViewController` and add it to the layout: ```swift title=ViewController.swift var mapView: MPIMapView? override func viewDidLoad() { super.viewDidLoad() // Set up MPIMapView and add to view mapView = MPIMapView(frame: view.frame) if let mapView = mapView { self.view.addSubview(mapView) } } ``` ### Load the Venue `loadVenue` is a `MPIMapView` function that allows you to render the venue in your app by passing in an `options` object and an optional `showVenueOptions` object. The required properties of `options` object are `venue`, `clientId`, `clientSecret`. To get you started, we’ve provided a Mappedin id and secret that has access to some demo venues. When you’re ready to start using your own venues with the SDK, you will need to contact a Mappedin representative to get your own unique id and secret. | Property | Value | | -------------- | --------------------------------------------------------------------------- | | `venue` | `mappedin-demo-mall` | | `clientId` | See Here | | `clientSecret` | See Here | You can also pass in MPIOptions.ShowVenue to modify the properties of the map such as the background color, the map that is displayed first and whether all locations are labeled on initialization. Add the following `mapView.loadVenue` call inside the optional unwrapping of `mapView`. ```swift title=ViewController.swift mapView.loadVenue(options: MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall"), showVenueOptions: MPIOptions.ShowVenue( labelAllLocationsOnInit: true, backgroundColor: "#ffffff" )) ``` To display the data in an alternative language present in the Mappedin system, you can request the data with custom headers: ```swift MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall", headers: [MPIHeader(name: "Accept-Language", value: "en")] ) ``` ### Showing cached venue data `showVenue` is an alternative method to load the venue without using any API calls to retrieve the data. Instead, the data must be passed into the `showVenue` method which takes in a data string (which can be retrieved from a file). > Use this sample JSON file to get started with the `showVenue` method. Here is an example: ```swift // use showVenue to load venue if let path = Bundle.main.path(forResource: "mappedin-demo-mall", ofType: "json") { venueDataString = try? String(contentsOfFile: path) if let venueDataString = venueDataString { mapView.showVenue( venueResponse: venueDataString, showVenueOptions: MPIOptions.ShowVenue(labelAllLocationsOnInit: true, backgroundColor: "#CDCDCD") ) } } ``` ## Result ```swift title=ViewController.swift import UIKit import Mappedin class ViewController: UIViewController { var mapView: MPIMapView? override func viewDidLoad() { super.viewDidLoad() mapView = MPIMapView(frame: view.frame) if let mapView = mapView { self.view.addSubview(mapView) mapView.loadVenue(options: MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall"), showVenueOptions: MPIOptions.ShowVenue( labelAllLocationsOnInit: true, backgroundColor: "#ffffff" )) } } } ``` You should see something that looks like this: !iOS SDK v5 - quickstart ### Interactivty # Interactivty > The MPIMapClickDelegate protocol enables an app to receive an MPIMapClickEvent, which include the objects that were clicked. The MPIMapClickEvent includes the following properties, which describe the click event. - Floating Label - Map - Blue Dot - Path - Polygon - Position In order to use MPIMapClickDelegate, it must be added to an MPIMapView. ```swift mapView?.mapClickDelegate = self ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: AddingInteractivityVC.swift ## Floating Labels To allow floating labels to be clicked, they must be set to be interactive. ```swift mapView?.floatingLabelManager.labelAllLocations( options: MPIOptions.FloatingLabelAllLocations(interactive: true)) ``` When clicked, the MPIMapClickEvent provides an array of MPIMapClickEvent.FloatingLabelClicked of all floating labels that were clicked. The array contains the floating label's location as an MPINode and it's text as a String. The code sample below checks if the floating label array is empty and if not, appends the text of the first floating label clicked to `message`. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { var message = "" if (!mapClickEvent.floatingLabels.isEmpty) { message.append("Floating Label Clicked: ") message.append(mapClickEvent.floatingLabels.first?.text ?? "") message.append("\n") } } ``` ## Maps MPIMapClickEvent includes an array of MPIMaps corresponding to every map that a user's click passed through. These are in order of first to last intersected. For example, if the user clicked on a map showing the second floor of a building, the second floor map would be first in the array followed by the first floor. The code sample takes the first map in the array and stores the first map's name in a variable called `title`. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { let title = mapClickEvent.maps.first?.name ?? "" } ``` ## Blue Dot MPIMapClickEvent contains a boolean value that is set to true if the user clicked on or near the Blue Dot representing their current location. The code sample below demonstrates detecting if the user clicked near the Blue Dot. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { if (mapClickEvent.nearBlueDot) { // The user clicked near the Blue Dot. } } ``` ## Paths MPIMapClickEvent includes a list of paths the user clicked on. Multiple paths are included if the user clicked on a junction where paths cross. These are in order of first to last intersected by the click point and will be empty if no paths were clicked. When drawing a path, MPIOptions.Path must be used to enable a path to be clickable by setting interactive to true. This applies when using both MPIJourneyManager and MPIPathManager. ```swift // Creating a clickable path using MPIJourneyManager let journeyOpt = MPIOptions.Journey(pathOptions: MPIOptions.Path(interactive: true)) self.mapView?.journeyManager.draw(directions: directions, options: journeyOpt) // Creating a clickable path using MPIPathManager self.mapView?.pathManager.add(nodes: directions.path, options: MPIOptions.Path(interactive: true)) ``` The code sample below detects if the user clicked on an interactive path. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { var message = "" if (!mapClickEvent.paths.isEmpty) { message.append("You clicked a path.\n") } } ``` ## Polygons MPIMapClickEvent provides a list of MPINavigatable.MPIPolygons that the user's click event passes through. These are in order of first to last intersected and will be empty if no polygons were clicked. The code sample below detects if a polygon was clicked and if it was, appends the location name of the polygon to `message`. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { var message = "" if (!mapClickEvent.polygons.isEmpty) { message.append("Polygon clicked: ") message.append(mapClickEvent.polygons.first?.locations?.first?.name ?? "") message.append("\n") } } ``` ## Position / Coordinates MPIMapClickEvent can be used to determine the exact point on the map where the user clicked by observing the MPIMap.MPICoordinate it contains. The `MPICoordinate's` properties provide the x and y coordinates and latitude and longitude of the user's click. The code sample below illustrates how to extract the `MPICoordinate` values from `MPIMapClickEvent` and append them to `message`. ```swift func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { var message = "" message.append("Coordinate Clicked: \nLatitude: ") message.append(mapClickEvent.position?.latitude.description ?? "") message.append("\nLongitude: ") message.append(mapClickEvent.position?.longitude.description ?? "") } ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: AddingInteractivityVC.swift ### Locations # Locations > An MPILocation represents a specific area of interest such as a store location, meeting room or washroom. They contain a rich amount of metadata about a location such as a its name, description, operating hours, logo, phone number and more. > A complete class that uses the code snippet in this guide can be found in the Mappedin iOS Github repo: ListLocations.swift An MPILocation's MPINodes and MPIPolygons can be on multiple MPIMaps, or in multiple non-adjacent places on the same `MPIMap`. For example, all washrooms at a given venue could belong to one `MPILocation`. A washroom location might a have a few `MPIPolygons` spread throughout the venue for each washroom that exists. On the other hand, a department store could live on multiple floors. A single store can just have one presence, and therefore one MPIPolygon. Some Locations have a single point with `MPINodes`. ## Location Types Each location has a single MPILocation.type, which allows filtering or grouping locations of interest to the user. Location types can be unique for each venue. For example a mall may use types of amenity, landmark and tenant. A stadium could use gate, seat and restaurant. An office could use desk, meeting room and washroom. ## Location Categories Each location has 0 to many MPILocation.categories. Categories provide another way to filter and group locations. Location categories are also unique for each venue. Examples of categories for a mall could be Clothing, Electronics, Footwear and Food & Drink. ## Code Sample The code sample below retrieves all locations for a venue, filters those that have a type of tenant, a description, a small logo and sorts them alphabetically by name. ```swift // sortedLocations is an array of MPILocations sortedLocations = mapView?.venueData?.locations.filter { $0.type == "tenant" && // Filter locations with a type of tenant. $0.description != nil && // Filter locations with a description. $0.logo?.small != nil // Filter locations with a small logo. } ?? .init() sortedLocations.sort { $0.name < $1.name } ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin iOS Github repo: ListLocations.swift ### Markers # Markers > The Mappedin SDK for iOS allows adding custom markers to the map. These are elements containing arbitrary HTML that the SDK will anchor to an MPINode on the map, and automatically handle rotation and repositioning to face the camera. > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: MarkersVC.swift ## Drawing Markers The createMarker methods of the MPIMapView allows an app to draw a marker at a specified map node or coordinate. An app can draw a marker when a user taps on a polygon using the `onPolygonClick` function exposed by the MPIMapViewDelegate. Get the polygon entrance node from the touched polygon to position the marker and pass this to the `createMarker` method. It is also possible to access the polygon's location to display the location name in the `contentHtml`. ```swift func onPolygonClicked(polygon: MPIPolygon) { guard let location = polygon.locations?.first else { return } guard let entrance = polygon.entrances?.first else { return } mapView?.createMarker( node: entrance, contentHtml: "
\(location.name)
") } ``` !iOS Markers Alternatively, pass an MPICoordinate to draw a marker at a specified latitude/longitude. ```swift if let coordinate = mapView?.currentMap?.createCoordinate( latitude: 43.51905183293411, longitude: -80.53701846381122) { mapView?.createMarker(coordinate: coordinate, contentHtml: "
Marker
") } ``` !iOS Coordinate Marker Using a coordinate doesn't require a pre-configured node and the marker can be placed at any arbitrary location on the map. ## Removing Markers Markers can be removed from the map by passing the marker id returned from `createMarker` to removeMarker/>). By adjusting the `onPolygonClicked` example to store the marker id after it has been created, it can be passed to removeMarker/>) before creating another. ```swift // UIViewController var markerId: String? // MPIMapViewDelegate func onPolygonClicked(polygon: MPIPolygon) { guard let location = polygon.locations?.first else { return } guard let entrance = polygon.entrances?.first else { return } if let markerId = markerId { mapView?.removeMarker(id: markerId) } mapView?.createMarker( node: entrance, contentHtml: "
\(location.name)
") } ``` ## Marker Options Individual marker anchoring and rank can be tweaked via the MPIOptions.Marker parameter of createMarker. ```swift mapView?.createMarker( node: someNode, contentHtml: "
Marker
", markerOptions: MPIOptions.Marker( rank: 4.0, anchor: MPIOptions.MARKER_ANCHOR.CENTER)) ``` The `rank` parameter enables you to set the priority or rank of the markers. The ranks are closely associated with Mappedin JS enum MPIOptions.CollisionRankingTiers. The default rank is `3.0` (HIGH). The `anchor` option sets the default position of the marker. The positions are defined in the MPIOptions.MARKER_ANCHOR enum. The default position is CENTER. > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: MarkersVC.swift ### Migration Guide # Migration Guide On December 7th, 2022 Mappedin released Mappedin SDK for iOS v5. This short migration guide explains the steps needed to migrate from version 4. - Mappedin SDK for iOS is available from CocoaPods - See the API reference for more details ## MapView ### Labels Labels have been divided into two individual objects with their own methods. ```swift // Before mapView.labelAllLocations() // After mapView.floatingLabelManager.labelAllLocations() mapView.flatLabelManager.labelAllLocations() ``` Removed `mapView.labelPolygon()` and `mapView.removeAllLabels()`. Use `mapView.floatingLabelManager` or `mapView.flatLabelManager` instead ```swift // Before mapView.labelPolygon() // After mapView.floatingLabelManager.add() mapView.flatLabelManager.add() ``` ```swift // Before mapView.removeAllLabels() // After mapView.floatingLabelManager.removeAll() mapView.flatLabelManager.removeAll() ``` Deprecated methods `mapView.drawJourney()` and `mapView.clearJourney()` have been removed. ```swift // Before mapView.drawJourney() mapView.clearJourney() // After mapView.journeyManager.draw() mapView.journeyManager.clear() ``` ### Camera Controls - Removed `mapView.cameraControlsManager`. Use `mapView.cameraManager` instead - Removed `mapView.Focus()`, use `mapView.CameraManager.FocusOn` instead - Renamed `MPIOptions.FocusPadding` to `MPIOptions.CameraPadding` ## BlueDot Removed deprecated `delegate.onBlueDotUpdated` event. Use `delegate.onBlueDotPositionUpdate` and `delegate.onBlueDotStateChange` to determine the state and position of BlueDot instead. All `MPIBlueDot` members are available in the `MPIBlueDotPositionUpdate` event. ```swift // Before delegate.onBlueDotUpdated(blueDot: MPIBlueDot) // After delegate.onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) ``` `mapView.enableBlueDot` & `mapView.disableBlueDot` have been removed. use `mapView.blueDotManager` instead ```swift // Before mapView.enableBlueDot() mapView.disableBlueDot() // After mapView.blueDotManager.enable() mapView.blueDotManager.disable() ``` ## Data Objects - `MPINode.map` now resolves to a `MPIMap` instead of string - `MPIPolygon.map` now resolves to a `MPIMap` instead of string - `MPILocation.categories` now contains an array of `MPICategory` objects instead of strings - Removed `MPILocation.color` property. ### Outdoor View # Outdoor View > > Outdoor view is currently an experimental component of the Mappedin SDK. While in this state, some features may not behave as intended and APIs may be changed. Experimental features are being tested together with early adopters to fine-tune them. The Mappedin SDK's outdoor view capability allows an application to display a Mappedin map on top of an outdoor tile set. Currently the Mappedin SDK makes use of OpenStreetMap vector images retrieved from a Mappedin hosted tile server. Use of outdoor view requires MultiBufferRendering to be enabled. !Mobile-SDK-Outdoor-View ## Enable Outdoor View To enable outdoor view, set OutdoorView.enabled and ShowVenue.multiBufferRendering to `true` as shown in the code snippet below. ```swift highlight={8-9} // See Trial API key Terms and Conditions // https://developer.mappedin.com/docs/demo-keys-and-maps/ mapView.loadVenue(options: MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall" ), showVenueOptions: MPIOptions.ShowVenue(multiBufferRendering: true, outdoorView: MPIOptions.OutdoorView(enabled: true)) ) ``` ### 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. The SDK is available from CocoaPods by adding the following to your `Podfile` `pod 'MappedIn', '5.8.2` { // Release notes template: // https://keepachangelog.com/en/1.0.0/#how // ## vXX.XX.XX - Month Day, Year // _ Added // _ Changed // _ Deprecated // _ Removed // _ Fixed // _ Security } ## v5.8.2 - May 8, 2025 - Changed to not restrict pan bounds when switching maps. ## v5.8.1 - April 24, 2025 - Fixed error handling in `getPolygonsAtCoordinate` and `getPolygonsAtScreenCoordinate`. ## v5.8.0 - April 15, 2025 - Added support for MPIOptions.FloatingLabelAppearance.Marker.iconSize, iconFit, iconPadding, & iconScaleInterpolation. - Fixed MPIData decode error when map data is invalid. ## v5.7.0 - April 8, 2025 - Added - Support for MPICameraManager.setSafeAreaInsets. - Added - Return FloatingLabel ID when adding a FloatingLabel. - Fixed - FloatingLabels added using a coordinate are not clickable. - Fixed - MPIOptions.AddFloatingLabel.interactive and MPIOptions.AddFloatingLabel.scale are missing. ## v5.6.1 - March 20, 2025 - Fixed - MPIOptions.FocusOnOptions.padding is not working. ## v5.6.0 - February 28, 2025 - Added support for specifying other `things` to be included in the map using `MPIOptions.Init.things`. - Added `MPILocation.extra`, `MPIVenue.extra`, `MPIPolygon.extra` and `MPINode.extra`, which are used to access custom map data. ## v5.5.0 - January 17, 2025 - Added support for `MPILocation.instances`. - Added new options to `MPIOptions.OutdoorView` to allow more customization of the outdoor view and to support EU tile servers. - Multiple bug fixes. ## v5.4.0 - May 14, 2024 - Added markers animate and setPosition. - Added getMappedinCoordinateAtScreenCoordinate - Added Camera interactions enable, disable, and set ## v5.3.1 - Mar 8, 2024 - Fixed some geometry is not being drawn. ## v5.3.0 - Mar 8, 2024 - Added the ability to change languages without recreating the map. - Added the ability to override the Blue Dot’s bearing. - Added the ability to draw paths using coordinates. - Added ambient occlusion. - Added outdoor view. - Added shadingAndOutlines, which makes the tops and edges of walls more visible. - Added Dynamic performance, which will attempt to maintain above 25 FPS. - Fixed noisy ambient occlusion when monitor DPI is less than 2. - Fixed FloatingLabels.labelAllLocations() not respecting polygon logo images. - Fixed map failing to load when image textures cannot be downloaded. - Fixed the MappedinPolygon locations getter returning duplicate results. - Fixed special characters not yielding search results with OfflineSearch. - Fixed FloatingLabels not positioning at their correct height. - Fixed polygon hover color for polygons that have set color. - Fixed default value for automatically changing maps in dynamic focus mode to math indoor being fully visible. - Fixed image flipping when maps changed. - Fixed floating label rendering height. - Fixed default ambient occlusion performance. - Fixed clearAllPolygonColors not working. ## v5.2.1 - Dec 19, 2023 - Fixed - Add missing init methods to MPIOptions structs - Fixed - Expose removeTooltip in MPIMapView ## v5.2.0 - Dec 18, 2023 - Added - Tooltips - Added - Interactivity for Tooltips and Markers - Added - Unique context for iOS analytics - Improved - Analytics events. - Improved - Collider performance. - Fixed - focusOn animation performing redundant rotations. - Fixed - Cases where textures would fail to load. - Fixed - Journey pulse animation when arrows were turned off. - Fixed - BlueDot showing on incorrect floor. - Fixed - Invalid error message when adding a FloatingLabel to a polygon with an entrance. ## v5.1.9 - Nov 10, 2023 - Docs - Updated MPIOptions to include defaults - Added - Add floating labels to coordinates - Added - Venue Language Selection - Fixed - Instances where a duplicate path would be returned in the click event. - Fixed - Initial Marker placement - Fixed - Performance when adding multiple labels one-by-one - Fixed - A crash when modifying paths during map transition ## v5.1.8 - Oct 6, 2023 - Fixed Map could crash if changed while a journey is being drawn. - Fixed Blue Dot appeared darker when multibufferRendering is enabled. ## v5.1.7 - Sept 27, 2023 - Added MPIClickListener and MPIClickEvent to allow detecting the coordinates of the user's click as well as the maps, paths and/or floating labels they clicked on. - Added MPIPathManager to allow adding and removing interactive paths. - Added Multibuffer rendering. - Added xRayPaths. - Fixed setting backgroundAlpha to 0 would result in it being set to 1. - Fixed flat labels and geometry showing for two floors at once. ## v5.1.6 - Sept 15, 2023 - Changed default accessibility value of getDirections & getDistance to false to match Mappedin JS. - Fixed the appearance of paths when the last node is on a different map. - Fixed an issue with BlueDot when using STATE.FOLLOW and Journey. - Fixed BlueDot exiting follow mode after receiving position updates. - Fixed an issue where focusOn would not fit targets within viewbox if tilt and rotation changed. - Fixed the extra warnings in the console. - Fixed a bug in polygon material loading. - Fixed end caps of routes to be level with the path instead of sinking them to the floor. ## v5.1.5 - July 21, 2023 - Fixed BlueDot exiting follow mode after receiving position updates. ## v5.1.4 - July 17, 2023 - Added `useRotationMode` to `MPIOptions.BlueDot`. - Fixed an issue where BlueDot `setState` was not working. ## v5.1.3 - July 5, 2023 - Added `getDistance` - Added `useDraftData` flag to `MPIOptions.Init` ## v5.1.2 - May 1, 2023 - Add `getPolygonsAtCoordinate` and `getPolygonsAtScreenCoordinate` functions ## v5.1.1 - March 7, 2023 - Fix a bug where polygon highlight meshes were incorrectly positioned ## v5.1.0 - February 17, 2023 - Add support for icons in Floating Labels ## v5.0.3 - January 18, 2023 - Fix issues with removing and relabelling Flat Labels ## v5.0.0 - December 7th, 2022 > This release includes changes from all previous beta versions. - Improved camera API. Now uses a single class, `MPICameraManager` - Deprecated old functions in favor of new API structure - `labelAllLocations` has been split into two separate functions: `MPIFlatLabelsManager.labelAllLocations` and `MPIFloatingLabelsManager.labelAllLocations` - Add `emitAnalyticsEvents` flag to `loadVenue` - Add method to control `Camera.maxTilt` Migrating from v4? See migration guide for more details. ## v5.0.0-beta.7 - November 14, 2022 - Add `emitAnalyticsEvents` flag to `loadVenue` - Add method to control `Camera.maxTilt` ### Search # Search > The Mappedin SDK for iOS provides a way to search for locations and categories using their names, descriptions and tags. The search is performed locally on the device, meaning it can be used when a device is offline. > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: SearchVC.kt ## Methods for Searching MPISearchManager offers two different methods to perform a search, `MPISearchManager.search` and `MPISearchManager.suggest`. The `search` method is used to search for locations and categories based on a query string. Results are returned as a List containing MPISearchResultLocations and MPISearchResultCategorys, which both extend from MPISearchResultCommon. The `suggest` method is used to generate suggested search terms for a given query string and returns a List of MPISearchSuggestions that contains a String with the suggestion. ## Understanding Search Results Search results contain a score and details about how it was matched. When displaying the results to a user, the results should be sorted to show the highest score first. ## Code Example Below is a code sample that performs the following operations: 1. Performs a search using the String `query`. 2. Filters the results that contain locations (`MPISearchResultLocation`), removing categories (`MPISearchResultCategory`). 3. Filters locations that contain the type of “tenant”. Note that location types will be unique to each venue and are chosen by the map administrator. 4. Sorts the results by score with the highest score first. ```swift mapView?.searchManager.search(query: searchText) { results in let searchLocations = results // Filter the results that contain locations. .compactMap { $0 as? MPISearchResultLocation } // Filter locations that contain the type of “tenant”. .filter({$0.object.type == "tenant"}) // Sort the results by score with the highest score first. .sorted(by: {$0.score > $1.score}) } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: SearchVC.kt ### Tool Tips # Tool Tips > A Tooltip is similar to a marker, except, it has a mechanism to avoid collisions with other items on the map by positioning itself relative to the point where it's added. This is done automatically and makes it useful for long text with icons. Tooltips can also be clickable, which makes them ideal for showing connection points such as elevators and switching to the following map after an interaction from the user. Tooltips could be used as helpful hints for a user to navigate, with prompts such as "Take elevator to Floor 2" at a certain node on the map. > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: TooltipsVC.kt ## Drawing Tooltips When creating a Tooltip, the app specifies some HTML to display and the node to attach it too. The tooltip will be anchored to a specified node or coordinate in one of eight anchor orientations, which are defined in MPIOptions.TooltipAnchor. The possible anchor types are `top`, `bottom`, `left`, `right`, `topLeft`, `topRight`, `bottomLeft`, and `bottomRight`. This describes the position of the tooltip relative to the node or coordinate to which it is attached. Tooltip options such as interactivity and collision rank are specified using MPIOptions.Tooltip. The code below uses createTooltip/>) to create a tooltip and store its Id. Note that you can add a tooltip to a coordinate/>) or node/>). ```swift mapView?.createTooltip( node: entrance, contentHtml: """
\(location.name)
""", tooltipOptions: MPIOptions.Tooltip(collisionRank: MPIOptions.CollisionRankingTiers.medium)) { id in if (id != nil) { tooltipId = id } } ``` ## Custom Tooltips While regular Tooltips come with some base styling, custom tooltips allow an app to have complete control over the element's appearance. Creation is similar to a regular tooltip, with the addition of a CSS selector string that allows the SDK to target the tooltip. The code below uses createCustomTooltip/>) to create a tooltip and store its Id: ```swift mapView?.createCustomTooltip( coordinate: coordinate, contentHtml: """
Tool Tip
""", selector: ".my-tooltip-wrapper" ){ id in createdTooltipId = id } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: TooltipsVC.kt ### Turn by Turn Directions # Turn by Turn Directions > Text-based turn-by-turn directions can be a helpful aid to the map view and Blue Dot. Mappedin SDK for iOS offers text directions whenever a route is requested. ```swift mapView?.getDirections(to: destination, from: departure) { directions in instructions = directions?.instructions ?? [MPIInstruction]() } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: TurnByTurnDirections.kt ## Directions and Instructions To request directions, call MPIMapView.getDirections(to: MPIDestinationSet...)-642dy/>) or MPIMapView.getDirections(to: MPINavigatable...)-6mpgh/>), which returns an MPIDirections object. MPIDirections provides the path as an array of MPINodes, the total walking distance in meters and a list of text-based instructions for navigation. MPIInstruction array is used for turn-by-turn directions. The key components are: - `instruction` text used to guide the user - `distance` in meters for this instruction segment The `instruction` may include a store or a location name. That happens when the SDK is able to determine a relevant location nearby the instruction that could be helpful in navigating the venue. The MPIAction includes an MPIActionType and MPIBearingType. These types represent what the user should do and the direction to do it. The `action` object can be used to localize instructions. The Mappedin SDK provides the following action and bearing text in English only. The text could be substituted for another language before being displayed to a user. MPIActionType - MPIActionType.ARRIVAL = Arrival - MPIActionType.DEPARTURE = Departure - MPIActionType.EXITVORTEX = ExitVortex - MPIActionType.TAKEVORTEX = TakeVortex - MPIActionType.TURN = Turn MPIBearingType - MPIBearingType.LEFT = Left - MPIBearingType.RIGHT = Right - MPIBearingType.SLIGHTLEFT = SlightLeft - MPIBearingType.SLIGHTRIGHT = SlightRight - MPIBearingType.STRAIGHT = Straight > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: TurnByTurnDirections.kt ### Using SwiftUI # Using SwiftUI > A view with an MPIMapView can be created for use in SwiftUI by making use of an UIViewRepresentable. This allows an app with a SwiftUI based UI to make use of MPIMapView to display a map. To allow calling methods of the MPIMapView, MPIMapView is instantiated as a state variable. The MPIMapViewDelegate and MPIMapClickDelegate interfaces are implemented within a Coordinator to communicate changes from the view to other parts of the app's SwiftUI interface. Within the makeUIView, `mapView.loadVenue` is called, which accepts MPIOptions.Init containing the venue details and then renders the map. MPIMapViewDelegate and MPIMapClickDelegate are then added to `mapView`. ```swift struct MapViewRepresentable: UIViewRepresentable { var mapView = MPIMapView(frame: .zero) class Coordinator: NSObject, MPIMapViewDelegate, MPIMapClickDelegate { var parent: MapViewRepresentable init(_ parent: MapViewRepresentable) { self.parent = parent } func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { print("The map was clicked") } func onDataLoaded(data: Mappedin.MPIData) { print("Venue Data Loaded") } func onFirstMapLoaded() { print("First Map Loaded") } func onMapChanged(map: Mappedin.MPIMap) { print("Map Changed") } // Implement the rest of the MPIMapClickDelegate methods here. func makeUIView(context: Context) -> MPIMapView { mapView.loadVenue( options: MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall" )) mapView.delegate = context.coordinator mapView.mapClickDelegate = context.coordinator return mapView } func updateUIView(_ webView: MPIMapView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } } ``` The MPIOptions.Init object contains a venue, clientId, clientSecret, and optional headers. To get started use the [Mappedin Id and Secret that has access to demo venues](/docs/demo-keys-and-maps. To start using your venues with the SDK you will need to contact a Mappedin representative to get your own unique Id and Secret. The MapViewRepresentable is now ready to be added to a view. The code sample below shows adding it to a ContentView. ```swift struct ContentView: View { @State private var map = MapViewRepresentable() var body: some View { map Button("Remove Labels") { map.mapView.floatingLabelManager.removeAll() } } } ``` ## Full Code Sample Below is a complete code sample for using MPIMapView with a MPIMapViewDelegate and MPIMapClickDelegate in SwiftUI. ```swift import SwiftUI import Mappedin struct MapViewRepresentable: UIViewRepresentable { var mapView = MPIMapView(frame: .zero) class Coordinator: NSObject, MPIMapViewDelegate, MPIMapClickDelegate { var parent: MapViewRepresentable init(_ parent: MapViewRepresentable) { self.parent = parent } func onClick(mapClickEvent: Mappedin.MPIMapClickEvent) { print("The map was clicked") } func onDataLoaded(data: Mappedin.MPIData) { print("Venue Data Loaded") } func onFirstMapLoaded() { print("First Map Loaded") } func onMapChanged(map: Mappedin.MPIMap) { print("Map Changed") } func onPolygonClicked(polygon: Mappedin.MPIPolygon) {} func onNothingClicked() {} func onBlueDotPositionUpdate(update: Mappedin.MPIBlueDotPositionUpdate) { print("Blue Dot Position Update") } func onBlueDotStateChange(stateChange: Mappedin.MPIBlueDotStateChange) { print("Blue Dot State Change") } func onStateChanged(state: Mappedin.MPIState) { print("State Changed") } func onCameraChanged(cameraChange: Mappedin.MPICameraTransform) { print("Camera Changed") } } func makeUIView(context: Context) -> MPIMapView { mapView.loadVenue( options: MPIOptions.Init( clientId: "5eab30aa91b055001a68e996", clientSecret: "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", venue: "mappedin-demo-mall" )) mapView.delegate = context.coordinator mapView.mapClickDelegate = context.coordinator return mapView } func updateUIView(_ webView: MPIMapView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } } struct ContentView: View { @State private var map = MapViewRepresentable() var body: some View { map Button("Remove Labels") { map.mapView.floatingLabelManager.removeAll() } } } #Preview { ContentView() } ``` ### Wayfinding # Wayfinding > Wayfinding is one of the most often used features of digital maps and the Mappedin SDK for iOS makes it incredibly easy to draw a path from A to B. It only takes a known start and end location to generate directions, which can be passed to `MPIJourneyManager` to draw it on the map. > A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: ABWayfindingVC.swift ## Single-Destination Wayfinding We can use the `onFirstMapLoaded` function of the MPIMapViewDelegate to present wayfinding directions when the map loads. Select your departure and destination locations from `venueData` and supply them as the "to" and "from" for `getDirections()`. In the callback, pass the resulting directions to `journeyManager.draw()` to display the route. ```swift func onFirstMapLoaded() { let departure = mapView?.venueData?.locations.first(where: { $0.name == "Pet World" }) let destination = mapView?.venueData?.locations.first(where: { $0.name == "Microsoft" }) mapView?.getDirections(to: destination!, from: departure!) { directions in self.mapView?.journeyManager.draw(directions: directions!) } } ``` `journeyManager` exposes the MPIJourneyManager helper class to display single and multi-destination wayfinding easily on the map. Functionality of the `Journey` could be replicated by drawing the paths and adding well designed tooltips at connection points. !iOS AB Wayfinding ## Wayfinding From A User's Location To find out how to start the journey from the user's current location, refer to Wayfinding from Blue Dot in the Blue Dot Guide. ## Multi-Floor Wayfinding Using `MPIJourneyManager` helper, no additional work is needed to provide wayfinding between floors. Whenever a user needs to switch a floor, an interactive tooltip with an icon indicating the type of connection (such as an elevator or escalator) will be drawn. By tapping the tooltip, the map view switches to the destination floor. !iOS Multi-Floor Wayfinding ## Wayfinding Using Accessible Routes When requesting directions for a journey, it is important to consider the needs of the user. Users with mobility restrictions may require a route that avoids stairs and escalators and instead uses ramps or elevators. The MPIMapView.getDirections()-6mpgh/>) method accepts an accessible boolean flag, which allows specifying whether an accessible route should be returned. By default, the shortest available route is chosen. The following code demonstrates how to request directions that make use of accessible routes. ```swift mapView?.getDirections(to: destination!, from: departure!, accessible: true) { directions in self.mapView?.journeyManager.draw(directions: directions!) } ``` ## Multi-Destination Wayfinding With the Mappedin SDK, it's possible to draw a path including multiple waypoints. To generate directions, create an MPIDestinationSet from polygons, locations or nodes and request directions from a starting location with `getDirections()`. Create a couple of properties for the UIViewController which we will use to store the current step number and the destination list. Use the `onFirstMapLoaded` function in MPIMapViewDelegate to draw the route once the map is ready. ```swift // UIViewController var step: Int = 0 var destinations: [MPILocation?] = .init() // MPIMapViewDelegate func onFirstMapLoaded() { let departure = mapView?.venueData?.locations.first(where: { $0.name == "Parking Lot E" }) destinations = [ mapView?.venueData?.locations.first(where: { $0.name == "Apple" }), mapView?.venueData?.locations.first(where: { $0.name == "Microsoft" }), mapView?.venueData?.locations.first(where: { $0.name == "ThinkKitchen" }), mapView?.venueData?.locations.first(where: { $0.name == "Parking Lot E" }), ] mapView?.getDirections(to: MPIDestinationSet(destinations: destinations as! [MPINavigatable]), from: departure!) { directions in self.mapView?.journeyManager.draw(directions: directions!) } } ``` Once the user has completed a segment of the journey, the active step can be changed with journeyManager.setStep()/>). The following will move through the steps when user clicks outside of interactive polygons on the map using the `onNothingClicked` function. ```swift func onNothingClicked() { step += 1 mapView?.journeyManager.setStep(step: step % destinations.count) } ``` !iOS Multi-Destination Wayfinding ## Wayfinding Without Using Journey While MPIJourneyManager is a great utility for drawing a single path with all the features, it may sometimes be limiting. For instance, you may only draw one `Journey` at a time, and subsequent calls to `journeyManager.draw()` will overwrite the previous. For full control over the route, draw the path using the MPIPathManager.add() and pass it the `directions.path`. ```swift func onFirstMapLoaded() { let departure = mapView?.venueData?.locations.first(where: { $0.name == "ThinkKitchen" }) let destination = mapView?.venueData?.locations.first(where: { $0.name == "American Eagle" }) guard departure != nil && destination != nil else { return } mapView?.getDirections(to: departure!, from: destination!) { directions in self.mapView?.pathManager.add(nodes: directions!.path) } } ``` This creates a basic path with no extra features. Using this method, you will need to handle both multi-floor navigation and multi-destination wayfinding manually using markers and by drawing multiple paths. We can draw a secondary path the same way as the first while providing different start and end locations. To make this new path stand out, style it a different color by changing the `color` property. The documentation for MPIOptions.Path covers the other options available to you. ```swift self.mapView?.pathManager.add(nodes: directions!.path, options: MPIOptions.Path(color: "green")) ``` !iOS Multi-Path Wayfinding ## X-Ray Paths > X-Ray Paths are currently an experimental component of the Mappedin SDK. While in this state, some features may not behave as intended and APIs may be changed. Experimental features are being tested together with early adopters to fine-tune them. Maps with narrow corridors can benefit from using X-Ray Paths for wayfinding. When enabled, X-Ray Paths cause polygons, such as walls or desks, to become transparent when a path is drawn behind them. This allows the path to remain visible. The screenshot below illustrates a path between cabins on a cruise ship with X-Ray Paths disabled on the left and enabled on the right. !X-Ray Paths - Cruise Ship X-Ray Paths are enabled and disabled using the boolean `xRayPath` parameter of MPIOptions.ShowVenue object passed to loadVenue/>). X-Ray Paths are enabled by default, but they do require `multiBufferRendering` to also be enabled. `multiBufferRendering` is currently disabled by default. The code sample below demonstrates enabling both `xRayPath` and `multiBufferRendering`. ```swift highlight={7} mapView.loadVenue(options: MPIOptions.Init( clientId: "clientId", clientSecret: "clientSecret", venue: "venueSlug" ), showVenueOptions: MPIOptions.ShowVenue(multiBufferRendering: true, xRayPath: true)) ``` > Note: A complete class that uses the code snippets in this guide can be found in the Mappedin iOS Github repo: ABWayfindingVC.swift