Anonymous by default
From first launch the SDK mints a stable anonymous id (anon_ followed by 21 random characters) and stores it on the device. Everything works before anyone logs in: events queue and send, sessions are tracked, and the push subscription registers under that id. Nothing about the SDK requires a logged-in user.
When you later call identify, the device switches to the real id and its history comes with it, so the events someone generated during onboarding are still there once they sign up.
Identify
identify(_ externalId: String, email: String? = nil, identityHash: String? = nil, attributes: [String: JSONValue]? = nil). It returns immediately and does its work on a serial queue, so identity calls never race each other.
Three things happen. The subscriber is created or updated with the id, email and attributes you passed, along with the device context. If the identity actually changed, the queued events are flushed and the push subscription is re-registered under the new id.
Call it at login, and on every launch where the user is already signed in. It is an idempotent upsert on your own id, so calling it repeatedly costs nothing.
externalId is your own user id. It is the same id your backend addresses in PUT /v1/subscribers/:externalId and in to on a send, which is what keeps the app and your server talking about the same person. See Subscribers.Attributes
[String: JSONValue], and JSONValue is expressible by string, integer, float, boolean, nil, array and dictionary literals, so ordinary Swift literals work without any wrapping.
The server merges what the device sends into what is already on the subscriber. Adding and changing keys from the app is safe, and nothing your backend set is wiped by a call from the device.
The SDK maintains one attribute of its own, the device’s notification permission, refreshed on launch and on every change. Segments read it like any other attribute.
Logout
Identity verification
A client key is embedded in your binary, so on its own it lets any caller claim any external id. Verification closes that: your backend computes an HMAC of the user’s id under the tenant’s identity secret and hands the result to the app at login.identify and the subscriber is marked verified. The SDK stores it and attaches it to every call it makes from then on, including events, subscription updates and preference changes, for as long as the user stays identified. logout discards it along with the id.