Skip to main content
Version: iOS SDK 2.2 (beta)

iOS User Guide

danger

This verison of the SDK is still in beta and public APIs are subject to change.

Welcome

image of example wayfinding map

Audience

VMSDK documentation is designed for people familiar with basic mobile development for iOS. The latest generated docs for the iOS SDK can be found here.

Supported Platforms

VMSDK supports iOS 13.x+. Consult your map provider’s documentation for their own supported platforms.

Install and run the reference app

  1. Make sure you have version 14.2 or later of Xcode.
  2. If you don't already have the CocoaPods tool, install it on macOS by running this command from the terminal: sudo gem install cocoapods
  3. Download our sample app here.
  4. In the extracted directory, navigate to the vmsdk-sampleapp folder.
  5. Install the dependencies using CocoaPods: pod install
  6. In the extracted directory, navigate to vmsdk-sampleapp/VMMS-Demo.xcworkspace and launch it in Xcode.
  7. The reference app uses the Google Maps SDK and the MapLibre SDK, so you will need to generate a Google API Key and generate a MapTiler API Key.
  8. Once you’ve generated a Google Maps API key and a MapTiler API Key, open Supporting Files/VMMSDemoConstants.h and update the GMS_API_KEY and MAPTILER_API_KEY variables.
  9. Build and run the app on the simulator.

Implement

Add the dependencies to your Podfile

The VMSDK_Core pod is required to use any features. If you also want to enable wayfinding features, add a reference to the VMSDK_Wayfinding pod.

pod 'VMSDK_Core', '~> 2.2.0-beta.3'
pod 'VMSDK_Wayfinding', '~> 2.2.0-beta.3'

You also need to add a reference to the CocoaPods spec source if your Podfile does not have it already.

source 'https://github.com/CocoaPods/Specs.git'

Load the VMD file and display your map

Now you’re ready to write code to load the venue data info from the VMD file. Consult the example in the SDK demo: Controller/VMBaseViewController.swift

To get started, create a VMDParser to load the VMD data.

let basePath = "https://your_vmd_endpoint.com/\(VENUE_ID)/venue_map_\(VENUE_ID)"
let fileCollection = VMDFileCollection(basePath: basePath, andVenueId: VENUE_ID as String)
let parser = self.createVMDParser(fileCollection, delegate: self)
parser?.parse(withMapProvider: .apple)

Implement the VMDParserDelegate protocol to be notified when the VMD is finished loading.

public func didFinishLoadingVenueMapData(_ vmd: VMMSMap)
{
let frame = vmd.geolocated && self.baseMap != nil ? self.baseMap!.frame : self.view.frame;

var vmMapView = VMVectorMapView(frame: frame, vmd: vmd)
vmMapView.venueId = VENUE_ID;
vmMapView.tileBaseURL = "https://your_vmd_endpoint.com/vector-tiles";
vmMapView.vectorCommonBaseURL = "https://your_vmd_endpoint.com/vector-common";
vmMapView.minZoom = 16.0;
vmMapView.maxZoom = 22.0;
vmMapView.delegate = self;
vmMapView.style = self.venueStyle;
vmMapView.map = self.vmd;
vmMapView.attachInView(self.view, self.vmd?.geolocated ?? false ? self.baseMap: nil)
}

At this point, you should have a map display of the world with your map tiles superimposed.

Legacy Support

If you need to add support for parsing legacy venue map data files to your application, be sure to include the VMSDK_Legacy reference in your Podfile. Replace instances of VMDParser.parse() with VMLegacyVMDParser.parse(). The VMLegacyVMDParser also supports loading non-legacy VMDs.

caution

NOTE: Vector map tiles and wayfinding is not supported for legacy venue maps.

Customizing your map's look and feel

If you use vector map tiles, as opposed to raster map tiles, you have great flexibility in styling your map. For more information, see Vector Map Tile Style Spec.

Global map styling

The easiest way to style your map is to create a Map Style JSON configuration file that follows the Vector Map Tile Style Spec. For a full example, see style_default.json in the demo project.

//load your custom style from style_default.json configuration file
let venueStyle = VMVenueStyle(config: NSBundle.main.url(forResource: @"style_default", ofType: "json"), venueId: "venue_map_sample")

//apply the style to your map
let mapView = ...
mapView.style = venueStyle;

Styling individual units

You can now add custom styles to individual map units. This allows you to override the global map style defined in your map's VMVenueStyle configuration for a single element.

//get the map unit you want to style
let unit = ... //some map unit
let mapView = ... // some mapview

//... see class documentation for a full list of styleable attributes
let style = VMVenueLayerStyle(fillColor: UIColor.blue.withAlphaComponent(0.5))
style.outlineColor = UIColor.black;
style.fontSize = 24;
style.iconName = "custom-icon";
//apply the style to the unit. If the map unit is not visible, it will be
//applied the next time it is shown.
mapView.setStyle(style, forUnit: unit);

