> For the complete documentation index, see [llms.txt](https://docs.roivenue.com/roivenue-resources/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roivenue.com/roivenue-resources/howto/roivenue-measurement-implementation/tracking-in-ios-applications.md).

# Tracking in iOS Applications

### Prerequisites

Xcode 15+

Swift 5.9+

Network permissions (internet access)

A valid Roivenue propertyId (UUID)

### What the Tracking API Expects

The Roivenue endpoint accepts events in JSON format. Each event represents a user interaction and includes basic context (timestamp, device info, URL, etc.) plus optional conversion data.

```
{
  "propertyId": "adbfb3aa-xxxx-xxxx-xxxx-xxxxxxxx",
  "type": "pageview",
  "timestamp": "2024-10-30T07:50:40",
  "url": "https://example.com/?utm_source=facebook",
  "deviceId": "20904e-f1e3-79f2-b5b7-00d6a7bfe57e",
  "userId": "someUserHashed",
  "referer": "$direct",
  "screenWidth": 1170,
  "screenHeight": 2532,
  "utmSource": "facebook",
  "utmMedium": "cpc",
  "utmCampaign": "autumn_sale",
  "conversionType": "purchase",
  "conversionId": "ORD-001",
  "conversionValue": 149.99,
  "currencyCode": "EUR"
}
```

### Implementation Overview

You’ll build a single reusable `RoivenueTracker` Swift class:

* Automatically collects **device metadata**
* Sends **pageview** events when views appear
* Sends **conversion** events with purchase details

### Capturing and Managing UTM Parameters

When the app is opened via a **universal link** or **deep link**, UTM parameters can be passed in the URL.\
Roivenue tracking uses these UTMs to attribute events to the correct marketing source.

#### Step 1 — Create the UTM Manager

Create a helper class to extract, store, and clear UTM parameters. (You might already have something similar).

```
import Foundation

class UTMManager {
    static let shared = UTMManager()

    private let utmKeys = [
        "utm_source",
        "utm_medium",
        "utm_campaign",
        "utm_content",
        "utm_term"
        // and more parameters like gclid
    ]

    private var sessionUTMs: [String: String] = [:]

    // Capture UTMs from the initial app open URL
    func store(from url: URL) {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
              let queryItems = components.queryItems else { return }

        var result: [String: String] = [:]
        for key in utmKeys {
            if let value = queryItems.first(where: { $0.name == key })?.value {
                result[key] = value
            }
        }

        if !result.isEmpty {
            sessionUTMs = result
        }
    }

    // Retrieve UTMs for event enrichment
    func allUTMs() -> [String: String] {
        sessionUTMs
    }

    // Reset UTMs on app cold start
    func clear() {
        sessionUTMs.removeAll()
    }
}
```

#### Step 2 — Hook into App Lifecycle

Reset UTMs on **cold start**, and store new ones when a deep link contains them.

```
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UTMManager.shared.clear() // reset on fresh launch
        return true
    }

    func application(_ application: UIApplication,
                     continue userActivity: NSUserActivity,
                     restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        if let url = userActivity.webpageURL {
            UTMManager.shared.store(from: url)
        }
        return true
    }
}
```

#### Step 3 — Create the Tracker Class

```
import Foundation
import UIKit

struct RoivenueEvent: Codable {
    let propertyId: String
    let type: String
    let timestamp: String
    let url: String?
    let deviceId: String
    let userId: String?
    let screenWidth: Int
    let screenHeight: Int
    let conversionType: String?
    let conversionId: String?
    let conversionValue: Double?
    let currencyCode: String?
    let utmSource: String?
    let utmMedium: String?
    let utmCampaign: String?
    let utmContent: String?
    let utmKeyword: String?
}

class RoivenueTracker {
    static let shared = RoivenueTracker()

    private let endpoint = URL(string: "https://tr.roivenue.com/c")!
    private let propertyId = "adbfb3aa-xxxx-xxxx-xxxx-xxxxxxxx"
    private let deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString

    func trackEvent(
        type: String,
        url: String? = nil,
        userId: String? = nil,
        conversionType: String? = nil,
        conversionId: String? = nil,
        conversionValue: Double? = nil,
        currencyCode: String? = nil
    ) {
        let utms = UTMManager.shared.allUTMs()
        let event = RoivenueEvent(
            propertyId: propertyId,
            type: type,
            timestamp: ISO8601DateFormatter().string(from: Date()),
            url: url,
            deviceId: deviceId,
            userId: userId,
            screenWidth: Int(UIScreen.main.bounds.width),
            screenHeight: Int(UIScreen.main.bounds.height),
            conversionType: conversionType,
            conversionId: conversionId,
            conversionValue: conversionValue,
            currencyCode: currencyCode,
            utmSource: utms["utm_source"] ?? "$direct",
            utmMedium: utms["utm_medium"],
            utmCampaign: utms["utm_campaign"],
            utmContent: utms["utm_content"],
            utmKeyword: utms["utm_term"]
        )

        send(event)
    }

    private func send(_ event: RoivenueEvent) {
        guard let body = try? JSONEncoder().encode(event) else { return }

        var request = URLRequest(url: endpoint)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = body

        URLSession.shared.dataTask(with: request).resume()
    }
}
```

#### Step 4 — Track Page Views

```
import SwiftUI

struct HomeView: View {
    var body: some View {
        VStack {
            Text("Home")
        }
        .onAppear {
            RoivenueTracker.shared.trackEvent(
                type: "pageview",
                url: "app://home"
            )
        }
    }
}
```

#### Step 5 — Track Conversions

```
RoivenueTracker.shared.trackEvent(
    type: "conversion",
    conversionType: "purchase",
    conversionId: "ORDER-001",
    conversionValue: 249.99,
    currencyCode: "EUR"
)
```
