System calling screen with CallKit

Integrate Amazon SNS and CallKit

Yusuke Kawanabe
6 min readOct 30, 2016

Apple announced to open API to use the native calling screen on iOS with VoIP (Voice over IP) apps at WWDC16. We used to send push notifications to notify phone calls to users. From iOS10, third-party apps can also use the native calling screen to notify users by using CallKit.

I will summarize my experience with CallKit.

Abstract

  1. Setup Amazon SNS
  2. Initial setup on the app with PKPushRegistry to receive VoIP push notification.
  3. Send VoIP push notifications from Amazon SNS
  4. Show the system calling screen with CallKit

Receive VoIP push notifications from Amazon SNS

Setup Amazon SNS

Apps have to be in the background to show the system calling screen. VoIP push notifications allow us to wake up background apps.

Apps are assured to run in the background when it receives VoIP push notification. This let us initiate VoIP call. I did not notice this difference from regular push notifications and local notifications and ended up spending a lot of time.

However, since Parse and Firebase did not support VoIP push notifications at the moment I worked on this (Aug. 2016), I decided to use Amazon’s push notification service, SNS.

Setting up VoIP push notification on SNS is pretty much the same procedure as for regular push notifications.

The first thing to do is set up certificates. You can create VoIP push certificates from Apple Developer Website as for the regular push notifications. Select VoIP Service Certificate, download the certificate, and export the P12 file.

Second, you need to create an application on SNS. Select SNS from the AWS dashboard.

Then, select Application > Create platform application.

Then input the required information in the configuration.

  • Application name: App name of your choice
  • Push notification platform: Apple development or Apple production
  • Push certificate type: VoIP push certificate
  • Choose P12 file: Select the VoIP push notification P12 file created using a certificate from Apple Developer Website
  • Enter Password: Enter the password you specified when you created a p12 file

That’s all you need for the SNS settings. Of course, you need to hook this up to your server for your production. The server should provide a simple API to connect the two.

Configuration in the app to receive VoIP push notifications

The next step is to configure the iOS project. Select your project file and enable Background in Capability for the targets for which you want to allow the push notification. Select Voice over IP and Background fetch so that you can receive VoIP push notifications and process the necessary steps in the background.

Now, we will get a device token with PKPushRegistry. As shown below, we set PKPushTypeVoIP to a PKPushRegistry instance. The instance you specify as a delegate will receive VoIP push notification-related callbacks.

Cal setupPushKit method in AppDelegate to receive PKPushRegistry's callbacks.

We need to parse device tokens after receiving credentials in JSKPushKitManager. You can receive the credentials in the delegate method of PKPushRegistry.

In this example, we print out the device token with NSLog() and add the token to SNS manually for testing purposes. For production, I recommend you to send that to your server. (You can register directly to AWS from the client. However, there are some pitfalls with this, and I don't recommend doing this.)

When the client receives push notifications, the delegate method below is called.

Sending VoIP push notification from Amazon SNS

Now that we have a device token, we will register this to SNS for testing. On the SNS dashboard, select “Application”, select “Create platform endpoint,” and add the device token. After adding the token, Endpoint ARN for the device will be created. SNS will use this endpoint for sending notifications for the particular device.

In the device token register view, you can also add user data. You can add user ID etc. to support sending push notifications to a particular device.

Let’s send a VoIP push notification to a device. Select the device you want to send a notification. Clicking “Push to Endpoint” will let you fire the push notification. If you have already finished setting up on the client, you can see a log of the notification receipt.

Show the system calling screen with CallKit

Now that you can send VoIP push notifications, the only thing left to show you is showing the system calling screen.
The classes you will use is below.

  • CXProvider: The class asks the system to show the calling screen.
  • CXProviderConfiguration: Configuration for the calling screen for your app.
  • CXCallUpdate: Configuration for the call

I created JSKSystemCallProvider class to add call-related features.

I initiate CXProvider with をCXProviderConfiguration. You can configure ringtones, app display names, app icons etc with CXProviderConfiguration.

Start call

To start a call, you call - (void)reportNewIncomingCallWithUUID:(NSUUID *)UUID update:(CXCallUpdate *)update completion:(void (^)(NSError *_Nullable error))completion; in CXProvider.

When a user taps on the start calling button, the completion handler success() is going to be called.

Call-related information will be passed to CXCallUpdate instance. You can set it up to allow video calls, and group calls, etc. with this class.

https://gist.github.com/jeffsuke/3c26909853906ee6a4a758ea5e6d18b6

Finish call

Since you can open an app after starting a call, we need to think both cases where a user finishes the call from the app and the system calling screen.

You can finish the system call by calling - (void)reportCallWithUUID:(NSUUID *)UUID endedAtDate:(nullable NSDate *)dateEnded reason:(CXCallEndedReason)endedReason; in CXProvider. You need to call this method after finishing the WebRTC call in your app to make sure the system calling screen will not end up in a weird state.

If a user finishes calls on the system calling screen, the delegate method below will be called.

When the delegate method is called, the calling status is “finishing call”. You can update the calling screen to “Ended call” by calling [action fulfill];.

Pitfalls with the system calling screen

Behavior is different depending on if the device is locked or not

Although tapping the start calling button will open the app with an unlocked device, the system calling screen will be provided, and the app will start running in the background with a locked device. The app will start WebRTC call in the background in this case. Since we can’t provide alert views outside of an app, we won’t be able to give microphone and camera permission if the call started while the device is locked. To avoid a situation where users can hear but the other person on the phone can not hear, you need to give permission before the call starts while the app is open. I encountered this bug with Skype while testing the feature.

Life cycle with VoIP push notifications

An app will start the process in the background when it receives VoIP push notifications, and the method below will be called.

Then,

will be called. If you have asynchronous tasks on app launch that need to be finished before the call, you may need to wait for that.

I wrote about my experience working on VoIP push notifications on Amazon SNS and the implementation of the system calling screen with CallKit. You can provide a consistent experience to users by receiving VoIP push notifications and showing the system calling screen.

The sample project Apple provides is very helpful.

References

--

--

Yusuke Kawanabe

Software Engineer at Facebook. Authored “Mastering Auto Layout with Best Practices”. Co-authored “iOS 11 programming”.