"How to" for the iOS SDKTools

Integrating the SDK

Prerequisites

  • Xcode version: Xcode 5 or above.
  • Architectures: armv7, arm64
  • Deployment Target: iOS 6.0
  • Memory management: ARC (Automatic Reference Counting)

Adding the SDKTools to the project as source code

Steps to integrate:

  • Integrate the SKMaps.framework into your project. For instructions see http://developer.skobbler.ro/getting-started/ios#sec01
  • Drag the SDKTools project file into your project.
  • Drag the SKTNavigationResources.bundle to your project. When prompted, select Copy items into destination group's folder.
  • Drag the SKAdvisorResources.bundle to the project if you haven't done so already.
  • Select the project and choose your target. Open the Target Dependencies and add SDKTools.
  • Select the project and choose your target. Open the Build Phases tab and add the following frameworks in the Link Binary with Libraries section:
    • AVFoundation.framework
    • CoreTelephony.framework
    • libSDKTools
  • If you want to have navigation while in background go to your info.plist file and add the "Required background modes". For this key add "App plays audio or streams audio/video using AirPlay" and "App registers for location updates".
  • Select the project and choose your target. Go to Build Settings and add the SDKTools folder to the header search paths.
  • Copy the localized strings from SKTNavigationResources.bundle/Localizable.strings to your English localization file (by default Supporting Files/InfoPlist.strings file).
  • Import SDKTools/Navigation/SKTNavigationManager.h.

Adding the SDKTools to the project via cocoapods

  • Since the SDK is no more freely distributed we are no longer offering support by Cocoapods.

Navigation component

This component provides an integral UI and logic for implementing a step by step navigation scenario or free drive navigation scenario. It also deals with audio advices and other settings.

Creating the NavigationManager

Prior to initializing the NavigationManager, create your SKMapView. This will be used by the navigation manager.

The NavigationManager is initialized using initWithMapView: method. The provided map view will be used for navigation.

#import <SDKTools/Navigation/SKTNavigationManager.h>

@interface MapDisplayViewController() 
@property (nonatomic, strong) SKTNavigationManager *navigationManager;
@end

@implementation MapDisplayViewController

- (void)viewDidLoad{
   [super viewDidLoad];
   SKMapView *mapView = [[SKMapView alloc] initWithFrame:CGRectMake( 0.0f, 0.0f,  CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) )];
   [self.view addSubview:mapView];
   self.navigationManager = [[SKTNavigationManger alloc] initWithMapView:mapView];
}
@end

Displaying the UI

The navigation and free drive UI is optimized for full screen display and it should be used as an overlay for the map view.

The navigation manager has a property called mainView that contains all the other necessary views. The mainView should be added as a subview to the view that contains your mapView, above the mapView.

- (void)viewDidLoad{
   [super viewDidLoad];
   SKMapView *mapView = [[SKMapView alloc] initWithFrame:CGRectMake( 0.0f, 0.0f,  CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) )];
   [self.view addSubview:mapView];
   self.navigationManager = [[SKTNavigationManger alloc] initWithMapView:mapView];
   [self.view addSubview:self.navigationManager.mainView];
}

To correctly display the UI full screen when running iOS 7 use the isUnderStatusBar property of the mainView. You can use the Additions category of the UIDevice contained in the Additions folder.

if ([UIDevice majorSystemVersion] >= 7) {
    self.navigationManager.mainView.isUnderStatusBar = YES;
}

The UI also supports both portrait and landscape orientations for both iPhones and iPads (retina & non retina).

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    self.navigationManager.mainView.orientation = ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) ? SKTUIOrientationPortrait : SKTUIOrientationLandscape;
}

Considerations when using View controller-based status bar appearance:
When displaying the UI under the status bar (iOS 7, fullscreen), the status bar style needs to be updated accordingly. By default the library does this by using [UIApplication sharedApplication].statusBarStyle. However, this does not work when View controller-based status bar appearance is set to YES. To solve this, the mainView provides a delegate through which it will notify when the status bar style should be changed. Example implementation:

self.navigationManager.mainView.baseViewDelegate = self;
....

- (UIStatusBarStyle)preferredStatusBarStyle {
    return self.navigationStatusBarStyle;
}

-(void)baseView:(SKTBaseView *)view requiresStatusBarStyle:(UIStatusBarStyle)style {
    self.navigationStatusBarStyle = style;
    [self setNeedsStatusBarAppearanceUpdate];
}

Please note that you do not need to implement this when the View controller-based status bar appearance is set to NO.

Starting navigation

To start a navigation use the startNavigationWithConfiguration: method of the SKTNavigationManager.

The method requires as parameter an SKTNavigationConfiguration object. This object allows to customize different aspects of the navigation. We also support three types of navigation: pedestrian, bicycle and car. Turn by turn audio advices can be played using TTS (Text to speech) or mp3 files. For more details see the SKTNavigationConfiguration class.

For a quick start SKTNavigationConfiguration provides a default configuration. The only thing that has to be set is the destination coordinate and the navigation type (by default is set to SKNavigationTypeReal which is not very useful when using the simulator or standing in place).

SKTNavigationConfiguration *configuration = [SKTNavigationConfiguration defaultConfiguration];
configuration.destination = CLLocationCoordinate2DMake(latitude, longitude);
configuration.navigationType = SKTNavigationTypeSimulation;

[self.navigationManager startNavigationWithConfiguration:configuration];

