# 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"
)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.roivenue.com/roivenue-resources/howto/roivenue-measurement-implementation/tracking-in-ios-applications.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
