Push Notification for iOS¶
Settings for Push Notification¶
See Settings for APNs Certificate (iOS) to setup.
Removing the push notification badge from your app icon¶
By checking 'Show badge icon' in the 'Create New Push Notification' form, a badge icon is automatically shown above the icon of your app.
In order to remove the badge icon, you will have to set the class attribute applicationIconBadgeNumber of UIApplication
to zero at an arbitrary time that fits your use case.
For example if you want to remove the badge every time the app gets activated, you should set applicationIconBadgeNumber
to zero in the delegate method applicationDidBecomeActive: of UIApplicationDelegate
.
// AppDelegate.m
@implementation AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
...
application.applicationIconBadgeNumber = 0;
...
// AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func applicationDidBecomeActive(_ application: UIApplication) {
...
application.applicationIconBadgeNumber = 0
...
Note
If you enable the 'Show badge icon' option, you should always implement a way to remove the badge icon as well.
Send Device Token to Repro¶
Send Device token and Registration ID to Repro to use push notification.
Turn on Push Notifications from Capabilities in Xcode.
Next, add the following code.
// AppDelegate.m
#import <Repro/Repro.h>
#import <UserNotifications/UserNotifications.h>
...
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
return YES;
}
// AppDelegate.swift
import Repro
import UserNotifications
...
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
if #available (iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge], completionHandler: { (granted, error) in
})
application.registerForRemoteNotifications()
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
return true
}
// AppDelegate.m
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[Repro setPushDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"Remote Notification Error: %@", error);
}
// AppDelegate.swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Repro.setPushDeviceToken(data: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Remote Notification Error: \(error)")
}
application:didRegisterForRemoteNotificationsWithDeviceToken:
method will be called when it succeeds to receive the device token, so please pass the device token to Repro inside there.
When it fails to receive device token, application:didFailToRegisterForRemoteNotificationsWithError:
method will be called, so please handle the error accordingly.
After implementing above code and launch your app, you will see a dialog asking for permission for the push notification.
When you choose OK on this dialog, device token will be set with the application didRegisterForRemoteNotificationsWithDeviceToken:
method, and the device becomes eligible to receive push notifications.
Once you finished all above steps, preparation for push notification is all completed.
Option: setup to receive Rich Notification¶
Rich notification feature has been added to iOS 10 which allows you to use image, video and audio file for the notification. Receiving rich notification requires the steps below.
Create Notification Service Extension¶
Click File -> New -> Target... from your Xcode menu.
Click iOS -> Notification Service Extension -> Next.
Fill in Product Name and click Finish.
Click Activate
if you see a dialog like the below:
Implement Notification Service Extension¶
Write the below in Notification Service Extension
.
#import <UserNotifications/UserNotifications.h>
...
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary *attachment = request.content.userInfo[@"rpr_attachment"];
if (!attachment) {
contentHandler(self.bestAttemptContent);
return;
}
NSString *urlStr = attachment[@"url"];
NSString *type = attachment[@"type"];
NSURL *url = [NSURL URLWithString:urlStr];
[[[NSURLSession sharedSession] downloadTaskWithURL:url
completionHandler:^(NSURL * _Nullable location,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (error) {
contentHandler(self.bestAttemptContent);
return;
}
NSString *fileName = [NSString stringWithFormat:@"%@.%@", [[NSUUID UUID]UUIDString], type];
NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:nil];
NSError *attachError = nil;
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"IDENTIFIER" URL:fileURL options:nil error:&attachError];
if (!attachError) {
self.bestAttemptContent.attachments = @[attachment];
}
contentHandler(self.bestAttemptContent);
}] resume];
}
import UserNotifications
...
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let attachment = request.content.userInfo["rpr_attachment"] as? [String: String] {
if let urlString = attachment["url"], let fileURL = URL(string: urlString), let type = attachment["type"] {
URLSession.shared.downloadTask(with: fileURL) { (location, response, error) in
if let location = location {
let fileName = UUID().uuidString + "." + type
let tmpFile = "file://".appending(NSTemporaryDirectory()).appending(fileName)
let tmpUrl = URL(string: tmpFile)!
try? FileManager.default.moveItem(at: location, to: tmpUrl)
if let attachment = try? UNNotificationAttachment(identifier: "IDENTIFIER", url: tmpUrl, options: nil) {
self.bestAttemptContent?.attachments = [attachment]
}
}
contentHandler(self.bestAttemptContent!)
}.resume()
}
} else {
contentHandler(self.bestAttemptContent!)
}
}
After finishing the above implementation, see Create Push Notification.
Option: Using Universal-Links¶
It is possible to custom handle the opening of Universal-Links (also called App-Links) when Push Notifications or In-App Messages are tabbed. (For simplification from now only called Universal-Links.)
Please follow the examples below in order to add a custom Universal-Link callback handler which is executed every time an Universal-Link would be opened from a Push-Notification or In-App Message.
How to register a regular expression to match Universal-Links.¶
In order to use Universal-Link callbacks, a regular expression based url matcher must be added. For iOS apps, these matcher regular expressions can be added in the AppDelegate
or in the Apps designated .plist
file. For Android apps your class that extents Application
can be used or if that is not possible, the regular expressions can also be added to your AndroidManifest.xml
file.
// AppDelegate.m
#import <Repro/Repro.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Repro addOpenUrlFilterRegEx:@"https://example\\.com/page/"];
[Repro addOpenUrlFilterRegEx:@"your-domain\\.com/.+\?universal=true"];
...
}
// .plist file
/*
<dict>
<key>RPROpenUrlFilterRegExList</key>
<array>
<string>https://example\.com/page/</string>
<string>your-domain\.com/.+\?universal=true</string>
</array>
...
</dict>
*/
// MyApplication.java
import io.repro.android.Repro;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Repro.addOpenUrlFilterRegEx("https://example\\.com/page/");
Repro.addOpenUrlFilterRegEx("your-domain\\.com/.+\\?universal=true");
...
}
...
}
// AndroidManifest.xml
// To specify multiple URL patterns, enter them separated by commas.
<application>
...
<meta-data
android:name="io.repro.android.OpenUrlFilterRegExList"
android:value="https://example\\.com/page/;your-domain\\.com/.+\\?universal=true">
</meta-data>
</application>
// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
Repro.add(openUrlFilterRegEx: "https://example\\.com/page/")
Repro.add(openUrlFilterRegEx: "your-domain\\.com/.+\\?universal=true")
...
}
// .plist file
/*
<dict>
<key>RPROpenUrlFilterRegExList</key>
<array>
<string>https://example\.com/page/</string>
<string>your-domain\.com/.+\?universal=true</string>
</array>
...
</dict>
*/
Please follow native iOS and Android instructions in order to add the filters
to the plist/AndroidManifest files or the main AppDelegate/Application classes.
Please follow native iOS and Android instructions in order to add the filters
to the plist/AndroidManifest files or the main AppDelegate/Application classes.
// YourAppNameScene.cpp
bool YourAppName::init() {
ReproCpp::addOpenUrlFilterRegEx("https://example\\.com/page/");
ReproCpp::addOpenUrlFilterRegEx("your-domain\\.com/.+\\?universal=true");
...
}
Please follow native iOS and Android instructions in order to add the filters
to the plist/AndroidManifest files or the main AppDelegate/Application classes.
Please follow native iOS and Android instructions in order to add the filters
to the plist/AndroidManifest files or the main AppDelegate/Application classes.
Adding a callback handler for opening Universal-Links¶
Next a callback handler for custom processing of Universal-Links is added. The URL that was about to be opened is passed to the callback, where then depending on the URL custom code can be executed.
#import <Repro/Repro.h>
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Repro setOpenUrlCallback:^(NSURL *url) {
if ([url.host isEqualToString:@"example.com"]) {
// In case of your universal link perform navigation, present content, ...
handleUniversalLink(url);
}
}];
...
[Repro setup:@"YOUR_APP_TOKEN"];
...
}
// Set a callback that is executed everytime an URL is about to be opened
Repro.setOpenUrlCallback { url in
if url.host == "example.com" {
// In case of your universal link perform navigation, present content, ...
handleUniversalLink(url)
}
}
...
Repro.setup(token: "YOUR_APP_TOKEN")
...
import io.repro.android.Repro;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// In order to execute a callback when a push notification is tapped,
// the callback must be set in Application, not Activity.
Repro.setOpenUrlCallback(new Repro.OpenUrlCallback() {
// ** This callback method is executed in the main thread **
@Override
public void onOpened(Uri uri) {
if ("example.com".equals(uri.getHost())) {
// In case of your universal link perform navigation, present content, ...
}
}
});
...
Repro.setup(this, YOUR_APP_TOKEN);
...
}
...
}
Repro.setOpenUrlCallback((uri) { // uri is of type Uri
debugPrint("Universal Link Callback Handler received: " + uri.toString());
});
// Somewhere before Repro.Setup(...)
Repro.SetOpenUrlCallback(uri => // uri is of type Uri
{
Debug.Log("Universal Link Callback Handler received: " + uri.ToString());
});
bool YourAppName::init() {
ReproCpp::addOpenUrlFilterRegEx(...);
ReproCpp::setOpenUrlCallback([](const char *url) {
if (strcmp(url, "example.com") == 0) {
// In case of your universal link perform navigation, present content, ...
}
});
ReproCpp::setup("YOUR_APP_TOKEN")
...
}
Repro.setOpenUrlCallback(function(url) { // url is of type String
alert('Universal Link Callback Handler received: ' + url);
if (url.includes("http://example.org")) {
// In case of your universal link perform navigation, present content, ...
}
});
Repro.setOpenUrlCallback( (url) => { // url is of type String
console.log('Universal Link Callback Handler received: ' + url);
if (url.includes("http://example.org")) {
// In case of your universal link perform navigation, present content, ...
}
});