Apps für die Apple Watch entwickeln

Details

Datenaustausch zwischen Prozessen

Um schneller Daten anzeigen zu können, werden heruntergeladene Fahrplandaten in einem Cache gespeichert, auf den sowohl die iPhone-App als auch die WatchKit Extension zugreifen. Aufgrund von Sicherheitsbeschränkungen haben die beiden Prozesse allerdings keinen gegenseitigen Zugriff auf ihre Daten. In iOS gibt es deshalb die Möglichkeit, über eine sogenannte App Group einen gemeinsamen Bereich zu definieren, auf den alle Extensions und Apps zugreifen können, die Teil der Gruppe sind. Eine App Group lässt sich über das Capabilities Tab eines Targets in Xcode anschalten. Dabei werden automatisch die benötigten Berechtigungen angelegt. Am einfachsten kann eine App Group über die NSUserDefaults API genutzt werden:

let defaults = NSUserDefaults(suiteName:
"group.com.claushoefele.TransitHopper")!

Dabei bezeichnet suiteName den Namen der in Xcode abgelegten App Group. Sobald das NSUserDefaults-Objekt auf diese Weise initialisiert wurde, werden die darin gespeicherten Daten in den gemeinsamen Bereich der App Group gespeichert. Alle Klassen, die das Protokoll NSCoding unterstützen, lassen sich durch diesen Mechanismus serialisieren:

func updateObjectForKey<T: NSCoding>(key: String, item: T) {
let data = NSKeyedArchiver.archivedDataWithRootObject(item)
defaults.setObject(data, forKey: key)
}

Datenänderungen signalisieren

Neben dem Datenaustausch stellt sich noch das Problem der Signalisierung. Woher weiß ein Prozess, dass sich Daten geändert haben? Hier hilft das sogenannte Darwin Notification Center. Es sendet Benachrichtigungen an alle ihre Abonnenten. Im Gegensatz zum bekannteren NSNotificationCenter funktioniert das auch prozessübergreifend. Die API lässt sich so wie im folgenden Beispiel nutzen:

func sendDarwinNotification(identifier: String) {
let darwinNotificationCenter =
CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterPostNotification(darwinNotificationCenter,
identifier, nil, nil, 1);
}

In ähnlicher Weise kann CFNotificationCenterAddObserver verwendet werden, um solche Benachrichtigungen zu empfangen. Leider lassen sich mit dieser außer dem Identifier keine weiteren Daten mitschicken. Dazu muss man auf den oben beschriebenen App-Group-Mechanismus zurückgreifen.

Code und Ressourcen teilen mit Frameworks

Wer eine WatchKit- zusammen mit einer iOS-App entwickelt, wird schnell feststellen, dass viele Klassen in beiden Targets gleich sind. So teilt TransitHopper beispielsweise Code zum Lesen und Schreiben von Daten im gemeinsamen App-Group-Verzeichnis und zum Versenden von Darwin Notifications (siehe vorherige Abschnitte). Die gemeinsamen Klassen können zwar zu beiden Targets statisch gelinkt werden, allerdings führt das zu erhöhtem Speicherverbrauch durch die Duplizierung.

Ab iOS 8 lassen sich stattdessen Embedded Frameworks benutzen, die das Konzept von Shared Libraries auf Apples Betriebssystemen implementieren. Dabei werden in einem separaten Xcode-Target Code und statische Ressourcen gebündelt, das dann Extensions und Apps nutzen können, was aber nur einmal in den Speicher zu laden ist.

Wer den Package-Manager CocoaPods nutzt, kann ab Version 0.36 Abhängigkeiten auch als Frameworks einbinden. Das funktioniert selbst für eigenen Code, der als Pod zur Verfügung steht. TransitHopper nutzt das zum Beispiel für eine Bibliothek zur Abfrage der Abfahrtsdaten.

Mit Handoff eine Aktion auf dem iPhone weiterführen

Um den Wechsel zwischen Geräten zu vereinfachen, hat Apple in iOS 8 und OS X Yosemite eine Reihe von Verbesserungen eingeführt. Zum Beispiel lässt sich mit Handoff eine E-Mail auf der Uhr lesen und dann auf dem iPhone beantworten. Handoff funktioniert mit allen Geräten, die beim selben iCloud-Account angemeldet sind. Wenn Apps Handoff unterstützen, äußert sich das in einem App-Icon in der linken, unteren Ecke des iPhone-Sperrbildschirms. Durch Hochschieben des Icons werden der Bildschirm entsperrt und die entsprechende App gestartet.

WatchKit-Apps können Handoff durch Aufruf von updateUserActivity innerhalb eines WKInterfaceController aktivieren, so wie in folgendem Beispiel:

let identifier = "open"
let userInfo = [String: String]()
updateUserActivity(identifier, userInfo: userInfo, webpageURL: nil)

Dabei lassen sich unterschiedliche Aktivitäten per Identifier ansteuern, denen Entwickler jeweils noch Parameter übergeben können. Zum Beispiel können sie die Detailseite einer Zugverbindung mit der Detailseite in der iPhone-App verknüpfen und die Verbindungsdaten als Parameter übermitteln.

Statt den Uhrenträger mit vielen Informationen zu erschlagen, werden bei TransitHopper nur die wichtigsten Informationen auf der Uhr angezeigt. Durch geschicktes Verknüpfen der WatchKit- und iPhone-Apps durch Handoff lassen sich dann mehr Details anzeigen. Die iPhone- ist damit quasi eine Verlängerung der WatchKit-App, und der Nutzer kann selbst entscheiden, zu welchem Zeitpunkt genügend Informationen zur Beantwortung der Frage "Wann ist die nächste Abfahrt?" zur Verfügung stehen.