Apps für die Apple Watch entwickeln

Seite 2: Anwendungsentwicklung

Inhaltsverzeichnis

Entwickler können drei Arten von Anwendungen für die Watch entwickeln:

  • WatchKit-Apps werden vom Homescreen der Uhr gestartet und sind die leistungsstärkste Art und Weise, um Anwendungen für die Apple Watch zu erstellen.
  • Glances sind optionale Erweiterungen von WatchKit-Apps und werden mit einer Wischgeste nach oben in der Uhrenansicht aktiviert. Sie zeigen die wichtigsten Informationen einer App zusammengefasst auf einem Screen an und ähneln damit den Widgets in der Heute-Ansicht des iPhones.
  • Notifications werden zwar automatisch von der Apple Watch unterstützt, wenn ein iPhone sie empfängt, Entwickler können sie aber anpassen, damit sie für den Uhrträger einfacher zu nutzen sind.

Allen Erweiterungen gemeinsam ist die Tatsache, dass grundsätzlich ein iOS-Gerät (normalerweise ein iPhone) mit der Uhr verbunden sein muss. Dabei teilt sich die Uhr die Arbeit mit dem iPhone: Beim Ausführen des Codes auf dem Smartphone werden auf der Uhr statische Ressourcen wie Bilder und Layoutinformationen gelagert. Vom Code erzeugte Daten werden dann drahtlos an die Uhr geschickt und angezeigt.

Die allgemeine Erwartung ist, dass ein zukünftiges SDK auch WatchKit-Apps ermöglicht, die unabhängig vom iPhone funktionieren.

Am Beispiel der Fahrplan-App TransitHopper sollen hier die technischen Herausforderungen einer WatchKit-App erläutert werden. Dabei handelt es sich um eine App, die nahegelegene Abfahrten des Nah- und Fernverkehrs in einer Liste darstellt und sowohl eine Glance- als auch eine WatchKit-App unterstützt.

Die Fahrplan-App im Beispiel umfasst eine Glance sowie Bildschirme für Zugverbindungen.

Eine Fahrplan-App ist sowohl auf der Apple Watch als auch auf dem iPhone nützlich. Das ist unbedingt notwendig, da sich WatchKit-Apps nur zusammen mit einer iOS-App ausliefern lassen.

WatchKit-Apps lassen sich einfach als zusätzliches Target in Xcode anlegen. Dabei können Entwickler auch gleich Vorlagen für eine Notification und eine Glance erzeugen. Die Trennung zwischen Code, der auf dem Smartphone ausgeführt wird, und statischen Ressourcen, die sich auf der Uhr befinden, sieht man an den von Xcode erzeugten Verzeichnissen: Der Ordner mit der Endung "WatchKit Extension" enthält den Code, der mit "WatchKit App" die statischen Ressourcen. Zu Anfang ist die wichtigste statische Ressource die Storyboard-Datei, in der das User Interface der App definiert wird. Storyboards dienen der grafischen Anordnung von Bildschirmen und UI-Elementen und sind bei WatchKit-Apps Pflicht geworden. Man kann also das User Interface nicht per Code zur Laufzeit erstellen (es lassen sich
allerdings dynamische Listen über Tabellen erstellen sowie einzelne UI-Elemente verstecken oder anzeigen).

Storyboards ermöglichen die grafische Anordnung von UI-Elementen einer WatchKit-App. Um die Abfahrten der Züge und Busse anzuzeigen, enthält das Storyboard im obigen Beispiel Bildschirme mit einer Liste der Abfahrten und einen Detail-Screen für jede Verbindung. Des Weiteren gibt es noch eine Glance, die die nächste Abfahrt anzeigt. Dabei sind die Texte im Storyboard noch Platzhalter für die eigentlichen Daten, die der Code zur Laufzeit einfügt.

Storyboard

Um die UI mit Inhalten zu befüllen, ist eine Klasse anzulegen, die von WKInterfaceController ableitet und einem Bildschirm im Storyboard zugeordnet ist. Diese Klasse erfüllt eine ähnliche Funktion wie
UIViewController für iPhone-Apps. Hier zum Beispiel ein Auszug aus der Klasse, die Details der Abfahrten darstellt:

