A watchOS 2 WatchKit Map Tutorial

Revision as of 19:58, 27 October 2016 by Neil (Talk | contribs) (Text replacement - "<table border="0" cellspacing="0">" to "<table border="0" cellspacing="0" width="100%">")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Revision as of 19:58, 27 October 2016 by Neil (Talk | contribs) (Text replacement - "<table border="0" cellspacing="0">" to "<table border="0" cellspacing="0" width="100%">")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
PreviousTable of ContentsNext
Supporting Different Apple Watch Display Sizes in watchOS 2An Overview of Notifications in watchOS 2


Purchase the full edition of this watchOS 2 App Development Essentials book in eBook ($12.99) or Print ($27.99) format
watchOS 2 App Development Essentials Print and eBook (ePub/PDF/Kindle) editions contain 35 chapters.

Buy Print


WatchKit currently provides limited support for displaying maps within an app running on an Apple Watch device. The features offered by the WKInterfaceMap class consist of the ability to display a designated map region and to add annotations in the form of colored pins or custom images at specified locations within the defined region. When tapped by the user, the Map object opens the built-in Apple Watch Map app configured to display the same region. The situation has improved slightly with watchOS 2 in that the Core Location framework is now available for use within the WatchKit extension. As will be demonstrated in this chapter, this allows tasks such as obtaining the user’s current location to be performed from within the WatchKit extension without the need to request the information from the companion iOS app.

The remainder of this chapter will work through a basic tutorial designed to highlight some of the key features of the WKInterfaceMap class. The project will demonstrate the use of the Core Location framework to obtain current location information and use it to display the current location as an annotation within a WKInterfaceMap object.

Creating the Example Map Project

Start Xcode and create a new iOS project. On the template screen choose the Application option located under watchOS in the left hand panel and select iOS App with WatchKit App. Click Next, set the product name to MapDemoApp, enter your organization identifier and make sure that the Devices menu is set to Universal. Before clicking Next, change the Language menu to Swift and switch off all of the Include options. On the final screen, choose a location in which to store the project files and click on Create to proceed to the main Xcode project window.

Designing the WatchKit App User Interface

Select the Interface.storyboard file and drag and drop a Map and Slider object from the Object Library onto the scene canvas so that the layout matches that shown in Figure 31 1:


Watchkit map sample ui2.png

Figure 31-1


Select the Slider object, display the Attributes Inspector and configure the following property values:

  • Value: 1
  • Minimum: 1
  • Maximum: 10
  • Steps: 10

Display the Assistant Editor and establish an outlet connection from the Map object named mapObject. With the Assistant Editor still displayed, establish an action connection from the Slider object to a method named changeMapRegion.


Configuring the Containing iOS App

Before any application can begin to track location information when running in the background it must first seek permission to do so from the user. This can be achieved by making a call to the requestWhenInUseAuthorization method of the CLLocationManager instance.

Select the InterfaceController.swift file for the WatchKit extension target and modify the code to import the CoreLocation framework, store a reference to the CLLocationManager instance and call the requestWhenInUseAuthorization method within the awakeWithContext method:

import WatchKit
import Foundation
import CoreLocation

class InterfaceController: WKInterfaceController {

    @IBOutlet var mapObject: WKInterfaceMap!
    var locationManager: CLLocationManager = CLLocationManager()

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)

        locationManager.requestWhenInUseAuthorization()
    }
.
.
}

The requestWhenInUseAuthorization method call requires that a specific key-value pair be added to the Information Property List dictionary contained within the application’s Info.plist file. The value takes the form of a string describing the reason why the application needs access to the user’s current location. In the case of background access to location information, the NSLocationWhenInUseUsageDescription key must be added to the property list.

Within the Project Navigator panel, load the Info.plist file (the one located under the MapDemoApp target, not either of the WatchKit target Info.plist files) into the editor. The key-value pair needs to be added to the Information Property List dictionary. Select this entry in the list and click on the + button to add a new entry to the dictionary. Within the new entry, enter NSLocationWhenInUseUsageDescription into the key column and, once the key has been added, double-click in the corresponding value column and enter the following text:

This information is required to identify your current location

Once the entry has been added to the Info.plist file it should appear as illustrated in Figure 31-2:

Watchkit add usage description key.png

Figure 31-2