Styling individual buildings

You can now add custom styles to individual buildings. This allows you to override the global map style defined in your map's VMVenueStyle configuration for a single element.

//get the building you want to style
let building = ... //some map building
let mapView = ... // some mapview

//... see class documentation for a full list of styleable attributes
let style = VMVenueLayerStyle(fillColor: UIColor.blue.withAlphaComponent(0.5))
style.outlineColor = UIColor.black;
style.fontSize = 24;
//apply the style to the building. If the building is not visible, it will be
//applied the next time it is shown.
mapView.setStyle(style, forBuilding: building);

Responding to VMMapView events and callbacks

There are numerous ways to customize the behavior of your mapview by responding to the following events:

Map loading complete

In some scenarios, you may want to wait until the mapview has completed loading before proceeding to a next step. You can wait for the didFinishLoadingMapView callback.

/// Called when map view has finished loading so you can do any additional setup
///
/// - Parameter mapView: the mapView
public override func didFinishLoading(_ mapView: VMMapView)

Changes in map position

Anytime the map's position changes, you can act accordingly.

caution

If your VMMapView is overlaid on another provider's map (such as Google Maps or Apple Maps), you should use this method to ensure the maps' positions stay in sync with each other.

/// Called when the map position changes
///
/// - Parameters:
/// - newLocation: the new map location
/// - newZoom: the new map zoom
/// - newBearing: the new map bearing
/// - newTilt: the new map tilt
public override func didChangeCameraPosition(toLocation newLocation: CLLocationCoordinate2D, toZoom newZoom: Float, toBearing newBearing: Double, toTilt newTilt: Double)

Room selection/highlighting

When the user taps the map on a specific point or room, you can respond to those events appropriately. If you return true to canSelectUnit, the map will also highlight the selected shape using the color defined in your Map Style json for the layer-id of floor_selected_unit_[FLOOR]. See Customizing your map's look and feel above for more information.

/// Called to see if it’s possible to select a unit
///
/// - Parameter unit: the unit to select
/// - Returns: true to allow selection, false otherwise
func canSelectUnit(_ unit: VMMSMapUnit) -> Bool;

/// Called after a new unit has been selected
///
/// - Parameter unit: the unit that is selected
func didSelectUnit(_ unit: VMMSMapUnit?);

Building selection

When the user taps the map on a specific building, you can respond to those events appropriately by implementing canSelectBuilding and didSelectBuilding delegate methods. See Customizing your map's look and feel above for more information.

/// Called to see if it’s possible to select a building
///
/// - Parameter building: the building to select
/// - Returns: true to allow selection, false otherwise
func canSelectBuilding(_ building: VMMSBaseBuilding) -> Bool;

/// Called after a new building has been selected
///
/// - Parameter unit: the unit that is selected
func didSelectBuilding(_ building: VMMSBaseBuilding?);

Adding your own annotations to the mapview

You can programmatically add annotations to the map to suit your needs by creating a new VMPointAnnotation object and adding it to the map:

let marker = VMPointAnnotation();

marker.coordinate = ...//set the location you want the annotation to appear on
//the map

marker.title = //give it a title, which you can use to reference in additional
//callbacks

marker.floorNumber = //give the icon a floor number that it should be
//displayed on (it's going to be removed when you're not looking at that floor
//of the map)

marker.floorId = //set the floor id to the VMMSBaseFloor object's uid that
//this marker belongs on

let mapView : VMMapView? = ...

mapView?.addAnnotation(marker);

You can configure the appearance of your custom annotation using these callbacks:

/// Called to provide a custom image for a point annotation
///
/// - Parameter annotation: the annotation
/// - Returns: the custom image
func imageForPointAnnotation ( _ annotation: VMPointAnnotation
) -> UIImage?;

/// Called to provide a custom view for a point annotation
///
/// - Parameter annotation: the annotation
/// - Returns: the custom view
func viewForPointAnnotation( _ annotation: VMPointAnnotation )
-> UIView?;

Responding to errors that occur in the SDK

There are numerous instances where an error could occur within the VMSDK at any of the many steps above. You can be notified of the error by implementing any of the following callbacks:

VMD parsing errors

If any errors are encountered while parsing your venue map data files, this method will be called within the SDK with more detailed information about the error:

/// Called when the VMD file FAILS to load
/// - Parameter error: the exception that was raised during load
func didFailToLoadVenueMapData(error: Error?)

Map display errors

If any errors are encountered when your map is loaded and rendered on screen through the VMMapView object, this method will be called within the SDK with more detailed information about the error:

