Push Notification

This page describes the Push Notifications of ChannelIO iOS SDK (hereafter referred to as SDK).

Set up APNs credentials

Step 1. Create Push Notification Key (Key, Key ID)

๐Ÿ“˜

If your service already provides push notifications, skip the steps below.

  1. Issue a key with the Apple Push Notification Service enabled on theย Certificates, Identifiers & Profiles > Keysย page.

  1. Check the Key ID. Note that you can only download the issued credential key once.

  1. Check Team ID in. Account > Membership Page.

  1. Go to Account > Membership tab, and check your Team ID.

Step 2. register APNs Auth Key (.p8) to Channel

  1. Launch ChannelTalk (PC), go to Settings > Security and Development > Mobile SDK Push Section.

  1. Upload the downloaded Key file, and fill the Key ID, Bundle ID, and Team ID.

Step 3. register the device token to Channel

Register the device token via initPushToken to receive remote notifications in Channel.

Referring to the example below, add initPushToken to the application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  ChannelIO.initPushToken(deviceToken)
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [ChannelIO initPushTokenWithDeviceToken:deviceToken];
}

Using ChannelIO Notification

Storing Channel.io notification

When aย Channel.ioย remote notification arrives in the background or terminate state, you can save the notification to show when the app is in the foreground state appropriately.

The following is an example of when the user taps the push notification. If it is a Channel.io notification, it notifies you that you have received a push, and saves the notification.

//iOS 10 and above
func userNotificationCenter(_ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo
    if ChannelIO.isChannelPushNotification(userInfo) {
        ChannelIO.receivePushNotification(userInfo)
        ChannelIO.storePushNotification(userInfo)
    }
    completionHandler()
}
//iOS 10 and above
- (void)userNotificationCenter:(UNUserNotificationCenter *)center 
  didReceiveNotificationResponse:(UNNotificationResponse *)response  
  withCompletionHandler:(void (^)())completionHandler {
  NSDictionary *userInfo = response.notification.request.content.userInfo;
    if ([ChannelIO isChannelPushNotification:userInfo]) {
        [ChannelIO receivePushNotification:userInfo completion: nil];
        [ChannelIO storePushNotification: userInfo];
    }
    completionHandler();
}

Open ChannelIO chat

If you succeed in saving Channel.io remote notifications through storePushNotification, you can open a chat containing saved push messages via openStoredPushNotification method.

Add the following example to the ViewController where you want to open the Channel.io chat.

class ViewController : UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    
    if ChannelIO.hasStoredPushNotification() {
      ChannelIO.openStoredPushNotification()
    }
  }
}
@implementation ViewController

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    if ([ChannelIO hasStoredPushNotification]) {
        [ChannelIO openStoredPushNotification]
    }
}

@end

Combine with follow-up feature

To prevent push notifications and sms from arriving, you need to notify Channel that remote notification arrives.

Go to the Capabilities tab and add Background Mode. Turn on both Background fetch and Remote notification.

Next, add the receivePushNotification method to notify the channel that a push notification has arrived successfully. See the example below.

func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if ChannelIO.isChannelPushNotification(userInfo) {
        // This line
        ChannelIO.receivePushNotification(userInfo)
        ChannelIO.storePushNotification(userInfo)
    }
    completionHandler()
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    if ([ChannelIO isChannelPushNotification:userInfo]) {
        [ChannelIO receivePushNotification:userInfo completion: ^{
            completionHandler(UIBackgroundFetchResultNoData);
        }];
        [ChannelIO storePushNotification: userInfo];
    } else {
        completionHandler(UIBackgroundFetchResultNoData);
    }
}

Notification Extension

This section describes the Notification Extension of SDK. This Extension helps you appropriately respond to theย Channel.io remote push notification, even when the app is background or terminated.

Channel.io recommends adding this Extension. If you do not add this, both follow-up texting and remote notification may arrive.

Extension Setup

  1. From the Xcode Menu, Click File > New > Targetโ€ฆ .
  2. In the select a new target window, click Notification Service Extension.

Xcode creates a new Target, and if Swift, you can see two generated files. Three for Objective-C.

The Description of the newly generated file is:

  • NotificationService.swift : Write the code used inside the extension target.
  • Info.plist : info.plist file of extension Target

Setting up Package

1. CocoaPods

Add the SDK dependency to the newly created Extension Target. Modify the Podfile as follows:

target 'your-notification-extention-target-name' do
  ...
  pod 'ChannelIOSDK', podspec: 'https://mobile-static.channel.io/ios/latest/xcframework.podspec'  ...

