Documentation
iOS (Swift)
Add the SDK, configure it, handle ATT properly, bind purchases, forward universal links.
Requirements
- The package ships its own privacy manifest (
PrivacyInfo.xcprivacy) with the required-reason API declarations, so App Store Connect's ITMS-91053 check passes without work on your side. NSUserTrackingUsageDescriptioninInfo.plist. Without it the ATT prompt cannot appear at all, and without consent there is no advertising id.- Your App Privacy answers on App Store Connect should include the SDK's collection: device identifiers (IDFV, and IDFA when authorised), purchase history, product interaction and usage data, used for analytics and, if you push conversions back to ad platforms, third-party advertising. Confirm against your own app's actual usage.
1. Add the dependency
CocoaPods, pinned to a tag. The pod is served from git, not the CocoaPods trunk, so the source must be named:
pod 'RoasSensor', :git => 'https://github.com/rishabhrk2345/Roas-ios-SDK.git', :tag => '0.1.10'Or Swift Package Manager: add the same repository URL as a package and pin the same version.
2. Configure
The entry point is configure, not initialize -- the Android name does not compile here. Call it once at launch.
import RoasSensor
// AppDelegate.application(_:didFinishLaunchingWithOptions:), or your SwiftUI App's init
Roas.configure(
publicKey: "YOUR_PUBLIC_KEY",
requestTrackingAuthorization: false, // ask later, at a moment of your choosing (§3)
appSecret: Bundle.main.infoDictionary?["ROAS_APP_SECRET"] as? String,
baseUrl: "https://api.roassensor.com"
)baseUrl is printed by the panel and must be passed explicitly, for the same reason as on Android: the default is the production host, and a wrong one fails silently.
3. App Tracking Transparency
Left at its default, the ATT prompt fires about half a second after configure -- a cold-start system alert before the user has seen anything, which is the classic way to depress opt-in. Every denial costs the IDFA, and iOS never asks again.
Pass requestTrackingAuthorization: false and ask at a moment you choose, after onboarding:
Roas.requestTrackingAuthorization {
// called once the user has answered, either way
}SKAdNetwork registration happens once, at install, automatically. To receive Apple's postbacks, add both copy keys to Info.plist with the domain shown on the app's SKAdNetwork tab. Apple keeps only the domain and posts to its own /.well-known/ path on it:
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://roassensor.com</string>
<key>AdAttributionKit</key>
<dict>
<key>AttributionCopyEndpoint</key>
<string>https://roassensor.com</string>
</dict>Report conversion values on key events with Roas.updateConversionValue(_:coarse:), and always send the coarse value: below Apple's privacy threshold it is the only one that survives. Within each of Apple's three windows (days 0–2, 3–7 and 8–35) a value only ever rises, so reporting engaged after purchase never erases the purchase. Pass allowDecrease: true only if your schema deliberately re-encodes. What each value means is set on the app's SKAdNetwork tab, and changing it re-reads every postback already received.
4. The app secret
An .xcconfig that is not committed, surfaced through Info.plist:
// Secrets.xcconfig (gitignored)
ROAS_APP_SECRET = your-secret
// Info.plist
<key>ROAS_APP_SECRET</key>
<string>$(ROAS_APP_SECRET)</string>nil, which is exactly right: absent must mean absent, never an empty string.5. Bind purchases to the install
Apple's notification names the transaction, not the buyer. The field that carries our visitor id through a purchase is StoreKit's appAccountToken. It must be a UUID; Roas.appAccountToken() derives one from the visitor id, and the server reverses it.
var options: Set<Product.PurchaseOption> = []
if let token = Roas.appAccountToken() { // nil before configure() -- never pass an empty token
options.insert(.appAccountToken(token))
}
let result = try await product.purchase(options: options)
// Optional: report it now instead of waiting for Apple's notification.
if case .success(.verified(let transaction)) = result {
Roas.verifyPurchase(transactionId: String(transaction.id))
await transaction.finish()
}verifyPurchase only ever names a transaction id; we fetch Apple's own signed copy of it and book what Apple says it was worth. It needs an In-App Purchase key on your account, and it dedupes against the notification that follows.
6. Universal links
// SwiftUI
.onOpenURL { url in Roas.handleDeepLink(url) }
// UIKit
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let url = userActivity.webpageURL { Roas.handleDeepLink(url) }
return true
}A first install from an ad is attributed through Apple Search Ads attribution or a deferred match; a tap by someone who already has the app arrives here.
7. Website visitors (optional, 0.1.14+)
The App Store passes nothing from a tap on your website to the app it installs. So when an iPhone visitor taps your smart banner or one of your hosted "Download the app" links, the snippet copies that link, with a one-time code, to the clipboard. Read it back on first open and the install takes that exact tap's source: the banner, or the Google or Meta ad that brought the visitor to your website.
// once, early in the first session, after Roas.configure(...)
Roas.claimPastedClick { result in
if result.matched { print("came from", result.channel) }
}It first checks, without any prompt, whether the clipboard holds a web link at all, and only reads it when it does. It asks at most once per install.
See Web to app for what the website side does.
8. Identity and funnel events
Roas.identify(email: user.email) // hashed on device
Roas.track(.beginCheckout, properties: [RoasProps.productId: "pro_yearly"])Same event vocabulary as Android, same rule: productId must equal the App Store product id so the funnel and the sale line up, and nothing sent here is ever revenue.
Check it worked
- Run the app on a device once.
Roas.setLogLevel(.debug)prints each delivery; a healthy launch showsfirst-open -> 201. - On the app's Testing tab, an install row appears with your SDK version and
signed. - Before submitting: a Release archive contains
RoasSensor.bundle/PrivacyInfo.xcprivacy, and Xcode Organizer's Privacy Report lists it.