Delphi für iOS und Push-Notifications

Vorab: In diesem Blog-Beitrag stelle ich im Wesentlichen nur die Infos zusammen, die kürzlich
von Anders Ohlsson bzw. Luis Felipe González Torres veröffentlicht wurden. Ich denke, dass es einigen von Euch hilfreich sein könnte, wenn ich die Informationen mit einer - nach meiner Auffassung - geordneteren Struktur zusammenfasse. Ich werde mich aber eng an seinem Beispiel orientieren.

Bei den sog. “Push-Notifications” handelt es sich um ein asynchrones Benachrichtigungsverfahren, mit dem Apps “von außen”, also z.B. einem Webserver, nahezu in Echtzeit kontaktiert werden können. Ein Projekt, das sich mit Push-Notifications beschäftigt, besteht grundsätzlich aus zwei Komponenten: Dem Empfänger (der App) und dem Sender der Push-Notifications.

Wichtig zu wissen ist, dass Push-Notifications grundsätzlich nur auf dem iOS-Gerät und nicht im iOS-Simulator funktionieren (Stand Juni 2013). Das bedeutet, dass die App zwingend auf dem Gerät selbst getestet werden muss – zumindest der Funktionsbereich, der die Push-Notifications umfasst.

Apple sieht zwei Szenarien vor, um Push-Notifications zu versenden: “Distribution” und “Development”. Das Distribution-Szenatio ist das für den Live-Betrieb der App, wenn diese von den Anwendern aus dem App-Store geladen werden kann. Hier gelten gewisse Restriktionen, die in der Entwickler-Dokumentation im Developer-Center von Apple nachgelesen werden können. Parallel dazu existiert das “Development”-Szenario, das für Tests verwendet werden kann. Hier ist man sehr viel freier, auch fehlerhafte Notifications senden zu dürfen. Der Unterschied zwischen “Distribution” und “Development” manifestiert sich u.a. im Provisioning-Profile, in den Entitlements und zu guter letzt auch im APN-Server, den man für den Versand der Notifications nutzt.

Im Folgenden soll ausschließlich im Development-Szenario gearbeitet werden.

Die Push-Notifications selbst sind nicht so sonderlich kompliziert. Doch das gesamte Verfahren mit dem Zusammenspiel der App, der Zertifikate und den Provisionierungs-Profilen bietet leider reichlich Spielraum, um sich ordentlich zu verheddern – insbesondere, wenn man neu in diese Thematik einsteigt. Ich kann folgenden Weg empfehlen, mit dem ich selbst sehr gute Erfahrungen gemacht habe:

Nutzt für den ersten Schritt Xcode!
Es gibt sehr viele, teils sehr gute, Tutorials, wie man mit Xcode ein Mini-Projekt auf die Beine stellt, um Push-Notifications zu empfangen. Wenn man so ein Projekt nachgebaut hat - und das ist im Minimalfall ein Fünfzeiler - hat man einerseits ein Grundverständnis für das Thema und darüber hinaus auch schon die Sicherheit, dass die Zertifikate und Profile zusammenpassen. Mit dieser Gewissheit im Rücken kann man sich dann an Delphi heranwagen und die fehlenden Bausteinchen zusammensetzen. Ich selbst habe erfolgreich das folgende Tutorial verwendet:
Apple Push Notification Services in iOS 6 Tutorial.

Der Empfang von Push-Notifications wird mit Delphi XE4 leider nicht unmittelbar unterstützt. Glücklicherweise sind es nicht mehr viele Schritte, die noch zu gehen sind.

Luis Felipe hat ein schönes Demo-Projekt zum Download bereitgestellt. Ladet Euch zur Vorbereitung sein Demo-Archiv herunter, wir werden uns immer wieder daraus bedienen:
http://lfgonzalez.visiblogs.com/2013/05/delhi-xe4-ios-adicionando-soporte-a-notificaciones-remotas-video-y-codigo-fuente/apnserverandclient/

Allgemeine Vorbereitungen

Grundlage für dieses Beispiel soll eine leere “FireMonkey Mobile Application” sein. In diesem Fall nenne ich sie “PushNTest”. Der Name selbst ist relativ egal, er muss nur konsequent im Projekt, den Zertifikaten und Profilen beibehalten werden.

Als erstes müssen wir eine Datei des FireMonkey-Frameworks für unsere Zwecke verändern. Zu diesem Zweck legen wir von der Datei “FMX.Platform.iOS.pas” eine Kopie an, legen diese Kopie in das Projektverzeichnis unserer Anwendung und fügen diese Datei auch gleich dem Projekt hinzu. Aus urheberrechtlichen Gründen ist es mir nicht möglich, die komplette Datei anzufügen, stattdessen habe ich kurz notiert, welche fünf Stellen geändert werden müssen: Download Patch “FMX.Platform.iOS.pas”. Im Archiv von Luis Felipe ist zwar eine gepatchte Datei enthalten, diese ist aktuell jedoch nicht auf Basis des Update #1 von Delphi XE4 erstellt worden, so dass ich es für den saubereren Weg halte, die aktuelle Datei per Hand anzupassen. Zudem wird noch eine weitere Unit (“ApplePushRemoteUnit.pas”) benötigt, welche einige Ereignis-Handler bereitstellt. Diese findet sich ebenfalls in dem o.g. Archiv von Luis Felipe. Auch diese Datei legen wir ins Projektverzeichnis und fügen sie dem Projekt hinzu.

