iOS
Later sections cover user attributes, the user identifier, placement details, custom parameters, and more.
Testing? Your app starts in test mode, so you'll need a test device to see content while you build.
Installation
See the iOS example apps on GitHub.
Minimum iOS version: 13.0, or 15.0 for SDK 3.8.1 and later.
Replace <version> below with your SDK version from GitHub releases.
CocoaPods
platform :ios, '13.0' # Use '15.0' for SDK 3.8.1 and later.
use_frameworks!
target 'MyApp' do
pod 'TapResearch', '<version>' # Omit the GitHub tag's leading "v".
end
Then run pod install in the Terminal.
Swift Package Manager
Add the following URL in the Xcode Swift Package Manager:
https://github.com/TapResearch/TapResearch-iOS-SDK
- Set the Dependency Rule to Exact Version or Up to Next Major Version and enter your SDK version
- 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".
An agent running outside Xcode (a terminal or IDE agent) can add this Swift Package by editing your Xcode project file directly. An agent running inside Xcode (for example Xcode's built-in coding assistant) can't — Xcode holds the project file open, so it can't be modified while Xcode is running. In that case, add the package yourself with the steps above, then hand control back to the agent to continue.
Manual installation
Download the source archive for a release from GitHub releases, drag TapResearchSDK.xcframework into your project, and set it to Embed & Sign under Frameworks, Libraries, and Embedded Content.
Importing the SDK
Import the SDK in each file that calls it:
- Swift
- Objective-C
import TapResearchSDK
#import <TapResearchSDK/TapResearchSDK.h>
Integration
Callbacks
The TapResearch SDK has two main 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
onTapResearchSdkReady(required): Called when the SDK is ready for use. We recommend showing content only after this callback is called.onTapResearchDidError(required): Called when an error has occurred in the TapResearch SDK. For the full list, see the error codes page.onTapResearchDidReceiveRewards(optional): Called when a user has earned a reward(s). You can implement it here or set a separate reward delegate.onTapResearchQuickQuestionResponse(optional): Called when a quick question result is being returned.
The class that conforms to the TapResearchSDKDelegate protocol (ideally your SceneDelegate) must implement
onTapResearchSdkReady and onTapResearchDidError.
Reward object
Each reward is a TRReward. See TRReward for its properties, including the iOS payoutEvent values.
Quick Question response
New to Quick Questions? See Quick Questions for what they are and the response payload.
onTapResearchQuickQuestionResponse is an optional method of TapResearchSDKDelegate. It is called when a Quick Question response is being returned, and receives a TRQQDataPayload object.
- Swift
- Objective-C
Use a class to implement the SDK's delegates and pass it to TapResearch at initialization. In UIKit you can adopt the protocol on your SceneDelegate instead.
import TapResearchSDK
class TapResearchSDKDelegates: NSObject, TapResearchSDKDelegate {
func onTapResearchSdkReady() {
// The SDK is ready: send user attributes, show content, etc.
if let error = TapResearch.sendUserAttributes(attributes: ["attribute1": "some player attribute", "a_number": 12]) {
print("sendUserAttributes: \(error.code) \(error.localizedDescription)")
}
}
func onTapResearchDidError(_ error: NSError) {
print("onTapResearchDidError: \(error.code) \(error.localizedDescription)")
}
func onTapResearchDidReceiveRewards(_ rewards: [TRReward]) {
for reward in rewards {
print("reward: currency = \(reward.currencyName ?? "unknown"), amount = \(reward.rewardAmount)")
}
}
func onTapResearchQuickQuestionResponse(_ qqPayload: TRQQDataPayload) {
for question: TRQQDataPayloadQuestion in qqPayload.questions {
// ...
}
}
}
@interface SceneDelegate () <TapResearchSDKDelegate>
@end
@implementation SceneDelegate
- (void)onTapResearchSdkReady {
NSLog(@"onTapResearchSdkReady");
}
- (void)onTapResearchDidError:(NSError *)error {
NSLog(@"onTapResearchDidError: %ld %@", (long)error.code, error.localizedDescription);
}
- (void)onTapResearchDidReceiveRewards:(NSArray<TRReward *> *)rewards {
NSLog(@"onTapResearchDidReceiveRewards: %lu reward(s)", (unsigned long)rewards.count);
}
- (void)onTapResearchQuickQuestionResponse:(TRQQDataPayload *)qqPayload {
NSLog(@"onTapResearchQuickQuestionResponse: %@", qqPayload);
}
@end
TapResearchRewardDelegate
onTapResearchDidReceiveRewards: Called when a user has earned a reward(s).
Implementing onTapResearchDidReceiveRewards on your TapResearchSDKDelegate is optional.
If you implement it, rewards can be sent to your app as soon as the SDK is ready. If not, your app will not receive any rewards until a reward delegate has been set.
Setting a reward delegate
To set a reward handler when you are ready to receive rewards, use:
- Swift
- Objective-C
TapResearch.setRewardDelegate(myRewardDelegate)
[TapResearch setRewardDelegate:myRewardDelegate];
The object you pass in must conform to TapResearchRewardDelegate and implement onTapResearchDidReceiveRewards.
When you no longer need the reward delegate you can simply pass nil to the same SDK function.
If you set the reward delegate from a view controller, for example in viewDidLoad, make sure to call TapResearch.setRewardDelegate(nil) when the view controller is no longer in the stack.
Anytime you set a new reward delegate, or nil, it replaces the previously set value. For example: if you have a reward delegate available at init time then later set another reward delegate, the original delegate will be replaced by the new one.
TapResearchQuickQuestionDelegate
onTapResearchQuickQuestionResponse: Called when a quick question result is being returned.
To set a Quick Questions response handler when you are ready to receive Quick Question responses, use:
- Swift
- Objective-C
TapResearch.setQuickQuestionDelegate(myQuickQuestionDelegate)
[TapResearch setQuickQuestionDelegate:myQuickQuestionDelegate];
The object you pass in must conform to TapResearchQuickQuestionDelegate and implement onTapResearchQuickQuestionResponse.
When you no longer need the Quick Questions response delegate you can simply pass nil to the same SDK function.
If you set the Quick Questions response delegate from a view controller, for example in viewDidLoad, make sure to call TapResearch.setQuickQuestionDelegate(nil) when the view controller is no longer in the stack.
Anytime you set a new Quick Questions response delegate, or nil, it replaces the previously set value. For example: if you have a Quick Questions response delegate available at init time then later set another Quick Questions response delegate, the original delegate will be replaced by the new one.
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
class TapResearchContentDelegates: NSObject, TapResearchContentDelegate {
func onTapResearchContentShown(forPlacement placement: String) {
print("\(#function): \(placement) was shown")
}
func onTapResearchContentDismissed(forPlacement placement: String) {
print("\(#function): \(placement) was dismissed")
}
}
@interface ViewController () <TapResearchContentDelegate>
@end
@implementation ViewController
- (void)onTapResearchContentShownForPlacement:(NSString *)placement {
NSLog(@"onTapResearchContentShown(%@)", placement);
}
- (void)onTapResearchContentDismissedForPlacement:(NSString *)placement {
NSLog(@"onTapResearchContentDismissed(%@)", placement);
}
@end
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 your app's API token, shown on your app's Settings tab in the Publisher 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.
Keep your API token and placement tag in a separate Swift config file (for
example TapResearchCredentials.swift) and pass them to initialize. Do not
add them to Info.plist — TapResearch never reads them from there, and on
SwiftUI projects where Info.plist is auto-generated, hand-creating one to hold
credentials will break your build. The token is only ever used in the
initialize call.
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.
The user identifier passed through initialization must be unique! Only initialize once you are able to provide a unique identifier. Re-using the same user identifier is not supported and will prevent your users from accessing our service.
- Swift (UIKit)
- SwiftUI
- Objective-C
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
// SceneDelegate conforms to TapResearchSDKDelegate
TapResearch.initialize(withAPIToken: apiToken, userIdentifier: userIdentifier, sdkDelegate: self) { (error: NSError?) in
if let error = error {
print("\(error.code) \(error.localizedDescription)")
}
}
}
Initialize the SDK in the init() method of your root view.
let tapSDKDelegates: TapResearchSDKDelegates
init() {
tapSDKDelegates = TapResearchSDKDelegates()
TapResearch.initialize(withAPIToken: apiToken, userIdentifier: userId, sdkDelegate: self.tapSDKDelegates) { (error: NSError?) in
if let error = error {
print("\(#function): \(error.code), \(error.localizedDescription)")
}
}
}
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// SceneDelegate conforms to TapResearchSDKDelegate
[TapResearch initializeWithAPIToken:API_TOKEN
userIdentifier:UNIQUE_USER_ID
sdkDelegate:self
completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Error on initialize: %ld, %@", (long)error.code, error.localizedDescription);
}
}];
}
To change the user after the SDK has started, call setUserIdentifier.
Send user attributes
See User attributes and targeting for more.
User attributes are used to target specific users with content (ex: an offer). The most recent values received are used when determining which content to show.
Pass a dictionary of attributes to sendUserAttributes after the SDK is ready.
Attribute names must be strings and must not begin with tapresearch_, which is reserved for TapResearch. Use string or numeric values for targeting. For booleans, send "true"/"false" or 0/1 instead of native booleans.
Set clearPreviousAttributes to false to merge new values with existing attributes, or true to replace the attributes you previously sent.
- 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)
}
}
- (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]);
}
}
You can also pass user attributes when you first initialize the SDK. This is done by changing the TapResearch.initialize(...) call to additionally pass a userAttributes dictionary and the clearPreviousAttributes flag.
If user attributes are known at SDK initialization, it is preferable to pass them through initialize compared to using sendUserAttributes. This will result in quicker load times for targeted content.
- Swift
- Objective-C
let dict: [String: Any] = ["app-user": userIdentifier, "roller-type": "high", "roller-type-valid-until": Date().timeIntervalSince1970 + ViewController.threeHours]
TapResearch.initialize(withAPIToken: apiToken,
userIdentifier: userIdentifier,
userAttributes: dict,
clearPreviousAttributes: true,
sdkDelegate: self) { (error: NSError?) in
if let error = error {
print(error.localizedDescription)
}
}
NSDictionary *dict = @{ @"string": @"a string", @"number": @1 };
[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);
}
}];
Setting the user identifier
You can change the user identifier after the SDK is ready by calling setUserIdentifier with a string.
- Swift
- Objective-C
TapResearch.setUserIdentifier("some-user-id")
[TapResearch setUserIdentifier:@"some-user-id" completion:nil];
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 = TapResearch.isReady()
BOOL ready = [TapResearch isReady];
Getting placement details
Placement details can change as our SDK refreshes placements in the background, so it is not recommended to store or cache these values. Instead, refetch the details each time you need to use them, e.g. to display something in your UI.
To get details for a placement call getPlacementDetails:
- Swift
- Objective-C
let details: TRPlacementDetails? = TapResearch.getPlacementDetails("your-placement-tag") { (error: NSError?) in
// Handle error
}
if let details {
// Use the placement details
}
TRPlacementDetails *details = [TapResearch getPlacementDetails:@"your-placement-tag"
errorHandler:^(NSError * _Nullable error) {
if (error) {
// Handle the error
}
}];
if (details) {
// Use the placement details
}
The TRPlacementDetails objects:
@objc public final class TRPlacementDetails : NSObject, Encodable {
@objc public let name : String
@objc public let contentType : String
@objc public let currencyName : String
@objc public let isSale : Bool
@objc public let saleType : String?
@objc public let saleEndDate : String?
@objc public let saleMultiplier : Double
@objc public let saleDisplayName : String?
@objc public let saleTag : String?
@objc public let bonusBarProgress : TRBonusBarProgress?
}
@objc public final class TRBonusBarProgress : NSObject, Encodable {
@objc public let isActive : Bool
@objc public let currentCompletes : Int
@objc public let bonusWindowEndAt : String?
@objc public let bonusTiers : [TRBonusTier]?
@objc public let error : NSError?
}
@objc public final class TRBonusTier : NSObject, Encodable {
@objc public let tierNumber : Int
@objc public let completesNeeded : Int
@objc public let rewardAmount : Int
@objc public let status : String?
}
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.
canShowContent accepts an error block. It is optional in Swift; in Objective-C you must pass a block or nil.
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 are listed on your app's Manage tab in the Publisher Dashboard.
- Swift (UIKit)
- SwiftUI
- Objective-C
let canShow: Bool = TapResearch.canShowContent(forPlacement: placementTag) { (error: NSError?) in
// Optional: handle the error
}
if canShow {
let customParameters: [String: Any] = ["param1": 123, "param2": "abc"]
TapResearch.showContent(forPlacement: placementTag, delegate: self, customParameters: customParameters) { (error: NSError?) in
if let error = error {
print("\(error.code) \(error.localizedDescription)")
}
}
}
if TapResearch.canShowContent(forPlacement: placementTag) {
let customParameters: [String: Any] = ["param1": 123, "param2": "abc"]
TapResearch.showContent(forPlacement: placementTag, delegate: self.tapResearchDelegates, customParameters: customParameters)
} else {
print("Placement \(placementTag) not ready")
}
BOOL canShow = [TapResearch canShowContentForPlacement:placementTag error:^(NSError * _Nullable error) {
// Optional: handle the error
}];
if (canShow) {
[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 in your server-to-server postbacks.
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. Values must be strings, numbers, or booleans.
- Swift
- Objective-C
let customParameters: [String: Any] = ["param1": 123, "param2": "abc"]
TapResearch.showContent(forPlacement: placementTag, delegate: self, customParameters: customParameters) { (error: NSError?) in
if let error = error {
print("Error \(error.code) \(error.localizedDescription)")
}
}
[TapResearch showContentForPlacement:placementTag
delegate:self
customParameters:@{@"custom_param_1": @"test text", @"custom_param_2": @12}
completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Error on showContent: %ld, %@", (long)error.code, error.localizedDescription);
}
}];
Granting boosts
You can grant a boost to a user with the grantBoost function using Boosts you have set up for your app.
- Swift
- Objective-C
First implement the TapResearchGrantBoostResponseDelegate to receive the result of granting a boost:
func onTapResearchGrantBoostResponse(_ response: TapResearchSDK.TRGrantBoostResponse) {
if response.success {
// Perhaps update display in response to a successful boost
}
else {
if let error = response.error {
// Handle the error
}
}
}
Grant a boost:
// Assuming self implements TapResearchGrantBoostResponseDelegate
TapResearch.grantBoost("BOOST_TAG", delegate: self) { (error: NSError?) in
// Handle any error
}
First implement the TapResearchGrantBoostResponseDelegate to receive the result of granting a boost:
- (void)onTapResearchGrantBoostResponse:(TRGrantBoostResponse * _Nonnull)response {
if (response.success) {
// Perhaps update display to indicate boost granted
}
else {
if (response.error) {
// Handle the error
}
}
}
Grant a boost:
// Assuming self implements TapResearchGrantBoostResponseDelegate
[TapResearch grantBoost:@"YOUR_BOOST_TAG"
delegate:self
errorHandler:^(NSError * _Nullable error) {
// Handle any error
}];
The TRGrantBoostResponse object:
@objc public final class TRGrantBoostResponse : NSObject, Encodable {
@objc public let boostTag : String
@objc public let success : Bool
@objc public let error : NSError?
}
NotificationCenter events
Available from SDK 3.8.0--rc0.
Instead of implementing a delegate method for some callbacks, you can observe a notification on the default NotificationCenter.
| Notification name | Posted when | Replaces | userInfo |
|---|---|---|---|
TapResearch.TapResearchSDKReady | The SDK is ready to be used | onTapResearchSdkReady (TapResearchSDKDelegate) | — |
TapResearch.TapResearchDidError | An error occurs | onTapResearchDidError (TapResearchSDKDelegate) | TapResearch.ErrorKey → NSError |
TapResearch.TapResearchContentShown | TapResearch content is shown | onTapResearchContentShown (TapResearchContentDelegate) | TapResearch.PlacementTagKey → placement tag String |
TapResearch.TapResearchContentDismissed | TapResearch content is dismissed | onTapResearchContentDismissed (TapResearchContentDelegate) | TapResearch.PlacementTagKey → placement tag String |
To register, add an observer for each notification you need, for example TapResearch.TapResearchContentShown:
func registerForNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(handleTapResearchNotification(_:)), name: TapResearch.TapResearchContentShown, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleTapResearchNotification(_:)), name: TapResearch.TapResearchDidError, object: nil)
}
@objc func handleTapResearchNotification(_ notification: Notification) {
if let placementTag = notification.userInfo?[TapResearch.PlacementTagKey] as? String {
// Content shown (or dismissed) for placementTag
}
if let error = notification.userInfo?[TapResearch.ErrorKey] as? NSError {
// Handle the error
}
}
Need help?
If you're experiencing issues or need assistance, reach out to developers@tapresearch.com.
Stay updated: watch the TapResearch iOS SDK on GitHub to get notified about new releases.