Teak Features

Login

Teak’s functionality depends on knowing when a user plays and on what device they play.

At every game launch, send the player’s ID to Teak using login:withConfiguration:

Example
// ...
let playerConfiguration = TeakUserConfiguration()
Teak.login(YOUR_PLAYER_ID, with:playerConfiguration)

Make sure that your player ID is a unique ID for the player in your game.

What Player ID should you use?

Your game probably has a user ID to store progress, coin balances, and other useful data. Use that ID with Teak too.

  • It should uniquely identify the current player.

  • Ideally the same ID that is used in your game’s backend.

  • Send it as early as possible in the game’s lifecycle.

Having a consistent ID between your game and Teak makes customer support easier, and makes life easier for your analytics team.

Player Configuration

Additional data, such as a player’s email address and Facebook ID, can be passed to Teak by setting the appropriate fields on the TeakUserConfiguration object passed to login:withConfiguration:

If you need to update any of this additional data during a game session, call Teak.login() with an appropriately configured TeakUserConfiguration object. Teak.login() will never clear or remove information about a player, and fields that are not set on the TeakUserConfiguration object will be ignored.

Providing Additional Data Example
// ....
let playerConfiguration = TeakUserConfiguration()
playerConfiguration.email = "YOUR_PLAYER_EMAIL"
playerConfiguration.facebookId = "YOUR_PLAYER_FACEBOOK_ID"
Teak.login("YOUR_PLAYER_ID", with:playerConfiguration)

User Configuration

In addition to email and Facebook id, TeakUserConfiguration carries per-user data collection opt-outs, set at login time. These are distinct from a player’s channel opt-in/out preferences — see Notification Channels and Opt-Out Preferences.

Property Effect

optOutIdfa

Opts out of IDFA collection for this user. See Disabling Automatic Data Collection for the app-wide equivalent.

optOutPushKey

Opts out of Push Key (push token) collection for this user. See Disabling Automatic Data Collection for the app-wide equivalent.

optOutFacebook

Deprecated — see SDK 5 Behaviors.

Example
let playerConfiguration = TeakUserConfiguration()
playerConfiguration.optOutIdfa = true
Teak.login("YOUR_PLAYER_ID", with:playerConfiguration)

Disabling Automatic Data Collection

In addition to the per-user opt-outs above, you can disable IDFA, Push Key, or Facebook access token collection for your whole app by adding the following keys to your Info.plist:

Info.plist
<key>TeakEnableIDFA</key>
<false/>
<key>TeakEnablePushKey</key>
<false/>
<key>TeakEnableFacebook</key>
<false/>

Each key defaults to true (collection enabled) when absent. IDFA collection is also subject to the OS-level advertising tracking authorization, regardless of this setting. TeakEnableFacebook only has an effect when TeakSDK5Behaviors has been set to false — see SDK 5 Behaviors.

Push Token Refresh

By default, Teak automatically re-registers for remote notifications at every app launch (silently, with no permission prompt) so its push token stays current. To take over this responsibility yourself, add the TeakDoNotRefreshPushToken boolean key to your Info.plist and set it to true, then call refreshPushTokenIfAuthorized whenever you want Teak to refresh its push token.

Info.plist
<key>TeakDoNotRefreshPushToken</key>
<true/>

Logging Out

When a player logs out of your game, tell Teak with logout. This is a client-side call: it doesn’t clear the device’s association with the player, but it does stop Teak from associating any further events — like a notification click — with that player until you call Teak.login() again.

Example
Teak.sharedInstance()?.logout()

Rewards

Whenever your game should grant a reward to a player Teak will let you know by posting a notification to the default notification center with the name Notification.Name(TeakOnReward)

Teak does not provide any in-game UI to inform a player if they received a reward or not. You should add an observer for the Notification.Name(TeakOnReward) notification which detects if the reward was granted or denied, and informs the player what happened.

This callback will be concurrent with the Teak Reward Endpoint server to server call.

Example Reward Listener
NotificationCenter.default.addObserver(forName: Notification.Name(TeakOnReward), object: nil, queue: nil, using:{notification in
    let rewardStatus = notification.userInfo!["status"] as! String
    let rewardId = notification.userInfo!["teakRewardId"] as! String
    switch rewardStatus {
    case "grant_reward":
        let reward = notification.userInfo!["reward"]! as! Dictionary<String, NSNumber>
        print("Reward with id \(rewardId) Granted! \(reward)")
    case "already_clicked":
        print("You already claimed this reward!")
    case "expired":
        print("The reward has expired")
    case "too_many_clicks":
        print("Too many other players already claimed this reward")
    case "exceed_max_clicks_for_day":
        print("You've already claimed too many rewards today")
    case "not_for_you":
        print("This reward is restricted to a different player")
    case "invalid_post":
        print("The reward id was not recognized")
    default:
        print("Unknown status")
    }
})

