SDK

Device Registration

In this page you'll learn how device registration should be handled using Notificare's iOS library. We'll dive deeper into all the functionality available for you in order to correctly manage devices in your app.

Please note that APNS will only work on a real device. There is also has a clear distinction between development and production servers. Whenever you debug your app from Xcode in a real device, your app will be using the development environment and whenever you archive your app for Adhoc, AppStore or Enterprise you will be using Production. This makes a difference when it comes to APNS since you will be getting two different identifiers for the same device.

Device registration will happen every time your app is launched. You also should never assume that the device token will never change, it does, and Notificare's iOS library will handle this automatically for you. By default our iOS library will automatically assign a device identifier for you. Users will not be prompted to accept alerts, sounds or badges and therefore remote notifications will not be available until requested. This will at least allow you to use all the other features of Notificare without requesting permissions for push notifications.

If you do want to request for remote notifications permission the simplest way to make sure this is done correctly is to request this registration every time after the onReady method has been triggered. In your AppDelegate.m or AppDelegate.swift, add the following to the onReady delegate method:

- (void)notificarePushLib:(NotificarePushLib *)library onReady:(nonnull NotificareApplication *)application {
    [[NotificarePushLib shared] registerForNotifications];
}
func notificarePushLib(_ library: NotificarePushLib, onReady application: NotificareApplication) {
    NotificarePushLib.shared().registerForNotifications()
}

But there might be cases, where you want to postpone calling registerForNotifications for the first time in your app (e.g.: after you have on-board a user, after login, etc). When that is the case you should still make sure you invoke this method in the onReady delegate. To avoid invoking it for first time users you can use the following:

- (void)notificarePushLib:(NotificarePushLib *)library onReady:(nonnull NotificareApplication *)application {
    if ([[NotificarePushLib shared] remoteNotificationsEnabled]) {
        [[NotificarePushLib shared] registerForNotifications];
    }
}
func notificarePushLib(_ library: NotificarePushLib, onReady application: NotificareApplication) {
    if (NotificarePushLib.shared().remoteNotificationsEnabled()) {
        NotificarePushLib.shared().registerForNotifications()
    }
}

If everything is OK, invoking this method will request a device token from APNS and register it in Notificare. This will also be the case if you do not request registerForNotifications, just instead we will only register with Notificare. In response, the following delegate will be triggered:

-(void)notificarePushLib:(NotificarePushLib *)library didRegisterDevice:(NotificareDevice *)device {

}
func notificarePushLib(_ library: NotificarePushLib, didRegister device: NotificareDevice) {

}

In case you request a register for notifications, if your app does does not declare the necessary entitlements, it's not correctly provisioned or simply there was some other error while registering for notifications, the following delegate will be triggered instead:

- (void)notificarePushLib:(NotificarePushLib *)library didFailToRegisterForNotificationsWithError:(NSError *)error{
    //Handle error
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    //Handle Error
}

The very first time you request for push notifications and you are not using iOS 12's Provisional Authorization mode, the user will also be prompted with a permission dialog requesting to show alerts, sounds or a badge. Even if the user denies it, you will still receive a device token which you will be registered in Notificare. This will still enable you to enjoy all the functionality available in Notificare, except we will not be able show alerts in lock screen or notification center, play sounds or show a badge when you send a notification. If you use the inbox functionality messages will still be shown in the messages list.

In iOS 10 and higher, your app can determine which kind of notification settings the user has authorized via the Notification Center object, as shown below:

[[[NotificarePushLib shared] userNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

}];
NotificarePushLib.shared().userNotificationCenter.getNotificationSettings(completionHandler: { (UINotificationSettings) -> Void in

})

Additionally you can determine if device has a APNS token:

if ([[NotificarePushLib shared] remoteNotificationsEnabled]) {
    //User has a APNS token
}
if NotificarePushLib.shared().remoteNotificationsEnabled() {
    //User has a APNS token
}

And if he/she has allowed your app to present alerts, sounds or badges, by using the property below:

if ([[NotificarePushLib shared] allowedUIEnabled]) {
    //User has allowed alerts, sounds and badges
}
if NotificarePushLib.shared().allowedUIEnabled() {
    //User has allowed alerts, sounds and badges
}

Additionally the following delegate will be triggered (when a user first chooses, on launch or when app becomes active) letting you know if you can post user notifications:

