Apple Push Notification with Java Spring Framework

June 02, 2016

I am implementing a Java Spring Framework server which sends an Apple Push Notification to an iPhone using Swift. Here are the things you will need:

1. Account setup

Assume you already have an Apple developer account with certificates, login https://developer.apple.com/ and go to the tab Identifiers and click “+” to add a new one. Fill in the prefix and suffix, then remember to check the box “Push Notifications

e1622 1picbwehzdgvg9bl4miemkw

32f31 1xetpx7t01kw9vosuki6tmw

Then select Edit, and scroll to the Push Notifications section, you will see the “Configurable” orange indicators. Create and download the Development SSL certificate using your CSR file. Double click to run and add to your key chain.

1612a 1jucgurb7bi4 qcxubqvdwa

Open KeyChain Access, find the certificate and export it as .p2 file. You will be prompt to enter a password, which better not to leave it blank. Otherwise the Java Spring app may not be able to parse empty string. Save this file, which will be used in later steps.

b0fe5 1reswezectdr pxtrt0iyma

2. Xcode setup

Create a new Xcode project, such as Single View Application. In the Capabilities tab, enable ‘Push Notifications’ with your Apple ID accounts login.

fa4c6 1p3kk5turipw19flwwosesq

In the AppDelegate.swift, add a method to create an instance of settings, which get the user’s permission with prompt when the app launches:

    func registerForPushNotifications(application: UIApplication) {
      let notificationSettings = UIUserNotificationSettings(
      forTypes: [.Badge, .Sound, .Alert], categories: nil)
      application.registerUserNotificationSettings(notificationSettings)
    }

Invoke it when the finished launching:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
     // Override point for customization after application launch.
     registerForPushNotifications(application)
     return true
    }

In the same AppDelegate.swift file, add the method below, which handles when user accepts or declines the permissions.

    func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
      if notificationSettings.types != .None {
        application.registerForRemoteNotifications()
     }
    }

Add the method below to handle if the registration is successful:

    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
      let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
      var tokenString = “”

      for i in 0..<deviceToken.length {
        tokenString += String(format: “%02.2hhx”, arguments:[tokenChars[i]])
      }

      print(“Device Token:”, tokenString)
    }

Also for failure case of the registration:

    func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
      print(“Failed to register:”, error)
    }

You can now try and run the app. Notice that you will need a physical device, instead of simulator to test it out. If things go well, you should see the device token in the console. Record this for later use.

8055a 1ve8hjnaqrul2eovvcmbrna

3. Java Spring Server setup

Create a Java spring framework server using your favourite IDE, like NetBeans or Intellij. In this example, we are using maven build, with pom.xml file like this:

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

    <groupId>com.victorleungtw</groupId>
     <artifactId>push-notification</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <packaging>jar</packaging>

    <name>push-notification</name>
     <description>Push Notifcation</description>

    <parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>1.3.5.RELEASE</version>
     <relativePath/> <! — lookup parent from repository →
     </parent>

    <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <java.version>1.8</java.version>
     </properties>

    <dependencies>
     <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-rest</artifactId>
     </dependency>

     <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
     </dependency>

     <dependency>
     <groupId>org.springframework.restdocs</groupId>
     <artifactId>spring-restdocs-mockmvc</artifactId>
     <scope>test</scope>
     </dependency>

     </dependencies>

     <build>
     <plugins>
     <plugin>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-maven-plugin</artifactId>
     </plugin>
     </plugins>
     </build>

    </project>

Then we utilize the notnoop library from Maven Repository. Add this dependency to the pom.xml

    <dependency>
      <groupId>com.notnoop.apns</groupId>
      <artifactId>apns</artifactId>
      <version>1.0.0.Beta6</version>
    </dependency>

When the server started, it would look for the main, with example PushNotificationApplication.java below:

    package com.victorleungtw;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    [@SpringBootApplicatio](http://twitter.com/SpringBootApplicatio)n
    public class PushNotificationApplication {

    public static void main(String[] args) {
     SpringApplication.run(PushNotificationApplication.class, args);
     }
    }

As a simple demo, we will create a NotificationController.java

    package com.victorleungtw;

    import java.util.Map;
    import java.util.Date;

    import java.util.concurrent.atomic.AtomicLong;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import com.notnoop.apns.APNS;
    import com.notnoop.apns.ApnsService;

    /**
     *
     * [@author](http://twitter.com/author) victorleungtw
     */
    [@RestController](http://twitter.com/RestController)
    public class NotificationController {
     private static final String template = “Hello, %s!”;
     private final AtomicLong counter = new AtomicLong();
     ApnsService service = null;

    [@RequestMapping](http://twitter.com/RequestMapping)(“/notification”)
     public Notification notification([@RequestParam](http://twitter.com/RequestParam)(value=”name”, defaultValue=”World”) String name) {

     System.out.println(“Sending an iOS push notification…”);

     ApnsService service = APNS.newService()
     .withCert(“**XXX.p12**”, “**YOUR_PASSWORD**”)
     .withSandboxDestination()
     .build();

     String payload = APNS.newPayload()
     .alertBody(“Can’t be simpler than this!”)
     .alertTitle(“test alert title”).build();

     String token = “**b8f3b2513caa.....**”;

     System.out.println(“payload: “+payload);

     service.push(token, payload);

     System.out.println(“The message has been hopefully sent…”);

     return new Notification(counter.incrementAndGet(),
     String.format(template, name));
     }
    }

Replace “XXX.p12”, “YOUR_PASSWORD” and “b8f3b2513caa…..” with your actual .p12 file path, password and the device token respectively. If you are not sure about the file path, you can put the .p12 file at the same level as your /src folder, i.e. the root of the project folder. That’s it. Run the following commands:

    mvn install

And start the server:

    mvn spring-boot:run

Open your browser, once you hit the route http://localhost:8080/notification, you should be able to recieve a notification!

Let me know if you have any questions :)


Profile picture

Experience in software development, application architecture, and deploying cloud solutions for enterprise customers. Strong hands-on skills with a Master's degree in Computer Science and business acumen with a master of business administration (MBA) in Finance. Certified in Amazon Web Services (AWS), Google Cloud Platform (GCP), Microsoft Azure, Kubernetes (CKA, CKAD, CKS, KCNA) and Scrum (PSM, PSPO) with experience in building banking products from scratch. Connect on Linkedin

© 2022, @victorleungtw