Request Notification Permissions

To use push notifications you are required to ask the player if you can send them notifications. Do that with the requestNotificationPermissions: call.

Permissions Example
// ...
Teak.requestNotificationPermissions({accepted, error in
    print("User accepted notifications: \(accepted)")
})

You can still make this call even if the player cannot be prompted for permissions again. In that case the callback will be called with the current notification state.

Notifications

Scheduling a Notification

You can schedule a notification to be delivered for the current player by using scheduleNotificationForCreative:secondsFromNow:personalizationData:. The optional personalizationData dictionary is sent along for templating the notification’s content on the dashboard.

This corresponds to a Local Schedule on the dashboard — see Local Notification Tags for how the personalizationData keys you send become template tags in the Message.

Don’t use client-scheduled notifications for new-player or lapsing-player flows — have your marketing team set up a Triggered Schedule for those instead.
Example (Objective-C)
NSDictionary* personalizationData = @{
    @"test_data": @"hello there",
    @"other_data": @"straight from the app"
};
TeakOperation* op = [TeakNotification scheduleNotificationForCreative:@"my_creative_id"
                                                        secondsFromNow:5
                                                  personalizationData:personalizationData];
Example (Swift)
let personalizationData: [String: Any] = [
  "test_data": "hello there",
  "other_data": "straight from the app"
]
let op = TeakNotification.scheduleNotification(forCreative: "my_creative_id",
                                                secondsFromNow: 5,
                                                personalizationData: personalizationData)

This returns a TeakOperation, a plain NSOperation. Read the result by attaching a dependent operation — Teak enqueues op internally before returning it, so a completion block set directly on op would race.

Reading the result (Objective-C)
NSBlockOperation* whenDone = [NSBlockOperation blockOperationWithBlock:^{
  TeakOperationNotificationResult* result = (TeakOperationNotificationResult*)op.result;
  if (result.error) {
    NSLog(@"Notification errors: %@", [result toDictionary]);
  } else {
    NSLog(@"Notification scheduled: %@", [result toDictionary]);
  }
}];
[whenDone addDependency:op];
[[[NSOperationQueue alloc] init] addOperation:whenDone];
Reading the result (Swift)
let whenDone = BlockOperation {
  if let result = op?.result as? TeakOperationNotificationResult {
    if result.error {
      print("Notification errors: \(result.toDictionary())")
    } else {
      print("Notification scheduled: \(result.toDictionary())")
    }
  }
}
if let op = op {
  whenDone.addDependency(op)
}
OperationQueue().addOperation(whenDone)

Canceling a Scheduled Notification

To cancel a notification that you’ve scheduled from the client use cancelScheduledNotification:.

Example (Objective-C)
[TeakNotification cancelScheduledNotification:@"scheduleId"];
Example (Swift)
TeakNotification.cancelScheduledNotification("scheduleId")

Cancel All Client-Scheduled Notifications

Using cancelAll will cancel all of the notifications which were scheduled from client-side code.

Example (Objective-C)
[TeakNotification cancelAll];
Example (Swift)
TeakNotification.cancelAll()

Application Badge Count

A dashboard notification can only set your app icon’s badge to 0 or 1. To show an arbitrary count — for example, an in-game unread count — set it yourself with setApplicationBadgeNumber:.

Example
Teak.sharedInstance()?.setApplicationBadgeNumber(5)

Notification Channels and Opt-Out Preferences

Teak can reach players over several channels: mobile push and email. Each channel, and each category within a channel, has an opt-in state. You can read a player’s current preferences and update them from your game, for example to build an in-game notification-settings screen. These are the player’s per-channel delivery preferences — distinct from the data-collection opt-outs on TeakUserConfiguration (see Disabling Automatic Data Collection).

Reading Opt-Out Preferences

A player’s channel preferences arrive in the TeakUserData notification, which Teak posts after your game calls login:withConfiguration:. Observe it and read the per-channel status from the notification’s userInfo. Each of pushStatus and emailStatus is a dictionary whose state is one of opt_out, available, opt_in, absent, or unknown.

Example
NotificationCenter.default.addObserver(forName: Notification.Name(TeakUserData), object: nil, queue: nil, using:{notification in
    if let pushStatus = notification.userInfo?["pushStatus"] as? [String: Any] {
        print("Push opt-in state: \(pushStatus["state"] ?? "unknown")")
    }
})

Updating Opt-Out Preferences

Update an entire channel with setState:forChannel:, or a single category within a channel with setState:forChannel:andCategory:. The channel is one of TeakChannelTypeMobilePush or TeakChannelTypeEmail.

