iOS SDK reference
Версия: ShopStoryPlayerSDK 0.2.0-preview.2. Описание сверено с исходниками immutable tag. Установка и собираемый starter: native iOS. Совместимость и приёмка: версии и выпуск.
Конфигурация
try ShopStorySDK.shared.configure(
applicationId: applicationId,
hostAppVersion: hostAppVersion,
isEnabled: true,
followMePlayerEnabled: true,
manualMinimizeEnabled: true
)
| Параметр | Тип / default | Назначение |
|---|---|---|
applicationId | String, обязательный | Выданный проект. SDK обрезает пробелы и приводит значение к нижнему регистру |
hostAppVersion | String? = nil | Версия приложения для диагностики |
isEnabled | Bool = true | Локальное разрешение SDK UI; remote policy может дополнительно его запретить |
followMePlayerEnabled | Bool = true | Локальное разрешение Follow Me |
manualMinimizeEnabled | Bool = true | Разрешение ручного скрытия floating player к краю |
Некорректный applicationId вызывает ShopStorySDKConfigurationError.invalidApplicationId и отключает SDK surfaces без завершения host app. Успешный configure начинает cache-aware обновление policy; он не означает готовность видео. Обновление policy, её cache и транспорт принадлежат SDK.
Product lookup
let request: ShopStoryProductStreamStateRequest =
ShopStorySDK.shared.loadProductStreamState(
feedProductId: feedProductId
) { result in
// Result<ShopStoryProductStreamState, ShopStoryProductStreamStateError>
handleProductState(result)
}
// При уходе с PDP или замене товара:
request.cancel()
handleProductState — функция host app. Completion доставляется асинхронно на main actor. Храните request для текущего товара. cancel() отменяет незавершённый запрос и подавляет его completion; ожидать callback отмены не нужно.
| Результат | Действие host app |
|---|---|
.success(.active(entrypoint)) | Показать SDK entrypoint для текущего товара |
.success(.unavailable(reason)) | Убрать SDK-блок без пустого placeholder |
.failure(error) | Убрать SDK-блок, сохранить PDP рабочей и записать typed error в диагностику |
ShopStoryProductStreamUnavailableReason содержит noActiveStream, scheduled, archived, disabled, notEligible. Это варианты типа SDK. Опубликованный HTTP-контракт сейчас возвращает no_active_stream для недоступного видео; наличие остальных enum cases не обещает их доступность в Public API.
Entrypoint
ShopStoryProductDetailEntrypoint возвращается SDK; приложение не собирает его из URL.
| Поле | Тип | Назначение |
|---|---|---|
streamId | String | Идентификатор эфира или записи |
feedProductId | String | Товарный ID клиента, с сохранением ведущих нулей |
productId | String? | Внутренний ID ShopStory для диагностики |
previewURL, posterURL | URL? | Медиа preview и poster, если доступны |
startTime | Int? | Стартовая позиция из состояния товара |
mediaOrientation | ShopStoryMediaOrientation | portrait, landscape, square, unknown |
Адрес player внутри entrypoint не является публичным полем. Передавайте объект в coordinator.load(entrypoint:) целиком.
Ошибки lookup
ShopStoryProductStreamStateError | Проверка |
|---|---|
sdkNotConfigured | configure успешно выполнен до lookup |
runtimePolicyDisabled | Проект и текущая runtime policy разрешают SDK UI |
invalidProductIdentifier | Передан непустой строковый feedProductId из фида |
networkFailure | Доступна сеть; повтор разрешён только пока товар остаётся текущим |
invalidResponse, decodingFailed | Версия SDK соответствует контракту; приложите очищенную диагностику в поддержку |
requestSuperseded | Конфигурация SDK изменилась до завершения lookup; используйте актуальный запрос |
Не заменяйте техническую ошибку утверждением «у товара нет видео»: для этого есть успешный .unavailable.
Player coordinator
ShopStoryPlayerCoordinator и его delegate работают на @MainActor. Integration root удерживает один coordinator на активную playback session. delegate — weak; host app удерживает его отдельно.
| API | Поведение |
|---|---|
init(delegate:) | Создаёт coordinator; delegate необязателен |
playerView: UIView | Opaque native view. Внутренний WebView не передаётся host app |
attach(to: UIView) | Монтирует player view в контейнер с constraints по его границам |
load(entrypoint:) | Загружает разрешённый товарный entrypoint |
load(trustedPlayerURL:) | Для native списка эфиров: только HTTPS URL, полученный напрямую из ShopStory API |
detach() | Снимает view с контейнера без завершения playback session |
pausePlayback(reason:) | Приостанавливает media; причины: appBackgrounded, hostRequested, surfaceBlocked, teardown |
resumePlayback() | Возобновляет media с учётом текущего состояния coordinator |
teardown() | Завершает session и освобождает player. Для нового воспроизведения создайте новый coordinator |
При full-screen → PDP → Follow Me переносите тот же playerView через detach / attach. Повторный load не нужен. Полный пример owner: Full-screen player и Follow Me.
Product actions
Delegate получает ShopStoryProductAction через shopStoryPlayerCoordinator(_:didReceive:). kind — openProduct или addProduct. payload содержит необязательные name, feedProductId, feedProductGroupId, url, vendorCode, source.
Проверьте payload.feedProductId и разрешите его через native catalogue. url — справочное поле, оно не должно управлять native navigation. В delegate нет completion: действие передаётся в одну сторону. Приложение самостоятельно открывает PDP или выполняет Cart API и показывает подтверждённый результат в своём UI.
Lifecycle diagnostics
Delegate получает ShopStoryPlayerLifecycleEvent через shopStoryPlayerCoordinator(_:didUpdate:).
| Event | Смысл |
|---|---|
pageLoaded | Завершилась навигация страницы player. Это не подтверждение первого video frame |
closeRequested | Player запросил закрытие; host завершает session |
backgroundPaused, foregroundRestored | Изменился app lifecycle; перед показом Follow Me снова проверьте surface и policy |
navigationFailed(String) | Ошибка загрузки; строка предназначена для очищенной диагностики |
navigationBlocked(String), bridgeMessageBlocked(String) | SDK отклонил navigation или bridge message; не обходите проверку |
runtimePolicyDisabled | SDK UI отключён runtime policy; освободите host container |
productActionDecodeFailed(kind) | Product action не удалось разобрать; не выполняйте commerce-действие |
Состояния coordinator не заменяют commerce events. Публичного native события «оплаченный заказ» в этом контракте нет.
Surface и floating player
После изменения видимого экрана вызывайте setCurrentSurface(_:). Доступные стандартные значения: home, liveList, catalog, search, productDetail, cart, checkout, payment, auth, profile, unknown. Для дополнительной поверхности используйте ShopStorySurface(rawValue:) и согласованную policy. Неизвестный экран не следует считать разрешённым.
shouldOpenPlayer() проверяет разрешение player UI. shouldShowFollowMePlayer(on:) проверяет заданную или текущую поверхность. Геометрию возвращают followMeGeometry(for:) и overloads с mediaOrientation.
После configure создайте и удерживайте makeFloatingPlayerController(). Его show(using:) применяет геометрию; hide(to:) скрывает к краю; restore() восстанавливает; suppress() убирает UI по policy; close() закрывает floating UI. Закрытие controller не заменяет coordinator.teardown() при завершении session.
SwiftUI host может использовать ShopStoryProductDetailPreview, ShopStoryProductDetailEntrypointView и ShopStoryManagedFloatingPlayerContainer. Containers и route owner остаются частью приложения; ориентируйтесь на sample выбранной версии.
Обновление с preview.1
В товарной интеграции замените старый load(playerURL:) передачей entrypoint целиком:
coordinator.load(entrypoint: entrypoint)
Для списка эфиров старый load(playerURL:) заменён на load(trustedPlayerURL:). Не передавайте в него URL из product action или пользовательского ввода. Затем обновите lockfile и повторите приёмку.
Разбор проблем: диагностика Mobile SDK. Flutter reference описывает отдельный adapter contract.