Tobias Czyzewski

AWS Developer and Solutions Architect

Cloud Solutions with AWS

Systematic Trading Operator

Tobias Czyzewski

AWS Developer and Solutions Architect

Cloud Solutions with AWS

Systematic Trading Operator

Blog Post

How to build a scalable iOS notification app for cloud monitoring

How to build a scalable iOS notification app for cloud monitoring

The concepts and solutions described in this article were developed for BidFlow Technologies‘ software ecosystem and are deployed in production.

Running a systematic trading business in the cloud poses different kinds of risk, from operational risks to technical risks. Amongst others, technical risks include computer center failures, cloud service malfunctions, bugs in proprietary trading software and outages of third-party APIs. All these failures can interrupt and adversely affect communication with utilized trading venues, which in turn impacts the business’s mandatory risk management. To avoid technical errors causing a rippling effect that disproportionately inflates error costs, these errors have to be detected and taken care of as soon as possible.

Errors can be handled by a technical administrator in a timely manner if he is notified as soon as these are automatically detected. Malfunctions can also be manually detected by said technical administrator if they are regularly informed about everyday business operations – the business operations start to behave noticeably different if an undetected error is impacting them.

Nowadays, mobile devices are almost always worn on the body. For the purposes mentioned above, they therefore constitute a fitting device to send monitoring notifications to. But to receive them, a notification app is required on the mobile device. This post discusses the details of designing and building such a scalable iOS notification app to monitor business operations in the cloud in real-time.


Requirement analysis

Before we get into the depth of designing and building the notification app backend and the app itself, let’s discuss some basic requirements it should meet:

  • user authentication (messages should be fanned out on a per-user basis and only sent to mobile devices on which the corresponding user is currently signed in)
  • messages categorized per topic (users should be able to utilize custom topics (notification channels) where messages are sent to)
  • persistent messages on signed in mobile devices (offline capabilities to read received messages even if the mobile device is currently offline)
  • real-time synchronization (if the mobile device is online, store and display new messages immediately)
  • mobile push notification capabilities (when the notification app is not currently active, messages should also be delivered as push notifications)

The simple notification app developed for BidFlow Technologies is called Trading Cloud Messenger (abbreviation: TCM). For visual reference and a better understanding of the stated requirements, the app’s only three views are shown below. If no user is signed in, the app greets the user with a login view. If a user is signed in, the default view is a list of user-specific topics to which messages are sent. These topics can be clicked to open a topic view which displays its corresponding message feed.

Login view
Login view
Topic list view
Topic list view
Topic view with message feed
Topic view with message feed

Solution overview

Based on the formulated requirements, it is reasonable to build a client–server architecture. Each mobile device with the notification app installed is a client to the server (cloud backend) that handles user authentication, serves per-user data and fans out push notifications. Below, the diagram shows how such a cloud backend can be structured. TCM‘s cloud backend is implemented in exactly this way.

After elaborate research, it became clear that AWS Amplify solves most of the required features out-of-the-box (please note that Amplify was under heavy development by the time TCM was developed, so device token management and push notifications were implemented outside of Amplify). Next to integrated user management with Amazon Cognito, Amplify comes with a powerful DataStore feature which handles real-time data synchronization and persistence on mobile devices. So all in all, Amplify meets the user authentication, persistent messages and real-time synchronization requirements. The message categorization per topic can be fulfilled on a data model level that will be discussed later on.

For mobile push notifications, Amazon Simple Notification Service (Amazon SNS) deemed most suitable. It can utilize Firebase Cloud Messaging (FCM), a cross-platform push notification service from Google, to deliver push notifications to mobile devices. Therefore, these services fulfil the requirement of being able to send and receive mobile push notifications.

The next sections discuss the details how Amplify manages users and serves the data layer for the notification app and how push notifications are fanned out via Amazon SNS and Firebase. It will then become clear why the services that have not been covered yet are embedded in the cloud backend’s infrastructure.

User management and data layer

Amplify comes with built-in support for Cognito and can either incorporate Cognito resources that already exist or create new ones. In the case of TCM, Cognito resources are managed outside of Amplify and are therefore linked instead of generated all over again. In addition, TCM has no sign-up capabilities. Users are registered externally and are from that moment authorized to sign in with TCM.

As mentioned before, Amplify’s DataStore feature is well suited for use in the notification app’s data layer as it can synchronize and persist data on mobile devices in real-time. To be able to fetch data which is driving the notification app from the cloud backend, DataStore has to connect to it via an API. The API has to make sure that only authenticated users can access it and that only data that the user is authorized to access can be retrieved. With Amplify’s GraphQL API it is possible to implement these features. For this, data is modeled with GraphQL schemas and is then used by the GraphQL API and DataStore to manage data in exactly the way it is specified in the schemas.

The notification app utilizes a rather simple data model as, per requirements, it only needs topic and message types. These can be modeled in a many-to-one relationship: every topic can have many messages and in turn each message should belong to exactly one topic.

Let’s have a look how the GraphQL schema for a such data model looks like:

type Topic @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  name: String! @index(name: "byName", queryField: "getTopicByName")
  arn: String!
}

type Message @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  type: Int!
  title: String!
  body: String!
  createdAt: AWSDateTime!
  topicId: ID! @index(name: "byTopicIdAndCreatedAt", sortKeyFields: ["createdAt"], queryField: "getMessageByTopicIdAndCreatedAt")
  topic: Topic @hasOne(fields: ["topicId"])
}

