Push Notification

Set up APNs credentials

Step 1. Creating a Push Notification Key (Key, Key ID)

๐Ÿ“˜

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

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

  1. Retrieve your Key ID. Please be aware that you can download the issued credential key only once.

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

  1. Navigate to Account > Membership tab, and verify your Team ID.

Step 2. Registering the APNs Auth Key (.p8) with Channel

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

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

Step 3. Registering the Device Token with Channel

Registering the device token using initPushToken to receive remote notifications in Channel.

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

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

Using Channel Talk Notification

Storing Channel Talk Notifications

When aย Channel Talkย remote notification arrives in the background or terminated state, you can save the notification to display it appropriately when the app is in the foreground state.

Here is an example of what happens when the user taps the push notification. If it is a Channel Talk notification, it notifies Channel Talk that you have received a push notification and saves it.

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();
}

Opening ChannelIO Chat

If you have successfully stored Channel Talk remote notifications using the storePushNotification, you can open a chat containing saved push messages via the openStoredPushNotification method.

Add the following example to the ViewController where you want to open the Channel Talk 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

Integrating with the Follow-up Feature

To prevent push notifications and SMS from being delivered simultaneously, you need to notify Channel when a remote notification arrives.

Navigate to the Capabilities tab and add Background Mode. Enable both Background fetch and Remote notification.

Then, add the receivePushNotification method to notify Channel that a push notification has arrived successfully. Refer to 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 extension enables appropriate handling of Channel Talk remote push notifications, even when the app is running in the background or has been terminated.

Channel Talk recommends adding this Extension. If you do not add this, you may receive both follow-up text messages and remote notifications.

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. For Swift, you can see two generated files, and three for Objective-C.

The description of the newly generated file are as follows:

  • 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 following example based on 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