Cordova Push Notification Tutorial iOS (Phonegap)

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Cordova Push Notification Tutorial iOS (Phonegap)

      [lexicon]Cordova[/lexicon], iOS, PHP und Push-Nachrichten - wie das geht, erfahrt Ihr hier!


      Was wird benötigt?
      • Ein iPhone oder iPad
      • Eine aktive Teilnahme am iOS Developer Programm.
        • Ihr benötigt ein neues Provisioning Profile sowie ein SSL Zertifikat von Apple.
      • Einen Server mit Internetverbindung bzw. einen Mac zum Testen.

      Was ist eine Push-Nachricht?
      Eine Push-Nachricht ist eine kurze Mitteilung die auf Eurem Device auftaucht. Push-Nachrichten funktionieren unabhängig davon, ob eine App gerade läuft oder nicht. Die gesamte Nachricht die an den Server gesendet wird, wird als [lexicon]Payload[/lexicon] bezeichnet. Im [lexicon]Payload[/lexicon] enthalten sein können bspw.:
      • Device Token (Pflicht)
      • Alert - entspricht der Nachricht
      • Sound - entspricht dem Sound der abgespielt werden soll wobei "default" hier der Standard iOS Sound ist.
      • [lexicon]Badge[/lexicon] - entspricht der Zahl, welche an der App angezeigt wird.
      • Sie wird als [lexicon]Json[/lexicon] Codiert und darf max. 256Bytes groß sein.
      Ein Beispielpayload könnte so aussehen:
      {"aps":{"alert":"Hello, world!","sound":"default"}}


      Was sollte man zu Push-Nachrichten wissen?
      • Es gibt keine Garantie, dass sie übermittelt werden. Auch nicht, wenn der Server sie aktzeptiert.
      • Man kann nicht nachvollziehen, ob die Nachricht zugestellt wurde oder nicht.
      • Befindet sich ein iPhone bspw. in einem WLAN-Netzwerk in welchem die [lexicon]APNS[/lexicon] ([lexicon]Apple Push Notification Service[/lexicon]) Ports geblockt sind, kommt die Nachricht nicht an.
      • Der Server wird für eine bestimmte Zeit versuchen, die Nachricht trotzdem zuzustellen. Passiert dies allerdings nicht, ist sie nicht mehr zustellbar.
      • Das Versenden von Push-Nachrichten kann teuer werden. Je nachdem, wie viel Userdaten bzw. Device-Tokens gespeichert wurden und wie oft diese versendet werden, muss der Server entsprechende Kapazitäten vorweisen.

      [lexicon]Cordova[/lexicon] Push iOS - Schritt 1 - Erstellen eines Certificate Signing Requests (CSR)
      1. Wir starten die "Schlüsselbundverwaltung" und klicken oben auf "Schlüsselbundverwaltung" -> "Zertifikatassistent" -> "Zertifikat einer Zertifizierungsinstanz anfordern".
      2. Als nächstes sollte sich ein Fenster öffnen in welchem Ihr die Felder "E-Mail Adresse" und "Allgemeiner Name" befüllt. Allgemeiner Name stellt hierbei den Namen dar, der in der Schlüsselbundverwaltung angezeigt wird. Der Übersichtlichkeit halber sollte hier ein Name genommen werden, der auf den ersten Blick in der Schlüsselbundverwaltung zu finden ist. Außerdem müsst ihr noch "Auf der Festplatte sichern" anklicken. Anschließend könnt ihr mit einem Klick auf "Fortfahren" das Zertifikat auf Eurer Festplatte sichern.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 2 - Erstellen einer .p12 Datei aus einem privaten Schlüssel
      1. In der Schlüsselbundverwaltung führt ihr einen Rechtsklick auf den soeben erstellten Schlüssel aus und klickt auf "[i]Schlüsselname[/i] exportieren".
      2. Wählt einen Speicherort und ein Passwort aus, welches ihr Euch bestenfalls gleich auf einen Zettel mit notiert und klickt auf "Sichern".


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 3 - Erstellen einer App ID für Push-Nachrichten
      1. Wir loggen uns in das Apple Membercenter ein und klicken auf App IDs.
      2. Mit einem Klick auf das "+" oben Rechts erstellen wir eine neue App ID (wichtig an dieser stelle: Wildcard App-IDs können nicht benutzt werden!).
      3. Nicht vergessen: Unten, unter dem Punkt "App Services" "Push Notifications" anklicken und dann die App ID speichern.
      4. Die App ID müsste nun mit in Eurer Liste der Apps auftauchen. Ihr Klickt die soeben erstellte App ID an und wählt auf "Edit".
      5. Nun wählt Ihr unter "Push Notifications" "Create Certificate" aus.
      6. Nun sollt Ihr wieder ein Zertifikat einer Zertifizierungsinstanz anfordern. Da wir dies aber zuvor bereits schon getan haben, klickt ihr auf "Continue" und wählt nun Eure zuvor erstellte Datei aus.
      7. Nach einem kurzen Moment könnt ihr unter dem Punkt "Download" Euer Zertifikat herunterladen. Wichtig hierbei ist, dass das Development Zertifikat nur 3 Monate gültig ist.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 4 - Erstellen einer PEM Datei
      1. Ihr solltet nun drei Dateien haben:
        1. Das CSR File
        2. Den exportierten Private Key als .p12
        3. Das SSL Zertifikat - aps_development.cer
      2. Da wir unsere Push Nachrichten später über PHP verschicken, kompilieren wir die Dateien nun wie folgt:
        1. Wir öffnen das [lexicon]Terminal[/lexicon] und manövrieren über "cd" in den Ordner, in welchem sich die Zertifikate befinden.
        2. Folgender Befehlt konvertiert die .cer Datei in eine .pem Datei
          1. openssl x509 -in aps_development.cer -inform der -out PushTestCert.pem
        3. Nun konvertieren wir mit folgendem Befehl unseren Private Key in eine .pem Datei
          1. openssl pkcs12 -nocerts -out PushTestKey.pem -in Keyname.p12 - Keyname muss natürlich ersetzt werden durch den Namen Eures Schlüssels!.
          2. Als nächstes müsst Ihr ein Passwort vergeben und anschließend Enter drücken. Passwort noch einmal bestätigen und schon habt ihr die zweite .pem Datei.
        4. Wir kombinieren nun die erstellten .pem Dateien in eine Datei mit dem Befehl:
          1. cat PushTestCert.pem PushTestKey.pem > combined.pem
        5. Die Zertifikate haben wir nun erstellt. Zunächst testen wir jetzt erst einmal, ob wir überhaupt eine Verbindung zum Apple Server herstellen können mit folgendem Befehl
          1. telnet gateway.sandbox.push.apple.com 2195
          2. In Eurem [lexicon]Terminal[/lexicon] sollte nun:
            1. Trying 17.110.226.164... Connected to gateway.sandbox.push-apple.com.akadns.net. Escape character is '^]'.
          3. stehen. Wenn das der Fall ist, funktioniert Eure Verbindung zum Apple-Server auf jeden Fall und wir können nun unsere Zertifikate testen.
        6. Um die Zertifikate zu testen senden wir einen openssl connect an den [lexicon]APNS[/lexicon] Server mit folgendem Befehl:
          1. openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushTestCert.pem -key PushTestKey.pem
          2. nun müssen wir das festgelegte Passwort für die Key-Datei eingeben und sollten eine ausführliche Antwort vom Server zurückbekommen. Wenn die Verbindung erfolgreich war könnt ihr nun eine kurze Zeichenfolge eingeben, bspw.: "asdf", Enter drücken und der Server sollte die Verbindung beenden.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 5 - Erstellen eines Provisioning Profiles
      1. Wir erstellen unter Provisions Profiles im Apple Membercenter nun ein neues Provisioning Profile für unsere App.
        1. Ihr klickt nun auf das "+" oben Rechts und wählt als nächstes "iOS App Development". Es folgt ein Klick auf Continue.
        2. Nun wählt Ihr die zuvor erstellte App-ID aus und auf der nächsten Seite das einzuschließende Zertifikat.
        3. Jetzt nur noch die Geräte auswählen, die diese App testen dürfen und auf der nächsten Seite einen Namen vergeben. Dann ist das Provisioning Profile erfolgreich erstellt worden.
        4. Wir laden das Profile herunter und speichern es ab.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 6 - Erstellen einer Push-App

      Die Vorbereitungen sind nun abgeschlossen, wir können nun unsere App erstellen, welche die Push Nachrichten empfangen soll.
      1. Wie in [lexicon]Cordova[/lexicon] üblich fangen wir wie folgt an:
        1. cordova create PushTestApp com.appidhier.de PushTestApp (appidhier muss natürlich mit der zuvor erstellten App ID aus dem Membercenter ersetzt werden!)
        2. cd PushTestApp
        3. cordova platform add ios
      2. Als nächstes folgen einige Plugins, welche für die Verwendung von Push für unser Beispiel zwingend erforderlich sind:
        1. cordova plugin add org.apache.cordova.device
        2. cordova plugin add org.apache.cordova.dialogs
        3. cordova plugin add org.apache.cordova.file-transfer
        4. cordova plugin add org.apache.cordova.console
        5. cordova plugin add https://github.com/phonegap-build/PushPlugin.git
      3. Abschließend wird die App mit "cordova build" erstellt und wir öffnen das generierte XCode-Projekt.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 7 - Bearbeiten der erstellten App und des [lexicon]Cordova[/lexicon] Push Plugins
      1. Wir überarbeiten unsere index.[lexicon]html[/lexicon] und fügen zunächst jQuery hinzu damit wir mit Ajax arbeiten können. Die minified.js ist die empfohlene Datei in Version 1.11.2.
      2. Wir binden die Datei in unsere index.[lexicon]html[/lexicon] mit folgendem Schnipsel ein:
        1. <script src="js/jquery-1.11.2.min.js"></script>
      3. Anschließend ergänzen wir den Code für das [lexicon]Cordova[/lexicon] Push [lexicon]Plugin[/lexicon]
        • Quellcode

          1. var pushNotification;
          2. document.addEventListener("deviceready", function(){
          3. pushNotification = window.plugins.pushNotification;
          4. readyForPush();
          5. });

        • Quellcode

          1. function readyForPush() {
          2. $("#app-status-ul").append('<li>registering ' + device.platform + '</li>');
          3. if ( device.platform == 'android' || device.platform == 'Android' || device.platform == "amazon-fireos" ){
          4. pushNotification.register(
          5. successHandler,
          6. errorHandler,
          7. {
          8. "senderID":"replace_with_sender_id",
          9. "ecb":"onNotification"
          10. });
          11. } else if ( device.platform == 'blackberry10'){
          12. pushNotification.register(
          13. successHandler,
          14. errorHandler,
          15. {
          16. invokeTargetId : "replace_with_invoke_target_id",
          17. appId: "replace_with_app_id",
          18. ppgUrl:"replace_with_ppg_url", //remove for BES pushes
          19. ecb: "pushNotificationHandler",
          20. simChangeCallback: replace_with_simChange_callback,
          21. pushTransportReadyCallback: replace_with_pushTransportReady_callback,
          22. launchApplicationOnPush: true
          23. });
          24. } else {
          25. pushNotification.register(
          26. tokenHandler,
          27. errorHandler,
          28. {
          29. "badge":"true",
          30. "sound":"true",
          31. "alert":"true",
          32. "ecb":"onNotificationAPN"
          33. });
          34. }
          35. }
          Alles anzeigen

        • Quellcode

          1. function successHandler (result) {
          2. alert('result = ' + result);
          3. }

        • Quellcode

          1. // result contains any error description text returned from the plugin call
          2. function errorHandler (error) {
          3. alert('error = ' + error);
          4. }

        • Quellcode

          1. // iOS
          2. function onNotificationAPN (event) {
          3. if ( event.alert )
          4. {
          5. navigator.notification.alert(event.alert);
          6. }
          7. if ( event.sound )
          8. {
          9. var snd = new Media(event.sound);
          10. snd.play();
          11. }
          12. if ( event.badge )
          13. {
          14. pushNotification.setApplicationIconBadgeNumber(successHandler, errorHandler, event.badge);
          15. }
          16. }
          Alles anzeigen

        • Code der gesamten index.[lexicon]html[/lexicon]
          • HTML-Quellcode

            1. <!DOCTYPE html>
            2. <html>
            3. <head>
            4. <meta charset="utf-8" />
            5. <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, target-densitydpi=device-dpi" />
            6. <link rel="stylesheet" type="text/css" href="css/index.css" />
            7. <script type="text/javascript" src="js/jquery-1.11.2.js"></script>
            8. <script type="text/javascript" src="cordova.js"></script>
            9. <script>
            10. function readyForPush() {
            11. $("#app-status-ul").append('<li>registering ' + device.platform + '</li>');
            12. if ( device.platform == 'android' || device.platform == 'Android' || device.platform == "amazon-fireos" ){
            13. pushNotification.register(
            14. successHandler,
            15. errorHandler,
            16. {
            17. "senderID":"replace_with_sender_id",
            18. "ecb":"onNotification"
            19. });
            20. } else if ( device.platform == 'blackberry10'){
            21. pushNotification.register(
            22. successHandler,
            23. errorHandler,
            24. {
            25. invokeTargetId : "replace_with_invoke_target_id",
            26. appId: "replace_with_app_id",
            27. ppgUrl:"replace_with_ppg_url", //remove for BES pushes
            28. ecb: "pushNotificationHandler",
            29. simChangeCallback: replace_with_simChange_callback,
            30. pushTransportReadyCallback: replace_with_pushTransportReady_callback,
            31. launchApplicationOnPush: true
            32. });
            33. } else {
            34. pushNotification.register(
            35. tokenHandler,
            36. errorHandler,
            37. {
            38. "badge":"true",
            39. "sound":"true",
            40. "alert":"true",
            41. "ecb":"onNotificationAPN"
            42. });
            43. }
            44. }
            45. // iOS
            46. function onNotificationAPN (event) {
            47. if ( event.alert )
            48. {
            49. navigator.notification.alert(event.alert);
            50. }
            51. if ( event.sound )
            52. {
            53. var snd = new Media(event.sound);
            54. snd.play();
            55. }
            56. if ( event.badge )
            57. {
            58. pushNotification.setApplicationIconBadgeNumber(successHandler, errorHandler, event.badge);
            59. }
            60. }
            61. function tokenHandler (result) {
            62. // Your iOS push server needs to know the token before it can push to this device
            63. // here is where you might want to send it the token for later use.
            64. alert('device token = ' + result);
            65. }
            66. // result contains any error description text returned from the plugin call
            67. function errorHandler (error) {
            68. alert('error = ' + error);
            69. }
            70. </script>
            71. <title>Hello World</title>
            72. </head>
            73. <body>
            74. <script>
            75. var pushNotification;
            76. document.addEventListener("deviceready", function(){
            77. pushNotification = window.plugins.pushNotification;
            78. readyForPush();
            79. });
            80. </script>
            81. </body>
            82. </html>
            Alles anzeigen

      4. Sollte die App starten und ihr erhaltet in der [lexicon]Konsole[/lexicon] lediglich die Ausgabe "Attempting to [lexicon]badge[/lexicon] the application icon but haven't received permission from the user to [lexicon]badge[/lexicon] the application" versucht das lokal eingebundene jQuery durch das auf dem Google Server zu ersetzen und nehmt hierfür folgenden Code:
        • <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
      5. Sollte die Fehlermeldung "Keine gültige aps-environment-Entitlement-Zeichenkette für Apps gefunden" gibt es ein Problem mit Eurem Provisioning Profile.


      [lexicon]Cordova[/lexicon] Push iOS - Schritt 8 - Das Device Token für Push Nachrichten.
      • In der [lexicon]Konsole[/lexicon] sollte nun unter dem Punkt "device token = " Euer Device Token auftauchen. Das Device Token ist für jedes Gerät und für jede App einmalig. Dieses kann zusammen mit dem [lexicon]Badge[/lexicon] in einer MySQL Datenbank gespeichert und aufbewahrt werden.
      • Wenn Ihr nun Euer Device Token erhalten habt, könnt ihr testen, ob Ihr Push Nachrichten empfangen könnt. Hierzu kann man sich aus dem Mac Appstore die App "APN Tester Free" herunterladen.
      • Wir starten nach dem Installieren die App und geben bei Device Token unser Device Token ein. In der zweiten Zeile finden wir den [lexicon]Payload[/lexicon]. Hier können wir eine beliebige Nachricht eingeben und unter [lexicon]Badge[/lexicon] können wir die Zahl festlegen, die an der App angezeigt wird. Wichtig hierbei ist: Die Anführungszeichen hinter [lexicon]Badge[/lexicon] müssen entfernt werden, so dass lediglich die Zahl hinter dem Doppelpunkt steht - ansonsten kommt es zu Fehlern in der Übermittlung! Unter dem Punkt "Certificate" fügen wir nun unser "aps_development.cer" ein und lösen mit einem Klick auf "Push" die Nachricht und das [lexicon]Badge[/lexicon] aus.

      Das wars im großen und ganzen auch schon. Die App ist für Push vorbereitet und die Nachricht sollte auf Eurem Device angekommen sein. Anbei findet Ihr noch ein Beispiel von learn-by-example.com:

      PHP-Quellcode

      1. <?php
      2. // authentication
      3. $host = "localhost";
      4. $user = "db_username";
      5. $pass = "db_password";
      6. $dbname = "db_name";
      7. // create connection with database
      8. $con = mysql_connect($host,$user,$pass);
      9. // check whether database connection is successful
      10. if (!$con) {
      11. // if connection not successful then stop the script and show the error
      12. die('Could not connect to database: ' . mysql_error());
      13. } else {
      14. // if database connection successful then select the database
      15. mysql_select_db($dbname, $con);
      16. }
      17. // get the id, token from database
      18. $result = mysql_query("SELECT id,token FROM `device_tokens` ORDER BY id");
      19. //Setup notification message
      20. $body = array();
      21. $body['aps'] = array('alert' => 'This is push message');
      22. $body['aps']['notifurl'] = 'http://www.mydomain.com';
      23. $body['aps']['badge'] = 2;
      24. //Setup stream (connect to Apple Push Server)
      25. $ctx = stream_context_create();
      26. stream_context_set_option($ctx, 'ssl', 'passphrase', 'password_for_apns.pem_file');
      27. stream_context_set_option($ctx, 'ssl', 'local_cert', 'apns_pem_certificate.pem');
      28. $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
      29. stream_set_blocking ($fp, 0);
      30. // This allows fread() to return right away when there are no errors. But it can also miss errors during
      31. // last seconds of sending, as there is a delay before error is returned. Workaround is to pause briefly
      32. // AFTER sending last notification, and then do one more fread() to see if anything else is there.
      33. if (!$fp) {
      34. //ERROR
      35. echo "Failed to connect (stream_socket_client): $err $errstrn";
      36. } else {
      37. // Keep push alive (waiting for delivery) for 90 days
      38. $apple_expiry = time() + (90 * 24 * 60 * 60);
      39. // Loop thru tokens from database
      40. while($row = mysql_fetch_array($result)) {
      41. $apple_identifier = $row["id"];
      42. $deviceToken = $row["token"];
      43. $payload = json_encode($body);
      44. // Enhanced Notification
      45. $msg = pack("C", 1) . pack("N",
      46. $apple_identifier) . pack("N", $apple_expiry) . pack("n", 32) .
      47. pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",
      48. strlen($payload)) . $payload;
      49. // SEND PUSH
      50. fwrite($fp, $msg);
      51. // We can check if an error has been returned while we are sending, but we also need to
      52. // check once more after we are done sending in case there was a delay with error response.
      53. checkAppleErrorResponse($fp);
      54. }
      55. // Workaround to check if there were any errors during the last seconds of sending.
      56. // Pause for half a second.
      57. // Note I tested this with up to a 5 minute pause, and the error message was still available to be retrieved
      58. usleep(500000);
      59. checkAppleErrorResponse($fp);
      60. echo 'Completed';
      61. mysql_close($con);
      62. fclose($fp);
      63. }
      64. // FUNCTION to check if there is an error response from Apple
      65. // Returns TRUE if there was and FALSE if there was not
      66. function checkAppleErrorResponse($fp) {
      67. //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID).
      68. // Should return nothing if OK.
      69. //NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait
      70. // forever when there is no response to be sent.
      71. $apple_error_response = fread($fp, 6);
      72. if ($apple_error_response) {
      73. // unpack the error response (first byte 'command" should always be 8)
      74. $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
      75. if ($error_response['status_code'] == '0') {
      76. $error_response['status_code'] = '0-No errors encountered';
      77. } else if ($error_response['status_code'] == '1') {
      78. $error_response['status_code'] = '1-Processing error';
      79. } else if ($error_response['status_code'] == '2') {
      80. $error_response['status_code'] = '2-Missing device token';
      81. } else if ($error_response['status_code'] == '3') {
      82. $error_response['status_code'] = '3-Missing topic';
      83. } else if ($error_response['status_code'] == '4') {
      84. $error_response['status_code'] = '4-Missing payload';
      85. } else if ($error_response['status_code'] == '5') {
      86. $error_response['status_code'] = '5-Invalid token size';
      87. } else if ($error_response['status_code'] == '6') {
      88. $error_response['status_code'] = '6-Invalid topic size';
      89. } else if ($error_response['status_code'] == '7') {
      90. $error_response['status_code'] = '7-Invalid payload size';
      91. } else if ($error_response['status_code'] == '8') {
      92. $error_response['status_code'] = '8-Invalid token';
      93. } else if ($error_response['status_code'] == '255') {
      94. $error_response['status_code'] = '255-None (unknown)';
      95. } else {
      96. $error_response['status_code'] = $error_response['status_code'].'-Not listed';
      97. }
      98. echo '<br><b>+ + + +
      99. + + ERROR</b> Response Command:<b>' .
      100. $error_response['command'] .
      101. '</b>&nbsp;&nbsp;&nbsp;Identifier:<b>' .
      102. $error_response['identifier'] .
      103. '</b>&nbsp;&nbsp;&nbsp;Status:<b>' .
      104. $error_response['status_code'] . '</b><br>';
      105. echo 'Identifier is the rowID
      106. (index) in the database that caused the problem, and Apple will
      107. disconnect you from server. To continue sending Push Notifications, just
      108. start at the next rowID after this Identifier.<br>';
      109. return true;
      110. }
      111. return false;
      112. }
      113. ?>
      Alles anzeigen

      Ich habe das Script bereits getestet - es funktioniert einwandfrei. Man könnte nun bspw. die Device Tokens in einer Datenbank speichern und bei einer Aktion die auf einer Webseite ausgeführt wird eine Push Nachricht auslösen.

      Ich hoffe, ich konnte Euch einen kleinen Einblick in Push vermitteln und bin für Feedback jederzeit offen!

      Viele Grüße,
      Tammo