end

2. Carthage, Swift Package Manager

  1. Navigate to the project settings for the newly created target.
  2. Go to the General tab.
  3. Add the ChannelIOFront package to the Frameworks and Libraries category.

NotificationService.swift (NotificationService.m)

Add the example below according to your project language settings.

import UserNotifications
import ChannelIOFront

class NotificationService: UNNotificationServiceExtension {
  var contentHandler: ((UNNotificationContent) -> Void)?
  var content: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    self.content        = (request.content.mutableCopy() as? UNMutableNotificationContent)
      
    // this line need to combine sms feature during app terminated
    ChannelIO.receivePushNotification(request.content.userInfo)
      
    if let bca = self.content {
      func save(_ identifier: String, data: Data, options: [AnyHashable: Any]?) -> UNNotificationAttachment? {
        let directory = URL(fileURLWithPath: NSTemporaryDirectory())
          .appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString, isDirectory: true)
        
        do {
          try FileManager.default.createDirectory(
            at: directory, withIntermediateDirectories: true, attributes: nil
          )
          let fileURL = directory.appendingPathComponent(identifier)
          try data.write(to: fileURL, options: [])
          return try UNNotificationAttachment.init(
            identifier: identifier, url: fileURL, options: options
          )
        } catch {
          
        }
        
        return nil
      }
      
      func exitGracefully(_ reason: String = "") {
        let bca = request.content.mutableCopy() as? UNMutableNotificationContent
        contentHandler(bca!)
      }
      
      guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
        return exitGracefully()
      }
      
      let userInfo : [AnyHashable: Any] = request.content.userInfo
      
      guard let attachmentURL = (userInfo["thumbUrl"] ?? userInfo["avatarUrl"]) as? String else {
          return exitGracefully()
        }
      
      guard let imageData = try? Data(contentsOf: URL(string: attachmentURL)!) else {
        return exitGracefully()
      }
      
      guard let attachment = save("\(attachmentURL.hashValue).png", data: imageData, options: nil) else {
        return exitGracefully()
      }
      
      content.attachments = [attachment]
      contentHandler(content.copy() as! UNNotificationContent)
    }
  }
  
  override func serviceExtensionTimeWillExpire() {
    if let contentHandler = contentHandler, let bac = self.content {
        contentHandler(bac)
    }
  }
}
@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    if (self.bestAttemptContent != nil) {
        UNMutableNotificationContent *content = request.content.mutableCopy;
        NSDictionary *userInfo = request.content.userInfo;
      
        NSString *attachmentURL = userInfo[@"avatarUrl"];
        if (attachmentURL == nil) {
            return [self exitGracefully:request withContentHandler:contentHandler];
        }
      
        NSURL *url = [[NSURL alloc] initWithString:attachmentURL];
        NSData *imageData = [[NSData alloc] initWithContentsOfURL:url];
      
        if (imageData == nil) {
            return [self exitGracefully:request withContentHandler:contentHandler];
        }
      
        UNNotificationAttachment *attachment = [self save:[NSString stringWithFormat:@"%ld.png", attachmentURL.hash] data:imageData options:nil];
        if (attachmentURL == nil) {
            return [self exitGracefully:request withContentHandler:contentHandler];
        }
      
        content.attachments = @[attachment];
        self.contentHandler(content.copy);
    }
}

- (void)exitGracefully:(UNNotificationRequest* )request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    UNMutableNotificationContent *bca = request.content.mutableCopy;
    contentHandler(bca);
}

- (UNNotificationAttachment *)save:(NSString *)identifier data:(NSData *)data options:(NSDictionary *)options {
    NSURL *directory = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory() isDirectory:YES]
                        URLByAppendingPathComponent:NSProcessInfo.processInfo.globallyUniqueString];
  
    NSError *error = [[NSError alloc] init];
    [[NSFileManager defaultManager] createDirectoryAtURL:directory withIntermediateDirectories:YES attributes:nil error:&error];
  
    if (error != nil) {
        return nil;
    }
  
    NSURL *fileURL = [directory URLByAppendingPathComponent:identifier];
    [data writeToURL:fileURL atomically:YES];
    UNNotificationAttachment *ret = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:fileURL options:options error:&error];
    if (error != nil) {
        return nil;
    }
    return ret;
}

- (void)serviceExtensionTimeWillExpire {
    self.contentHandler(self.bestAttemptContent);
}

@end