Push Notifications and CallKit

Using VoIP push notifications and CallKit with the Sinch SDK.

Use the Sinch SDK together with Apple VoIP push notifications and CallKit to provide the best possible end user experience. VoIP push notifications are a special type of push notifications that Apple support as part of Apple Push Notification service (APNs) which enables fast and high-priority notifications. CallKit is an iOS framework that lets you integrate the Sinch VoIP calling functionality with a iOS native system look and feel.

To fully enable VoIP push notifications in your application, the following steps are required, and this document will guide you through these in more detail:

  • Configure your iOS app to use VoIP push notifications and CallKit .
  • Create and upload an APNs Signing Key for your Sinch Application in the Sinch Developer Portal .
  • Configure SINClient to let Sinch manage push notifications (both client-side and server-side).
  • Integrate use of Sinch push APIs with CallKit .
  • Ensure APNs environment is matching the app entitlements and codesigning.
️Important!

PushKit requires you to use CallKit when handling VoIP calls.

Configure iOS App with Push Notifications Capability

iOS apps must have the proper entitlements to use push notifications. To add these entitlements to your app, enable the Push Notifications capability in your Xcode project. See Apple documentation here for details on this particular step.

Acquiring a Push Device Token

SINManagedPush is a component used to simplify acquiring a push device token and registering the token with a SINClient. SINManagedPush will make use of PushKit to acquire a push device token, and will automatically register the token with the SINClient when a client is created (later in the application life cycle). SINManagedPush should be created as early as possible in the application's life cycle.

Copy
Copied
@interface AppDelegate () <SINManagedPushDelegate>
@property (nonatomic, readwrite, strong) SINManagedPush push;
@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options {

    self.push = [Sinch managedPushWithAPSEnvironment:SINAPSEnvironmentDevelopment];
    self.push.delegate = self;

    [self.push setDesiredPushType:SINPushTypeVoIP];
}
info

When creating the SINManagedPush instance, the Apple Push Notification service Environment must be specified and it must match how your application is code signed and provisioned. Please see the section APS Environments and Provisioning for details.

SINManagedPush

SINManagedPush is a very lightweight component and its life cycle can be independent of the life cycle of a SINClient. It should be created once, and shouldn't be disposed (that would prevent receiving push notifications via PushKit).

Enabling Push Notifications for SINClient

To make Sinch manage push notifications for you end-to-end, both client-side and server-side in terms of acting APNs Provider, it's necessary to enable managed push notifications when configuring a SINClient.

Copy
Copied
NSError *error;
id<SINClient> client = [Sinch clientWithApplicationKey:@"<application key>"
                                       environmentHost:@"ocra.api.sinch.com"
                                                userId:@"<user id>"
                                                 error:&error];

[client enableManagedPushNotifications];

Configuring an APNs Authentication Signing Key

To enable Sinch to act as APNs Provider on your behalf, you must provide Sinch with an APNs Authentication Token Signing Key. You create this signing key in your Apple Developer Account and upload the key file to your Sinch Developer Account.

  1. Create an APNs Key in your Apple Developer Account . See the Apple developer documentation on creating the key here .
  2. Take the key file ( .p8 ) from step 1) and upload it for your Sinch Application in your Sinch Developer Account .

🔒 Your Signing Key is Stored Securely

Sinch will store your signing key in encrypted form using AES256-GCM (AES in GCM mode, using 256-bit keys).

NOTE: We only support APNs Token-based authentication. We no longer support APNs certificate-based functionality.

CallKit

Apple Requirements on Use of VoIP Push Notifications and CallKit

Apple introduced stricter requirements for using VoIP push notifications since iOS 13. iOS apps built using the iOS 13 SDK and make use of VoIP push notifications must report each received push notification as an incoming call to CallKit.

When linking against the iOS 13 SDK or later, your implementation must report notifications to the CallKit framework by calling the method -[CXProvider reportNewIncomingCallWithUUID:update:completion:]. Further, it must report this within the same run-loop, before the delegate method invocation scope -[PKPushRegistryDelegate pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:] ends. In terms of the Sinch SDK, this means your implementation must report to CallKit in your implementation of -[SINManagedPushDelegate managedPush:didReceiveIncomingPushWithPayload:forType:].

Copy
Copied
// (Assuming CallKit CXProvider accessible as self.provider)
- (void)managedPush:(SINManagedPush*)managedPush
  didReceiveIncomingPushWithPayload:(NSDictionary *)payload
                            forType:(NSString *)pushType {

  id<SINNotificationResult> notification = [SINManagedPush queryPushNotificationPayload:payload];

  if ([notification isValid] && [notification isCall]) {
      NSUUID* callId = [[NSUUID alloc] initWithUUIDString:notification.callResult.callId];

      CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
      callUpdate.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:notification.callResult.remoteUserId];

      [self.provider reportNewIncomingCallWithUUID:callId
                                            update:callUpdate
                                            completion:^(NSError *_Nullable error) {
                                              if (error) {
                                                // Hangup call
                                              }
                                            }];

  }
  }

// (For a more complete example, see the Sinch sample app SinchCallKit.xcodeproj)