Werfen wir kurz einen Blick darauf, was diese Änderungen überhaupt machen: Der erste wichtige Schritt besteht darin, dass die App sich auf dem Gerät überhaupt für Push-Notifications anmeldet. Dazu wurde von Luis Felipe der Event-Handler “applicationDidFinishLaunchingWithOptions()” um einen Aufruf von “registerForRemoteNotificationTypes();” erweitert. Besagter Event-Handler wird von iOS automatisch aufgerufen, sobald die App vollständig gestartet ist. Für uns bedeutet dies, dass sich die App bei jedem Start automatisch für Push-Notifications registriert – wir müssen dazu nichts weiter unternehmen. Wenn diese Registrierung geklappt hat, wird der Event-Handler “didRegisterForRemoteNotificationsWithDeviceToken()” vom System aufgerufen und der App ihr “DeviceToken” überreicht – mehr oder weniger feierlich. Unter diesem Device-Token ist sie für die Push-Notifications zu erreichen, er ist quasi ihre “Adresse”. Wenn der Server später an genau diese App auf genau diesem gerät eine Push-Notification versenden möchte, dann muss er diese an den eben erhalten Device-Token senden.

Was wir ebenfalls gleich erledigen können, ist die Angabe des “Bundle-Identifiers” sowie des “Bundle-Names”. Diese beiden Werte können in den Projekt-Optionen unter dem Punkt “Version Info” eingetragen werden. Für beide Werte geben wir die App-ID ohne Präfix an.


Einbinden der eigenen Entitlements

Die sog. “Entitlements-Dateien” sind wichtiger Bestandteil der Konfiguration einer iOS.App. Normalerweise verwaltet die Delphi-IDE Aufbau und Inhalt einer solchen Entitlements-Datei selbstständig. In diesem Fall jedoch wollen wir zusätzliche Informationen in diese Datei einbetten und müssen der Delphi-IDE daher die Verantwortung über diese Datei entziehen.

