iOS SDK (Swift & Objective‑C)
RapidoReach surfaces a rewarded survey offerwall inside your iOS app. The SDK handles:
- Registering / identifying the user
- Checking survey availability for a placement
- Presenting the offerwall UI
- Notifying you about offerwall lifecycle + reward events
Requirements
- Minimum iOS version: 12.0
- Swift 5.x (if integrating in Swift)
- CocoaPods or Swift Package Manager (SPM)
Integration Checklist
- Create an app in the RapidoReach dashboard and copy your API key.
- Install the SDK (CocoaPods or SPM).
- Add
NSUserTrackingUsageDescription(recommended). - Configure the SDK at app startup.
- Register the user with
fetchAppUserID(). - (Optional) Send user attributes for targeting.
- Check
canShowContent(tag:)before you show the offerwall. - Present the offerwall from a view controller.
- Reward users via server callback (recommended) or client-side callbacks (optional).
Get Your API Key
Create a publisher account and an iOS app in the RapidoReach dashboard, then copy your API Key:
Installation
- CocoaPods
- Swift Package Manager
- Manual
- Add the pod to your
Podfile:
platform :ios, '12.0'
use_frameworks!
target 'YourApp' do
pod 'RapidoReach', '~> 1.0.5'
end
- Install:
pod install
- Open the generated
.xcworkspace.
- In Xcode:
File→Add Package Dependencies… - Add the package URL:
https://github.com/rapidoreach/iOS-SDK-Podfile
- Select product
RapidoReachand add it to your app target.
You can download the latest version of the SDK from our GitHub page:
https://github.com/rapidoreach/iOS-SDK-Podfile
- Download the repo and locate
RapidoReach.framework. - In Xcode, add
RapidoReach.frameworkto your app target and ensure it’s embedded (Embed & Sign). - Ensure your app links the required Apple frameworks (Xcode usually adds these automatically):
UIKitWebKitAdSupportCoreTelephony
Then import RapidoReach and continue with Initialization.
Migrating from older iOS SDK docs
If you previously used older docs that referenced:
- Pod
RapidoReachSDK(old) - Import
import RapidoReachSDK(old)
Update to:
- Pod
RapidoReach - Import
import RapidoReach
App Tracking Transparency (IDFA)
RapidoReach uses the Advertising Identifier (IDFA) when available. On iOS 14+, IDFA is only available after the user grants tracking permission.
- Add
NSUserTrackingUsageDescriptionto your app’sInfo.plist. - If you want IDFA-based device identification, show the ATT prompt before the first call to
RapidoReach.shared.fetchAppUserID().
- Swift
- Objective‑C
import AppTrackingTransparency
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { _ in
// After this completes, call RapidoReach.configure + fetchAppUserID (see below)
}
}
@import AppTrackingTransparency;
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// After this completes, call RapidoReach.configure + fetchAppUserID (see below)
}];
}
If the user denies permission, the SDK falls back to an app-scoped identifier (IDFV) and caches a stable device ID for future sessions.
Backend Configuration (Optional)
By default, the SDK uses RapidoReach production services. You typically do not need to set a backend URL.
If RapidoReach provides you a staging/regional/testing backend URL, set it before calling configure(apiKey:user:):
RapidoReach.shared.updateBackend(baseURL: URL(string: "<BACKEND_BASE_URL>")!)
Initialization
Initialize the SDK once at app start:
- (Optional) Choose backend with
updateBackend(baseURL:) - Configure with your API key and your app’s user identifier:
configure(apiKey:user:) - Set callbacks/delegates
- Register the user:
fetchAppUserID()
What is user?
The user parameter is your app’s user identifier. It should be:
- Stable (doesn’t change between launches)
- Unique per user
- Safe to send to your backend callback (don’t include secrets)
If you have logged-in accounts, use your internal user id. If you have guest users, use a guest id you generate and persist.
- Swift (UIKit)
- Swift (SwiftUI)
- Objective‑C
import RapidoReach
import UIKit
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
RapidoReach.shared.configure(apiKey: "<YOUR_API_KEY>", user: "<YOUR_APP_USER_ID>")
RapidoReach.shared.delegate = self
RapidoReach.shared.rewardDelegate = self
RapidoReach.shared.sdkDelegate = self
RapidoReach.shared.fetchAppUserID()
return true
}
}
import RapidoReach
import SwiftUI
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene { WindowGroup { ContentView() } }
}
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
RapidoReach.shared.configure(apiKey: "<YOUR_API_KEY>", user: "<YOUR_APP_USER_ID>")
RapidoReach.shared.fetchAppUserID()
return true
}
}
@import RapidoReach;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[RapidoReach shared] configureWithApiKey:@"<YOUR_API_KEY>" user:@"<YOUR_APP_USER_ID>"];
// Objective‑C integrations use callback blocks (Swift delegates are not exposed to ObjC).
[[RapidoReach shared] setsurveysAvailableCallbackWithSurveyAvailableCallback:^(BOOL available) {
NSLog(@"Surveys available: %@", available ? @"YES" : @"NO");
}];
[[RapidoReach shared] setrewardCenterOpenedCallbackWithRewardCallback:^{
NSLog(@"Offerwall opened");
}];
[[RapidoReach shared] setrewardCenterClosedCallbackWithRewardCallback:^{
NSLog(@"Offerwall closed");
}];
[[RapidoReach shared] setRewardCallbackWithRewardCallback:^(NSInteger reward) {
NSLog(@"Total rewards: %ld", (long)reward);
}];
[[RapidoReach shared] fetchAppUserID];
return YES;
}
If your RapidoReach dashboard/account uses “Test Users” (or a similar test flow), use a test user identifier to see test content during QA. Switch your app/dashboard configuration to “Live” before release if required by your setup.
Send User Attributes
Send user attributes to improve survey targeting. Attributes are merged server-side and can be cleared using clearPrevious: true.
Attribute keys prefixed with tapresearch_ are reserved and will be rejected.
- Swift
RapidoReach.shared.sendUserAttributes(
["age": 25, "gender": "male", "zip": "90210"],
clearPrevious: false
) { error in
if let error = error {
print("sendUserAttributes failed: \(error)")
}
}
Setting the User Identifier
If your user changes (login/logout), update the SDK and re-register:
RapidoReach.shared.setUserIdentifier("<NEW_APP_USER_ID>")
RapidoReach.shared.fetchAppUserID()
Checking if the SDK is Ready
The SDK is “ready” when the user has been registered/refreshed.
if RapidoReach.shared.isReady {
// You can now check placements / present the offerwall
}
In SwiftUI (iOS 13+), you can observe readiness using the provided helper:
@StateObject private var readiness = RapidoReachReadiness()
Getting Placement Details (Swift)
Placements let you control where and how you show RapidoReach content (e.g. a “default” placement, a “home” placement, etc).
Use getPlacementDetails(tag:) to fetch raw placement configuration/metadata from the backend:
RapidoReach.shared.getPlacementDetails(tag: "default") { result in
switch result {
case .success(let details):
print("Placement details: \(details)")
case .failure(let error):
print("Placement details error: \(error)")
}
}
Displaying a Placement (Offerwall)
Check availability first (recommended)
RapidoReach.shared.canShowContent(tag: "default") { result in
switch result {
case .success(let ready):
guard ready else { return }
RapidoReach.shared.presentOfferwall(from: self, title: "Earn Rewards")
case .failure(let error):
print("Placement check failed: \(error)")
}
}
Present the offerwall
Present from any UIViewController:
- Swift
- Objective‑C
import RapidoReach
import UIKit
final class ViewController: UIViewController {
@IBAction func didTapEarnRewards() {
RapidoReach.shared.presentOfferwall(from: self, title: "Earn Rewards")
}
}
@import RapidoReach;
- (void)didTapEarnRewards {
[[RapidoReach shared] presentOfferwallFrom:self title:@"Earn Rewards" customParameters:nil completion:nil];
}
presentSurvey vs presentOfferwall
presentOfferwall(from:title:customParameters:)is the preferred API name.presentSurvey(...)is an alias that shows the same UI.
Passing Custom Parameters
Custom parameters are forwarded to the offerwall/survey providers.
Rules:
- Values must be JSON-serializable (
String,Int,Double,Bool, arrays/dictionaries of those) - Keep the payload small (avoid large nested objects)
RapidoReach.shared.presentOfferwall(
from: self,
title: "Earn Rewards",
customParameters: ["sub_id": "12345", "screen": "home"]
)
Survey List & Direct Survey Links (Swift)
If you want to show a specific survey (outside the standard offerwall UI), you can list surveys and request a direct entry URL.
listSurveys, canShowSurvey, hasSurveys, and showSurvey are Swift APIs (not exposed to Objective‑C).
List surveys for a placement
RapidoReach.shared.listSurveys(tag: "default") { result in
switch result {
case .success(let surveys):
print("Surveys: \(surveys)")
case .failure(let error):
print("listSurveys error: \(error)")
}
}
Open a specific survey URL
showSurvey(surveyId:tag:) returns a URL; you must open it yourself (e.g. SFSafariViewController).
import SafariServices
RapidoReach.shared.showSurvey(surveyId: "<SURVEY_ID>", tag: "default") { result in
switch result {
case .success(let url):
DispatchQueue.main.async {
let safari = SFSafariViewController(url: url)
self.present(safari, animated: true)
}
case .failure(let error):
print("showSurvey error: \(error)")
}
}
Quick Questions (Swift)
Some placements can return “quick questions” (lightweight questions used for better targeting).
Quick question APIs are Swift APIs (not exposed to Objective‑C).
Check for quick questions
RapidoReach.shared.hasQuickQuestions(tag: "default") { result in
print("Has quick questions: \(try? result.get())")
}
Fetch quick questions
RapidoReach.shared.fetchQuickQuestions(tag: "default") { result in
switch result {
case .success(let payload):
print("Quick questions payload: \(payload)")
case .failure(let error):
print("fetchQuickQuestions error: \(error)")
}
}
Answer a quick question
RapidoReach.shared.answerQuickQuestion(
id: "<QUESTION_ID>",
placement: "default",
answer: "yes"
) { result in
switch result {
case .success(let response):
print("answerQuickQuestion response: \(response)")
case .failure(let error):
print("answerQuickQuestion error: \(error)")
}
}
Rewards
Server-side callbacks (recommended)
For fraud prevention and idempotency, reward your users on your server using your RapidoReach callback endpoint configured in the dashboard.
Client-side rewards (optional)
If your app is configured for client callback mode, you can receive reward information in-app.
If you enable client callbacks, ensure your reward flow is idempotent. Do not award the user twice if you also have a server callback configured.
Swift: metadata-first reward callbacks
Use RapidoReachRewardDelegate for per-transaction metadata:
extension AppDelegate: RapidoReachRewardDelegate {
func onRewards(_ rewards: [RapidoReachRewardPayload]) {
for reward in rewards {
print("transaction=\(reward.transactionIdentifier ?? "n/a") amount=\(reward.rewardAmount)")
}
}
}
Swift: totals (informational)
RapidoReachDelegate.didGetRewards(_:) provides a totals object. This is best used for display/UX (e.g. “You have X coins pending”) rather than as a secure source of truth.
Refresh rewards (client mode)
RapidoReach.shared.fetchRewards()
In server-side callback mode, fetchRewards() is a no-op and won’t trigger in-app rewards.
Objective‑C: totals callback
Objective‑C integrations can use:
[[RapidoReach shared] setRewardCallbackWithRewardCallback:^(NSInteger totalRewards) {
NSLog(@"Total rewards: %ld", (long)totalRewards);
}];
Offerwall Lifecycle Callbacks
- Swift (delegates)
- Objective‑C (blocks)
extension AppDelegate: RapidoReachDelegate {
func didSurveyAvailable(_ available: Bool) {
print("Surveys available: \(available)")
}
func didOpenRewardCenter() {
print("Offerwall opened")
}
func didClosedRewardCenter() {
print("Offerwall closed")
}
func didGetUser(_ user: RapidoReachUser) {
print("SDK user: \(user.id)")
}
func didGetError(_ error: RapidoReachError) {
print("SDK error: \(error.localizedDescription)")
}
}
[[RapidoReach shared] setsurveysAvailableCallbackWithSurveyAvailableCallback:^(BOOL available) {
NSLog(@"Surveys available: %@", available ? @"YES" : @"NO");
}];
[[RapidoReach shared] setrewardCenterOpenedCallbackWithRewardCallback:^{
NSLog(@"Offerwall opened");
}];
[[RapidoReach shared] setrewardCenterClosedCallbackWithRewardCallback:^{
NSLog(@"Offerwall closed");
}];
Errors
RapidoReachError provides a localized description and (when applicable) a simple error code.
Example handling:
func didGetError(_ error: RapidoReachError) {
if let code = error.code {
print("RapidoReach error code: \(code.rawValue)")
}
print("RapidoReach error: \(error.localizedDescription)")
}
SwiftUI Convenience Components (Optional)
If you use SwiftUI (iOS 13+), you can use the provided button:
import RapidoReach
RapidoReachOfferwallButton(title: "Surveys", placement: "default")
ATS (Local HTTP Testing Only)
If you point the SDK to a non-HTTPS backend (e.g., http://<YOUR_DEV_HOST>), add an ATS exception in your app Info.plist.
Avoid NSAllowsArbitraryLoads for production; prefer exception domains for specific hosts used during development.
Best Practices
- Call
RapidoReach.shared.configure(apiKey:user:)before any other SDK method. - Set delegates/callbacks before
fetchAppUserID()so you don’t miss events. - Call
canShowContent(tag:)before presenting the offerwall. - Present from the top-most view controller (avoid presenting while another modal is already shown).
Troubleshooting
- Offerwall doesn’t show: ensure
fetchAppUserID()has been called andcanShowContent(tag:)returnstrue. - Wrong backend environment: ensure you haven’t overridden
updateBackend(baseURL:)(only set it if RapidoReach provided you a custom URL). - IDFA is missing: request ATT permission on iOS 14+ before the first registration call.