これはiOSインテグレーター向けの最短のオンボーディングパスです。初期化から、画面への映像表示、制御指示の送信、体験の終了までをカバーします。完全なメソッドシグネチャ、フィールド、エラーコードについては、iOS SDK APIリファレンスを参照してください。
完全なメソッドシグネチャ、フィールド、エラーコードについては、iOS SDK APIリファレンス を参照してください。
0. 構築内容
「ワールドに入る → リアルタイムビデオ体験 → インタラクション → 終了」という最小限のエンドツーエンドループです。ランタイムのエントリーポイントは、グローバルな HappyOysterEngine.shared と、それが作成する1回限りのセッションハンドル OysterTravel です。
OysterStream.register()
HappyOysterEngine.shared: initialize → updateToken → createTravel
OysterTravel: videoView / events → start → sendInstruct / sendCommand → end
1. 要件
項目 | 要件 |
|---|---|
最小 OS | iOS 15.0+ |
言語 | Swift( |
スレッディング | パブリックAPIは |
インポート |
|
注記リアルタイム通信(AliRTC)はSDK内にカプセル化されており、インテグレーターがRTC APIを直接操作することはありません。
1.1 CocoaPods を使用して SDK を追加
SDKはCocoaPodsサブスペックを通じてコンパイル済みバイナリ(xcframework)として配布されており、パブリックCocoaPods Trunkに公開されています。バージョンで直接参照でき、ローカルのpodspecファイルは不要です。
# HappyOysterSDK / AliVCSDK_ARTC are both published on the public CocoaPods source.
source 'https://cdn.cocoapods.org/'
platform :ios, '15.0'
use_frameworks!
target 'YourApp' do
# Aggregate entry point (Core + World); `import HappyOysterSDK` and you're set.
pod 'HappyOysterSDK'
# Optional default UI components (video view, control HUD).
pod 'HappyOysterSDK/UI'
# Video stream + AliRTC engine adapter (already depends on Stream; no need to declare it separately).
pod 'HappyOysterSDK/StreamAliRTC'
# RTC vendor binary: weak-linked by the SDK, not redistributed with it — bring your own (public CocoaPods source).
pod 'AliVCSDK_ARTC', '7.11.0'
end
次に pod install を実行し、生成された .xcworkspace を開きます(.xcodeproj ではありません)。
注記HappyOysterSDK/StreamAliRTC を導入する場合、AliVCSDK_ARTC is required です。これが欠落している場合、SDKはサイレントにLoopbackにフォールバックします。接続は成功し running に達しますが、shows a black screen with no error となります。
2.2種類の認証情報(落とし穴を避けるためにまず理解してください)
SDKは not 自身で認証情報を取得または更新しません。すべての認証情報を注入する必要があります:
認証情報 | 取得元 | 用途 | 注入方法 |
|---|---|---|---|
HTTP 認証トークン | アプリが 独自のバックエンド から取得します | ゲートウェイへのSDK呼び出し用の一般的な認証(長期間有効ですが、更新が必要です) |
|
ワンタイムチケット | Travel クレデンシャル API を介して交換した後、サーバーによって発行されます | 単一の体験に使用され、使用後すぐに無効化されます |
|
注記これら2つは not interchangeable です。updateToken は一般的な認証用であり、ticket は1回限りの参加用認証情報です。AK/署名鍵はサーバー上にのみ存在し、クライアントに公開されることはありません。
3. 統合手順
ライフサイクル:ストリームエンジンの登録 → 初期化 → トークンの注入 → セッションの作成 → ビデオのアタッチ+イベントのサブスクライブ → 開始 → インタラクション → 終了。
ステップ 1:ストリームエンジンを登録する
ビデオレンダリングの単一エントリーポイントです。アプリ起動時に1回呼び出してください。繰り返し呼び出しても安全です。
import HappyOysterSDK
import HappyOysterStream
OysterStream.register()
ステップ 2:SDK の初期化
他のAPIを呼び出す前に、一度初期化する必要があります。To switch gateway or model, just call it again — アイドル状態であれば最新の構成が有効になり、注入されたトークンは保持されます。トラベルの実行中のみ呼び出しが無視されるため、その場合は先に end() を実行してください。
let engine = HappyOysterEngine.shared
engine.initialize(config: OysterConfig(
apiHost: "[workspace-id].[region].maas.aliyuncs.com", // Model Studio gateway host
model: "happyoyster-1.0-adventure" // Versioned model name enabled for your account, required
))
// Optional: override the log level / signalling callback timeout
// OysterConfig(apiHost: "…", model: "…", logLevel: .debug, callbackTimeoutMs: 30_000)
// Both apiHost and model are required with no default: Happy Oyster is split into
// per-mode sub-models. See the Happy Oyster model documentation for available names
// and versions; they must match the account and region of your token.
ステップ 3:HTTP認証トークンを注入する(プッシュ)
独自のバックエンドから一時的なModel Studio API Keyを取得して注入してください。有効期限が切れた後は再注入が必要です。
let token = await fetchTokenFromYourBackend()
engine.updateToken(token)
ステップ 4:セッションハンドルの作成
使い捨ての ticket から OysterTravel を作成します。Nothing is connected yet が、ビデオビューはすでに利用可能です。
let travel = try engine.createTravel(ticket: ticket)
ステップ 5:ビデオビューのアタッチとイベントのサブスクライブ
SDKがビューを提供し、ホスト側がそれを配置します。初期のステータス変更を見逃さないよう、before start() サブスクライブしてください。
containerView.addSubview(travel.videoView) // SwiftUI: OysterVideoView(travel:)
let eventTask = Task {
for await event in travel.events {
switch event {
case .statusChanged(let status): render(status) // running / paused / ended / failed…
case .error(let error): handle(error) // error.code / error.kind — see the API Reference
}
}
}
ステップ 6:体験の開始
接続して再生を開始します。その後、SDKが automatically リアルタイム接続とステータスポーリングを維持し、events を通じてステータスを通知します。
let data = try await travel.start()
// data.encryptedTravelId —— identifier of this experience, used for diagnostics / server reconciliation
// data.encryptedWorldId —— identifier of the world you entered
// data.mode —— adventure / directing / acting, determines the interaction UI
// data.aspectRatio —— "9:16" / "16:9"; set for acting only, use it to pick the player orientation
注記start(maxExperienceTimeSec:) は adventure にのみ適用されます。directing および acting はこのパラメータを無視します。
このトラベルにおけるワールドのモードは、ステップ 2 で initialize に渡した model と一致している必要があります。モードごとのモデル(happyoyster-1.0-adventure / -directing / -acting)を使用する場合、各モデルは独自のゲートウェイアプリケーションルートとなるため、1回の initialize で対応できるのは単一モードのワールドのみです。ticket はそのワールドのモードのルート配下でサーバーによって発行されたものであり、start() はそれを現在設定されている model のルートに送信します。不一致はこのステップで失敗します。
異なるモードのワールドに入る前に、一致するモデルを指定して再度 initialize() を呼び出してください。cleanup() や再 updateToken() は不要です:
HappyOysterEngine.shared.initialize(config: OysterConfig(
apiHost: "…", model: "happyoyster-1.0-acting")) // the model for this world's mode
アイドル状態(トラベルが実行中でない場合)は最新の構成が有効になり、注入されたトークンは保持されます。SDKは事前にモデルとワールドの一致を検証できません。mode はこのステップのレスポンス(data.mode)で初めて届きます。呼び出し前、SDKが保持するのは不透明な ticket とモデル名のみです。単一モードのみを提供するアプリは影響を受けず、初期化は1回で済みます。
注記トラベルの実行中に initialize() を繰り返し呼び出すと、警告と共に無視されます。先に end() を実行してください。
ステップ 7:リアルタイムインタラクション(モードに応じて選択)
if data.mode == .adventure {
// Adventure: direction/view/action control (fire-and-forget; never throws, failures come via events)
travel.sendCommand(OysterAdventureCommand(translation: .front, interaction: .jump))
} else {
// Directing and acting: text instructions
_ = try await travel.sendInstruct(content: "Pan to the castle; the hero starts running")
}
sendCommand は内部で 42ms(24FPS)の最新優先サイクルでスロットリングされるため、ホスト側は高頻度で呼び出すことができます:
- ジャンプ、攻撃、しゃがみ、ダッシュなどのワンショットアクションは1回だけ送信されます。
- 移動と視点回転は、入力中は継続的に送信され、離した時点で呼び出しを停止するだけです。
- リリース時に
noneを送信したりflushCommands()を呼び出したりする必要はありません。SDKは自身で停止コマンドを生成することはなく、リアルタイムチャネルが静かになるとサーバー側でアクションが終了します。
ステップ 8:一時停止/再開/巻き戻し(モード依存)
_ = try await travel.pause() // supported by directing and acting
_ = try await travel.resume()
_ = try await travel.rewind(toSec: 10) // directing only
注記adventureはこれらの機能をnoneもサポートしていません。actingは一時停止と再開をサポートしますが、巻き戻しはサポートしません。そのため、これらのモードでは巻き戻しのエントリーポイントを非表示にしてください。不一致の呼び出しは、SDKによってローカルで拒否されます(103003 / 103002)。
ステップ 9:体験の終了
接続を解除し、ポーリングを停止して、すべてのセッションリソースを解放します。ticket は消費されます。冪等性があります — every exit path must funnel into it。
_ = try? await travel.end()
eventTask.cancel()
注記SDKの破棄時やゲートウェイの切り替え時には await engine.cleanup() を呼び出してください。
4. 完全な例(SwiftUI)
import SwiftUI
import HappyOysterSDK
import HappyOysterStream
@main
struct MyApp: App {
init() {
OysterStream.register() // Step 1
HappyOysterEngine.shared.initialize(config: OysterConfig( // Step 2
apiHost: "[workspace-id].[region].maas.aliyuncs.com",
model: "happyoyster-1.0-adventure"
))
}
var body: some Scene { WindowGroup { TravelScreen() } }
}
struct TravelScreen: View {
@State private var travel: OysterTravel?
@State private var eventTask: Task<Void, Never>?
var body: some View {
ZStack {
if let travel {
OysterVideoView(travel: travel) // Step 5: SDK provides the view, host places it
.ignoresSafeArea()
} else {
Color.black.ignoresSafeArea()
}
}
.task { await start() }
.onDisappear { Task { await end() } }
}
@MainActor private func start() async {
let engine = HappyOysterEngine.shared
engine.updateToken(await fetchToken()) // Step 3
do {
let ticket = await fetchTicket() // issued by your server
let travel = try engine.createTravel(ticket: ticket) // Step 4
self.travel = travel
eventTask = Task { // Step 5
for await event in travel.events {
switch event {
case .statusChanged(let status): print("status: \(status)")
case .error(let error): print("error: \(error.code)")
}
}
}
let data = try await travel.start() // Step 6
if data.mode == .adventure { // Step 7
travel.sendCommand(OysterAdventureCommand(translation: .front))
} else {
_ = try await travel.sendInstruct(content: "Suddenly it starts to pour")
}
} catch let error as OysterSDKError {
// Handle start failure (see the error code table in the API Reference)
} catch {}
}
@MainActor private func end() async {
_ = try? await travel?.end() // Step 9
eventTask?.cancel()
travel = nil
}
}
5. ベストプラクティス
- Lifecycle:アプリ起動時にできるだけ早く
OysterStream.register()+initialize()をそれぞれグローバルに1回ずつ呼び出してください。リアルタイム接続とリソースの解放を確実にするため、体験ページを離れる際は常にend()を呼び出してください。OysterTravelは single-use です。終端状態に達した後は、createTravelを通じて新しいものを作成してください。 - Token renewal:体験を開始する前にHTTPトークンが最新であることを確認してください。認証関連のエラーコールバックを受け取った場合は、新しいトークンを取得して
updateTokenを呼び出してください(致命的ではなく、体験は終了しません)。 - Error severity:致命的なエラーの場合、SDKは現在の体験を自動的に終了し、
eventsの.errorを通じてエラーを通知し、failed終端状態に移行します。「体験開始」前の画面に戻る必要があります。致命的でないエラーは通知されるのみで、再試行可能です。完全なエラーコード表については APIリファレンス を参照してください。 - Mode adaptation:ディレクティングおよびアクティングモードではテキスト入力(
sendInstruct)が表示されます。アドベンチャーモードではコントロールウィジェット(sendCommand)が表示されます。巻き戻しのエントリーポイントが表示されるのはディレクティングのみです。アクティングワールドの場合、aspectRatioからプレーヤーの向きを選択してください(デフォルトは縦画面9:16)。
6. 次のステップ
- 完全なAPI(
pause()/resume()/rewind(toSec:)、データモデル、エラーコード)→ iOS SDK APIリファレンス