The WatchKit app must now be run and the location tracking request approved. Select MapDemoApp WatchKit App in the Xcode run target menu and launch the app on a device or simulator session. The first time that the app is launched, the system will display a message on the Apple Watch indicating that permission must be given on the iPhone device to track location information (Figure 31-3). Using the paired iPhone tap the Allow button on the request panel to enable this access.


Watchkit location permission.png

Figure 31-3


Getting the Current Location

The next step is to instruct the location manager to request the user’s current location. This involves declaring the InterfaceController class as implementing the CLLocationManagerDelegate protocol, assigning the class as the delegate for the location manager and then the location update. Note that the code also declares a variable in which to store the current map location:

import WatchKit
import Foundation
import CoreLocation

class InterfaceController: WKInterfaceController, CLLocationManagerDelegate {

    @IBOutlet var mapObject: WKInterfaceMap!
    var locationManager: CLLocationManager = CLLocationManager()
    var mapLocation: CLLocationCoordinate2D?

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)

        locationManager.requestWhenInUseAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.requestLocation()
    }
.
.
}

When the location manager receives a location update, it will call the didUpdateLocations method on the delegate. This class now needs to be implemented in the InterfaceController.swift file along with the didFailWithError delegate method as follows:

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
    let currentLocation = locations[0]
    let lat = currentLocation.coordinate.latitude
    let long = currentLocation.coordinate.longitude

    self.mapLocation = CLLocationCoordinate2DMake(lat, long)

    let span = MKCoordinateSpanMake(0.1, 0.1)

    let region = MKCoordinateRegionMake(self.mapLocation!, span)
    self.mapObject.setRegion(region)

    self.mapObject.addAnnotation(self.mapLocation!, 
			withPinColor: .Red)
}

func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {

    print(error.description)
}

The code begins by accessing the current location data from the array passed to the method and extracting the latitude and longitude values.

These values are then used to create a CLLocationCoordinate2D object which is stored in the previously declared mapLocation variable:

self.mapLocation = CLLocationCoordinate2DMake(lat, long)

Next a span value is defined to dictate the area that will be covered by the map region. This is then used along with the current location to create the region that will be displayed by the map:

let span = MKCoordinateSpanMake(0.1, 0.1)
let region = MKCoordinateRegionMake(self.mapLocation!, span)

Finally, the map object is configured to display the region and a red pin added to mark the current location:

self.mapObject.setRegion(region)
self.mapObject.addAnnotation(self.mapLocation!, withPinColor: .Red)

The color of the pin is specified using the WKInterfaceMapPinColor constant which provides the following color options:

  • WKInterfaceMapPinColor.Red
  • WKInterfaceMapPinColor.Green
  • WKInterfaceMapPinColor.Purple

With these changes made to the interface controller class, compile and run the WatchKit app which, when running, should display a map region centered around the user’s current location as shown in Figure 31 4:


Watchkit map app running.png

Figure 31-4


When running in the simulator, the location will be based on the current setting of the Debug -> Location menu.

Adding Zooming Support

A zooming effect can be added to a map by enlarging and reducing the currently displayed region. For the purposes of this example, the current region will be modified by the Slider object. Previously in this chapter the Slider object in the WatchKit app scene was connected to an action method named changeMapRegion. Edit the InterfaceController.swift file, locate this method and modify it as follows to change the region span based on the current slider setting:

@IBAction func changeMapRegion(value: Float) {

    let degrees:CLLocationDegrees = CLLocationDegrees(value / 10)

    let span = MKCoordinateSpanMake(degrees, degrees)
    let region = MKCoordinateRegionMake(mapLocation!, span)

    mapObject.setRegion(region)
}

Run the WatchKit app again and check that changes to the Slider object are reflected in the currently displayed map region giving the effect of zooming in and out of the map.

Summary

Maps are represented in WatchKit by the WKInterfaceMap class. This class is limited to displaying a static map region together with optional annotation markers in the form of pins or custom images. Tapping on the map launches the built-in Apple Watch Map app configured to display the same region where a wider range of options are available to the user.

This chapter has worked through the creation of an example project intended to highlight the basic features of the WKInterfaceMap class and to demonstrate the use of the Core Location framework in obtaining location data on behalf of the Map object.


Purchase the full edition of this watchOS 2 App Development Essentials book in eBook ($12.99) or Print ($27.99) format
watchOS 2 App Development Essentials Print and eBook (ePub/PDF/Kindle) editions contain 35 chapters.

Buy Print



PreviousTable of ContentsNext
Supporting Different Apple Watch Display Sizes in watchOS 2An Overview of Notifications in watchOS 2