Ein Tutorial zu iOS 8 App-Erweiterungen
Veröffentlicht: 2022-03-11Nur 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 sollten. Apple hat einen unglaublichen Durchbruch bei Hardware und Benutzererfahrung erzielt. Wir vergessen jedoch oft, dass sie auch Standards dafür setzen, wie ein mobiles Betriebssystem funktionieren sollte und wie eine Smartphone-Anwendung erstellt werden sollte.
Der Bau von Betonwänden zwischen den Anwendungen, die sie vollständig isolierten und sich gegenseitig nicht bewusst machten, 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 ausführen können.
„Abstinenz ist der beste Schutz!“ - aber wo bleibt da der Spaß?
Sie brauchten eine Weile; Zu lange, wenn Sie mich fragen, aber mit iOS 8 hat Apple beschlossen, etwas Spaß zu haben. iOS 8 führte ein neues Konzept namens App Extensions ein. Diese neue Funktion hat die Mauern zwischen den Anwendungen nicht niedergerissen, aber einige Türen geöffnet, die einen sanften, aber spürbaren Kontakt zwischen einigen Apps ermöglichten. Das neueste Update gab iOS-Entwicklern die Möglichkeit, das iOS-Ökosystem anzupassen, und wir sind gespannt darauf, dass sich dieser Weg ebenfalls öffnet.
Was sind die iOS 8 App-Erweiterungen und wie funktionieren sie?
Einfach ausgedrückt, bietet iOS 8 App Extensions eine neue Methode zur Interaktion mit Ihrer Anwendung, ohne sie zu starten oder auf dem Bildschirm anzuzeigen.
Wie erwartet hat Apple dafür gesorgt, dass sie über alles auf dem Laufenden bleiben, sodass es nur eine Handvoll neuer Einstiegspunkte gibt, die Ihre Anwendung bieten kann:
- Heute (auch als Widget bezeichnet) – eine Erweiterung, die in der Heute-Ansicht der Benachrichtigungszentrale angezeigt wird, zeigt kurze Informationen und ermöglicht die Ausführung schneller Aufgaben.
- Teilen – eine Erweiterung, die es Ihrer App ermöglicht, Inhalte mit Benutzern in sozialen Netzwerken und anderen Freigabediensten zu teilen.
- Aktion – eine Erweiterung, mit der benutzerdefinierte Aktionsschaltflächen im Aktionsblatt erstellt werden können, damit Benutzer Inhalte anzeigen oder transformieren können, die aus einer Host-App stammen.
- Fotobearbeitung – eine Erweiterung, mit der Benutzer ein Foto oder Video in der Fotos-App bearbeiten können.
- Document Provider – eine Erweiterung, die verwendet wird, um anderen Apps den Zugriff auf die von Ihrer App verwalteten Dokumente zu ermöglichen.
- Benutzerdefinierte Tastatur - eine Erweiterung, die die Systemtastatur ersetzt.
App-Erweiterungen sind keine eigenständigen Apps. Sie bieten eine erweiterte Funktionalität der App (auf die von anderen Apps, den sogenannten Host-Apps, zugegriffen werden kann), die effizient und auf eine einzelne Aufgabe ausgerichtet sein soll. Sie haben ihre eigene Binärdatei, ihre eigene Code-Signatur und ihren eigenen Satz von Elementen, werden aber über den App Store als Teil der Binärdatei der enthaltenden App bereitgestellt. Eine (enthaltende) App kann mehr als eine Erweiterung haben. Sobald der Benutzer eine App mit Erweiterungen installiert, sind diese auf iOS verfügbar.
Schauen wir uns ein Beispiel an: Ein Benutzer findet ein Bild mit Safari, klickt auf die Schaltfläche „Teilen“ 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, leitet Daten über die instanziierten Kommunikationskanäle des Systems weiter, und sobald die Aufgabe erledigt ist, reißt Safari die Erweiterungsansicht herunter. Kurz darauf bricht das System den Prozess ab und Ihre Bewerbung wurde nie auf dem Bildschirm angezeigt. Dennoch hat es eine Funktion zum Teilen von Bildern abgeschlossen.
iOS ist mithilfe der Kommunikation zwischen Prozessen dafür verantwortlich, dass die Host-App und eine App-Erweiterung zusammenarbeiten können. Die Entwickler verwenden High-Level-APIs, die vom Erweiterungspunkt und dem System bereitgestellt werden, sodass sie sich nie 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 die Aktion eines Benutzers. Dann instanziiert das System die App-Erweiterung und richtet einen Kommunikationskanal zwischen ihnen ein. Die Ansicht der Erweiterung wird im Kontext der Host-App angezeigt, wobei die in der Anfrage der Host-App empfangenen Elemente verwendet werden. Sobald die Ansicht der Erweiterung angezeigt wird, kann der Benutzer damit interagieren. Als Reaktion auf die Aktion des Benutzers vervollständigt die Erweiterung die Anfrage der Host-App, indem sie die Aufgabe sofort ausführt/abbricht oder, falls erforderlich, einen Hintergrundprozess initiiert, um sie auszuführen. Unmittelbar danach bricht die Host-App die Ansicht der Erweiterung ab und der Benutzer kehrt zu seinem vorherigen Kontext innerhalb der Host-App zurück. Die Ergebnisse aus der Durchführung dieses Prozesses könnten an die Host-App zurückgegeben werden, sobald der Prozess abgeschlossen ist. Die Erweiterung wird normalerweise bald beendet, nachdem sie die von der Host-App empfangene Anfrage abgeschlossen hat (oder einen Hintergrundprozess gestartet hat, um sie auszuführen).
Das System öffnet die Erweiterung einer Benutzeraktion aus der Host-App, die Erweiterung zeigt die Benutzeroberfläche an, führt einige Aufgaben aus und gibt Daten an die Host-App zurück (sofern dies für den Typ der Erweiterung geeignet ist). Die enthaltende App wird nicht einmal ausgeführt, während ihre Erweiterung ausgeführt wird.
Erstellen einer App-Erweiterung – praktisches Beispiel mit der Today-Erweiterung
Die Heute-Erweiterungen, auch Widgets genannt, befinden sich in der Heute-Ansicht der Mitteilungszentrale. Sie sind eine großartige Möglichkeit, dem Benutzer aktuelle Inhalte zu präsentieren (z. B. Wetterbedingungen anzuzeigen) oder schnelle Aufgaben auszuführen (z. B. Erledigtes im Widget einer To-Do-Listen-App zu markieren). Ich muss hier darauf hinweisen, dass die Tastatureingabe nicht unterstützt wird .
Lassen Sie uns eine Today-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 befolgen Sie die 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 zu Datei -> Neu -> Ziel in Xcode navigieren. Von hier aus wählen wir die Vorlage für unser neues Ziel aus, um eine Today-Erweiterung hinzuzufügen.
Im nächsten Schritt können wir unseren Produktnamen wählen. Das ist der Name, der in der Heute-Ansicht der Mitteilungszentrale angezeigt wird. Auch in diesem Schritt besteht die Möglichkeit, die Sprache zwischen Swift und Objective-C zu wählen. Nach Abschluss dieser Schritte erstellt Xcode eine Today-Vorlage, die standardmäßige Header- und Implementierungsdateien für die Hauptklasse (mit dem Namen TodayViewController
) mit der Datei Info.plist
und einer Schnittstellendatei (einem Storyboard oder einer .xib-Datei) bereitstellt. Die Info.plist
-Datei sieht standardmäßig so aus:
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
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 Ansichtscontrollers als Wert hinzu.
Ein Heute-Widget sollte:
- sorgen dafür, dass der Inhalt immer aktuell aussieht
- angemessen auf Benutzerinteraktionen reagieren
- gute Leistung (iOS-Widgets müssen den Speicher sinnvoll nutzen oder sie werden vom System beendet)
Teilen von Daten und einem gemeinsam genutzten Container
Die App-Erweiterung und die enthaltende App haben beide Zugriff auf die freigegebenen Daten in ihrem privat definierten freigegebenen Container – was eine Möglichkeit einer indirekten Kommunikation zwischen der enthaltenden App und der Erweiterung darstellt.
Liebst du es nicht, wie Apple diese Dinge so „einfach“ macht? :)
Das Teilen von Daten NSUserDefaults
ist einfach und ein häufiger Anwendungsfall. Standardmäßig verwenden die Erweiterung und die enthaltende App separate NSUserDefaults
-Datensätze und können nicht auf die Container der jeweils anderen zugreifen. Um dieses Verhalten zu ändern, hat iOS App Groups eingeführt. Nachdem Sie App-Gruppen für die enthaltende App und die Erweiterung aktiviert haben, verwenden Sie anstelle von [NSUserDefaults standardUserDefaults]
[[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"]
für den Zugriff auf denselben freigegebenen Container.
Aktualisieren des Widgets
Um sicherzustellen, dass der Inhalt immer auf dem neuesten Stand ist, bietet die Today-Erweiterung eine API zum Verwalten des Status eines Widgets und zum Handhaben von Inhaltsaktualisierungen. Das System erfasst gelegentlich Momentaufnahmen der Widget-Ansicht, sodass, wenn das Widget sichtbar wird, die neueste Momentaufnahme angezeigt wird, bis sie durch eine Live-Version der Ansicht ersetzt wird. Eine Konformität mit dem NCWidgetProviding
Protokoll ist wichtig, um den Status eines Widgets zu aktualisieren, bevor ein Snapshot erstellt wird. Sobald das Widget den widgetPerformUpdateWithCompletionHandler:
empfängt, sollte die Ansicht des Widgets mit dem neuesten Inhalt aktualisiert werden, und der Vervollständigungs-Handler sollte mit einer der folgenden Konstanten aufgerufen werden, um das Ergebnis der Aktualisierung zu beschreiben:

-
NCUpdateResultNewData
– Der neue Inhalt erfordert ein Neuzeichnen der Ansicht -
NCUpdateResultNoDate
– Das Widget muss nicht aktualisiert werden -
NCUpdateResultFailed
– Während des Aktualisierungsvorgangs ist ein Fehler aufgetreten
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
Steuern, wann das Widget sichtbar ist
Um zu steuern, wann ein Widget angezeigt wird, verwenden Sie die Methode setHasContent:forWidgetWithBundleIdentifier:
aus der Klasse NCWidgetController
. Mit dieser Methode können Sie den Status des Inhalts des Widgets angeben. Es kann aus dem Widget oder aus seiner enthaltenden App aufgerufen werden (falls es aktiv ist). Sie können dieser Methode ein NO
oder YES
Flag übergeben, das definiert, ob der Inhalt des Widgets bereit ist oder nicht. Wenn der Inhalt nicht bereit ist, zeigt iOS Ihr Widget nicht an, wenn die Heute-Ansicht geöffnet wird.
NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];
Öffnen der enthaltenden App aus dem Widget
Das Today-Widget ist die einzige Erweiterung, die das Öffnen der enthaltenen App anfordern kann, indem sie die Methode openURL:completionHandler:
aufruft. Um sicherzustellen, dass die enthaltende App auf eine Weise geöffnet wird, die im Kontext der aktuellen Aufgabe des Benutzers sinnvoll ist, sollte ein benutzerdefiniertes URL-Schema (das sowohl das Widget als auch die enthaltende App verwenden können) definiert werden.
[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];
Überlegungen zur Benutzeroberfläche
Nutzen Sie beim Entwerfen Ihres Widgets die UIVisualEffectView
-Klasse und denken Sie daran, dass die Ansichten, die verschwommen/lebendig sein sollen, der contentView
und nicht direkt der UIVisualEffectView
werden müssen. Widgets (die dem NCWidgetProviding
Protokoll entsprechen) sollten zwischengespeicherte Zustände in viewWillAppear:
um mit dem Zustand der Ansicht aus der letzten viewWillDisappear:
und dann reibungslos zu den neuen Daten überzugehen, wenn sie ankommen, was bei einer normalen Ansicht nicht der Fall ist Controller (UI wird in viewDidLoad
und verarbeitet Animationen und das Laden von Daten in viewWillAppear
). Widgets sollten so konzipiert sein, dass sie eine Aufgabe ausführen oder die enthaltende App mit einem einzigen Antippen öffnen. Die Tastatureingabe ist innerhalb eines Widgets nicht verfügbar. Dies bedeutet, dass keine Benutzeroberflächen verwendet werden sollten, die eine Texteingabe erfordern.
Das Hinzufügen von Schriftrollen zu einem Widget, sowohl vertikal als auch horizontal, ist nicht möglich. Genauer gesagt, das Hinzufügen einer Scroll-Ansicht ist möglich, aber das Scrollen funktioniert nicht. Die horizontale Bildlaufgeste in einer Bildlaufansicht in der Heute-Erweiterung wird vom Benachrichtigungszentrum abgefangen, was dazu führt, dass von Heute zum Benachrichtigungszentrum gescrollt wird. Das vertikale Scrollen einer Bildlaufansicht innerhalb einer Heute-Erweiterung wird durch das Scrollen der Heute-Ansicht unterbrochen.
Technische Hinweise
Hier werde ich auf einige wichtige Dinge hinweisen, die Sie beim Erstellen einer App-Erweiterung beachten sollten.
Gemeinsame Funktionen aller Erweiterungen
Die folgenden Punkte gelten für alle Erweiterungen:
Das SharedApplication-Objekt ist gesperrt : 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 tabu : 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 Hardwareelemente 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 tabu : App-Erweiterungen können keine lang andauernden Hintergrundaufgaben ausführen, mit Ausnahme des Initiierens von Uploads oder Downloads, was unten besprochen wird.
AirDrop ist tabu : App-Erweiterungen können keine Daten mit AirDrop empfangen (aber senden).
Hochladen/Herunterladen im Hintergrund
Die einzige Aufgabe, die im Hintergrund ausgeführt werden kann, ist das Hochladen/Herunterladen mithilfe des NSURLSession object
.
Nachdem die Upload-/Download-Aufgabe initiiert wurde, 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 ausgeführt wird, startet das System die enthaltende App im Hintergrund und die application:handleEventsForBackgroundURLSession:completionHandler:
der App wird aufgerufen.
Für die App, deren Erweiterung eine NSURLSession
Hintergrundaufgabe initiiert, muss ein freigegebener Container eingerichtet sein, auf den sowohl die enthaltende App als auch ihre Erweiterung zugreifen können.
Stellen Sie sicher, dass Sie unterschiedliche Hintergrundsitzungen für die enthaltende App und jede ihrer App-Erweiterungen erstellen (jede Hintergrundsitzung sollte eine eindeutige Kennung haben). Dies ist wichtig, da jeweils nur ein Prozess eine Hintergrundsitzung verwenden kann.
Aktion vs. Teilen
Die Unterschiede zwischen den Action- und Share-Erweiterungen sind aus der Sicht eines Programmierers nicht ganz klar, da sie in der Praxis sehr ähnlich sind. Die Vorlage von Xcode für das Freigabeerweiterungsziel verwendet SLComposeServiceViewController
, das eine standardmäßige Benutzeroberfläche zum Erstellen von Ansichten bereitstellt, die Sie für das Teilen in sozialen Netzwerken verwenden können, dies ist jedoch nicht erforderlich. Eine Share-Erweiterung kann für ein vollständig benutzerdefiniertes Design auch direkt von UIViewController erben, genauso wie eine Action-Erweiterung von SLComposeServiceViewController
erben kann.
Der Unterschied zwischen diesen beiden Arten von Erweiterungen besteht 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 zum Übersetzen des ausgewählten Texts und zum Zurücksenden der Übersetzung an die Host-App verwendet wird). Mit der Share-Erweiterung können Sie Kommentare, Fotos, Videos, Audio, Links und mehr direkt von der Host-App aus teilen. Der UIActivityViewController
steuert sowohl Action- als auch Share-Erweiterungen, wobei Share-Erweiterungen als farbige Symbole in der oberen Reihe und die Action-Erweiterungen als monochrome Symbole 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 (z. B.: HealthKit- und EventKit-UI-Frameworks in iOS 8 sind für die Verwendung in keiner App-Erweiterung verfügbar).
Wenn Sie Code zwischen einer App und einer Erweiterung teilen, müssen Sie bedenken, dass selbst der Verweis auf eine API, die für die App-Erweiterung nicht zulässig ist, zur Ablehnung Ihrer App aus dem App Store führt. Sie können damit umgehen, indem Sie die gemeinsam genutzten Klassen in Hierarchien umwandeln, mit einem gemeinsamen Elternteil und verschiedenen Unterklassen für verschiedene Ziele. Eine andere Möglichkeit besteht darin, den Präprozessor durch #ifdef
-Prüfungen zu verwenden. Da es immer noch keine eingebaute Zielbedingung gibt, müssen Sie Ihre eigene erstellen.
Eine weitere gute Möglichkeit, dies zu tun, besteht darin, ein eigenes eingebettetes Framework zu erstellen. Stellen Sie einfach 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 Sie die Einstellung „Require Only App-Extension-Safe API“ auf „Yes“. Beim Konfigurieren des Xcode-Projekts muss in der Erstellungsphase „Dateien kopieren“ „Frameworks“ als Ziel für das eingebettete Framework ausgewählt werden. Wenn Sie das Ziel „SharedFrameworks“ wählen, wird Ihre Übermittlung 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 vorherigen iOS-Versionen verfügbar machen.
Apple Human Interface-Compliance
Beachten Sie beim Entwerfen einer App-Erweiterung die Apple-Richtlinien für die menschliche Benutzeroberfläche von iOS. 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 der gezielten Gerätefamilie in Xcode, indem Sie den Wert „iPhone/iPad“ (manchmal als universell bezeichnet) angeben.
Fazit
App-Erweiterungen haben definitiv die sichtbarste Wirkung in iOS 8. Da 79 % der Geräte bereits iOS 8 verwenden (gemessen vom App Store am 13. April 2015), sind die App-Erweiterungen unglaubliche Funktionen, die Apps nutzen sollten. Durch die Kombination der Einschränkungen der API und der Art und Weise des Datenaustauschs zwischen den Erweiterungen und der sie enthaltenden App scheint es Apple gelungen zu sein, eine der größten Beschwerden über die Plattform anzugehen, ohne ihr Sicherheitsmodell zu gefährden. Noch gibt es für die Drittanbieter-Apps keine Möglichkeit, ihre Daten direkt miteinander zu teilen. Obwohl dies ein sehr neues Konzept ist, sieht es sehr vielversprechend aus.