iOS
If your app is in test mode, you MUST use a test user. These are defined in "Test Devices" on the dashboard.
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 pageonTapResearchSdkReady
: 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 rewardsplacementIdentifier
will be the unique identifier for the placement - this can be used by your analytics to show which placement rewarded the usercurrencyName
will be the name of the currency name that was rewarded. This is theCurrency Name
that you set up in the dashboard. This is different from theDisplay Name
rewardAmount
will be the amount of the currency that was rewarded, in your currencyplacementTag
will be the tag of the placement that rewarded the user - its human readable and unique
Quick Question Response
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
- Objective C
Swift in SwiftUI
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
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()")
}
}
@interface SceneDelegate ()<TapResearchSDKDelegate>
...
- (void)onTapResearchDidReceiveRewards:(NSArray<TRReward *> *)rewards {
NSLog(@"onTapResearchDidReceiveRewards(rewards...)");
}
- (void)onTapResearchQuickQuestionResponse:(TRQQDataPayload *)qqPayload {
NSLog(@"[%@] onTapResearchQuickQuestionResponse(%@)", NSDate.now.description, qqPayload);
}
- (void)onTapReseachDidError:(NSError *)error {
NSLog(@"onTapReseachDidError: %@ %@", [error.userInfo[NSErrorTapResearchErrorCodeString] ?: @"(No code)", error.localizedDescription]);
}
- (void)onTapResearchSdkReady {
NSLog(@"[%@] onTapResearchSdkReady()", NSDate.now.description);
}
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
- Objective C
Swift in SwiftUI
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
class TapResearchDelegates: TapResearchContentDelegate {
//...
func onTapResearchContentShown(forPlacement placement: String) {
print("\(#function): \(placement) was shown")
}
func onTapResearchContentDismissed(forPlacement placement: String) {
print("\(#function): \(placement) was dismissed")
}
//...
}
@interface SomeClass ()<TapResearchContentDelegate>
...
- (void)onTapResearchContentShownForPlacement:(NSString *)placement {
NSLog(@"onTapResearchContentShown(%@)", placement);
}
- (void)onTapResearchContentDismissedForPlacement:(NSString *)placement {
NSLog(@"onTapResearchContentDismissed(%@)", placement);
}
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.
- Swift
- Objective C
Swift in UIKit
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)
}
}
}
Swift in SwiftUI
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.
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:
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)
}
}
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)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).
NSError *error;
[TapResearch initializeWithAPIToken:API_TOKEN userIdentifier:UNIQUE_USER_ID sdkDelegate:self error:&error];
if (error) {
NSLog(@"%@", error.localizedDescription);
}
}
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.
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.
NSDictionary *dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"a string", @1, nil]
forKeys:[NSArray arrayWithObjects:@"string", @"number", nil];
][TapResearch initializeWithAPIToken:apiToken userIdentifier:userIdentifier userAttributes:dict clearPreviousAttributes:YES sdkDelegate:self completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Error on initialize: %ld, %@", (long)error.code, error.localizedDescription);
}
}];
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.
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
.
- Swift
- Objective C
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)
}
}
// ...
// ...
- (void)sendUserAttributes {
NSDate *currentDate = [NSDate date];
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
NSString *iso8601String = [formatter stringFromDate:currentDate];
NSTimeInterval threeHours = 3 * 60 * 60;
NSNumber *rollerTypeValidUntil = @([currentDate timeIntervalSince1970] + threeHours);
NSNumber *someDouble = @1.11;
NSDictionary *dict = @{
@"app-user": userIdentifier,
@"roller-type": @"high",
@"roller-type-valid-until": rollerTypeValidUntil,
@"some-date": iso8601String,
@"some-double": someDouble
};
NSError *error = [TapResearch sendUserAttributesWithAttributes:dict clearPreviousAttributes:YES];
if (error) {
NSLog(@"%@", [error localizedDescription]);
}
}
// ...
//
Setting the user identifier
You can set the user identifier at any time. This is done by calling setUniqueUserIdentifier
with a string.
- Swift
- Objective C
TapResearch.setUserIdentifier("some-user-id")
[TapResearch setUserIdentifier: @"some-user-id"];
Checking if the SDK is ready
The SDK lets the app know it is ready using a callback:
- Swift
- Objective C
func onTapResearchSdkReady() {
}
- (void)onTapResearchSdkReady {
}
You can also check if the SDK is ready using the isReady
call:
- Swift
- Objective C
let ready: Bool = TapResearchSDK.isReady()
BOOL ready = [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.
- Swift
- Objective C
Swift in UIKit
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
}]);
Swift in SwiftUI
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")
}
if ([TapResearch canShowContentForPlacement:placementTag]) {
[TapResearch showContentForPlacement:placementTag delegate:self completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Error on showContent: %ld, %@", (long)error.code, error.localizedDescription);
}
}];
}
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
- Objective C
Swift in SwiftUI
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
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))")
}
}
[TapResearch showContentForPlacement:placementTag delegate:self customParameters:@{@"custom_param_1" : @"test text", @"custom_param_2" : @12} completion:^(NSError * _Nullable 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.