How can we help?

Mobile app device data migration

Overview

When Cordial's mobile SDK is initialized in your app, it automatically captures the device data necessary for device identification and routing of mobile app messages to your contacts. One of the most reliable ways to obtain up-to-date mobile device data is through the SDK while contacts interact with your app. But this method relies on your contacts having a version of the app installed that contains the Cordial SDK.

To ensure that you can send mobile app messages to your existing contactsno matter which version of the app they have installedyou can use Cordial's Data Jobs and an externally hosted data file to migrate device data to Cordial.

In this article, we'll break down the logic of a sample Smarty script that is designed to prepare device data from your import file and import it to your Cordial account.

Example mobile app object

Mobile app channel object

{
  "channels": {
    "push": {
      "address": {
        "5c98d21q-b3o8-42bc-9dac-e9d3c00e34e1": {
          "app": "1.0.0-MobileApp",
          "os": "android",
          "pushEnabled": true,
          "sdk": "1.0",
          "token": "do4-_NCZRbiKTo_l-8jVKy...",
          "v": "10",
"valid": "true" } }, "subscribedAt": "2020-04-16T20:52:21+0000", "unsubscribedAt": "", "subscribeStatus": "subscribed" } } }

Parameter details

The push channel key may differ depending on your account configuration.

Parameter Description
channels.push.address A unique identifier for the registered device (deviceID). Cordial's mobile SDK generates this value when contacts begin using your app for the first time after the SDK is initialized, but this value can also be generated using Smarty. Multiple unique devices can be associated with a single contact record.
channels.push.address.deviceID.app The version of your app installed on the device.
channels.push.address.deviceID.os Device operating system (iOS or Android).
channels.push.address.deviceID.pushEnabled Indicates whether push notifications are enabled (true of false).
channels.push.address.deviceID.sdk The version of Cordial's SDK in your app.
channels.push.address.deviceID.token APNS (Apple) or FCM (Android) issued device token.
channels.push.address.deviceID.v Device operating system version.
channels.push.address.deviceID.valid Shows that as of the last send, this device still had the app installed.
channels.push.unsubscribedAt The most recent date contact unsubscribed from the push channel.
channels.push.subscribedAt The most recent date contact subscribed to the push channel.
channels.push.subscribeStatus Contact's current push channel subscribe status.

In order to receive mobile app messages, contacts must be subscribed at the mobile app channel level, and, optionally, push notifications must be enabled at the device level to receive push notifications.

The deviceID is a unique identifier that distinguishes devices from one another in the Cordial database. The device token is a unique app identifier issued by APNS or FCM and used by Cordial to send notifications to the specific app instance installed on the contact's device (similar to an email address or a phone number).

Import code breakdown

This is our sample Smarty code in its entirety. You can scroll the code box or copy its contents for easier viewing in a different application.

{*
  Sample Row:
   - token -> The Device Token (required)
   - device_type -> The Device Platform (required)
   - alias -> The Email Address (optional)
   - opt_in -> Are Push Notifications Enabled (optional)
*}

{*Set a few variables*}
{$channelKey = 'push'}
{$token = $dataRecord.token}
{$device_type = $dataRecord.device_type}
{$deviceId = null}
{$device = null}
{$waitForResult = true}

