Overview
Attribution tells you how users discovered your app. SpendOwl fetches attribution data from Apple Search Ads and enriches it with campaign, ad group, and keyword details.
Automatic Attribution
Attribution is automatically fetched when you call configure(). For most apps, you don’t need to do anything else—the data flows to your dashboard automatically.
Manual Attribution Access
Use the attribution() method to access attribution data in your app:
async/await
Completion Handler
Task {
do {
let attribution = try await SpendOwl.attribution()
switch attribution.status {
case .attributed:
print("Campaign: \(attribution.campaignName ?? "unknown")")
print("Keyword: \(attribution.keyword ?? "unknown")")
case .organic:
print("Organic install")
case .unknown:
print("Attribution pending")
}
} catch {
print("Error: \(error)")
}
}
SpendOwl.attribution { result in
switch result {
case .success(let attribution):
if attribution.status == .attributed {
print("Campaign: \(attribution.campaignName ?? "unknown")")
}
case .failure(let error):
print("Error: \(error)")
}
}
Attribution Result
The AttributionResult struct contains all available attribution data:
public struct AttributionResult {
let id: String // Unique attribution ID
let status: AttributionStatus // .attributed, .organic, or .unknown
// Campaign data (only when status == .attributed)
let campaignId: Int?
let campaignName: String?
let adGroupId: Int?
let adGroupName: String?
let keywordId: Int?
let keyword: String?
// Additional data
let countryOrRegion: String? // "US", "GB", "JP", etc.
let clickDate: Date? // When the ad was clicked
let conversionType: String? // "Download" or "Redownload"
let supplyPlacement: String? // Ad placement type
}
Attribution Status
| Status | Description |
|---|
.attributed | User installed after clicking an Apple Search Ads ad. Campaign data is available. |
.organic | User found the app organically (not through ads). No campaign data. |
.unknown | Attribution couldn’t be determined. May be pending or unavailable. |
Use Cases
Display Campaign Info
Show users which campaign brought them:
struct WelcomeView: View {
@State private var campaignName: String?
var body: some View {
VStack {
Text("Welcome!")
if let campaign = campaignName {
Text("You found us through: \(campaign)")
.font(.caption)
}
}
.task {
if let attr = try? await SpendOwl.attribution(),
attr.status == .attributed {
campaignName = attr.campaignName
}
}
}
}
Analytics Integration
Send attribution to your analytics platform:
func trackAttribution() async {
guard let attribution = try? await SpendOwl.attribution() else { return }
Analytics.track("app_install", properties: [
"source": attribution.status == .attributed ? "paid" : "organic",
"campaign": attribution.campaignName ?? "none",
"keyword": attribution.keyword ?? "none",
"country": attribution.countryOrRegion ?? "unknown"
])
}
A/B Testing by Source
Customize experience based on acquisition source:
func getOnboardingFlow() async -> OnboardingFlow {
guard let attribution = try? await SpendOwl.attribution() else {
return .standard
}
if attribution.status == .attributed,
attribution.keyword?.contains("premium") == true {
return .premium
}
return .standard
}
Caching
Attribution data is fetched once and cached:
- First call fetches from Apple and SpendOwl servers
- Subsequent calls return the cached result
- Cache persists across app launches
Attribution doesn’t change after install, so caching is safe.
Error Handling
Handle attribution errors gracefully:
do {
let attribution = try await SpendOwl.attribution()
// Use attribution
} catch SpendOwlError.notConfigured {
// SDK not configured - call configure() first
} catch SpendOwlError.attributionUnavailable {
// Device doesn't support attribution (iOS < 14.3)
} catch SpendOwlError.networkError {
// Network issue - retry later
} catch {
// Other error
}
Next Steps