iOS Push notifications with AWS SNS

Hey everyone. This is Mahdi again!

Today we're going to see how to setup Amazon Web Services' Simple Notification Service (AWS SNS) for sending and receiving iOS push notifications. From A to Z!

Now, if you've ever tried setting it up before, you probably already know that Amazon's documentation is utterly wrong! The steps, while still complicated, are much easier.

Apple certificates mumbo-jumbo

First, we're going to enable the Apple Push Notification Service for our app on Apple's Developer site. So go there, open Identifiers, App IDs and choose your app ID. (Create an ID for your app if you don't have one already.)

After clicking the Edit button, you'll be presented with all the services you can enable for your app. Enable Push Notifications and click Create Certificate….

Now, this step is the most important and the most easily forgotten: To create the correct certificate, when you use Keychain Access to create the CSR you have to include your private key. For this, the menu item has to say "Request a Certificate From a Certificate Authority With “iOS Developer: [your_name]”…" and not simply "Request a Certificate From a Certificate Authority…".

To do this, in Keychain Access, choose the login Keychain on the left, then Certificates on bottom left, find your iPhone Developer: [your_name] certificate in the list, expand it and select the private key iOS Developer: [your_name] as shown in the picture.

Upload the generated CSR and download the certificate. Then, create a new provisioning profile for your app. Download your developer certificate again, just in case. You need to have everything up to date.

Do not forget to select your app's target in Xcode, go into Capabilities and enable Remote Notifications in Background Modes

Creating AWS SNS app & topic

Open your AWS console, choose Create New App and in the Push Platform list, select APNS_SANDBOX. (This assumes that you are setting up Push for development, of course)

As you can see, it asks you for a P12 File. So open Keychain Access, select login then Certificates again but this time choose the Apple Push certificate called Apple Development IOS Push Services: [your_app_id] and right click on it (i.e. the certificate, not the private key). Choose Export “Apple Development...”…, save it and give it a password.

On SNS, choose the exported certificate, enter your password, click Load Credentials from File and then Add New App.

Next, create a new topic on SNS. This should be very easy.

Good. Before proceeding to the next step, which is sending and receiving push notifications, you should have the following information available to you:

  • AWS Access Key
  • AWS Secret Key
  • AWS Region code
  • AWS SNS Application ARN (easily found by selecting your app in SNS)
  • AWS SNS Topic ARN (easily found by selecting your topic in SNS)

Sending and receiving push notifications!

Now we've reached the code part! Here's how it goes:

On iOS:

  • Ask Apple for device token
  • Tell AWS SNS to create platform endpoint for our app & that device token
  • Tell AWS SNS to subscribe the platform endpoint to a topic
  • Receive notifications!

On Rails:

  • Install AWS gem
  • Send a push notification to our topic

For the iOS part, we already open-sourced the class we're using at inket/MBAWSSNSManager on GitHub.

The usage is as follow:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self registerForNotifications];
}

- (void)registerForNotifications {
    MBAWSSNSManager* manager = [MBAWSSNSManager sharedManager];
    manager.acceptsNotificationTypeAlert = YES;
    manager.acceptsNotificationTypeBadge = YES;
    manager.acceptsNotificationTypeSound = YES;
    manager.awsAccessKey = @"<your_key>";
    manager.awsSecretKey = @"<your_other_key>";
    manager.awsRegion = <your_region (e.g. AWSRegionUSWest1)>;
    manager.awsApplicationArn = @"<your_app_arn>";
    [manager registerForNotifications];
}

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
    MBAWSSNSManager* manager = [MBAWSSNSManager sharedManager];
    manager.deviceToken = deviceToken;
    [manager subscribeToTopics:@[
                                 @"<your_topic_arn>"
                                 ]];
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error{
    NSLog(@"Failed to register with error : %@", error);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler {
    // do something
    NSLog(@"our notification: %@", userInfo);
    handler(UIBackgroundFetchResultNewData);
}

Feel free to take a look at how it MBAWSSNSManager works!

Finally, in your Rails app you could install the gem aws-sdk and use the following code to send a push notification to the topic:

message = "You've got mail!"
mail_id = "33459"

payload = {
  :default => message,
  "APNS_SANDBOX" => { # change to "APNS" when running push for distribution
    :aps => {
      :alert => message,
      :sound => "default", # sound file name
      "content-available" => 1 # trigger fetch if enabled
    },
    :mail_id => mail_id # example of extra data
  }.to_json
}.to_json

AWS.config(
  access_key_id: ENV['AWS_ACCESS_KEY_ID'],
  secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
  region: ENV['AWS_REGION']
)
AWS::SNS.new.client.publish(
  topic_arn: "<your_topic_arn>",
  message: payload,
  message_structure: 'json'
)

Done and done! Now you should receive the push notification on your phone instantly.

The process is quite confusing so if you have questions you could tweet me at @inket !

Until another time :)