Starting free drive

To start free drive use the startFreeDriveWithConfiguration: method SKTNavigationManager.

This method uses the same SKTNavigationConfiguration object. For a quick start you can use the default SKTNavigationConfiguration object.

SKTNavigationConfiguration *configuration = [SKTNavigationConfiguration defaultConfiguration];

self.navigationManager startFreeDriveWithConfiguration:configuration];

Navigation routes

Navigation requires calculating a route first. You can either calculate the route yourself and provide it using the routeInfo property of SKTNavigationConfiguration or you can let the library to do it for you. For adding intermediary points for calculating the route use the viaPoints property.

When route calculation is finished the user has the option to choose which route to be used for navigation.

Route calculation can be customized in a number of ways:

  • The route type (different profiles are available for car, bike and pedestrian routes).
  • Number of routes. The library it will attempt to calculate numberOfRoutes routes.
SKTNavigationConfiguration *configuration = [SKTNavigationConfiguration defaultConfiguration];
config.destination = CLLocationCoordinate2DMake(latitude, longitude);
config.navigationType = SKNavigationTypeSimulation;
config.routeType = SKRouteCarFastest;
config.numberOfRoutes = 1;

[self.navigationManager startFreeDriveWithConfiguration:configuration];

The navigation delegate

The SKTNavigationManager provides a delegate property to inform you when the navigation ends and the reason it ended. This is the time when you should stop the location update if you don't need it and reset the delegates for the following SKMaps APIs (if you are using them):

  • SKMapViewDelegate
  • SKNavigationDelegate
  • SKRoutingDelegate
  • SKPositionerServiceDelegate
- (void)navigationManagerDidStopNavigation:(SKTNavigationManager *)manager withReason:(SKTNavigationStopReason)reason {
    if (reason == SKTNavigationStopReasonRoutingFailed) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Error" message:@"Cannot calculate routes" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *okAlertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
            [self dismissViewControllerAnimated:YES completion:nil];
        }];
        [alertController addAction:okAlertAction];
        [self presentViewController:alertController animated:YES completion:nil];
    }
    self.mapView.delegate = self;
    [[SKPositionerService sharedInstance] cancelLocationUpdate];
    [SKRoutingService sharedInstance].navigationDelegate = self;
    [SKRoutingService sharedInstance].routingDelegate = self;
    [SKPositionerService sharedInstance].delegate = self;
}

Background mode

The navigation can also run while in background. The location will keep updating and the audio advices will still be played.

If you want to prevent this you can use the allowBackgroundNavigation property of the SKTNavigationConfiguration.

Audio advices

The audio advice language can be set to any of the languages available in the SKAdvisorResources.bundle. Use the advisorLanguage property to set the language you want.

The volume is controlled by the user.

By default the advices are played even when a call is active. This can be disabled using playAudioDuringCall property of the SKTNavigationConfiguration.

Map styles

To be more visually appealing, the navigation library uses different map styles and UI colors for day and night. You can choose which styles to be used for both by setting dayStyle and nightStyle in the SKTNavigationConfiguration. The styles are automatically changed at sunrise and sunset which are computed using http://williams.best.vwh.net/sunrise_sunset_algorithm.htm.

SKTNavigationConfiguration *configuration = [[SKTNavigationConfiguration alloc] init];
SKMapViewStyle *dayStyle = [SKMapViewStyle mapViewStyle];
dayStyle.styleFileName = @"CustomDayStyle.json";
dayStyle.resourcesFolderName = @"CustomDayStyle";
configuration.dayStyle = dayStyle;
SKMapViewStyle *nightStyle = [SKMapViewStyle mapViewStyle];
nightStyle.styleFileName = @"CustomNightStyle.json";
nightStyle.resourcesFolderName = @"CustomNightStyle";
configuration.nightStyle = nightStyle;

If you want to disable this behaviour you can use the automaticDayNight property.

Localization

All the strings that are used are localized. The keys and English translations can be found in the SKTNavigationResources.bundle/Localizable.strings.

Download component

Setting up the download component

The map JSON should be parsed before going forward with the download. Map download objects use information from the JSON (sizes, URLs, translations, etc.).

1. In AppDelegate after kSKTMapsVersionFileDownloadSuccessNotification notification is received, the map JSON should be downloaded and then parsed using

SKTMapsObject *skMaps = [SKTMapsObject convertFromJSON:jsonString];
SKTMapsObject encapsulates all the data from the map JSON and SKTPackages can be used to start the download process.

2. Instantiate the SKTDownloadManager in -application:didFinishLaunchingWithOptions: by calling [SKTDownloadManager sharedInstance];
The SKTDownloadManager should be instantiated as soon as possible in order to listen for connectivity changes.

3. Creating a download object.

SKTDownloadObjectHelper *region = [SKTDownloadObjectHelper downloadObjectHelperWithSKTPackage:package];

4. Starting a download

[[SKTDownloadManager sharedInstance] requestDownloads:@[region] startAutomatically:YES withDelegate:self];

Operations that can be performed during downloads

The SKTDownloadManager provides a delegate property to inform of changes regarding your downloads. Look for SKTDownloadManagerDelegate and implement the required methods.

The download manager support operations such as:

  • pause (for a specific download or for all)
  • resume (for a specific download or for all)
  • cancel (for a specific download or for all)
  • restart

Constraints of the download component

The download component supports only one concurrent download at a time.