Ein Tutorial über iOS 8 App Extensions

Wenige hatten es vorher versucht (siehe hier), aber es war Apple mit dem ersten iPhone, das definierte, wie ein Smartphone und ein mobiles Betriebssystem aussehen sollte. Apple hat einen unglaublichen Durchbruch in Sachen Hardware und Benutzerfreundlichkeit erzielt. Wir vergessen jedoch oft, dass das Unternehmen auch Maßstäbe dafür gesetzt hat, wie ein mobiles Betriebssystem funktionieren sollte und wie Smartphone-Anwendungen erstellt werden sollten.

Die Errichtung von Betonmauern zwischen den Anwendungen, die sie völlig isoliert und voneinander unbemerkt machen, war die beste Methode, um sie sicher zu halten und ihre Daten zu schützen. Alle Aktivitäten wurden von iOS genau überwacht, und es gab nur eine Handvoll Aktionen, die eine App außerhalb ihres Bereichs hätte durchführen können.

„Abstinenz ist der beste Schutz!“ –

Es hat eine Weile gedauert, zu lange, wenn Sie mich fragen, aber mit iOS 8 hat Apple beschlossen, ein wenig Spaß zu haben. iOS 8 führte ein neues Konzept namens App Extensions ein. Diese neue Funktion riss zwar nicht die Mauern zwischen den Anwendungen ein, aber sie öffnete einige Türen und ermöglichte einen sanften, aber spürbaren Kontakt zwischen einigen Apps. Das letzte Update gab iOS-Entwicklern die Möglichkeit, das iOS-Ökosystem anzupassen, und wir sind gespannt darauf, wie sich auch dieser Weg öffnen wird.

Was sind die iOS 8 App Extensions und wie funktionieren sie?

Einfach ausgedrückt, bieten die iOS 8 App Extensions eine neue Methode der Interaktion mit Ihrer Anwendung, ohne sie zu starten oder auf dem Bildschirm anzuzeigen.

Wie erwartet, hat Apple dafür gesorgt, dass sie über alles Bescheid wissen, daher gibt es nur eine Handvoll neuer Einstiegspunkte, die Ihre Anwendung bereitstellen kann:

  • Heute (auch Widget genannt) – eine Erweiterung, die in der Heute-Ansicht des Benachrichtigungscenters angezeigt wird, zeigt kurze Informationen an und ermöglicht die Ausführung schneller Aufgaben.
  • Share – eine Erweiterung, die es Ihrer App ermöglicht, Inhalte mit Nutzern in sozialen Netzwerken und anderen Sharing-Diensten zu teilen.
  • Action – eine Erweiterung, die es ermöglicht, benutzerdefinierte Aktionsschaltflächen im Aktionsblatt zu erstellen, um Nutzern die Möglichkeit zu geben, Inhalte, die aus einer Host-App stammen, anzuzeigen oder zu verändern.
  • Fotobearbeitung – eine Erweiterung, mit der Benutzer ein Foto oder ein Video innerhalb der Fotos-App bearbeiten können.
  • Dokumentenanbieter – eine Erweiterung, die es anderen Apps ermöglicht, auf die von Ihrer App verwalteten Dokumente zuzugreifen.
  • Benutzerdefinierte Tastatur – eine Erweiterung, die die Systemtastatur ersetzt.

App-Erweiterungen sind keine eigenständigen Apps. Sie bieten erweiterte Funktionen der App (auf die von anderen Apps, den sogenannten Host-Apps, zugegriffen werden kann), die effizient und auf eine einzige Aufgabe ausgerichtet sein sollen. Sie verfügen über eine eigene Binärdatei, eine eigene Codesignatur und einen eigenen Satz von Elementen, werden aber über den App Store als Teil der Binärdatei der enthaltenden App ausgeliefert. Eine (enthaltende) App kann mehr als eine Erweiterung haben. Sobald der Benutzer eine App mit Erweiterungen installiert, sind diese in ganz iOS verfügbar.

Betrachten wir ein Beispiel: Ein Benutzer findet ein Bild mit Safari, drückt die Teilen-Taste und wählt Ihre Anwendungserweiterung zum Teilen aus. Safari „spricht“ mit dem iOS Social Framework, das die Erweiterung lädt und präsentiert. Der Code der Erweiterung wird ausgeführt, Daten werden über die instanziierten Kommunikationskanäle des Systems übertragen, und sobald die Aufgabe erledigt ist, beendet Safari die Ansicht der Erweiterung. Kurz darauf beendet das System den Prozess, und Ihre Anwendung wurde nie auf dem Bildschirm angezeigt. Dennoch hat sie eine Funktion zur gemeinsamen Nutzung von Bildern ausgeführt.

