Monday, 9 March 2015

Sending iOS push notifications from JAVA

This example presumes that you've already registered your device for Push Notifications and retrieved the device token from the Apple Push Notification Service (APNS) on the iOS side.
The code below uses the https://github.com/notnoop/java-apns Java library to send notifications to the APNS server.

The code

Suggested files structure:
src/main/java/PushNotifications.java - simple java class to send notifications
src/main/resources/dev_cert.p12 - development certificate
src/main/resources/prod_cert.p12 - production certificate
pom.xml - maven config

pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>PushNotifications</groupId>
    <artifactId>PushNotifications</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>com.notnoop.apns</groupId>
            <artifactId>apns</artifactId>
            <version>1.0.0.Beta6</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.7</version>
        </dependency>
    </dependencies>

</project>

PushNotifications.java:
import com.notnoop.apns.APNS;
import com.notnoop.apns.ApnsService;
import com.notnoop.apns.ApnsServiceBuilder;

public class PushNotifications {

    public static void main(String [] args) {
        System.out.println("Sending an iOS push notification...");

        String token = "";
        String type = "dev";
        String message = "the test push notification message";

        try {
            token = args[0];
        } catch (Exception e) {
            System.out.println("Usage: PushNotifications devicetoken [message] [type prod|dev]");
            System.out.println("example: PushNotifications 1testdevicetoken3eb414627e78991ac5a615b4a2a95454c6ba5d18930ac137 'hi there!' prod");
            return;
        }
        try {
            message = args[1];
        } catch (Exception e) {
            System.out.println("no message defined, using '"+message+"'");
        }
        try {
            type = args[2];
        } catch (Exception e) {
            System.out.println("no API type defined, using "+type);
        }

        System.out.println("The target token is "+token);

        ApnsServiceBuilder serviceBuilder = APNS.newService();

        if (type.equals("prod")) {
            System.out.println("using prod API");
            String certPath = PushNotifications.class.getResource("prod_cert.p12").getPath();
            serviceBuilder.withCert(certPath, "password")
                    .withProductionDestination();
        } else if (type.equals("dev")) {
            System.out.println("using dev API");
            String certPath = PushNotifications.class.getResource("dev_cert.p12").getPath();
            serviceBuilder.withCert(certPath, "password")
                    .withSandboxDestination();
        } else {
            System.out.println("unknown API type "+type);
            return;
        }

        ApnsService service = serviceBuilder.build();


        //Payload with custom fields
        String payload = APNS.newPayload()
                .alertBody(message)
                .alertTitle("test alert title")
                .sound("default")
                .customField("custom", "custom value").build();

        ////Payload with custom fields
        //String payload = APNS.newPayload()
        //        .alertBody(message).build();

        ////String payload example:
        //String payload = "{\"aps\":{\"alert\":{\"title\":\"My Title 1\",\"body\":\"My message 1\",\"category\":\"Personal\"}}}";


        System.out.println("payload: "+payload);
        service.push(token, payload);

        System.out.println("The message has been hopefully sent...");
    }
}
Compile the code:
>mvn compile

Run the code:
>mvn exec:java -Dexec.mainClass="PushNotifications" -Dexec.args="1testdevicetoken3eb414627e789ba5d18930ac137 'My test iOS push notification!' dev"

Getting certificates from the Apple
Certificates are quite tricky and confusing part of iOS push notifications.

1. First of all we need to create a SigningRequest
Keychain Access -> Certificate Assistant -> Request a certificate from a Certificate Authority
Enter the apple dev email into the Certificate Assistant form, select the "Save to disk" radio
Save the Signing Request onto the disk (don't forget to give it some reasonable name);

2. Go to the developer.apple.com/account/ios/
Certificates, Identifiers & Profiles > App IDs > [the app id] > Edit > Push notifications >
Production SSL Certificate > Create / Create Additional,
ensure that Push Notifications enabled for your app,
Generate a new certificate using the SigningRequest from 1.
Download the generated certificate;

3. (on the Mac) Double click the certificate to import into the keychain;

4. Locate the Certificate in the Keychain Access, expand it to see the Key inside,
right click and Export "keyname"...
Save the p12 file;

5. Notice that after regenerating your certificates you need to refresh your provisioning profiles and use them to deploy your app. In xCode go to Preferences -> Accounts, selet your dev apple-id, click "View Details", do refresh. It is not going to be a problem, if you generated the certeficates before getting to code.

Links
If you use the PHP on you server, you may find the link below useful:
http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1