replaceFeedItems replaces a trailing range of a custom feed queue. Items up to and including the boundary are preserved; everything after it is replaced.
Use it to revise upcoming content while the feed is in use — to apply updated recommendations, to reflect a user action, or to correct a ranking decision after playback has begun.
setFeedItems replaces the entire queue and appendFeedItems adds to the end. Use replaceFeedItems when the viewer’s position must be preserved.
This operation requires a custom feed. Set feedSource to custom first — see Configuration and Custom feed input.
Boundary resolution
Every call takes a boundary: the last item to keep.
All three are resolved when the replacement is applied, against the queue and playhead as they stand at that moment. The difference is what each one is anchored to.
unseen and afterCount are relative: they are recomputed from the live playhead, so they follow the viewer. afterItem is absolute: you name an item before the call, and the SDK locates it later.
keepUpcoming counts entries in the queue, which is what itemIds lists.
afterItem also accepts an optional untilId to bound the end of the range, replacing a window rather than the entire tail.
No boundary can replace the item on screen or anything before it: a replacement always keeps the queue up to at least the viewer’s current position.
unseen keeps more than that. It anchors to the deepest position the viewer has ever reached, and that marker also moves on programmatic activation, so startAt and scrollToItem advance it. If the viewer then scrolls back, unseen still preserves everything up to that high-water mark, while afterCount and afterItem ignore that marker — they are bounded only by the current position, so they can replace the region between the playhead and the mark.Usage
- iOS
- Android
- React Native
RecommendationRefresh.swift
boundary defaults to .unseen. The remaining cases are .afterCount(keepUpcoming:) and .afterItem(afterId:untilId:). The method is main-actor bound.items array is legal, and truncates the queue at the boundary.
Give each item a stable id. Items are matched by id alone, so reusing an id for materially different content will not rebind the cell.
Queue state
The SDK exposes a read-back of the queue as it currently stands: the ordered item ids, the viewer’s position, and the deepest item reached. Use it to compute a boundary, or to reconcile the outcome of a call.- iOS
- Android
- React Native
Result fields
Use
removedIds, not droppedIds, to prune host-side state keyed by item id. removedIds lists items that were in the queue and are now gone; droppedIds lists incoming items that never entered it.
queueLength and remainingCount are both 0 on a rejection raised before the feed was consulted. Treat 0/0 on a rejection as unknown rather than as an empty queue.
A rejection never changes the queue. An applied replacement can still insert fewer items than you supplied.
Dropped items
insertedCount can be lower than the number of items you supplied. An item is dropped when:
- the viewer dismissed it earlier in this feed session, which blocks both its
idand its playback id — re-sending the same video under a newidis still dropped - its
idduplicates a kept item, or an earlier item in the same batch — the first occurrence wins - its playback id duplicates that of a kept item, or of an earlier item in the same batch
id where set, by playback id otherwise, and by the carousel id for a carousel. An item that the React Native bridge could not parse at all appears as __sk_unparsable_<index>, keyed to its position in the batch you sent.
droppedIds is empty on any rejection raised before the feed was consulted, including invalid_items, so an empty list does not by itself mean nothing was refused.
Rejection reasons
superseded warrants explicit handling. It indicates that the queue was replaced between the call and the apply, by a pull-to-refresh, a preload reseed, or your own setFeedItems. The replacement was discarded rather than applied to a queue that no longer existed. Re-read the queue state to determine whether the replacement remains appropriate.
Retry behaviour differs by reason. A feed_not_ready from an exhausted settle wait, and a boundary_behind_current from an afterItem boundary, are both worth retrying. A boundary_behind_current from afterCount means keepUpcoming was negative, which is a static bug — do not retry it.