- (void)notificarePushLib:(NotificarePushLib *)library didChangeNotificationSettings:(BOOL)granted{
    //Check if user allowed user notifications
}
func notificarePushLib(_ library: NotificarePushLib?, didChangeNotificationSettings granted: Bool) {
    //Check if user allowed user notifications
}

If your app has a settings view where you allow them to toggle notifications ON and OFF, the best approach is to let the user manage its notifications' settings in the device's settings. Instead of unregistering the device, you could choose to open the device's settings view using something like this:

NSURL *appSettings = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:appSettings]) {
    [[UIApplication sharedApplication] openURL:appSettings];
}
let url = URL.init(string: UIApplicationOpenSettingsURLString)
if UIApplication.shared.canOpenURL(url!) {
    UIApplication.shared.openURL(url!)
}

Register as a user

At this point your application should be able to handle anonymous device registrations but there is situations where you will want to register a device with a user profile. When you authenticate your users and want to assign the device to a user profile you need to make sure that when a user successfully logs in your app you invoke the following:

[[NotificarePushLib shared] registerDevice:@"USER_ID" withUsername:@"USERNAME" completionHandler:^(id  _Nullable response, NSError * _Nullable error) {
    //Device registered successfully
}];
NotificarePushLib.shared().registerDevice("USER_ID", withUsername: "USERNAME", completionHandler: {(_ response: Any?, _ error: Error?) -> Void in
    //Device registered successfully
})

You will also want to register users as anonymous once they sign out from your app. You will want to invoke the following whenever your users successfully terminates a session in your app:

[[NotificarePushLib shared] registerDevice:nil withUsername:nil completionHandler:^(id  _Nullable response, NSError * _Nullable error) {
    //Device registered successfully
}];
NotificarePushLib.shared().registerDevice(nil, withUsername: nil, completionHandler: {(_ response: Any?, _ error: Error?) -> Void in
    //Device registered successfully
})

Override Device Language

By default we will automatically collect the language and region of a device based on the NSLocale of the device. For most cases this will be enough but for those cases where you would like to override the device language and region combination to a strict selection of languages, you can do so by invoking the following method:

[[NotificarePushLib shared] updatePreferredLanguage:@"en-US" completionHandler:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
        //Handle success
    }
}];
NotificarePushLib.shared().updatePreferredLanguage("en-US", completionHandler: { response, error in
    if error == nil {
        //Handle success
    }
})

Eventually you can always retrieve the preferred language by invoking the following method:

[[NotificarePushLib shared] preferredLanguage];
NotificarePushLib.shared().preferredLanguage()

User Data Fields

There are use cases where simply associating an userID and userName will not suffice. For those cases you can make use of User Data Fields where you can create key-value pairs for your application and then use them to extend your device registration. Before you can implement them you will need to first create them in your application via our dashboard. This is described here.

Once you have created one or more fields, you can implement this functionality in your app. To retrieve the list of fields allowed in your app you should use the following method (make sure you call it after the onReady delegate):

[[NotificarePushLib shared] fetchUserData:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
        for (NotificareUserData * field in response) {
            NSLog(@"Key: %@", [field key]);
            NSLog(@"Label: %@", [field label]);
            NSLog(@"Value: %@", [field value]);
        }
    }
}];
NotificarePushLib.shared().fetchUserData({(_ response: Any?, _ error: Error?) -> Void in
    if error == nil {
        for field: NotificareUserData in response {
            print("Key: \(field.key())")
            print("Label: \(field.label())")
            print("Value: \(field.value())")
        }
    }
})

Whenever you want to update those fields with new values you should use the following method:

for (NotificareUserData * field in userDataFields) {
    [field setValue:@"SOME_VALUE"];
}

[[NotificarePushLib shared] updateUserData:userDataFields completionHandler:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
    //user Data Fields updated
    }
}];
for field: NotificareUserData in userDataFields {
    field.value = "SOME_VALUE"
}
NotificarePushLib.shared().updateUserData(userDataFields, completionHandler: {(_ response: Any?, _ error: Error?) -> Void in
    if error == nil {
        //user Data Fields updated
    }
})

Do Not Disturb

Each device registered with Notificare can be configured to specify a period of time during the day where it should not receive notifications. You can use this functionality in your app settings view to allow the user to provide a time range where messages will not be displayed in the lock screen or notification center. Please note that if you are using our inbox functionality these message will still be there.