When setting state from the SDK, you may only assign TeakChannelStateOptOut or TeakChannelStateAvailable.
Example
// Opt the player out of marketing emails.
Teak.sharedInstance()?.setState(TeakChannelStateOptOut, forChannel: TeakChannelTypeEmail)

// Make a specific push category available to the player again.
Teak.sharedInstance()?.setState(TeakChannelStateAvailable, forChannel: TeakChannelTypeMobilePush, andCategory: "daily_reminders")

Player Properties

Player Properties are strings or numbers associated with each player of your game in Teak. Player Properties have several uses, including:

  • Targeting players for a send by using the player properties Audience rule

  • Personalizing push notification content using custom tags

  • Personalizing email content using custom tags

  • Personalizing deep links from Links, push notifications, and emails to take players to a meaningful location in your game

You do not need to register the property in the Teak Dashboard prior to sending them from your game, however you will need to register them in the Teak Dashboard before using them in targeting. Only Player Properties set to permit client updates can be updated through the SDK. Player Properties set to permit server updates may only be updated through the Server API.

Number Property

To set a number property, use

setNumberProperty:value:

Example
Teak.setNumberProperty("bankroll", value: newCoinBalance)

Number Player Properties can store values between -999999999999999999999999999.999999999 and 999999999999999999999999999.999999999.

String Property

To set a string property, use

setStringProperty:value:

Example
Teak.setStringProperty("last_slot", value: "amazing_slot_name")

String Player Properties can store up to 16,384 unicode characters (including emoji).

Custom Analytics Events

You can send custom analytics events to Teak to track player behavior. Teak models an event as an action taken on a type of object, optionally with a specific instance of that object.

Example
Teak.sharedInstance()?.trackEvent(withActionId: "level_complete", forObjectTypeId: "level", andObjectInstanceId: "10")
Example
Teak.sharedInstance()?.incrementEvent(withActionId: "coins_spent", forObjectTypeId: "store", andObjectInstanceId: "gems", count: 50)

Deep Links are a way to link to specific screens in your game that will open when the game is launched from a notification or Universal Link.

These are useful for promoting new content or linking directly to sale content in the game.

For the marketing team to use Deep Links, they will have to add the URL to their notifications in the dashboard. So, keep a master list of active deep links that can be shared with your team, so everyone knows what is available for use.

Deep Linking with Teak is based on routes, which act like URLs. Route patterns may include named parameters, allowing you to pass in additional data.

You need to register your deep link routes before you call Teak.login().
Example
Teak.registerDeepLinkRoute("/store/:sku", name: "Store", description: "Open the store to the given SKU", block: {params in
    print("Taking the player to \(params["sku"]!)")
})

How Routes Work

Routes work like URLs where parts of the path can be a variable. In the example above, the route is /store/:sku. Variables in the path are designated with :. So, in the route /store/:sku there is a variable named sku.

This means that if the deep link used to launch the app was /store/io.teak.test.dollar was used to open the app, it would call the function and assign the value io.teak.test.dollar to the key sku in the dictionary that is passed in.

This dictionary will also contain any URL query parameters. For example:

/store/io.teak.test.dollar?campaign=email

In this link, the value io.teak.test.dollar would be assigned to the key sku, and the value email would be assigned to the key campaign.

Deep links are passed to an application as part of the launch. The Teak SDK holds onto the deep link information and waits until your app has finished launching, and initializing. Deep links will be processed when your game calls Teak.login().

A Deep Link route may be added to any notification or email in the Advanced section when setting up a Message or Link. We recommend documenting what routes are implemented and how to use them, with examples, for your marketing team to add to notifications, emails, and links.

Session Attribution

Each time your game launches Teak will post a notification with all of the attribution data it has for the launch, if available, to the default notification center with the name Notification.Name(TeakPostLaunchSummary)

This callback will be called after your game calls login:withConfiguration:, and is primarily intended to assist in reporting session attribution to other analytics systems.

Example PostLaunchSummary Listener
NotificationCenter.default.addObserver(forName: Notification.Name(TeakPostLaunchSummary), object: nil, queue: nil, using:{notification in
    let channelName = notification.userInfo!["teakChannelName"] as! Optional<String>
    if(channelName == nil) {
        print("Launch not attributed to a Teak source")
        return
    }
    print("Launch attributed to \(channelName!)")
    print("Launch came from a click on \(notification.userInfo!["teakCreativeName"] as! String)")
    print("Launch was \(notification.userInfo!["teakRewardId"] as! NSObject == NSNull() ? "not" : "") rewarded")
})

Debugging with a Log Listener

Teak emits internal log events as it runs. You can observe them by assigning a logListener block, which is useful for debugging your integration or forwarding Teak’s diagnostics into your own logging system.

Example
Teak.sharedInstance()?.logListener = { event, level, eventData in
    print("Teak \(level): \(event) \(eventData ?? [:])")
}