AI Integration Quick Reference
AI Integration Quick Reference
The two-phase contract
Each read fires up to two callbacks, always in this order:1
Cached phase
The SDK delivers a page from the local cache first. It fires even when the page is empty — an empty cached page is indistinguishable from a cache miss, by design. Render it right away.
2
Server phase
After a successful server response, the SDK delivers the server’s copy for the same slice and writes it through to the cache before the callback. Reconcile your displayed list against it.
- Offline, the read ends with an error. After the cached page, a read that gets no answer because the device is offline settles with
onError, codeERROR_INTERNET_UNAVAILABLE. A real server error reachesonErrorunchanged. The call always settles, as long as you wait for each fetch to return before starting the next one on the same request object. - The server phase never fires offline. It fires only on a genuine server response. Never treat a cached page as “end of history”; an empty cached page only means the end of the cached window.
With offline support off, these overloads behave like the classic call: the cached phase never fires, and the server result arrives in
onServer… instead of onSuccess. Nothing changes until you enable offline (see Enable Offline Support). Requires SDK 4.2.0 or later.Run one fetch at a time on a request object. A second fetch while the previous one is still in flight fires no cached phase. On
MessagesRequest, ThreadsRequest and ReactionsRequest it fails with ERROR_REQUEST_IN_PROGRESS. On the user, group, group-member and banned-member lists it returns an empty server result. Wait for the first fetch to finish, or create a new request.Read messages
MessagesRequest supports both directions. fetchPrevious pages toward older messages; fetchNext pages toward newer ones. Both have a two-phase overload.
fetchPrevious(onSuccess:onError:) and fetchNext(onSuccess:onError:) are unchanged and never deliver a cached result.
Read conversations
ConversationRequest pages in one direction with fetchNext.
Read the other lists
Every remaining list read gains the same two-phase overload, withonCached… / onServer… closures of the matching type plus onError.
The call shape is identical to the conversations example above — swap the request and the closure labels.
For
ReactionsRequest, fetchPrevious and fetchNext are recorded and replayed independently — each direction keeps its own cached pages. Users, groups, members, banned members, reactions, and the notification feed are served from a per-request snapshot rather than a shared store: the cached phase replays what the server returned online for the same builder settings, so a list you never fetched online has an empty cached page. Per-builder details are in the Configuration & API Reference.BlockedUserRequest is the exception: its cached page is the SDK’s own blocked-users list, which the SDK refreshes on login and on each catch-up sync and updates when you call blockUsers or unblockUsers. The first cached page holds the whole list and later pages are empty.Read a single item
getConversation and getMessageDetails each add a two-phase overload. Unlike the list reads, the cached callback fires only when the item is cached — never with an empty or nil object. A cache miss goes straight to the server phase online, or straight to onError offline.
getConversation takes a ConversationDetailsListener; getMessageDetails takes a MessageDetailsListener.
The SDK holds the
ConversationDetailsListener or MessageDetailsListener until the read ends, so you do not need to keep your own reference for a single lookup.Merge the two phases
The server phase replaces the same slice — it is a reconcile, not a second batch. Render the cached page straight away, then on the server phase diff it into your displayed list keyed by the item’s stable id (message id, conversation id, uid, guid). Do not append the server page to the cached one.When the cached page is empty
The cache answers a read only if every filter on it is locally computable. A single server-only predicate makes the entire cached phase return an empty list — with no error and no warning — while the server phase still answers in full. The most common server-only predicate isset(searchKeyword:), which blanks the cached phase on every request. Other examples on MessagesRequest are has(links:), updatesOnly(onlyUpdates:) and setInteractionGoalCompletedOnly(_:). Reading one sender’s messages in a group (set(uid:) together with set(guid:)) is also answered by the server only. On the entity lists, a keyword search or a presence filter (UsersRequest.set(status:), GroupMembersRequest.set(status:)) is never snapshotted, so it has no cached phase either.