Shipeasy
ReferenceSwift

Advanced

Attribute names listed in privateAttributes are usable for targeting but are stripped from every outbound track() payload — the server evaluates locally, so…

Generated from the SDK's own /docs/ — also served raw at https://shipeasy-ai.github.io/sdk-swift/pages/advanced.md.

Private attributes

Attribute names listed in privateAttributes are usable for targeting but are stripped from every outbound track() payload — the server evaluates locally, so private attrs never leave for evaluation; the only egress is /collect, where the listed keys are removed.

configure(apiKey: serverKey, privateAttributes: ["email", "ssn"])

bucketBy — custom bucketing identifier

Experiments and gates bucket on user_id (falling back to anonymous_id) by default. When the resource sets a bucketBy attribute (e.g. company_id), evaluation buckets on that attribute instead — so every user in a company gets the same variant. This is driven by the resource config (no SDK call needed); just make sure the named attribute is present in the bound user map:

// if the experiment's bucketBy is "company_id", include it in the user map
let client = try Client(["user_id": "u_123", "company_id": "acme"])
let r = await client.getExperiment("team_dashboard", defaultParams: nil)

Sticky bucketing

Pass a StickyBucketStore to configure(...) to lock a unit to its first-assigned variant. Once enrolled, changing the allocation % or weights won't re-bucket the unit — rotating the experiment salt is the reshuffle lever. Absent ⇒ deterministic (fully backward compatible).

let store = InMemoryStickyBucketStore()
configure(apiKey: serverKey, stickyStore: store)

StickyBucketStore is a protocol (get / set over StickyEntry), so you can back it with your own persistence (Redis, a DB, etc.). InMemoryStickyBucketStore is provided for tests and single-process use.

Anonymous visitors

For logged-out traffic you need a stable unit so a fractional rollout buckets the same on the server and in the browser. AnonId provides the cross-SDK __se_anon_id cookie primitives (this SDK is framework-agnostic, so it ships helpers rather than a middleware). In a server handler, resolve the id off the request Cookie header, bind it to the Client, and echo it back on the response:

let resolved = AnonId.resolve(cookieHeader: req.headers["cookie"].first)
let client = try Client(["anonymous_id": resolved.id])
let on = await client.getFlag("new_checkout")
if resolved.minted {
    res.headers.add(name: "set-cookie", value: AnonId.setCookieHeader(resolved.id, secure: true))
}

The cookie is non-HttpOnly by design so the browser SDK buckets identically; a request with no unit still resolves a fully-rolled (100%) gate as on. Cookie name + format are a cross-SDK contract (see 18-identity-bucketing.md).

Manual exposure

logExposure is on the bound Client — record an experiment exposure explicitly (rather than relying on getExperiment to log it). The unit is derived from the bound user; it's a no-op when the user has no unit or isn't enrolled:

let client = try Client(["user_id": "u_123"])
await client.logExposure("checkout_button")

Change listeners

Register a listener that fires after a fetch applies new data (HTTP 200, not 304) using the package-level onChange helper. It requires configure(..., poll: true) — no poll runs otherwise. Listeners never fire in testing/offline mode. onChange returns an unsubscribe closure:

let unsubscribe = await onChange {
    print("flag/experiment data refreshed")
}
// later…
unsubscribe()

SSR bootstrap

See i18n for the package-level bootstrapScriptTag / i18nScriptTag helpers used to hydrate the browser SDK on first paint with the same evaluated flags the server saw.

Was this page helpful?
✎ Edit this page

On this page