iOS User Guide
This verison of the SDK is still in beta and public APIs are subject to change.
Welcome
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
- Make sure you have version 14.2 or later of Xcode.
- If you don't already have the CocoaPods tool, install it on macOS by running this command from the terminal:
sudo gem install cocoapods
- Download our sample app here.
- In the extracted directory, navigate to the vmsdk-sampleapp folder.
- Install the dependencies using CocoaPods:
pod install
- In the extracted directory, navigate to vmsdk-sampleapp/VMMS-Demo.xcworkspace and launch it in Xcode.
- 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.
- 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
andMAPTILER_API_KEY
variables. - 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.
- Swift
- ObjC
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)
NSString* basePath = [NSString stringWithFormat:@"https://your_vmd_endpoint.com/%@/venue_map_%@", VENUE_ID, VENUE_ID];
VMDFileCollection *fileCollection = [[VMDFileCollection alloc] initWithBasePath:basePath andVenueId:VENUE_ID];
//parse the map - calls didFinishLoadingVenueMapData: or didFailToLoadMapWithError: if it fails
VMDParser* parser = [[VMDParser alloc] initWithFileCollection: fileCollection delegate:self];
[parser parseWithMapProvider:VMMapProviderTypeApple];
Implement the VMDParserDelegate
protocol to be notified when the VMD is finished loading.
- Swift
- ObjC
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)
}
/// Called when the VMD file is done loading SUCCESSFULLY
/// - Parameter: map a VMMSMap object with venue data
-(void)didFinishLoadingVenueMapData:(VMMSMap *)vmd
{
CGRect frame = vmd.geolocated && self.baseMap != nil ? self.baseMap.frame : self.view.frame;
VMMapView* vmMapView = [[VMVectorMapView alloc] initWithFrame: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 aboveView:self.vmd.geolocated ? 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.
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.
- Swift
- ObjC
//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;
//load your custom style from style_default.json configuration file
VMVenueStyle* style = [[VMVenueStyle alloc] initWithConfig:[NSBundle.mainBundle
URLForResource:@"style_default" withExtension:@"json" ]
venueId:@"venue_map_sample"];
//apply the style to your map
VMMapView* 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.
- Swift
- ObjC
//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);
//get the map unit you want to style
let unit = ... //some map unit
let mapView = ... // some mapview
VMVenueLayerStyle* style = [[VMVenueLayerStyle alloc] initWithFillColor:[UIColor.blueColor colorWithAlphaComponent:0.5f]];
style.outlineColor = UIColor.blackColor;
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.
- Swift
- ObjC
//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);
//get the map building you want to style
let building = ... //some map building
let mapView = ... // some mapview
VMVenueLayerStyle* style = [[VMVenueLayerStyle alloc] initWithFillColor:[UIColor.blueColor colorWithAlphaComponent:0.5f]];
style.outlineColor = UIColor.blackColor;
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.
- Swift
- ObjC
/// Called when map view has finished loading so you can do any additional setup
///
/// - Parameter mapView: the mapView
public override func didFinishLoading(_ mapView: VMMapView)
/// Called when map view has finished loading so you can do any additional setup
///
/// - Parameter mapView: the mapView
-(void)didFinishLoadingMapView:(VMMapView *)mapView
Changes in map position
Anytime the map's position changes, you can act accordingly.
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.
- Swift
- ObjC
/// 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)
/// 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
-(void)didChangeCameraPositionToLocation:(CLLocationCoordinate2D)newLocation
toZoom:(float)newZoom
toBearing:(double)newBearing
toTilt:(double)newTilt
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.
- Swift
- ObjC
/// 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?);
/// Called to see if it’s possible to select a unit
///
/// - Parameter unit: the unit to select
/// - Returns: true to allow selection, false otherwise
-(BOOL)canSelectUnit:(VMMSMapUnit *)unit
/// Called after a new unit has been selected
///
/// - Parameter unit: the unit that is selected
-(void)didSelectUnit:(VMMSMapUnit *)unit
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.
- Swift
- ObjC
/// 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?);
/// Called to see if it’s possible to select a unit
///
/// - Parameter building: the building to select
/// - Returns: true to allow selection, false otherwise
-(BOOL)canSelectBuilding:(VMMSBaseBuilding *)building
/// Called after a new building has been selected
///
/// - Parameter building: the building that is selected
-(void)didSelectBuilding:(VMMSBaseBuilding *)building
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:
- Swift
- ObjC
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);
VMPointAnnotation* marker = [[VMPointAnnotation alloc] init];
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
VMMapView* mapView = ...
[mapView addAnnotation:marker];
You can configure the appearance of your custom annotation using these callbacks:
- Swift
- ObjC
/// 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?;
/// Called to provide a custom image for a point annotation
///
/// - Parameter annotation: the annotation
/// - Returns: the custom image
- (UIImage *)imageForPointAnnotation:(VMPointAnnotation *)annotation
/// Called to provide a custom view for a point annotation
///
/// - Parameter annotation: the annotation
/// - Returns: the custom view
- (UIView *)viewForPointAnnotation:(VMPointAnnotation *)annotation
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:
- Swift
- ObjC
/// Called when the VMD file FAILS to load
/// - Parameter error: the exception that was raised during load
func didFailToLoadVenueMapData(error: Error?)
/// Called when the VMD file FAILS to load
/// - Parameter error: the exception that was raised during load
- (void)didFailToLoadVenueMapDataWithError:(NSError *)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:
- Swift
- ObjC
/// Called when the mapview fails to load
///
/// - Parameter error: the error that caused the failure
func didFailToLoadMapViewWithError(_ error: Error)
/// Called when the mapview fails to load
///
/// - Parameter error: the error that caused the failure
- (void)didFailToLoadMapViewWithError:(NSError *)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:
- Swift
- ObjC
//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
//Create a VMVectorWalkingPathOverlay object, which will handle interacting
//with the map to select start/endpoint locations for wayfinding
self.walkingPathOverlay = [[VMVectorWalkingPathOverlay alloc]
initWithMap:self.vmMapView];
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:
- Swift
- ObjC
/// 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])
/// Called when wayfinding path is found
/// - Parameter waypath: the Waypath that leads from the starting point to the ending point
- (void)didFinishFindingWaypath:(VMMSWaypath *)waypath
/// Called when turn by turn directions are completed
/// - Parameter turnByTurnDirections: list of directions
- (void)didFinishCreatingTurnByTurnDirections:(NSArray<VMMSMapDirectionStep *> *)turnByTurnDirections
Override auto-generated wayfinding directions and landmark names
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
.
- Swift
- ObjC
let options = VMMSWaypointLabelOptions()
let infoFile = VMDLocalFile(absoluteFilePath: "path_to_file.json")
let info = VMMSCustomMapInfo.load(mapInfoFile, for: self.vmd, options: options)
VMMSWaypointLabelOptions* options = [VMMSWaypointLabelOptions new];
VMDLocalFile* infoFile = [[VMDLocalFile alloc] initWithAbsoluteFilePath:@"path_to_file.json"];
VMMSCustomMapInfo* info = [VMMSCustomMapInfo load:mapInfoFile forMap:(VMWayfindingMap*)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:
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.
- Swift
- ObjC
/// 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);
/// Called to provide a custom image for the floor change annotation button
/// - Parameter annotation: the annotation
/// - Returns: the custom image
-(UIImage *)imageForFloorChangeAnnotation:(VMFloorChangePointAnnotation *)annotation
/// Called when floor change annotation is selected
/// - Parameter annotation: the annotation
-(void)didSelectFloorChangeAnnotation:(VMFloorChangePointAnnotation *)annotation
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:
- Swift
- ObjC
/// 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)
/// Called when an error occurred while attempting to find a waypath.
/// - Parameter error: The exception that was raised
- (void)didFailToFindWaypathWithError:(NSError *)error;
/// Called when the system is unable to generate turn by turn directions
/// - Parameter error: the exception that was raised
- (void)didFailToCreateTurnByTurnDirectionsWithError:(NSError*)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:
- Swift
- ObjC
/// Called to provide a custom image for a landmark annotation
/// - Parameter annotation: the annotation
/// - Returns: the custom image
func imageForLandmarkAnnotation( _ annotation: VMLandmarkAnnotation) -> UIImage?;
/// Called to provide a custom image for a landmark annotation
/// - Parameter annotation: the annotation
/// - Returns: the custom image
- (UIImage *)imageForLandmarkAnnotation:(VMLandmarkAnnotation *)annotation
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].