To retrieve a device's DnD preferences use the following method:

[[NotificarePushLib shared] fetchDoNotDisturb:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
        NSLog(@"%@", [response start]);
        NSLog(@"%@", [response end]);
    }
}];
NotificarePushLib.shared().fetchDoNotDisturb({(_ response: Any?, _ error: Error?) -> Void in
    if error == nil {
        print("\(response?.start())")
        print("\(response?.end())")
    }
})

You can update the DnD values for a device, using the following method:

NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *sComponents = [gregorian components: NSUIntegerMax fromDate: date];
[sComponents setHour:0];
[sComponents setMinute:0];
NSDate *startTime = [gregorian dateFromComponents:sComponents];

NSDateComponents *eComponents = [gregorian components: NSUIntegerMax fromDate: date];
[eComponents setHour:8];
[eComponents setMinute:0];
NSDate *endTime = [gregorian dateFromComponents:eComponents];

NotificareDeviceDnD * dnd = [NotificareDeviceDnD new];
[dnd setStart:startTime];
[dnd setEnd:endTime];

[[NotificarePushLib shared] updateDoNotDisturb:dnd completionHandler:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
    //DnD updated
    }
}];
var date = Date()
var gregorian = Calendar(calendarIdentifier: .gregorian)
var sComponents: DateComponents? = gregorian?.components(NSCalendar.Unit(rawValue: NSIntegerMax)!, from: date)
sComponents?.hour = 0
sComponents?.minute = 0
var startTime: Date? = gregorian?.date(from: sComponents!)

var eComponents: DateComponents? = gregorian?.components(NSCalendar.Unit(rawValue: NSIntegerMax)!, from: date)
eComponents?.hour = 8
eComponents?.minute = 0
var endTime: Date? = gregorian?.date(from: eComponents!)

var dnd = NotificareDeviceDnD()
dnd.start = startTime
dnd.end = endTime

NotificarePushLib.shared().updateDoNotDisturb(dnd, completionHandler: {(_ response: Any?, _ error: Error?) -> Void in
    if error == nil {
        //DnD updated
    }
})

And eventually if you want to clear any Do Not Disturb values from this device, you can use the method below:

[[NotificarePushLib shared] clearDoNotDisturb:^(id  _Nullable response, NSError * _Nullable error) {
    if (!error) {
        //DnD cleared
    }
}];
NotificarePushLib.shared().clearDoNotDisturb({(_ response: Any?, _ error: Error?) -> Void in
    if error == nil {
        //DnD cleared
    }
})

Test Devices

In SDK 2.6 and higher, it is possible to enable devices to be registered as test devices. This allows you to perform a wide range of tests in our dashboard by simply scanning a QR Code. In order to enable this functionality, the following steps are mandatory.

First you need to add the following URL Scheme in your app's Info.plist:

test url scheme

Where {APP_ID} must be replaced by your own app identifier (you can find that in Settings > Configure App).

Then you have to make sure that you've implemented the following method in your App Delegate:

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    [[NotificarePushLib shared]  handleOpenURL:url withOptions:options];
    return YES;
}
//For iOS 9 and lower when app is in background or foreground
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    NotificarePushLib.shared().handleOpen(url, withOptions: options)
    return true
}

For most apps, this method is already implemented in order to handle Deep Links but if it's not, it is crucial that you implement it. Without these steps, you can still perform test reports but you will need to find out the device manually.

Disable Notifications

It is also possible to disabled remote notifications and unregister from APNS. To do that use the following method:

[[NotificarePushLib shared] unregisterForNotifications];
NotificarePushLib.shared().unregisterForNotifications()

Doing this will remove the device from APNS and remote notifications will no longer be sent or received. When this method is invoked, we will automatically register a device identifier that although will never receive remote notifications, you will still maintain the same user profile, inbox messages and enjoy all the other services your plan supports. You can at anytime request a re-register with APNS if you wish to.

Un-launch Notificare

Finally, it is also possible to completely remove all data for a device, both locally in your app and remotely in our servers. To do that, use the following method:

[[NotificarePushLib shared] unlaunch];
NotificarePushLib.shared().unlaunch()

After invoking this, all the device's data will be destroyed and cannot be undone. Also, invoking any other method in Notificare will fail and the only way to start using the SDK again, is by invoking its counterpart, the launch method.