Skip to main content
Version: v2

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

  1. Create an app in the RapidoReach dashboard and copy your API key.
  2. Install the SDK (CocoaPods or SPM).
  3. Add NSUserTrackingUsageDescription (recommended).
  4. Configure the SDK at app startup.
  5. Register the user with fetchAppUserID().
  6. (Optional) Send user attributes for targeting.
  7. Check canShowContent(tag:) before you show the offerwall.
  8. Present the offerwall from a view controller.
  9. 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

  1. Add the pod to your Podfile:
platform :ios, '12.0'
use_frameworks!

target 'YourApp' do
pod 'RapidoReach', '~> 1.0.5'
end
  1. Install:
pod install
  1. Open the generated .xcworkspace.

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.

  1. Add NSUserTrackingUsageDescription to your app’s Info.plist.
  2. If you want IDFA-based device identification, show the ATT prompt before the first call to RapidoReach.shared.fetchAppUserID().
import AppTrackingTransparency

if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { _ in
// After this completes, call RapidoReach.configure + fetchAppUserID (see below)
}
}
Fallback behavior

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:

  1. (Optional) Choose backend with updateBackend(baseURL:)
  2. Configure with your API key and your app’s user identifier: configure(apiKey:user:)
  3. Set callbacks/delegates
  4. 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.

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
}
}
Testing (test users)

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.

Reserved keys

Attribute keys prefixed with tapresearch_ are reserved and will be rejected.

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)

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:

import RapidoReach
import UIKit

final class ViewController: UIViewController {
@IBAction func didTapEarnRewards() {
RapidoReach.shared.presentOfferwall(from: self, title: "Earn Rewards")
}
}

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"]
)

If you want to show a specific survey (outside the standard offerwall UI), you can list surveys and request a direct entry URL.

Swift-only APIs

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).

Swift-only APIs

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

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.

Avoid double-awarding

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()
info

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

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)")
}
}

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.

caution

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 and canShowContent(tag:) returns true.
  • 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.