iOS ist mit Hilfe der prozessübergreifenden Kommunikation dafür verantwortlich, dass die Host-App und eine App-Erweiterung zusammenarbeiten können. Die Entwickler verwenden High-Level-APIs, die von der Erweiterungsstelle und dem System bereitgestellt werden, so dass sie sich nicht um die zugrunde liegenden Kommunikationsmechanismen kümmern müssen.

Lebenszyklus

App-Erweiterungen haben einen anderen Lebenszyklus als die iOS-Apps. Die Host-App startet den Lebenszyklus der Erweiterung als Reaktion auf eine Aktion des Benutzers. Dann instanziiert das System die App-Erweiterung und baut einen Kommunikationskanal zwischen ihnen auf. Die Ansicht der Erweiterung wird im Kontext der Host-App unter Verwendung der in der Anforderung der Host-App empfangenen Elemente angezeigt. Sobald die Ansicht der Erweiterung angezeigt wird, kann der Benutzer mit ihr interagieren. Als Reaktion auf die Aktion des Benutzers schließt die Erweiterung die Anforderung der Host-App ab, indem sie die Aufgabe sofort ausführt/abbricht oder, falls erforderlich, einen Hintergrundprozess zur Ausführung der Aufgabe initiiert. Unmittelbar danach beendet die Host-Anwendung die Ansicht der Erweiterung und der Benutzer kehrt zu seinem vorherigen Kontext innerhalb der Host-Anwendung zurück. Die Ergebnisse der Ausführung dieses Prozesses können an die Host-App zurückgegeben werden, sobald der Prozess abgeschlossen ist. Die Erweiterung wird in der Regel kurz nach Beendigung der von der Host-App erhaltenen Anforderung beendet (oder startet einen Hintergrundprozess, um sie auszuführen).

Das System öffnet die Erweiterung für die Aktion eines Benutzers aus der Host-App, die Erweiterung zeigt die Benutzeroberfläche an, führt einige Arbeiten aus und gibt Daten an die Host-App zurück (wenn dies für den Typ der Erweiterung angemessen ist).

Erstellen einer App-Erweiterung – Praktisches Beispiel mit der Heute-Erweiterung

Die Heute-Erweiterungen, auch Widgets genannt, befinden sich in der Heute-Ansicht des Benachrichtigungszentrums. Sie eignen sich hervorragend, um dem Benutzer einen aktuellen Inhalt zu präsentieren (z. B. die Wetterbedingungen) oder um schnelle Aufgaben auszuführen (z. B. das Markieren von Erledigungen im Widget einer To-Do-Liste). Ich muss hier darauf hinweisen, dass die Tastatureingabe nicht unterstützt wird.

Lassen Sie uns eine Heute-Erweiterung erstellen, die die aktuellsten Informationen aus unserer App anzeigt (Code auf GitHub). Um diesen Code auszuführen, stellen Sie bitte sicher, dass Sie die App-Gruppe für das Projekt (neu) konfiguriert haben (wählen Sie Ihr Entwicklungsteam aus, denken Sie daran, dass der Name der App-Gruppe eindeutig sein muss und folgen Sie den Anweisungen von Xcode).

Erstellen eines neuen Widgets

Wie wir bereits gesagt haben, sind die App-Erweiterungen keine eigenständigen Apps. Wir brauchen eine enthaltende App, auf der wir die App-Erweiterung aufbauen. Sobald wir unsere enthaltende App haben, fügen wir ein neues Ziel hinzu, indem wir in Xcode zu Datei -> Neu -> Ziel navigieren. Von hier aus wählen wir die Vorlage für unser neues Ziel, um eine Today Extension hinzuzufügen.

Im nächsten Schritt können wir unseren Produktnamen wählen. Das ist der Name, der in der Heute-Ansicht des Notification Centers erscheinen wird. In diesem Schritt gibt es auch die Möglichkeit, die Sprache zwischen Swift und Objective-C zu wählen. Nach Abschluss dieser Schritte erstellt Xcode eine Heute-Vorlage, die Standard-Header- und Implementierungsdateien für die Hauptklasse (mit dem Namen TodayViewController) mit einer Info.plist-Datei und einer Schnittstellendatei (einer Storyboard- oder .xib-Datei) enthält. Die Info.plist-Datei sieht standardmäßig wie folgt aus:

