Skip to main content

API reference

The OptoLink Swift package exposes one entry point, OptoLink, plus its config, data, and error types, all in the OptoLink module (import OptoLink). Setup and initialization live in install & initialize; failure behavior in errors & retries.

The surface is async/await throughout: initialize is async, the telemetry trio is async throws, and links is an AsyncStream. There is no builder, no delegate, and no callback API.

// ---------- entry point ----------
public final class OptoLink: Sendable {
/** Idempotent, concurrency-safe (a second caller awaits the same init).
* Awaits the once-per-install deferred match. Network failure during
* match: logged, NOT thrown (never blocks startup; re-attempt via
* resolveDeferredLink). */
@discardableResult
public static func initialize(config: OptoLinkConfig) async -> OptoLink
public static func instance() -> OptoLink // preconditionFailure if not initialized
public let deviceId: String // UUID v4, persisted
public let config: OptoLinkConfig

// ---------- links ----------
/** Fresh AsyncStream per access: first yields the buffered last link
* (replay-last), then forwards future emissions. Never errors,
* never finishes — failures log via config.logger. */
public var links: AsyncStream<OptoLinkData> { get }
/** THE single ingestion point. Safe to call before initialize —
* the latest URL parks and is processed once initialization
* finishes (deferred link first, parked URL second). */
public func handle(url: URL)
/** Manual deferred re-attempt, regardless of the first-launch
* flag. nil = no match; throws on network failure. */
public func resolveDeferredLink() async throws -> OptoLinkData?

// ---------- identity & events (throw on failure; success implicit) ----------
public func setIdentity(externalId: String) async throws
public func clearIdentity() async throws
public func trackEvent(name: String,
properties: [String: String] = [:]) async throws

public func dispose() // test/state reset
}

// ---------- data ----------
public struct OptoLinkData: Sendable, Codable, Equatable {
public let linkId: String? // nil for raw direct links
public let path: String
public let params: [String: String]? // flat string → string
public let matchMethod: MatchMethod
public let confidence: MatchConfidence
public let isDeferred: Bool
public let score: Int? // backend matchScore; only FINGERPRINT_SCORED carries it
}
public enum MatchMethod: String, Sendable, Codable {
case clipboard = "CLIPBOARD"
case installReferrer = "INSTALL_REFERRER" // never emitted on iOS
case fingerprintExact = "FINGERPRINT_EXACT"
case fingerprintScored = "FINGERPRINT_SCORED"
case ipFuzzy = "IP_FUZZY"
case direct = "DIRECT"
case none = "NONE" // unknown wire values decode here
}
public enum MatchConfidence: String, Sendable, Codable {
case exact = "EXACT", high = "HIGH", medium = "MEDIUM", low = "LOW"
}

// ---------- errors ----------
public protocol OptoLinkError: Error, Sendable {}

public struct OptoLinkApiError: OptoLinkError, LocalizedError {
public let status: Int
public let requestId: String? // present when the error envelope carries one
public let details: [OptoLinkErrorDetail]? // 400 validation only
public let retryAfterSeconds: Int? // regexed from the 429 message
public let raw: String? // full body text verbatim
}
public struct OptoLinkConnectionError: OptoLinkError {
public let underlying: URLError
}
public struct OptoLinkErrorDetail: Sendable, Codable, Equatable {
public let message: String
public let field: String?
}

// ---------- config ----------
public struct OptoLinkConfig: Sendable {
public var apiKey: String // CLIENT-tier opl_sdk_… (no env fallback)
public var orgKey: String
public var baseUrl: URL // URL, not String: malformed input unrepresentable
public var timeout: TimeInterval = 10 // seconds per HTTP attempt
public var maxRetries: Int = 2
public var clipboardEnabled: Bool = true // opt-out of the deferred match's clipboard leg
public var logger: (any OptoLinkLogger)? = nil
}
public protocol OptoLinkLogger: Sendable {
func log(level: OptoLinkLogLevel, _ message: String, error: (any Error)?)
}
public enum OptoLinkLogLevel: Sendable {
case info, warning, error // info carries the SDK's decision trail
}
SourcematchMethodisDeferred
Verified Universal Link open, resolution succeeded.directfalse
Custom-scheme open, non-matching org, or backend unreachable at resolve time.direct (linkId: nil)false
First-launch clipboard match.clipboardtrue
First-launch fingerprint match.fingerprintExact / .fingerprintScoredtrue
First-launch IP match.ipFuzzytrue

confidence is backend-reported and applies to deferred matches; direct opens carry .exact. score is present only on .fingerprintScored. .installReferrer exists in the enum but is never emitted on iOS; the App Store carries no referrer.

To hold routing until onboarding finishes, subscribe to links only when ready; replay-last delivers the held link then. The SDK never opens anything on the app's behalf.

Release history: changelog.