iOS SDK
CohorlySwift is a standalone Swift Package for Cohorly, supporting iOS 15+ and macOS 12+. It uses URLSession for transport, batches events, and persists its queue to UserDefaults by default.
Install
CohorlySwift is distributed as a Swift Package - add it from the Cohorly
package repository.
Xcode
File > Add Package Dependencies..., enter the repo URL, pick a version rule, and add the Cohorly library product to your app target.
Package.swift
swiftdependencies: [.package(url: "https://github.com/Gitarcitano/cohorly-swift", from: "0.1.0"),],targets: [.target(name: "YourApp",dependencies: [.product(name: "Cohorly", package: "cohorly-swift")]),]
Initialize
Call Cohorly.initialize once near app launch. It configures and returns
the shared instance (Cohorly.shared). token is optional - omit it if
you don't use per-project tokens.
swiftimport Cohorlylet cohorly = Cohorly.initialize(token: "YOUR_PROJECT_TOKEN")cohorly.register(["app_version": "1.2.0"])cohorly.track("Screen Viewed", properties: ["screen": "Home"])cohorly.identify("user_123")cohorly.people.set(["plan": "pro"])// Manually flush (also happens automatically every 5s or every 20 events).await cohorly.flush()
API
| Member | Notes |
|---|---|
Cohorly.initialize(apiHost:token:...) | Configures and returns Cohorly.shared. Also accepts transport, storage, flushInterval, flushAt. |
track(_:properties:) | Queue an event. |
identify(_:) | Set the distinct id. On the first call after being anonymous, also posts /alias linking the previous anonymous id (best effort, not retried). No-op if the id is unchanged. |
reset() | Clears identity, super properties, and un-flushed events. Call this on logout - see Identity. |
register(_:) / unregister(_:) | Super properties merged into every event. |
people.set / setOnce / increment / unset / deleteUser | Profile updates sent to /engage. |
flush() async | Manually flush; resolves once any auto-triggered flush completes. |
currentDistinctId: String | Current distinct id. |
getFeatureFlag(_:fallback:) / isFeatureEnabled(_:fallback:) | Read a flag's variant/enabled value; never hits the network. |
getFeatureFlagDetails(_:fallback:) | The value plus source (network / persistence / fallback) and cache metadata. |
reloadFeatureFlags() async / onFeatureFlags(_:) | Re-evaluate flags / subscribe to updates. |
Feature flags
Flags are evaluated on initialize (opt out with loadFeatureFlags: false),
on identify() and on reset(), and cached in storage scoped by distinct id;
reads never hit the network.
flagPersistence (default .persistenceUntilNetworkSuccess) controls how
reads resolve against the persisted cache:
| Policy | Behavior |
|---|---|
.networkOnly | Never reads nor writes the cache on the serving path (identity changes still remove it - that is hygiene, not serving). Flags come only from network responses this session; a failed evaluation serves the fallback. |
.networkFirst | Prefers the network, but reads are synchronous, so a valid cache (inside the TTL) is served immediately like .persistenceUntilNetworkSuccess. Strict about freshness: a cache past the TTL is dropped, never served. |
.persistenceUntilNetworkSuccess | Serves a valid cache immediately, so startup doesn't flicker; the first network success takes over and re-persists. A failed evaluation keeps the cache serving. Trades freshness for offline availability: an expired cache still serves while inside Cohorly.flagCacheMaxStale (7 days). |
The cache carries a stored_at timestamp and expires after flagCacheTTL
(default 24 hours - Cohorly.defaultFlagCacheTTL, the Mixpanel reference
default shared by every Cohorly SDK). A legacy cache with no timestamp cannot
be dated, so it is served indefinitely rather than expired - the
graceful-upgrade path preserving the pre-TTL behavior - until the next
successful reload rewrites it in the stamped format. identify() and
reset() clear the previous identity's flags before fetching the new
identity's, so values never leak across users - onFeatureFlags listeners
are notified of the clear with an empty map, then again when the new
identity's flags land.
swiftlet variant = cohorly.getFeatureFlag("checkout-copy", fallback: "control")let details = cohorly.getFeatureFlagDetails("checkout-copy")// details.source: .network | .persistence | .fallback// details.enabled / details.payload; a fallback result reports// enabled = truthiness of the fallback and payload = nil// details.persistedAt / details.ttlMs when source == .persistence
A read that resolves to the fallback reports no exposure. Reads of known
flags emit $feature_flag_called once per (key, value) per identity, stamped
with $feature_flag_source ("network" or "persistence") plus
$feature_flag_persisted_at and $feature_flag_ttl_ms for persistence reads;
the same value served first from persistence and then from the network is
reported once.
Auto-captured properties
Every event includes:
$lib="ios"$os,$os_version,$model$screen_width/$screen_height(via UIKit where available - skipped on macOS/watchOS)
Events and profile updates are queued in memory and persisted to storage
(UserDefaults by default) so nothing is lost across relaunch. All mutable
state is confined to a private serial DispatchQueue, so the client is
safe to call from any thread.