Wenn Sie das von der Vorlage bereitgestellte Storyboard nicht verwenden möchten, entfernen Sie den Schlüssel NSExtensionMainStoryboard und fügen Sie den Schlüssel NSExtensionPrincipalClass mit dem Namen Ihres View-Controllers als Wert hinzu.

Ein Heute-Widget sollte:

  • sicherstellen, dass der Inhalt immer aktuell aussieht
  • angemessen auf Benutzerinteraktionen reagieren
  • gut funktionieren (iOS Widgets müssen den Speicher sinnvoll nutzen, sonst werden sie vom System beendet)

Gemeinsame Daten und ein gemeinsamer Container

Die App-Erweiterung und die sie enthaltende App haben beide Zugriff auf die gemeinsamen Daten in ihrem privat definierten gemeinsamen Container – was eine Möglichkeit der indirekten Kommunikation zwischen der enthaltenden App und der Erweiterung ist.

Ist es nicht toll, wie Apple diese Dinge so „einfach“ macht? 🙂

Die gemeinsame Nutzung von Daten über NSUserDefaults ist einfach und ein häufiger Anwendungsfall. Standardmäßig verwenden die Erweiterung und die darin enthaltene App getrennte NSUserDefaults-Datensätze und können nicht auf die Container des jeweils anderen zugreifen. Um dieses Verhalten zu ändern, hat iOS App-Gruppen eingeführt. Nach der Aktivierung von App-Gruppen für die enthaltende App und die Erweiterung kann statt initWithSuiteName:@"group.yourAppGroupName"] für den Zugriff auf denselben gemeinsamen Container verwendet werden.

Aktualisierung des Widgets

Um sicherzustellen, dass der Inhalt immer auf dem neuesten Stand ist, bietet die Heute-Erweiterung eine API für die Verwaltung des Widget-Status und die Handhabung von Inhaltsaktualisierungen. Das System erfasst gelegentlich Schnappschüsse der Ansicht des Widgets, so dass, wenn das Widget sichtbar wird, der neueste Schnappschuss angezeigt wird, bis er durch eine Live-Version der Ansicht ersetzt wird. Eine Konformität mit dem NCWidgetProviding-Protokoll ist wichtig, um den Zustand eines Widgets zu aktualisieren, bevor ein Schnappschuss aufgenommen wird. Sobald das Widget den widgetPerformUpdateWithCompletionHandler:-Aufruf erhält, sollte die Ansicht des Widgets mit dem neuesten Inhalt aktualisiert werden und der Completion-Handler sollte mit einer der folgenden Konstanten aufgerufen werden, um das Ergebnis der Aktualisierung zu beschreiben:

  • NCUpdateResultNewData – Der neue Inhalt erfordert ein erneutes Zeichnen der Ansicht
  • NCUpdateResultNoDate – Das Widget muss nicht aktualisiert werden
  • NCUpdateResultFailed – Während des Aktualisierungsprozesses ist ein Fehler aufgetreten

Steuern, wann das Widget sichtbar ist

Um zu steuern, wann ein Widget angezeigt wird, verwenden Sie die Methode setHasContent:forWidgetWithBundleIdentifier: der Klasse NCWidgetController. Mit dieser Methode können Sie den Zustand des Widget-Inhalts festlegen. Sie kann vom Widget oder von der App, die es enthält, aufgerufen werden (wenn es aktiv ist). Sie können ein NO– oder ein YES-Flag an diese Methode übergeben, um festzulegen, ob der Inhalt des Widgets bereit ist oder nicht. Wenn der Inhalt nicht bereit ist, zeigt iOS das Widget nicht an, wenn die Heute-Ansicht geöffnet wird.

Öffnen der enthaltenden App vom Widget aus

Das Heute-Widget ist die einzige Erweiterung, die das Öffnen der enthaltenden App durch den Aufruf der Methode openURL:completionHandler: anfordern kann. Um sicherzustellen, dass die enthaltende App auf eine Art und Weise geöffnet wird, die im Kontext der aktuellen Aufgabe des Benutzers sinnvoll ist, sollte ein benutzerdefiniertes URL-Schema (das sowohl vom Widget als auch von der enthaltenden App verwendet werden kann) definiert werden.

 completionHandler:nil];

Überlegungen zur Benutzeroberfläche

