Skip to main content
Version: 3.x

iOS

Notes on apps in test mode!

If your app is in test mode, you MUST use a test user. These are defined in "Test Devices" on the dashboard.

Support

If you are experiencing issues or need assistance, please reach out to developers@tapresearch.com

Installation

Example apps are available here

CocoaPods

To keep up to date with the latest version of the TapResearch iOS SDK, you can use CocoaPods to integrate the SDK into your project.

platform :ios, '12.0'
source 'https://github.com/CocoaPods/Specs.git'

use_frameworks!

target 'MyApp' do
pod 'TapResearch', '~> 3.2.5'
end

Then run in the Terminal: pod install

Swift Package Manager

Add the following URL in the Xcode Swift Package Manager:

https://github.com/TapResearch/TapResearch-iOS-SDK

  • Set Dependency Rule to "Exact Version" or "Up to Next Major Version"
  • Set the version value to 3.2.5
  • Set "Add to Project" to your project
  • Click "Add Package"

In the resulting "Choose Package Product for TapResearch-iOS-SDK" select the TapResearchSDK row and click "Add Package".

Manual Installation

You can download the latest version of the SDK from our GitHub page.

Minimum SDK version: 12.0

Integration

Callbacks

The TapResearch SDK has two delegates you can implement: TapResearchSDKDelegate and TapResearchContentDelegate. If you are implementing the delegates as their own objects make sure that the delegate objects derive from NSObject, for example in Swift: class MyDelegates: NSObject, TapResearchSDKDelegate {.

TapResearchSDKDelegate

  • onTapResearchDidReceiveRewards: Called when a user has earned a reward(s).
  • onTapResearchDidError: Called when an error has occurred in the TapResearch SDK. For the full list, see the error codes page
  • onTapResearchSdkReady: Called when the SDK is ready for use. We recommend showing content only after this callback is called.

The class, ideally SceneDelegate, that conforms to the TapResearchSDKDelegate protocol must define both the onTapResearchDidReceiveRewards and onTapResearchDidError methods. Defining the onTapResearchSdkReady is highly recommended.

Reward object

The reward object contains the following properties:

  • transactionIdentifier will be the unique identifier for the reward - this is used to prevent duplicate rewards
  • placementIdentifier will be the unique identifier for the placement - this can be used by your analytics to show which placement rewarded the user
  • currencyName will be the name of the currency name that was rewarded. This is the Currency Name that you set up in the dashboard. This is different from the Display Name
  • rewardAmount will be the amount of the currency that was rewarded, in your currency
  • placementTag will be the tag of the placement that rewarded the user - its human readable and unique
Quick Question Response
danger

Quick Question Response is a beta-feature!

A new delegate is available in TapResearchSDKDelegate:

  • onTapResearchDidReceiveRewards: Called when a Quick Question response is being returned.

This callback will return a TRQQDataPayload object.

Swift in SwiftUI

View in context

You can use a class to implement the SDK's delegates and pass that to TapResearch at initialization.

class TapResearchSDKDelegates : TapResearchSDKDelegate {

func onTapResearchDidError(_ error: NSError) {
print("\(#function): \(error.code), \(error.localizedDescription)")
}

func onTapResearchDidReceiveRewards(_ rewards: [TRReward]) {
print("\(#function): \(rewards.count) rewards awarded")

var i: Int = 0
for reward in rewards {
print("reward \(i): currency = \(reward.currencyName ?? "unknown"), amount = \(reward.rewardAmount)")
i += 1
}
}

func onTapResearchQuickQuestionResponse(_ qqPayload: TRQQDataPayload) {

for question: TRQQDataPayloadQuestion in qqPayload.questions {
//...
}
}

func onTapResearchSdkReady() {

if let error: NSError = TapResearch.sendUserAttributes(attributes: ["attribute1" : "some player attribute", "a_number" : 12]) {
print("sendUserAttributes: \(error.code) \(error.localizedDescription)")
}
print("\(#function)")
}
}

Swift in UIKit

View in context

class SomeClass: TapResearchSDKDelegate {

func onTapResearchDidReceiveRewards(_ rewards: [TRReward]) {
print("onTapResearchDidReceiveRewards(rewards...)")
}

func onTapResearchDidError(_ error: NSError) {
print("onTapResearchDidError: \(error.userInfo[NSError.TapResearchErrorCodeString] ?? "(No code)") \(error.localizedDescription)")
}

func onTapResearchQuickQuestionResponse(_ qqPayload: TRQQDataPayload) {
for question: TRQQDataPayloadQuestion in qqPayload.questions {
//...
}
}

func onTapResearchSdkReady() {
if let error: NSError = TapResearch.sendUserAttributes(attributes: ["attribute1" : "some player attribute", "a_number" : 12]) {
print("sendUserAttributes: \(error.code) \(error.localizedDescription)")
}
print("onTapResearchSdkReady()")
}

}

TapResearchContentDelegate

  • onTapResearchContentShown: Called when content is successfully presented to the user.
  • onTapResearchContentDismissed: Called when content is dismissed.

You can use these callbacks to pause/resume audio, animations, etc. when content is shown or dismissed.

Swift in SwiftUI

View in context

ViewController.swift
class SomeClass: TapResearchContentDelegate {
//...
func onTapResearchContentShown(forPlacement placement: String) {
print("\(#function): \(placement) was shown")
}

func onTapResearchContentDismissed(forPlacement placement: String) {
print("\(#function): \(placement) was dismissed")
}
//...
}

Swift in UIKit

View in context

ViewController.swift
class TapResearchDelegates: TapResearchContentDelegate {
//...
func onTapResearchContentShown(forPlacement placement: String) {
print("\(#function): \(placement) was shown")
}

func onTapResearchContentDismissed(forPlacement placement: String) {
print("\(#function): \(placement) was dismissed")
}
//...
}

Initialization

We recommend initializing TapResearch as early as possible in your application's lifecycle. This will ensure that your placements are refreshed by the time you are ready to show content.

To initialize the SDK, you'll need the API token attached to the app you set up in your TapResearch dashboard. Refer to the app setup guide for more information.

After obtaining your API Token, add the following snippet to the scene:willConnectToSession:options: method in your SceneDelegate class. Replace API_TOKEN with your API token and UNIQUE_USER_ID with a unique identifier within your application.

Note, UNIQUE_USER_ID must be set and cannot be left blank. Otherwise, initialization will fail. Do NOT use an anonymized user identifier.

If initializing the TapResearch SDK isn't possible inside the scene:willConnectToSession:options: method, try and do it as early as possible to ensure that your placements are refreshed by the time you are ready to show content.

View in context

Swift in UIKit

SceneDelegate.swift
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }

TapResearch.initialize(withAPIToken: apiToken, userIdentifier: userIdentifier, sdkDelegate: self) { (error: NSError?) in
if let e = error {
print(e.localizedDescription as Any)
}
}
}

View in context

Swift in SwiftUI

info

Note that you must initialize the SDK in the onAppear method of your root view. This is because the onAppear method is called after the app has finished launching.

IntegrationDemo_SwiftUIApp.swift
let tapSDKDelegates: TapResearchSDKDelegates

var body: some Scene {
WindowGroup {
ContentView().onAppear(perform: { [self] in
TapResearch.initialize(withAPIToken: self.sdkToken, userIdentifier: self.userId, sdkDelegate: self.tapSDKDelegates) { (error: NSError?) in
if let e = error {
print("\(#function): \(e.code), \(e.localizedDescription)")
}
}
})
}
}

required init() {
tapSDKDelegates = TapResearchSDKDelegates()
}

You can also pass user attributes when you first initialize the SDK. This is done by changing the TpResearch.initialize(...) to additionally pass a userAttributes dictionary and clearPreviousAttributes flag:

info

If user attributes are known at SDK initialization, it is preferable to pass them through initOptions compared to using sendUserAttributes. This will result in quicker load times for targeted content.

let dict: [AnyHashable:Any] = ["string" : "a string", "number" : 1]
TapResearch.initialize(withAPIToken: apiToken, userIdentifier: userIdentifier, userAttributes: dict, clearPreviousAttributes: true, sdkDelegate: self) { (error: NSError?) in
if let e = error {
print(e.localizedDescription as Any)
}
}

User attributes

See more on user attributes here.

User attributes are used to target specific users with content (ex: an offer). They can be set at any time and the most recent values received will be used when determining which content to show.

User attributes are passed as a dictionary of key-value pairs.

note

User attributes prefixed with tapresearch_ are reserved for internal use. Please do not use this prefix for user attributes as doing so will result in an error

The keys must be strings and the values must be one of:

  • String
  • Float
  • Integer

