Skip to content
OddlyticsRelease 1.0.2

Swift SDK ​

The Oddlytics Swift package records events in your app and sends them to your Worker. It has no dependencies and works on iOS 15 or later and macOS 12 or later.

Add the Package ​

In Xcode:

  1. Choose File > Add Package Dependencies...
  2. Enter https://github.com/OddOmens/Oddlytics.
  3. Add the Oddlytics library to your app target.

In a Package.swift:

swift
dependencies: [
    .package(url: "https://github.com/OddOmens/Oddlytics", from: "1.0.0")
]

Then add "Oddlytics" to your target's dependencies.

Configure at Launch ​

Call Analytics.configure once, as early as possible. Events tracked before it are dropped with a warning in the console.

SwiftUI:

swift
import SwiftUI
import Oddlytics

@main
struct MyApp: App {
    init() {
        Analytics.configure(
            endpoint: "https://oddlytics-worker.<your-subdomain>.workers.dev",
            apiKey: "your-auth-key",
            appId: "MyApp",
            debugMode: true    // false for release builds
        )
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

UIKit:

swift
import UIKit
import Oddlytics

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Analytics.configure(
            endpoint: "https://oddlytics-worker.<your-subdomain>.workers.dev",
            apiKey: "your-auth-key",
            appId: "MyApp"
        )
        Analytics.track("app_launched")
        return true
    }
}

Every parameter is described in Configuration.

Track Events ​

swift
Analytics.track("button_tapped")

The event name is any string up to 255 characters. Names like screen_view or purchase_completed work well, and the dashboard can show them as "Screen View" with the Pretty Format Event Names setting.

Metadata ​

Attach string key-value pairs to an event:

swift
Analytics.track("purchase_completed", metadata: [
    "item_id": "prod_123",
    "amount": "9.99",
    "currency": "USD"
])

Values are strings. Convert numbers and booleans yourself ("true", "9.99"). The whole metadata dictionary must stay under 10 KB, or the Worker rejects the batch it's in.

A common pattern is tracking screens:

swift
HomeView()
    .onAppear {
        Analytics.track("screen_view", metadata: ["screen": "Home"])
    }

Groups ​

Give related events a group, and the app's dashboard page gets a section for that group:

swift
Analytics.track("Melatonin", group: "Sleep Aids")
Analytics.track("Magnesium", group: "Sleep Aids", metadata: ["dosage": "5mg"])

The group is stored in the metadata under the key group. If you also pass a group key in metadata, the group: parameter replaces it.

Sessions ​

Each app launch gets a random session ID (a UUID). Every event carries it, and the dashboard counts distinct session IDs as sessions. The session ID doesn't change when the app goes to the background; it changes when the app is launched again or when you call:

swift
Analytics.resetSession()

Call it at moments that should start a new session, such as when someone signs out.

Batching and Retries ​

Events are not sent one at a time. The package keeps a queue in memory and sends it:

  • every batchInterval seconds (default 5),
  • as soon as batchSize events are waiting (default 10),
  • when the app is about to become inactive (iOS only), and
  • whenever you call Analytics.flush().
swift
Analytics.flush()

What happens to a batch depends on the Worker's answer:

ResponseWhat the package does
2xxDone.
4xx other than 429 (for example a wrong API key or an invalid event)Drops the batch.
429, 5xx, or no networkPuts the events back at the front of the queue and tries again with the next send.

Re-queued events stay only while there are fewer than 1,000 events waiting; past that, failed batches are dropped.

WARNING

The queue lives in memory only. Events that haven't been sent when the app is terminated are lost, and nothing is saved to disk for later. A long time offline can also grow the queue past 100 events, which the Worker rejects in one request, so those events are dropped once the network returns.

What Is Collected Automatically ​

Every event sends these fields along with the name and metadata you give it:

FieldValue
app_idThe appId you passed to configure.
platformAlways "iOS", also on macOS.
session_idA random UUID per launch (see Sessions).
user_idOn iOS, the device's identifier for vendor (UIDevice.current.identifierForVendor). On macOS, or if that isn't available, a random UUID saved in UserDefaults under oddlytics_user_id.
device_idOn iOS, the identifier for vendor again. Not sent on macOS. The Worker doesn't store this field.
timestampWhen the event was tracked, in seconds since 1970.

The package doesn't read the device model, OS version, locale, location, advertising identifier or any personal data. It sends nothing until you call track.

TIP

The identifier for vendor is the same for every app from the same developer account on a device. If several of your apps use one Oddlytics server, the dashboard can see that one person uses more than one of them. It resets when all of your apps are removed from the device.

Letting People Opt Out ​

The package has no opt-out switch. If you want one, keep the person's choice in your app and skip Analytics.track calls when they've opted out.

macOS ​

The package builds for macOS 12 or later, with two differences:

  • user_id is a random UUID kept in UserDefaults, and no device_id is sent.
  • There is no automatic send when the app goes to the background. Call Analytics.flush() yourself, for example when the app is about to quit.

API Reference ​

All public API is on the Analytics enum.

configure(endpoint:apiKey:appId:batchSize:batchInterval:debugMode:) ​

swift
static func configure(
    endpoint: String,
    apiKey: String,
    appId: String,
    batchSize: Int = 10,
    batchInterval: TimeInterval = 5.0,
    debugMode: Bool = false
)

Sets up the package, starts the send timer and, on iOS, the send-on-inactive observer. Call it once. With debugMode, it prints the endpoint, session ID, user ID and device ID.

track(_:group:metadata:) ​

swift
static func track(_ eventName: String, group: String? = nil, metadata: [String: String] = [:])

Adds an event to the queue. Does nothing (and prints a warning) if configure hasn't been called.

flush() ​

swift
static func flush()

Sends everything in the queue now.

resetSession() ​

swift
static func resetSession()

Starts a new session ID for the events that follow.

Configuration ​

A public struct holding the same settings as configure. No public method takes it, so use configure instead. Note that its own initializer defaults differ (batchSize: 20, batchInterval: 10.0); configure passes its own defaults of 10 and 5.0.

Odd Omens is a one-person studio building privacy-first software.