Friday, January 31, 2014

iOS APNS & Android GCM Push with Apache Cordova (Phonegap)

Here are my instructions for Hello World, a tutorial with Aerogear's Unified Push Server that allows you to send iOS and Android push notifications to Apache Cordova/Phonegap/HTML5 client applications. If you need help, the best place to hang out is on irc.freenode.net #aerogear channel

The instructions can be found in the readme.txt file and the sample code, while not necessary to follow the tutorial is also available at github https://github.com/burrsutter/hellopush The primary difference between the instructions in the readme.txt and the provided solution is that I added logic to address the OS configuration. If you are an Eclipse user, JBoss is adding a plugin for Phonegap/Apache Cordova project creation - here is a video to see it in action: https://vimeo.com/82204444
https://vimeo.com/81565404

My Aerogear Unified Push Server instance is running at OpenShift - sign-up is free and it only takes a few clicks to select the Aerogear Push 0.X quickstart and you are up and running.

Aerogear Cordova Push Plugin (HelloWorld)

0. getting the Cordova command line tool installed for Mac
sudo npm install -g cordova
cordova -version
3.3.1-0.1.2
http://cordova.apache.org/blog/releases/2013/07/23/cordova-3.html

Cordova Command Line Guide
http://cordova.apache.org/docs/en/3.0.0/guide_cli_index.md.html

npm itself comes from Node.js http://nodejs.org/
npm -version
1.3.25
node --version
v0.10.25
You will also need the Android SDK installed for your OS and in your PATH - commands like "android" and "adb devices" should execute
You will also need "curl", if you type in "curl" and hit return
curl: try 'curl --help' or 'curl --manual' for more information

1. Cordova create has the following sequence: "cordova create folderName bundleID appName"

cordova create hellopush com.burrsutter.hellopush "Hello Push"

2. edit hellopush (bring up your editor) if you open config.xml, you should see <name>Hello Push</name> <widget id="com.burrsutter.hellopush" the widget id becomes the bundle identifier, it must unique identify your iOS app, globally when you setup your iOS provisioning profile, you will need this unique bundle ID

3. cd hellopush

4. cordova platform add android

5. cordova plugin search push

6. cordova plugin add org.jboss.aerogear.cordova.push

7. There is a console.log in the default project as well as in the suggested example code, so make sure to also add the console plugin

cordova plugin add org.apache.cordova.console

and to know the mobile OS install the device plugin

cordova plugin add org.apache.cordova.device

8. index.html - insert the following below <div id="deviceready"> but inside <div class="app"> <font size="6"> <div id="notify">notifications </div> <div id="debug">debug </div> </font>

9. index.js

   successHandler: function (message) {
            var debug = document.getElementById("debug");
            console.log(message);
            debug.innerHTML = "success: " + message;
   },
   errorHandler: function (message) {
            var debug = document.getElementById("debug");
            console.log(message);
            debug.innerHTML = "error: " + message;
   },  
   onNotification: function (e) {
            // alert(e.alert);
            var notify = document.getElementById("notify");
            notify.innerHTML = e.alert;
   },  

    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 
    // 'receivedEvent' function, we must explicity call 
    // 'app.receivedEvent(...);'
    onDeviceReady: function() {
        var config = {
                senderID: "492580885002",
                pushServerURL: "https://aerogear-html5.rhcloud.com",
                variantID: "b3852e72-92e1-4b05-a4bd-b549438d4943",
                variantSecret: "13c9daca-457e-4837-bc26-769ba572a940"
        };
        push.register(
            app.successHandler,
            app.errorHandler,
            {
            	"badge": "true",
                "sound": "true",
                "alert": "true",
                ecb: "app.onNotification",
                pushConfig: config
            });
            
        app.receivedEvent('deviceready');
   },
Note: senderID, variantID and variantSecret were all setup in the Push Console at pushServerURL

http://aerogear.org/docs/guides/aerogear-push-android/google-setup/

http://aerogear.org/docs/guides/AdminConsoleGuide/