You need to relay the push notification to a SINClient. If you for some reason don't relay the push payload to a Sinch client instance using -[SINClient relayPushNotification:], you must instead invoke -[SINManagedPush didCompleteProcessingPushPayload:] so that the Sinch SDK can invoke the PKPushKit completion handler (which is managed by SINManagedPush).

Failing to report to CallKit

If a VoIP push notification is not reported to CallKit then iOS will terminate the application. Repeatedly failing to report calls to CallKit may cause the system to stop delivering any more VoIP push notifications to your app. The exact limit before this behavior kicks in is subject to Apple iOS implementation details and outside the control of the Sinch SDK.

Please also see Apple's Developer documentation on this topic.

Reporting outgoing calls to CallKit

While reporting incoming calls to CallKit's mandatory in order to process incoming VoIP push notifications, the same limitation doesn't apply to outgoing calls. Nevertheless, reporting outgoing calls to CallKit's still required in scenarios when an outgoing call is established (callee answers the call) while the caller app is in background, or the caller device is in locked state.

In such scenarios, as a privacy measure the OS will prevent the audio unit to be initialized for recording because the app is not in foreground, unless the outgoing call is reported to CallKit. For this reason the recommendation is that outgoing calls should be reported to CallKit as well.

Copy
Copied
// (Assuming CallKit CXCallController accessible as self.callController)
- (void)startOutgoingCall:(NSString *)destination {
  NSUUID *callId = [[NSUUID alloc] init];

  CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:destination];
  CXStartCallAction *initiateCallAction =
      [[CXStartCallAction alloc] initWithCallUUID:callId handle:handle];
  CXTransaction *initOutgoingCall = [[CXTransaction alloc] initWithAction:initiateCallAction];

  [self.callController requestTransaction:initOutgoingCall
                           completion:^void(NSError *error) {
                               if (error) {
                                 NSLog(@"%@", error);
                               }
                             }];
}

- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
  id<SINCall> call = [_client.callClient callUserWithId: action.handle.value];
  [action fulfill];
}

// (For a more complete example, see the Sinch sample app SinchCallKit.xcodeproj)

Extracting Call Information From a Push Payload

At the time when your application receives a push notification you will need to extract some key information about the call based on only the push notification payload. You will need to do this to conform with Apple requirements on reporting a VoIP push notification as an incoming call to CallKit, but you may also want to extract application-specific headers for the call. Use the method +[SINManagedPush queryPushNotificationPayload:] to extract call details from the raw push payload. Note that you can do this immediately and before you have created and started a SINClient instance.

Example

Copy
Copied
- (void)managedPush:(SINManagedPush *)managedPush
  didReceiveIncomingPushWithPayload:(NSDictionary *)payload
                            forType:(NSString *)pushType {

  id<SINNotificationResult> notification = [SINManagedPush queryPushNotificationPayload:payload];

  if ([notification isValid]) {
    id<SINCallNotificationResult> callNotification = [notification callResult];
    // Use SINCallNotificationResult to extract remote User ID, call headers, etc.
  }

Also see reference documentation for SINManagedPush and SINCallNotificationResult.

Unregister a Push Device Token

If the user of the application logs out or performs a similar action, the push notification device token can be unregistered using the method -[SINClient unregisterPushNotificationDeviceToken] to prevent further notifications to be sent to the particular device.

Apple Push Service Environments and Provisioning

When an iOS application is code signed, the embedded Provisioning Profile will have to match the Apple Push Notification service Environment (also referred to as APS Environment) specified in the app Entitlements.

This means how the the app is code signed and what Provisioning Profile is used has an effect on what value should be passed to +[Sinch managedPushWithAPSEnvironment:].

For example, if your application is signed with a Development provisioning profile it will be bound to the APS Development environment. If it's code signed with a Distribution provisioning profile it will be bound to the APS Production environment.

Typically a Debug build will be code signed with a Development provisioning profile and thus SINAPSEnvironmentDevelopment should be used. And typically a Release build will be code signed with a Distribution provisioning profile and thus SINAPSEnvironmentProduction should be used. You are responsible for selecting proper entitlements depending on your build type and signing profile.

iOS not Delivering Notifications

Under certain circumstances, iOS won't deliver a notification to your application even if it was received at device/OS level. Note that this also applies to VoIP push notifications. Exact behavior and limits are subject to iOS internal details, but well known scenarios where notifications won't be delivered are:

  • The end user has actively terminated the application. iOS will only start delivering notifications to the application again after the user has actively started the application again.
  • Your app hasn't been reporting VoIP push notifications to CallKit . Please see the separate sections above on how to report VoIP push notifications as CallKit calls.

Relevant Apple Resources

For more details on Apple PushKit and CallKit, see following Apple Developer Documentation:

SINManagedPush and SINClient Interaction

This section covers details on how SINManagedPush and SINClient interacts together (automatically).

SINManagedPush will make use of PKPushRegistry to acquire a push device token. If any SINClient instances exist, it will register the token via -[SINClient registerPushNotificationDeviceToken:type:apsEnvironment:], which will in turn register the token with the Sinch backend platform. If no instance of SINClient exists when SINManagedPush initially acquire the token, it will hold on to the token (in-process memory only) and register it with any SINClient that's created later during the whole application life cycle.

We'd love to hear from you!
Rate this content:
Still have a question?
 
Ask the community.