Diese Datei muss nur einmalig erstellt werden und dann künftig einfach nur vorhanden sein. Sie erfordert keinerlei weiteren Pflegeaufwand. Das Erstellen dieser Datei muss jedoch mit großer Sorgfalt erfolgen, da eine fehlerhafte Entitlements-Datei den Start einer App verhindert. Essentiell wichtig ist, dass als Zeilentrenner ausschließlich ein “LineFeed” (LF bzw. #10 bzw. $10 bzw. 0x10) verwendet wird und nichts anderes. Achtung: “Notepad” beispielweise ist nicht in der Lage eine derartige Datei zu schreiben. Ich selbst verwende hierfür beispielsweise “PSPad“.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
  <key>application-identifier</key>
  <string>XXXXXXXXXX.de.danielwolf.PushNTest</string>
  <key>aps-environment</key>
  <string>development</string>
  <key>get-task-allow</key>
  <true/>
</dict>
</plist>

Achtet darauf, in Eurer Datei selbstverständlich Eure eigene App-ID anzugeben. Nachdem diese Datei nun erstellt ist, muss sie beim Deployment berücksichtigt werden. In Delphi kann man dies unter “Project” -> “Deployment” einstellen. Zwei Dinge sind zutun:

  1. Die von der IDE bereitgestellte Datei “Entitlements.plist” deaktivieren, indem ganz links der Haken aus der Checkbox entfernt wird.
  2. Die eigene Datei mit dem Remote-Name “Entitlements.plist” integrieren. (Die Groß- und Kleinschreibung ist im Dateisystem von iOS von hoher Bedeutung.)

Achtet dabei auf die richtige Konfiguration, in der Combobox über der Dateiliste sollte “iOS-Device Platform” ausgewählt sein.

Einbinden des Zertifikats sowie des Provisioning-Profiles

Im sog. “Provisioning-Profile” steckt die Information für das iOS-Betriebssystem, dass die App Push-Notifications empfangen können soll. Dieses Profil muss also unbedingt berücksichtigt werden, wenn der Compiler und danach dann der Linker ihre Arbeit aufnehmen. Die Profile können in den Projekt-Optionen hinterlegt werden. Jedoch nur für die Ziel-Konfigurationen “Ad-hoc” und “AppStore”. In diesen Varianten werden sog. “.ipa-Dateien” erzeugt, welche eine Art Container-Format darstellen und alle benötigten Informationen enthalten. Diese “.ipa-Dateien” können von iTunes oder vom AppStore verarbeitet werden und beinhalten sämtliche Informationen, die nötig sind, um die App auf dem Gerät zu installieren.

Insgesamt werden wir (üblicherweise) zwei Provisioning-Profile benötigen: Eines für uns zum Testen “(Development”) und eines später für die Auslieferung (“Distribution”). Diese Profile müssen wir uns zuvor im Developer-Center von Apple anlegen und dann herunterladen und installieren. Letzteres geht auf dem Mac per Doppelklick auf die heruntergeladenen Dateien.

Diese Profile sind stets nur für einen ganz bestimmten Zweck, meist nur für eine App und dann auch da entweder nur für die Entwicklung oder die Verteilung. Ich kann hier nur noch mal eindringlich dazu raten, diesen Profilen sprechende Namen zu geben, damit man den Überblick nicht verliert.

Auch wenn wir vorläufig im “Development” bzw. “Debug”-Modus arbeiten, können wir die Informationen für die AppStore-Konfiguration schon hinterlegen:

Den korrekten Wert für das Feld “Developer Program Id” kann die IDE nach Angabe des Profiles selbst auslesen, sofern auf dem Mac der PA-Server läuft. Ein Klick auf die Schaltfläche “Load ID” erledigt den Rest.

So weit, so gut … aus der IDE heraus können wir jedoch keine .ipa-Dateien auf das Gerät bringen. Was die Delphi-IDE gegenwärtig nicht zu leisten vermag, ist die Installation des Provisioning-Profiles auf dem Gerät. Ohne passendes Provisioning-Profile jedoch keine App – das bedeutet, dass wir das Profil kurzerhand selbst installieren müssen. Wohlgemerkt ist dieser zusätzliche Handgriff ausschließlich auf unserem Entwicklungs-Gerät notwendig: Später bei der Verteilung der App durch den AppStore gibt es dann ja die “.ipa-Datei”, welche das Profil beinhaltet.

Wir starten den Xcode-Organizer und ziehen die passenden Profile per Drag ‘n Drop auf das Gerät. Fertig, kurz und schmerzlos.

Das sollte es gewesen sein. Wir haben nun hoffentlich alle Aspekte berücksichtigt und sämtliche Hürden aus dem Weg geräumt. Wir können die IDE nun anweisen, die App auf das Gerät zu kopieren und zu starten.

Starten der App (Client)

Beim ersten Start der App wird iOS nachfragen, ob man der Anwendung gestatten möchte, Push-Notifications anzuzeigen. Wenn diese Nachfrage kommt, ist ein ganz elementar wichtiger Schritt geschafft. Sollte diese Frage wider Erwarten nicht kommen, dann ist etwas im Bereich des Provisioning-Profiles falsch konfiguriert. Bevor diese Frage vom iOS-System nicht gestellt wurde, ist jeder Versuch, die App über eine Push-Notification erreichen zu wollen, vergebens.

Konfigurieren des Servers

Wir haben nun erfolgreich den Client geschrieben, der in der Lage ist, Push-Notifications zu empfangen. Was fehlt, ist das dazu passende Gegenstück – der Server. Diesen nehmen wir aus dem Beispielprojekt von Luis Felipe.

Wenn die App erfolgreich hochstartet, präsentiert sie den sog. Device-Token. Es handelt sich dabei um eine triviale ShowMessage()-Ausgabe. Gewiss wenig elegant, aber für den Moment absolut ausreichend. Die angezeigte Zeichenfolge ist der sog. “Device-Token”, welcher dem Server, der die Push-Notifications versenden soll, als Empfänger-Adresse dienen wird. Im aktuellen Test-Szenario werden wir den Device-Token per Hand fest in den Server kodieren. Damit kann der Server nur an dieses eine Gerät eine Push-Notification versenden, aber zum Testen langt das ja vorerst. Später lässt sich das System nach Belieben ausbauen.

Für den Server gibt es ein paar Aspekte zu beachten: Die Verbindung zum Apple APNS-Server erfolgt via SSL, in Delphi sind also die passenden OpenSSL-Bibliothenen zu hinterlegen, welche z.B. vom Server des INDY-Projects geladen werden können. Darüber hinaus ist das eigene Push-Zertifikat anzugeben (muss im Developer-Center von Apple erzeugt und geladen werden). Apple stellt dieses Zertifikat als “.cer”-Datei zu Verfügung, für die INDY-Komponenten werden wir dieses in das Format “.pem” wandeln müssen. Apple dokumentiert einen einfachen Weg, diese Konvertierung vorzunehmen:
iOS Developer Library: Installing the SSL Certificate and Key on the Server.

Im Zusammenhang mit dem Versand von Push-Notifications ist es wichtig zu wissen, dass Umlaute und sämtliche weitere Sonderzeichen die in der Nachricht (im “Payload”) enthalten sind, als UTF-8 codiert sein müssen. Ansonsten wird die Nachricht nicht oder mit leerem Inhalt zugestellt.

 
Weiterführende Dokumentation von Apple: iOS Developer Center: Push-Notifications

Für Fragen verweise ich gern auf mein Forum “Delphi-PRAXiS“.

1
Daniel Wolf

About the Author:

Daniel Wolf ist als Software-Architekt im Miniatur Wunderland Hamburg tätig, war zuvor viele Jahre als technischer Berater tätig und spricht regelmäßig auf Konferenzen. Er betreibt das Forum "Delphi-PRAXiS". » Mehr Details zur Person
  Verwandte Blog-Einträge