10. Run "adb devices" to see if an android device is plugged in correctly then cordova run android

this will install and launch the app on your plugged in, developer-ready Android phone/tablet

11. then send a message

curl -3 -u \
"f07c43a6-bb0a-4bb7-a1eb-a368db272212:e2cf19c3-6636-4712-bbe8-26b7c1ac9c09" \
   -v -H "Accept: application/json" -H "Content-type: application/json" \
   -X POST -d '{"message": {"alert":"Hello AeroGear", "badge":1}}' \
   https://aerogear-html5.rhcloud.com/rest/sender
NOTE: this is not wrapping correctly here in Blogger, here is the gist and I included a send.sh to make sending in numerous messages a little easier. Usage: ./send.sh "My Message" 3

where f07c43a6-bb0a-4bb7-a1eb-a368db272212
is your Application ID from the web console
where e2cf19c3-6636-4712-bbe8-26b7c1ac9c09
is the Master Secret also from the web console

Success, send a few more messages, changing the "Hello AeroGear Unified Push!"

Note: If you wish to deploy the app to another Android device, unplug the current one and plug in the new one - use "cordova run android" again and it will deploy to the new device - the app will still function and receive push notifications without being plugged in on USB.

12. Add iOS, if on Mac OS X, with XCode installed (via Mac AppStore is easiest) and you have previously paid your $99 and know how to get around at http://developer.apple.com

cordova platform add ios
13. Setup your provisioning profile:

http://aerogear.org/docs/guides/aerogear-push-ios/app-id-ssl-certificate-apns/ and

http://aerogear.org/docs/guides/aerogear-push-ios/provisioning-profiles/ and

http://aerogear.org/docs/guides/AdminConsoleGuide/

14. Add the variant for iOS via the Unified Push Server web console - uploading the .p12 file and its associated passphrase

15. look under platforms\ios and you should see a Hello Push.xcodeproj - double click on the .xcodeproj

If you see the General tab, double check the bundle identifier of com.burrsutter.hellopush or whatever you made yours

Modify the index.js (in XCode) for the proper variantID and variantSecret from the Unified Push Server console

16. Assuming you have previously registered your plugged in iOS device with the developer.apple.com portal, you can now hit the big arrow and target your device.

When the app installs it should prompt you to accept push notifications.

17. Look for a new installation/device token under your newly added iOS variant in the UPS web console. If you see no instance/device token then something failed.

18. Finally, send a message, it should hit all the android & iOS devices that you have deployed the app to.