Using this schema, Amplify automatically creates Amazon DynamoDB database tables for GraphQL types annotated with the @model directive. These tables can then be accessed through the GraphQL API and are used to store all the application data (based on how the data types are defined). The @auth directive configures authorization rules for data access. In this case it allows only the owner (creator) of a data record to read, update, and delete it.

The topic type consists of a unique topic ID (which is used as the primary key), a readable topic name for display purposes and an Amazon Resource Name (ARN) that links the topic to a SNS topic (for push notification purposes that will be discussed later on). Exclamation marks make the fields required. In addition, the topic name is flagged as a secondary index so that topics can also be queried by topic name (and not only by topic ID which is an obscure string).

The message type consists of a unique message ID (which is used as the primary key), a type of message for fine-grained categorization, a message title, a message body, an ISO 8601 timestamp at which the message was created and a topic ID to link the message to a topic. topic: Topic @hasOne(fields: [“topicId”]) marks the message to topic relationship as a many-to-one relationship and specifies topicId as the field to store the related topic ID. topicId, in conjunction with createdAt as a sort key, is flagged as a secondary index so that messages can also be queried by topic ID sorted by timestamp (and not only by message ID which is an obscure string). Therefore, the secondary indices allow the retrieval of a topic ID by topic name. This topic ID can then be used to query the topic’s messages sorted by message timestamp.

Thus, the GraphQL schema describes both topic and message types, defines the relationship between them, adds secondary indices for more powerful query capabilities and sets authorization rules to protect data records from being accessed by unauthorized users. Next to generating all necessary DynamoDB tables, Amplify’s GraphQL API also generates all required API endpoints based on this schema. These are created with AWS AppSync. DataStore can then use these endpoints to synchronize data from DynamoDB in real-time.

Push notifications

The last requirement is to solve push notification capabilities. The diagram below shows how SNS can be leveraged to send push notifications to mobile devices.

Source: <a href="https://aws.amazon.com/sns" target="_blank">AWS</a>

Before push notifications can be sent and received, a so-called platform application has to be created in SNS that links to a push notification service (Firebase Cloud Messaging in the case of TCM). Then, each mobile device must be registered as a platform endpoint to be able to receive push notifications from topics or direct channels. These platform endpoints are defined by tokens that uniquely represent a mobile device. In FCM they are called registration tokens.

The diagram below visualizes how mobile devices register with FCM and how they obtain such unique registration tokens.

Source: <a href="https://learn.microsoft.com/de-de/xamarin/android/data-cloud/google-messaging/firebase-cloud-messaging" target="_blank">Microsoft</a>

For TCM, the obtained registration tokens have to be sent to SNS so that it can create the required platform endpoints. This requires more thought out token management and the Mobile token management with Amazon SNS post on AWS Front-End Web & Mobile Blog describes best practices for registering tokens with SNS.

Amplify’s GraphQL API mentioned in the previous section is purely for application data management purposes. To fulfil more complex functionalities like the described token registration, additional API endpoints are required. For TCM, these endpoints are implemented with Amazon API Gateway in a REST API. Like Amplify, API Gateway can utilize existing Cognito resources to authenticate users and deny access for unauthorized users. Let’s have a look at the REST API’s endpoints that are available for TCM:

  • POST /tcm/register-device-token
  • POST /tcm/deregister-device-token
  • POST /tcm/create-topic
  • POST /tcm/delete-topic
  • POST /tcm/subscribe-to-topic
  • POST /tcm/unsubscribe-from-topic
  • POST /tcm/create-message

There are two endpoints for registering and deregistering mobile devices, the others are used for user-level functionality like creating and deleting topics, subscribing and unsubscribing from topic push notifications and creating messages (it is on purpose that users should not be able to delete messages as the notification app behaves like a log).

All these endpoints are required in addition to Amplify’s GraphQL API as it is not sufficient to create topics and messages in the data layer only – furthermore, SNS resources must be managed in conjunction with the data objects to realize the required push notification capabilities. This is done through the described endpoints, which execute AWS Lambda functions behind the scenes. They make sure that the necessary SNS platform endpoints get created, that registration token / user mappings get stored in a DynamoDB table, that SNS topics get created in conjunction with user topics, that SNS platform endpoints get subscribed to SNS topics and that messages get published to the associated SNS topic as soon as a new message is created. The chosen push notification service then delivers these messages as push notifications to all subscribed mobile devices while DataStore also stores them within the notification app on each device.

Cloud monitoring

At this point, all cloud infrastructure is in place to fulfil the requirements stated in the beginning of this post. Sending cloud monitoring messages to notification app instances is relatively easy now. From within the cloud, there just has to be acquired a Cognito ID token by authenticating as a user and then the REST API’s POST /tcm/create-message endpoint can be utilized to publish messages to all mobile devices that are logged in with the same user credentials.

Mobile client

The last piece of the puzzle is the mobile client that receives push notifications and is used to view message histories. TCM was developed with Swift as a native iOS client. It uses the Amplify Library for Swift to sign in users, to utilize Amplify’s DataStore feature and to access the cloud backend API endpoints. In addition, Firebase SDK is used to obtain registration tokens. By adding push notification capabilities to the iOS app, it is now fully functional and can monitor business operations in the cloud via messages in real-time.

Conclusion

This post gave an overview on how an iOS notification app can be built for real-time cloud monitoring that can be scaled to multiple users. First of all, basic requirements were formulated and then incorporated in the design of the notification app’s cloud backend. Based on this solution, a real-world notification app called Trading Cloud Messenger (TCM) got implemented at BidFlow Technologies and is running robustly in production.