Wenn Sie Ihr Widget entwerfen, nutzen Sie die Vorteile der Klasse UIVisualEffectView, wobei die Ansichten, die unscharf/lebendig sein sollen, zur Klasse contentView und nicht direkt zur Klasse UIVisualEffectView hinzugefügt werden müssen. Widgets (die dem NCWidgetProviding-Protokoll entsprechen) sollten zwischengespeicherte Zustände in viewWillAppear: laden, um mit dem Zustand des Views vom letzten viewWillDisappear: übereinzustimmen und dann nahtlos zu den neuen Daten überzugehen, wenn diese ankommen, was bei einem normalen View-Controller nicht der Fall ist (UI wird in viewDidLoad eingerichtet und behandelt Animationen und das Laden von Daten in viewWillAppear). Widgets sollten für die Ausführung einer Aufgabe oder das Öffnen der enthaltenen App mit einem einzigen Tippen konzipiert sein. Die Tastatureingabe ist innerhalb eines Widgets nicht möglich. Das bedeutet, dass jede Benutzeroberfläche, die eine Texteingabe erfordert, nicht verwendet werden sollte.

Das Hinzufügen von Bildläufen in einem Widget, sowohl vertikal als auch horizontal, ist nicht möglich. Oder genauer gesagt, das Hinzufügen einer Scroll-Ansicht ist möglich, aber das Scrollen wird nicht funktionieren. Eine horizontale Scroll-Geste in einer Scroll-Ansicht in der Heute-Erweiterung wird von der Benachrichtigungszentrale abgefangen, was zu einem Scrollen von Heute zur Benachrichtigungszentrale führt. Vertikales Scrollen in einer Scroll-Ansicht innerhalb einer Heute-Erweiterung wird durch das Scrollen der Heute-Ansicht unterbrochen.

Technische Hinweise

Hier werde ich auf einige wichtige Dinge hinweisen, die bei der Erstellung einer App-Erweiterung zu beachten sind.

Gemeinsame Merkmale aller Erweiterungen

Die folgenden Punkte gelten für alle Erweiterungen:

  • Das SharedApplication-Objekt ist tabu: App-Erweiterungen können nicht auf ein SharedApplication-Objekt zugreifen oder eine der Methoden verwenden, die sich auf dieses Objekt beziehen.

  • Kamera und Mikrofon sind nicht erlaubt: App-Erweiterungen können nicht auf die Kamera oder das Mikrofon des Geräts zugreifen (dies gilt jedoch nicht für alle Hardwareelemente). Dies ist auf die Nichtverfügbarkeit einiger APIs zurückzuführen. Um auf einige Hardware-Elemente in der App-Erweiterung zuzugreifen, müssen Sie prüfen, ob ihre API für App-Erweiterungen verfügbar ist oder nicht (mit der oben beschriebenen API-Verfügbarkeitsprüfung).

  • Die meisten Hintergrundaufgaben sind nicht zulässig: App-Erweiterungen können keine lang andauernden Hintergrundaufgaben durchführen, mit Ausnahme des Initiierens von Up- oder Downloads, was weiter unten besprochen wird.

  • AirDrop ist verboten: App-Erweiterungen können keine Daten über AirDrop empfangen (aber senden).

Hochladen/Herunterladen im Hintergrund

Die einzige Aufgabe, die im Hintergrund ausgeführt werden kann, ist das Hochladen/Herunterladen unter Verwendung der NSURLSession object.

Nach dem Starten der Upload-/Download-Aufgabe kann die Erweiterung die Anforderung der Host-App abschließen und ohne Auswirkungen auf das Ergebnis der Aufgabe beendet werden. Wenn die Erweiterung zum Zeitpunkt des Abschlusses der Hintergrundaufgabe nicht läuft, startet das System die enthaltende App im Hintergrund und die Delegate-Methode application:handleEventsForBackgroundURLSession:completionHandler: der App wird aufgerufen.

Die App, deren Erweiterung eine NSURLSessionHintergrundaufgabe initiiert, muss einen gemeinsam genutzten Container eingerichtet haben, auf den sowohl die enthaltende App als auch ihre Erweiterung zugreifen können.

Stellen Sie sicher, dass für die enthaltende App und jede ihrer App-Erweiterungen unterschiedliche Hintergrundsitzungen erstellt werden (jede Hintergrundsitzung sollte einen eindeutigen Bezeichner haben). Dies ist wichtig, da immer nur ein Prozess eine Hintergrundsitzung verwenden kann.

Action vs. Share

Die Unterschiede zwischen den Erweiterungen Action und Share sind aus der Sicht eines Programmierers nicht ganz klar, da sie in der Praxis sehr ähnlich sind. Die Xcode-Vorlage für das Ziel der Share-Erweiterung verwendet SLComposeServiceViewController, die eine standardmäßige Compose View UI bietet, die Sie für Social Sharing verwenden können, aber nicht müssen. Eine Share-Erweiterung kann auch direkt von UIViewController für ein vollständig benutzerdefiniertes Design erben, genauso wie eine Action-Erweiterung von SLComposeServiceViewController erben kann.