8 comments:

  1. Hi Hello!

    Thanks for your tutorial. It's very important, since the original documentation is not very clear and some times apparently contradictory :-(

    I managed to install the whole thing (I think...) on Cordova Android but the app crashes after few seconds because, according to the log, it can't get a connection to the server. It shouldn't crash because of this, but this is another matter...

    I wonder if the pushServerURL is incorrect:

    https://aerogear-metalpush.rhcloud.com:8443/simplepush

    ... or if there's another situation. The routine never reaches the success or error handlers.

    Any idea about this would be very useful.


    Thanks

    Miguel

    The code I use is this:

    var pushConfig = {
    senderID: "xxxxxx",
    pushServerURL: "https://aerogear-metalpush.rhcloud.com:8443/simplepush",
    variantID: "xxxxxx",
    variantSecret: "xxxxxxx"
    }

    try {
    push.register(successHandler, errorHandler, {"badge": "true", "sound": "true",
    "alert": "true", "ecb": "onNotification", pushConfig: pushConfig});

    } catch (err) {
    txt = "There was an error on this page.\n\n";
    txt += "Error description: " + err.message + "\n\n";
    alert(txt);

    }

    function successHandler(result) {
    var respush = document.getElementById("pushst");
    respush.innerHTML = "Serviço de notificações: " + result;
    }

    function errorHandler(error) {
    var respush = document.getElementById("pushst");
    respush.innerHTML = "Serviço de notificações: " +error;
    }


    The log I get is this:

    02-13 15:10:45.420: E/AndroidRuntime(24758): FATAL EXCEPTION: AsyncTask #2
    java.lang.RuntimeException: java.net.SocketTimeoutException: Read timed out
    02-13 15:10:45.420: E/AndroidRuntime(24758): at org.jboss.aerogear.android.impl.http.HttpRestProvider.post(HttpRestProvider.java:158)

    02-13 15:10:45.420: E/AndroidRuntime(24758): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:293)
    02-13 15:10:45.420: E/AndroidRuntime(24758): at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:505)
    02-13 15:10:45.420: E/AndroidRuntime(24758): at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
    02-13 15:10:45.420: E/AndroidRuntime(24758): at org.jboss.aerogear.android.impl.http.HttpRestProvider.getHeaderAndBody(HttpRestProvider.java:251)
    02-13 15:10:45.420: E/AndroidRuntime(24758): at org.jboss.aerogear.android.impl.http.HttpRestProvider.post(HttpRestProvider.java:154)
    02-13 15:10:45.420: E/AndroidRuntime(24758): ... 10 more

    ReplyDelete
  2. Hi again ;-)

    Yes the problem was with the URL. It should be instead:

    http://aerogear-metalpush.rhcloud.com/

    Now I get a "normal" error:

    02-13 16:00:07.272: E/HttpRestProvider(25972): Error on POST of http://aerogear-metalpush.rhcloud.com/rest/registry/device
    02-13 16:00:07.272: E/HttpRestProvider(25972): java.io.IOException: BufferedInputStream is closed
    02-13 16:00:07.272: E/HttpRestProvider(25972): at java.io.BufferedInputStream.streamClosed(BufferedInputStream.java:118)

    ReplyDelete
  3. Hello Miguel, yes, my example does not use the "SimplePush" URL as it does not use that feature - it focuses on APNS & GCM which are different than SimplePush - SimplePush is fairly different animal. I will ask the team to see if there is an answer for your error.

    ReplyDelete
  4. HEllo Miguel,

    thanks for the interest in on the AeroGear push bits. Let me reply


    Can you give me the link to the "original documentation" whuch you find is not very clear?

    Glad you noticed that the pushServerURL, should not contain the simplePush URL at all :- )I don't see Burr's post is mentioning 'simplePush', so I wonder why you this is required.

    The Android error you notice does, atm, not ring a bell. However we, yesterday, merged a PR to update the underlying AeroGear-Android library:
    https://issues.jboss.org/browse/agdroid-195 and https://github.com/aerogear/aerogear-pushplugin-cordova/pull/9

    Do you mind to give the latest master branch a shot ?


    Again, thanks for the interest in our push bits. We are interested in improving the user expericens. If you like, please join our mailing list for further discussions ?

    https://lists.jboss.org/mailman/listinfo/aerogear-dev

    Greetings,
    Matthias

    ReplyDelete
  5. Hello Miguel,

    I have submitted a PR to fix the documentation:

    https://github.com/aerogear/aerogear.org/pull/252

    Please review and comment there if things are still confusing.

    Thanks again for the interest!
    -Matthias

    ReplyDelete
  6. Yes, what induced my mistake on the pushserverURL parameter was that when I registered to Aeroger I got this info:

    Connection URL: https://$OPENSHIFT_APP_DNS:8443/simplepush

    Reading the service documentation more carefully I could find out that "simple push" is indeed a different thing....

    Thanks

    ReplyDelete
  7. Hello Burr, I saw your Mobile Dev using Eclipse talk at Great Wide Open and wanted to install the Mobile Dev parts of JBoss Tools right away. Do you know what the minimum features of JBoss Tools are required to install to get the Mobile Dev environment since I don't do JavaEE development?

    ReplyDelete
    Replies
    1. If you use the updatesite at http://download.jboss.org/jbosstools/updates/stable/kepler/

      you can select "JBoss Mobile Development"

      http://screencast.com/t/sg5Ed1W8XbP8

      Delete