A Guide to Amazon Simple Notification Service Mobile Push Self-registration for iOS
By Rich Cowper, Solutions Architect, AWS
Part 2: No back-end required!
In Part 1: Let’s get Pushy!, we walked through deploying an example app to a test iOS device and manually registering the device with Amazon SNS mobile push. We were also able to send push notifications to the device using the Amazon SNS console. In this next part, we are going to modify the example app from part 1 to utilize the AWS SDK for iOS and Amazon Cognito to self-register the device with your Amazon SNS mobile push app.
Let’s start right where we left off from Part 1.
(Continued from Part 1) Step 3: AWS SDK for iOS
We need the AWS SDK for iOS in order to interact with Amazon SNS mobile push directly from the device. To get the AWS SDK for iOS running in our example app, we can follow the directions in the SDK documentation.
Make sure to add AWSCognitoSync.framework as we will need it later.
We are going to be using a few different AWS services so we need to import a few libraries by adding the following to your AppDelegate.m above the first method, under the already existing import statement:
For troubleshooting, it is helpful to get some logging going on as well. The AWS SDK for iOS Core library comes with a function that will log responses from AWS APIs to the console. To enable it, add the following code to the didFinishLaunchingWithOptions: method in AppDelegate.m:
/* Add this so we can see logging to calls to AWS
AWSLogLevelError (default. Only error logs are printed to the console.)
[AWSLogger defaultLogger].logLevel = AWSLogLevelVerbose;
Step 4: Amazon Cognito for Authentication
Now that we have the AWS SDK for iOS installed and configured, we need security credentials to make calls to AWS APIs. In the past, we would deploy a TVM (token vending machine) server so that our device could request credentials to use with AWS APIs. This process secures your app by avoiding hard-coding access keys and security keys in the application itself, which is a security best practice.
Amazon Cognito changes things, and we are able to use it to get credentials and not have to run yet another server for TVM. If you haven’t read our latest series on Amazon Cognito, check out Part 1 and Part 2. Part 1 explains the authentication we are looking for here.
In this case, the example app is pretty simple, and we haven’t built any authentication of the user itself into it. In the real world, we would want some sort of authentication. We could build in authentication using Facebook, Google Gmail, Amazon Login, or a custom authentication service and integrate that with Amazon Cognito. But to keep things simple here, we will use Amazon Cognito’s unauthenticated user capabilities to get credentials.
Setting up an Amazon Cognito identity pool is very quick and straightforward:
- Log into the AWS console and open up the Amazon Cognito console at https://console.aws.amazon.com/cognito
- Click New Identity Pool.
- Fill in the form, giving your identity pool a name, leaving the identity providers blank and checking the box Enable Access to Unauthenticated Identities.
- Accept the defaults for the Identity and Access Management (IAM) step.
Don’t forget to download or copy the example code that is provided in the Amazon Cognito console after you create the identity pool as you will need it shortly.
You will notice that Amazon Cognito creates IAM roles: one for authenticated and one for unauthenticated users. We are using the unauthenticated role here, so we need to modify the IAM role policy to allow the devices to register with SNS mobile push.
Open up the IAM console and click Roles in the navigation pane. Find the unauthenticated role that was created as part of your Amazon Cognito identity pool. If you accepted the defaults during the identity pool IAM configuration step, the role will be named something along the lines of Cognito_<IdentityPoolName>_UnAuth_DefaultRole.
Click on the role and select Attach Role Policy. Apply the following policy:
Note the Resource section in the policy: This limits the policy to apply to only the Amazon SNS mobile app that you created in step 2. You will replace the ARN in the example with the ARN from your Amazon SNS mobile push app. You can find the ARN in the Amazon SNS console by opening the app you created.
Also notice the Statement block: This allows this role to register a device with SNS mobile push by calling the CreatePlatformEndpoint API action. It does not provide any other functionality because it doesn’t need any other functionality, following the security best practices of least required privilege.
Once we have the identity pool configured, we can now copy the Amazon Cognito code provided during identity pool creation into our example app so that we can retrieve credentials we will need later.
Since we are not doing anything more than just registering the device with Amazon SNS mobile push, we can put the Amazon Cognito code in the didRegisterForRemoteNotificationsWithDeviceToken: method as the first thing that is executed. There are probably better places to put this code so the credentials can be used elsewhere in the app, but to keep things simple we will place it here.
If you now run the example app on your device, you should see in the console in Xcode both the Amazon Cognito ID unique to this user and the APNS registration device token.
Step 5: Self-registration
Finally we have everything we need to self-register the device with Amazon SNS mobile push.
If you followed the example app setup and were able to send a push notification, you will have noticed that the device token produced in the console in Xcode has spaces in it and that you had to remove the spaces when registering the device with Amazon SNS mobile push via the Amazon SNS console. The same is true when we register via the AWS SDK for iOS. In order to parse the string to remove the spaces, here is a quick helper method that can be placed somewhere in AppDelegate.m:
/* This method converts the device token received from APNS to a string that Amazon SNS Mobile Push can understand (takes out spaces) */
NSString *rawDeviceTring = [NSString stringWithFormat:@”%@”, deviceTokenData];
NSString *noSpaces = [rawDeviceTring stringByReplacingOccurrencesOfString:@” ” withString:@””];
NSString *tmp1 = [noSpaces stringByReplacingOccurrencesOfString:@”<” withString:@””];
return [tmp1 stringByReplacingOccurrencesOfString:@”>” withString:@””];
(Credit for this method goes to another example app on GitHub.)
Now we can replace the code that printed the token to the console in the didRegisterForRemoteNotificationsWithDeviceToken: method with the following:
/* This is the code to actually register the device with Amazon SNS Mobile Push based on the token received */
NSString * myArn = @”arn:aws:sns:us-east-1:123456789123:app/APNS_SANDBOX/AmazonMobilePushExample”;
NSLog( @”Submit the device token [%@] to SNS to receive notifications.”, deviceToken );
AWSSNSCreatePlatformEndpointInput *platformEndpointRequest = [AWSSNSCreatePlatformEndpointInput new];
platformEndpointRequest.customUserData = @”MyUserID;iPhone5″;
platformEndpointRequest.token = [self deviceTokenAsString:deviceToken];
platformEndpointRequest.platformApplicationArn = myArn;
AWSSNS *snsManager = [[AWSSNS new] initWithConfiguration:configuration];
/* End Amazon SNS Mobile Push self registration */
Let’s look closer at what is going on in this code:
- The first line defines a variable to hold the ARN for your Amazon SNS mobile push app, making the call later a little bit easier to read. You find this ARN by opening the Amazon SNS mobile push app in the Amazon SNS console.
- The second line logs the device token to console in Xcode for troubleshooting purposes.
- The third line creates a CreatePlatformEndpointInput object, which holds the CreatePlatformEndpoint parameters that we need to send to Amazon SNS mobile push.
- The fourth line creates a service manager object that uses the configuration object from Amazon Cognito, which contains AWS API credentials for this user, and the CreatePlatformEndpointInput object created in the third line. This call actually sends the request to Amazon SNS mobile push and registers the device token with an endpoint in your Amazon SNS mobile push app.
Once you have the code inserted, go into your Amazon SNS mobile push app and delete the manually entered endpoint from your tests in part 1.
Now that we have all the code we need in the example app and we have everything configured, we can test!
Launch the application on your test device and watch the output in Xcode. You should see log inputs from the AWS SDK as it makes the calls to register the device token with Amazon SNS mobile push.
Now when you log into the Amazon SNS console and open your Amazon SNS mobile push app, you should be able to see that a new device is registered. Once you see the device, you can use the Amazon SNS console to send another test push notification.
Step 6: And Beyond!
We now have an example application that uses the AWS SDK for iOS, Amazon Cognito and Amazon SNS Mobile Push to auto-register itself on launch. None of this required a back-end server to accomplish, so our startup’s goal of a lean architecture has been accomplished.
The next step to tackle is now that everything is registered with Amazon SNS Mobile Push, how does our app back-end find out about new registrations and how do we send pushes from the back-end?
There are a couple of different approaches to interacting with Amazon SNS Mobile Push. You have already seen that you can use the Amazon SNS Console to send push notifications manually but clearly that doesn’t scale.
The AWS SDKs provide functions for sending push notifications from your back-end application. A Java example is included in the ‘snsmobilepush.zip’ file we downloaded in Part 1 under ‘SNSSamples’ directory. An example of how to get the sample working is available in Step 6 here.
That will cover sending push notifications programmatically, but how do we know the endpoint ARN to send to?
You can query Amazon SNS Mobile Push using one of the SDKs to get a list of endpoints. This is also good for getting the UserData as well on each endpoint. To see how that might work, read the documentation on Using Amazon SNS Mobile Push APIs and focus on the ListEndpointsByPlatformApplication call.
An even better approach is to utilize Amazon SNS in another way to get real-time notifications when a device is registered. If you go back to the Amazon SNS console and view your app, you can select it and hit the Application Actions drop-down. From there you will see the Configure Events option. This wizard helps you set up events that will send messages to a topic as they happen.
You can configure four events:
- Endpoint Created
- Endpoint Deleted
- Endpoint Updated
- Delivery Failure
You will need to create an Amazon SNS topic and get the ARN for that topic so you can enter it in the Configure Events box. For more information, see Create a Topic in the Amazon SNS Developer Guide.
Once you have the various events being sent to an Amazon SNS topic, you can subscribe HTTP endpoints and/or Amazon SQS queues to the topic so that your back end is sent a JSON-formatted message when the event happens.
Here is an example message received for an endpoint-created event:
From there, you can register the ARN you would send push notifications to in your database, most likely tied to a user ID.
Part 2 Conclusion
With just a few lines of code in your iOS app, and using AWS SDKs, you can deploy push notifications at scale without the need for a back-end API to register devices with Amazon SNS mobile push. This frees you up to focus in on the important part of your back end, which is most likely how to appropriately target push notifications that will enhance your app user’s experience. And of course, this pattern works in a very similar way across other platforms, too, so deploying to Android, Amazon Kindle, or other platforms is significantly simplified.