/// Called when the mapview fails to load
///
/// - Parameter error: the error that caused the failure
func didFailToLoadMapViewWithError(_ error: Error)

Enable Wayfinding

First you need to make sure the wayfinding data is getting parsed from the VMD, so replace usages of VMDParser.parse() with VMWayfindingVMDParser.parse().

If you want the waypath to be rendered on your map view, you'll have to create a VMVectorWalkingPathOverlay and add it to your mapview:

//Create a VMVectorWalkingPathOverlay object, which will handle interacting
//with the map to select start/endpoint locations for wayfinding
self.walkingPathOverlay = VMVectorWalkingPathOverlay(map: mapView)
self.walkingPathOverlay?.map = self.vmd
self.walkingPathOverlay?.delegate = self
.. and so on

Handle Wayfinding Events

Implement the VMMSWayfindingDelegate protocol to get notified of any callbacks from the SDK for wayfinding:

/// Called when wayfinding path is found
/// - Parameter waypath: the Waypath that leads from the starting point to the ending point
func didFinishFinding(_ waypath: VMMSWaypath)

/// Called when turn by turn directions are completed
/// - Parameter turnByTurnDirections: list of directions
func didFinishCreatingTurn(byTurnDirections directions: [VMMSMapDirectionStep])

Override auto-generated wayfinding directions and landmark names

caution

This section is experimental

You can use map information from a JSON file to override the VMD’s auto-generated wayfinding directions and landmark names. See this full example: Controller/VMMapViewController.m.

let options = VMMSWaypointLabelOptions()
let infoFile = VMDLocalFile(absoluteFilePath: "path_to_file.json")
let info = VMMSCustomMapInfo.load(mapInfoFile, for: self.vmd, options: options)

The data contained in your wayfinding info override file must be in JSON format, according to the following specs:

{
"points": [
{
"id": "node_waypoint_b1_f1_517",
"public-description": "the edge of the Basketball Court"
},
{
"id": "<the ID of the waypoint>",
"public-description": "<the description you want for this landmark/waypoint>"
}
],
"paths": [
{
"pathID": "node_path_b1_f1_722",
"p1": "node_waypoint_b1_f1_473",
"p2": "node_waypoint_b1_f1_567",
"description-d1": "along the sidewalk",
"description-d2": "along the sidewalk the other direction"
},
{
"pathID": "<the ID of the path>",
"p1": "<the ID of one of the waypoints>",
"p2": "<the ID of the other waypoint>",
"description-d1": "<description for traversing from P1 to P2>, leave blank to auto-generate",
"description-d2": "<description for traversing from P2 to P1>, leave blank to auto-generate"
}
]
}

Responding to wayfinding events and callbacks

Changing floors for wayfinding

When you have a waypath that spans multiple floors, the default behavior for the VMMapView is to draw a button on the map that looks like this:

image of wayfinding button

You can provide your own image that matches your own branding. Additionally, when the user selects that button, you must implement that behavior as well by specifying what floor to change to, etc. See BaseMapViewController.m in the iOS sample code for an example of how to appropriately respond to a floor change event.

/// Called to provide a custom image for the floor change annotation button
/// - Parameter annotation: the annotation
/// - Returns: the custom image
func imageForFloorChangeAnnotation( _ annotation: VMFloorChangePointAnnotation) -> UIImage?;

/// Called when floor change annotation is selected
/// - Parameter annotation: the annotation
func didSelectFloorChangeAnnotation( _ annotation: VMFloorChangePointAnnotation);

Responding to errors that occur in wayfinding

Wayfinding errors

Errors may occur during wayfinding, usually if no paths exist between your selected start & end destination. This method will be called within the SDK with more detailed information about the error:

/// Called when an error occurred while attempting to find a waypath.
/// - Parameter error: The exception that was raised
func didFailToFindWaypathWithError(_ error: Error)

/// Called when the system is unable to generate turn by turn directions
/// - Parameter error: the exception that was raised
func didFailToCreateTurnByTurnDirectionsWithError(_ error: Error)

Customizing wayfinding look and feel

All styling for wayfinding is now done through new styling properties added in v1.2 to the “wayfinding” section of the Vector map tile spec.

Landmark customization

In the turn-by-turn directions provided by the SDK for wayfinding, there are usually points of interest, or landmarks, that are part of each step and refer to actual places on the map. You can add special icons to the map to further highlight your landmarks:

/// Called to provide a custom image for a landmark annotation
/// - Parameter annotation: the annotation
/// - Returns: the custom image
func imageForLandmarkAnnotation( _ annotation: VMLandmarkAnnotation) -> UIImage?;

More Information

For assistance with the Aegir VMSDK, related questions, or information about other Aegir products and services, visit https://support.aegirmaps.com/ or contact Aegir Support at [email protected].