class TripDetailInterfaceController: WKInterfaceController {

@IBOutlet private weak var stationLabel: WKInterfaceLabel!
@IBOutlet private weak var map: WKInterfaceMap!

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

let trip = context as CCHTransportTrip
// Map
if let walkLeg = trip.legForWalkToDeparture() {
if let destinationLocation = walkLeg.stops.last as?
CCHTransportLocation {
map.addAnnotation(destinationLocation.coordinate,
withPinColor: .Red)
let coordinateRegion =
MKCoordinateRegionMakeWithDistance
(destinationLocation.coordinate, 500, 500)
map.setRegion(coordinateRegion)
}
}

// Station
if let departureLeg = trip.legForDeparture() {
let firstStop = departureLeg.stops.first as
CCHTransportLocation
setTitle(departureLeg.service.name)
stationLabel.setText(firstStop.name)
}
}
}

awakeWithContext lässt sich zur einmaligen Initialisierung von UI-Elementen benutzen. Im obigen Beispiel werden der Ort der Abfahrtshaltestelle auf einer Karte und der Name der Haltestelle in einem Label angezeigt. Zwei Aspekte sind dabei besonders interessant: Zum einen bekommt awakeWithContext ein Kontext-Objekt übergeben, das zur Übergabe von Parametern der aufrufenden Klasse dient. In diesem Fall ist die aufrufende Klasse der Bildschirm mit der Liste der Abfahrtsdaten. Wenn eine Verbindung vom Nutzer ausgewählt wurde, wird das entsprechende Verbindungsdatenobjekt über den Kontext an die Detailseite übergeben.

Zum anderen besitzen UI-Elemente in WatchKit ausschließliche Methoden zum Schreiben von Daten. Ein WKInterfaceLabel hat also eine Methode setText, aber keine entsprechende Methode zum Lesen des aktuellen Inhalts. Das macht Sinn, wenn man bedenkt, dass der Code auf dem iPhone ausgeführt wird, aber sich das eigentliche Label auf der Uhr befindet.

Alle Datenänderungen sind drahtlos zwischen beiden Geräten zu übertragen. Es ist also viel besser, sich den aktuellen Zustand selbst zu merken, statt die Daten wieder von der Uhr zurückzusenden.

Um die nächsten Abfahrten zu einem bestimmten Ziel zu laden, muss man sowohl den Ort des Benutzers kennen als auch einen Netzwerk-Request ausführen. Grundsätzlich lassen sich diese Funktionen in der WatchKit Extension ausführen. Da diese auf dem iPhone ausgeführt wird, kann sie auf den GPS-Empfänger und die Netzwerkverbindung des Smartphones zugreifen. Allerdings ist eine
WatchKit Extension kurzlebig, was vor allem der Interaktion mit der Uhr geschuldet ist: Statt wie beim iPhone minutenlang die App zu nutzen, erwartet Apple in seinen Empfehlungen, dass Nutzer lediglich Sekunden mit der Uhr verbringen. Sobald die WatchKit-App nicht mehr sichtbar ist, wird auch die WatchKit Extension beendet, sodass wenig Zeit für Netzwerk-Requests zur Verfügung steht. Apple empfiehlt deshalb, langwierige Arbeiten als Hintergrund-Task der iPhone-App zu übergeben. Dazu gibt es in WKInterfaceController die Methode openParentApplication, die sich folgendermaßen nutzen lässt:

private func callParentApp() {
let userInfo = ["input": "data"]
WKInterfaceController.openParentApplication(userInfo) { replyInfo,
error in
println("reply \(replyInfo)")
}
}

Durch den Aufruf von openParentApplication lässt sich ein Dictionary mit Daten übergeben, das von der WatchKit Extension an die iOS-App gesendet wird. Falls die App zu diesem Zeitpunkt noch nicht existiert, wird sie automatisch im Hintergrund gestartet. Nachdem sie die Daten behandelt hat, kann sie dann wiederum ein Dictionary zurückschicken.

Die iOS-App kann auf den Aufruf im Delegate von UIApplication reagieren:

func application(application: UIApplication!,
handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!,
reply: (([NSObject : AnyObject]!) -> Void)!) {
backgroundTask = application.beginBackgroundTaskWithExpirationHandler {

UIApplication.sharedApplication().endBackgroundTask(self.backgroundTask)
reply(nil)
}

reply(["output": "data"])

UIApplication.sharedApplication().endBackgroundTask(self.backgroundTask)
}

In diesem Fall schickt die iOS-App nur Beispieldaten zurück, aber Entwickler können sich vorstellen, dass stattdessen der Netzwerk-Request für neue Abfahrtsdaten stattfindet. Es empfiehlt sich, den auszuführenden Code mit den Methoden beginBackgroundTaskWithExpirationHandler und endBackgroundTask als Hintergrund-Task zu markieren, um so die maximale Ausführungszeit von iOS zur Verfügung gestellt zu bekommen.