If you want to use a date, please stringify an ISO8601 date or use a timestamp.

You can send user attributes as soon as the SDK is ready. This is done by calling sendUserAttributes with a Dictionary of attributes.

We suggest sending as many attributes as you think that you'll want to target on for surveys, special awards, etc.

An optional flag clearPreviousAttributes can be passed, set to true to clear previously-set attributes, the default is false.

ViewController.swift
func sendUserAttributes() {
let date = Date() // Current date and time
let formatter = ISO8601DateFormatter()

let iso8601String = formatter.string(from: date)
let dict: [AnyHashable:Any] = ["app-user": userIdentifier, "roller-type": "high", "roller-type-valid-until": Date().timeIntervalSince1970 + ViewController.threeHours, "some-date": iso8601String, "some-double": 1.11]
if let error = TapResearch.sendUserAttributes(attributes: dict, clearPreviousAttributes: true) {
print(error.localizedDescription as Any)
}
}
// ...

Setting the user identifier

You can set the user identifier at any time. This is done by calling setUniqueUserIdentifier with a string.

TapResearch.setUserIdentifier("some-user-id")

Checking if the SDK is ready

The SDK lets the app know it is ready using a callback:


func onTapResearchSdkReady() {
}

You can also check if the SDK is ready using the isReady call:


let ready: Bool = TapResearchSDK.isReady()

Displaying a placement

Placements are locations and moments inside the game when content can be shown. To show content for a placement, you'll need to specify a placement tag.

Note that the show content calls are wrapped in a canShowContent check. This is to ensure that the placement is ready to be shown. If the placement is not ready, the SDK will return false. This helps guard against showing the user something that is not ready to be shown yet.

If there is an error while showing a placement, it will be returned in the completion handler. The error will contain a code and a description.

Placement tags can be found in your TapResearch dashboard on the App Settings page.

View in context

Swift in UIKit

ViewController.swift
if TapResearch.canShowContent(forPlacement: text) {
let customParameters = ["param1": 123, "param2": "abc"] as [String : Any]

TapResearch.showContent(forPlacement: text, delegate: self, customParameters: customParameters) { (error: NSError?) in
if let e = error {
print("\(e.userInfo[NSError.TapResearchErrorCodeString] ?? "(No code)") \(e.localizedDescription)")
}
}
}

canShowContent now accepts an error response block, this is optional in Swift and required in Objective-C.

let canShow: Bool = TapResearch.canShowContent(forPlacement: placementTag) { (error: NSError?) in
// Handle error if needed
}
BOOL canShow = [TapResearch canShowContentForPlacement:placementTag error:^(NSError * _Nullable error) {
// Handle error if needed
}]);

View in context

Swift in SwiftUI

ContentView.swift
if TapResearch.canShowContent(forPlacement: placementTag) {
let customParameters = ["param1": 123, "param2": "abc"] as [String : Any]

TapResearch.showContent(forPlacement: placementTag, delegate: self.tapResearchDelegates, customParameters: customParameters)
} else {
print("Placement \(placementTag) not ready")
}

Passing custom parameters

Custom parameters allow you to receive additional information about the user when they complete a survey. This can be used to filter out users who have already completed a survey, or to target specific users.

These params will be returned via server-to-server callbacks.

There are a maximum of 5 custom parameters that can be passed per placement.

The below code is a zoomed-in view of the code above.

Parameters can only be ascii characters, Unicode is not supported.

Swift in SwiftUI

View in context

ContentView.swift
let customParameters = ["param1": 123, "param2": "abc"] as [String : Any]
TapResearch.showContent(forPlacement: placementTag, delegate: self, customParameters: customParameters){ (error: NSError?) in
if ((error) != nil) {
print("Error \(String(describing: error))")

}
}

Swift in UIKit

View in context

ViewController.swift
let customParameters = ["param1": 123, "param2": "abc"] as [String : Any]
TapResearch.showContent(forPlacement: placementTag, delegate: self, customParameters: customParameters){ (error: NSError?) in
if ((error) != nil) {
print("Error \(String(describing: error))")

}
}

Best practices

Screen orientation

It’s important that landscape apps not lock the screen rotation when rendering TapResearch. Landscape orientation within the TapResearch experience would result in degraded engagement and revenue performance. By contrast, apps that allow rotation to portrait mode for TapResearch screens typically achieve twice the earnings of apps that are locked in landscape.