Die Unterschiede zwischen diesen beiden Arten von Erweiterungen liegen darin, wie sie verwendet werden sollen. Mit der Action-Erweiterung können Sie eine Erweiterung ohne eigene Benutzeroberfläche erstellen (z. B. eine Erweiterung, die für die Übersetzung des ausgewählten Textes und die Rückgabe der Übersetzung an die Host-App verwendet wird). Mit der Share-Erweiterung können Sie Kommentare, Fotos, Videos, Audios, Links und vieles mehr direkt von der Host-App aus teilen. Die UIActivityViewController steuert sowohl Action- als auch Share-Erweiterungen, wobei Share-Erweiterungen als farbige Icons in der oberen Reihe und die Action-Erweiterungen als monochrome Icons in der unteren Reihe dargestellt werden (Bild 2.1).

Verbotene APIs

APIs, die in den Header-Dateien mit dem Makro NS_EXTENSION_UNAVAILABLE oder einem ähnlichen Makro für Nichtverfügbarkeit gekennzeichnet sind, können nicht verwendet werden (zum Beispiel: HealthKit und EventKit UI-Frameworks in iOS 8 sind für die Verwendung in einer App-Erweiterung nicht verfügbar).

Wenn Sie Code zwischen einer App und einer Erweiterung austauschen, müssen Sie bedenken, dass auch der Verweis auf eine API, die für die App-Erweiterung nicht erlaubt ist, zur Zurückweisung Ihrer App aus dem App Store führt. Sie können das Problem lösen, indem Sie die gemeinsam genutzten Klassen in Hierarchien umstrukturieren, mit einer gemeinsamen übergeordneten Klasse und verschiedenen Unterklassen für verschiedene Ziele.#ifdef Eine andere Möglichkeit ist die Verwendung des Präprozessors durch #ifdef Prüfungen. Da es immer noch keine eingebaute Zielbedingung gibt, müssen Sie Ihre eigene erstellen.

Eine andere nette Möglichkeit ist, ein eigenes eingebettetes Framework zu erstellen. Stellen Sie nur sicher, dass es keine APIs enthält, die für Erweiterungen nicht verfügbar sind. Um eine App-Erweiterung für die Verwendung eines eingebetteten Frameworks zu konfigurieren, navigieren Sie zu den Build-Einstellungen des Ziels und setzen die Einstellung „Require Only App-Extension-Safe API“ auf Yes. Beim Konfigurieren des Xcode-Projekts muss in der Build-Phase Copy Files“ als Ziel für das eingebettete Framework Framework Frameworks“ gewählt werden. Wenn Sie das Ziel „SharedFrameworks“ wählen, wird Ihre Einreichung vom App Store abgelehnt.

Ein Hinweis zur Abwärtskompatibilität

Obwohl App-Erweiterungen erst seit iOS 8 verfügbar sind, können Sie Ihre enthaltende App für die früheren iOS-Versionen verfügbar machen.

Einhaltung der Apple Human Interface Compliance

Beachten Sie beim Entwurf einer App-Erweiterung die iOS Human Interface Guidelines von Apple. Sie müssen sicherstellen, dass Ihre App-Erweiterung universell ist, unabhängig davon, welches Gerät Ihre enthaltende App unterstützt. Um sicherzustellen, dass die App-Erweiterung universell ist, verwenden Sie die Build-Einstellung „Targeted Device Family“ in Xcode und geben den Wert „iPhone/iPad“ an (manchmal auch „universal“ genannt).

Fazit

App-Erweiterungen haben definitiv die sichtbarsten Auswirkungen in iOS 8. Da bereits 79 % der Geräte iOS 8 nutzen (gemessen vom App Store am 13. April 2015), sind die App-Erweiterungen unglaubliche Funktionen, die Apps nutzen sollten. Mit der Kombination der API-Beschränkungen und der Art und Weise, wie Daten zwischen den Erweiterungen und der sie enthaltenden App ausgetauscht werden, scheint es Apple gelungen zu sein, einen der größten Kritikpunkte an der Plattform zu beseitigen, ohne sein Sicherheitsmodell zu gefährden. Es gibt immer noch keine Möglichkeit für Drittanbieter-Apps, ihre Daten direkt miteinander zu teilen. Obwohl dies ein sehr neues Konzept ist, sieht es sehr vielversprechend aus.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.