{*If the token or device_type is empty, skip this record*}
{if !empty($token) && !empty($device_type)}

  {if $utils->strContains("ios", $device_type, false)}
    {*For iOS tokens, we need to lowercase them before proceeding*}
    {$token = $token|lower}
  {/if}
  
  {*Search by token to see if the device already exists*}
  {$foundContact = $utils->getContactByDeviceToken($channelKey, $token)}
  
  {* If a contact was found, you can skip or perform other data operations on the record *}
  {if $foundContact}
    {*Do nothing as the device already exists, probably came in naturally via an app update with Cordial SDK*}
    
  {else}
    {*Device doesn't exist yet, we need to import it*}
    
    {*Generate a new DeviceID*}
    {$deviceId = $utils->generateUUID4()}
    
    {* Preparing a payload, required fields: 'os', 'pushEnabled', 'token', optional fields: 'sdk', 'app', 'v'  *}
    {*Setting values to 0 for now to signal these were imported*}
    {$device = ['os' => $device_type, 'token' => $token, 'v' => '0', 'sdk' => '0', 'app' => '0']}
    
    {*If we aren't given a value for pushEnabled, we will default to true as we will get feedback on the next send*}
    {if isset($dataRecord.opt_in)}
      {$device['pushEnabled'] = $dataRecord.opt_in}
    {else}
      {$device['pushEnabled'] = true}
    {/if}
    
    {*Add the device to the devices array to upsert*}
    {$payload["channels.$channelKey.address"] = [$deviceId => $device]}
     
    {*Set the secondary key to create a cID + device contact*}
    {$pk = "{$channelKey}:{$token}"}

    {*If an email is provided, we will use that as the secondary key so it updates an existing contact if the email exists*}
    {if !empty($dataRecord.alias)}
      {$payload["channels.email.address"] = $dataRecord.alias}
      {$pk = "email:{$dataRecord.alias}"}
    {/if}
 
    {$c = $utils->upsertContact($payload, $pk, true)}
    
    {if $device['pushEnabled'] === true}
      {*Also call update contact to update the subscribe status if they are opted-in*}
      {$contact = $utils->setcontact("cID:{$c._id}")}
      {$c = $utils->updateContact(["channels.$channelKey.subscribeStatus" => "subscribed"], $waitForResult, true, true)}
    {/if}
  {/if}
{/if}

Skip devices without device token and OS

Depending on how your import file was compiled, it may contain device data without the required device token and device OS values. A common reason these values may be missing is due to them being removed by the previous push provider when your app is uninstalled from the contact's device.

{*If the token or device_type is empty, skip this record*}
{if !empty($token) && !empty($device_type)}

Our script will skip device records without these values and continue uninterrupted.

Check if device token is associated with an existing contact record

Your import file could contain device data that already exists in Cordial if some of your contacts have been using a version of your app with Cordial's SDK installed. We're using the getContactByDeviceToken Smarty utility to check if any of the device tokens in the import file are associated with an existing contact record in your Cordial account.

{*Search by token to see if the device already exists*}
  {$foundContact = $utils->getContactByDeviceToken($channelKey, $token)}
  
  {* If a contact was found, you can skip or perform other data operations on the record *}
  {if $foundContact}
    {*Do nothing as the device already exists, probably came in naturally via an app update with Cordial SDK*}

Device data added via Cordial's SDK while contacts are using your app is usually current and may not need to be changed during migration. Our script does not update this data, but you could perform any number of operations on the incoming data prior to updating existing records.

Insert new device data

This part of the script will insert new device data from the import file.

{else}
    {*Device doesn't exist yet, we need to import it*}
    
    {*Generate a new DeviceID*}
    {$deviceId = $utils->generateUUID4()}
    
    {* Preparing a payload, required fields: 'os', 'pushEnabled', 'token', optional fields: 'sdk', 'app', 'v'  *}
    {*Setting values to 0 for now to signal these were imported*}
    {$device = ['os' => $device_type, 'token' => $token, 'v' => '0', 'sdk' => '0', 'app' => '0']}
    
    {*If we aren't given a value for pushEnabled, we will default to true as we will get feedback on the next send*}
    {if isset($dataRecord.opt_in)}
      {$device['pushEnabled'] = $dataRecord.opt_in}
    {else}
      {$device['pushEnabled'] = true}
    {/if}
    
    {*Add the device to the devices array to upsert*}
    {$payload["channels.$channelKey.address"] = [$deviceId => $device]}

To successfully insert new device data, the script will look for the required data fields within the import file. Some of the required values may be set by the script.

Required data fields

Parmeter Source
channels.push.address Our script will generate unique deviceID values using the generateUUID4 Smarty utility. 
channels.push.address.deviceID.os Import file.
channels.push.address.deviceID.pushEnabled Import file. If not provided, the script will default this value to true.
channels.push.address.deviceID.token Import file.

Setting the pushEnabled value to true does not set the push opt-in status of the actual device. Only contacts can opt-in and out of receiving push notifications by changing push settings on their device. This value is set to true so the next time you send a notification to those contacts, APNS or FCM will respond and verify if the device token is valid.

The optional values for device OS version, SDK version, and app version will default to 0 and can be updated at a later time as your contacts begin using the version of your app with Cordial's SDK initialized.

Use email address as secondary contact identifier

Some of your records may contain an email address. When this is the case, our script will use the email value as the secondary contact key to identify and update contact records:

{*If an email is provided, we will use that as the secondary key so it updates an existing contact if the email exists*}
  {if !empty($dataRecord.alias)}
    {$payload["channels.email.address"] = $dataRecord.alias}
    {$pk = "email:{$dataRecord.alias}"}
  {/if}
 
{$c = $utils->upsertContact($payload, $pk, true)}

Finally, if push notifications are enabled at the device level, our script will set contacts' subscription status at the push channel level to subscribed, ensuring contacts continue receiving push notifications.

{if $device['pushEnabled'] === true}
      {*Also call update contact to update the subscribe status if they are opted-in*}
      {$contact = $utils->setcontact("cID:{$c._id}")}
      {$c = $utils->updateContact(["channels.$channelKey.subscribeStatus" => "subscribed"], $waitForResult, true, true)}
    {/if}
  {/if}
{/if}

Cordial's mobile SDK alongside other providers

Cordial's mobile SDK is capable of running alongside other SDKs on the same app. In this scenario, Cordial's SDK assumes more of a listener role, waiting for your instructions on when and how to handle mobile app messages and other events.

This is a reliable way to test the SDK installation because it lets you selectively allow the new SDK to handle device and event data. After some time of testing and reaching satisfactory integration results, you can transition to letting Cordial's mobile SDK handle mobile app messages and events automatically.

Learn more by accessing Cordial's mobile SDK developer documentation on GitLab:

Initializing multiple SDKs within your app requires careful planning and can be demanding on your development team. Although it has many advantages over outright switching to the new SDK, the feasibility of this approach is dependent on the available resources at your disposal.

Comments

0 comments

Please sign in to leave a comment.