Skip to main content
SDK iOS

Panduan Integrasi HappyOyster iOS SDK

Ini adalah jalur onboarding terpendek untuk integrator iOS: dari inisialisasi, menampilkan gambar di layar, mengirimkan instruksi kontrol, hingga mengakhiri pengalaman. Untuk tanda tangan metode, bidang, dan kode kesalahan lengkap, lihat Referensi API iOS SDK.

Untuk tanda tangan metode, bidang, dan kode kesalahan lengkap, lihat Referensi API iOS SDK.

0. Apa yang Akan Anda Bangun

Sebuah loop end-to-end minimal berupa "masuk ke dunia → pengalaman video real-time → interaksi → akhiri". Titik masuk runtime adalah HappyOysterEngine.shared global dan handle sesi sekali pakai OysterTravel yang dibuatnya.
OysterStream.register()
HappyOysterEngine.shared: initialize → updateToken → createTravel
OysterTravel:             videoView / events → start → sendInstruct / sendCommand → end

1. Persyaratan

Item

Persyaratan

OS Minimum

iOS 15.0+

Bahasa

Swift (async/await)

Threading

API publik bersifat @MainActor; panggil pada thread utama

Impor

import HappyOysterSDK (titik masuk agregat, sudah @_exported Core + World)

Komunikasi real-time (AliRTC) dienkapsulasi di dalam SDK; integrator tidak pernah menyentuh API RTC secara langsung.

1.1 Tambahkan SDK melalui CocoaPods

SDK didistribusikan sebagai biner prakompilasi (xcframework) melalui subspec CocoaPods, yang dipublikasikan ke CocoaPods Trunk publik — referensikan langsung berdasarkan versi, tidak diperlukan file podspec lokal.
# 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
Kemudian jalankan pod install dan buka .xcworkspace yang dihasilkan (bukan .xcodeproj).
AliVCSDK_ARTC wajib disertakan setiap kali Anda menarik HappyOysterSDK/StreamAliRTC: jika hilang, SDK akan kembali secara diam-diam ke Loopback — ia tetap terhubung dan mencapai running, tetapi menampilkan layar hitam tanpa kesalahan.

2. Dua Jenis Kredensial (Pahami Terlebih Dahulu untuk Menghindari Masalah)

SDK tidak memperoleh atau memperbarui kredensial apa pun secara mandiri; Anda harus menyuntikkan semuanya:

Kredensial

Sumber

Tujuan

Cara Menyuntikkan

Token autentikasi HTTP

Aplikasi Anda mengambilnya dari backend Anda sendiri

Autentikasi umum untuk panggilan SDK ke gateway (berumur panjang, memerlukan pembaruan)

updateToken(_:); SDK hanya menyimpan yang terbaru

Tiket sekali pakai

Dikeluarkan oleh server Anda setelah pertukaran melalui API kredensial Travel

Digunakan untuk satu pengalaman, divalidasi segera setelah digunakan

Diteruskan sebagai argumen createTravel(ticket:)

Keduanya tidak dapat dipertukarkan: updateToken digunakan untuk autentikasi umum, sedangkan ticket adalah kredensial bergabung sekali pakai. Kunci AK / penandatanganan hanya ada di server Anda dan tidak pernah diekspos ke klien.

3. Langkah Integrasi

Siklus hidup: daftarkan stream engine → inisialisasi → suntikkan token → buat sesi → lampirkan video + berlangganan event → mulai → interaksi → akhiri.

Langkah 1: Mendaftarkan Stream Engine

Satu-satunya titik masuk untuk merender video. Panggil sekali saat peluncuran aplikasi; panggilan berulang aman dilakukan.
import HappyOysterSDK
import HappyOysterStream

OysterStream.register()

Langkah 2: Menginisialisasi SDK

Anda harus menginisialisasi sekali sebelum memanggil API lainnya. Untuk mengganti gateway atau model, cukup panggil lagi — saat menganggur, konfigurasi terbaru akan digunakan dan token yang disuntikkan tetap dipertahankan; panggilan hanya diabaikan saat travel sedang berlangsung, jadi lakukan end() terlebih dahulu.
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.

Langkah 3: Menyuntikkan Token Autentikasi HTTP (push)

Ambil Model Studio API Key sementara dari backend Anda sendiri dan suntikkan. Suntikkan ulang setelah kedaluwarsa.
let token = await fetchTokenFromYourBackend()
engine.updateToken(token)

Langkah 4: Membuat Handle Sesi

Buat OysterTravel dari ticket sekali pakai. Belum ada yang terhubung, tetapi tampilan video sudah tersedia.
let travel = try engine.createTravel(ticket: ticket)

Langkah 5: Melampirkan Tampilan Video dan Berlangganan Event

SDK menyediakan tampilannya, host menempatkannya. Berlangganan sebelum start() agar Anda tidak melewatkan perubahan status awal.
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
        }
    }
}

Langkah 6: Memulai Pengalaman

Hubungkan dan mulai mainkan. SDK kemudian secara otomatis mempertahankan koneksi real-time dan polling status, serta menampilkan status melalui 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:) hanya berlaku untuk adventure; directing dan acting mengabaikan parameter tersebut.
Mode dunia untuk travel ini harus cocok dengan model yang Anda teruskan ke initialize pada Langkah 2. Dengan model per-mode (happyoyster-1.0-adventure / -directing / -acting), setiap model merupakan rute aplikasi gateway-nya sendiri, sehingga satu initialize hanya melayani dunia dari satu mode; ticket dikeluarkan oleh server Anda di bawah rute mode dunia tersebut, dan start() mengirimkannya ke rute dari model yang dikonfigurasi saat ini — ketidakcocokan akan gagal pada langkah ini. Sebelum memasuki dunia dengan mode yang berbeda, panggil initialize() lagi dengan model yang cocok — tanpa cleanup() dan tanpa updateToken() ulang:
HappyOysterEngine.shared.initialize(config: OysterConfig(
    apiHost: "…", model: "happyoyster-1.0-acting"))   // the model for this world's mode
Saat menganggur (tidak ada travel yang berlangsung), konfigurasi terbaru akan digunakan dan token yang disuntikkan tetap dipertahankan. SDK tidak dapat memverifikasi kecocokan model/dunia untuk Anda di muka: mode hanya tiba dalam respons langkah ini (data.mode); sebelum panggilan dilakukan, SDK tidak memegang apa pun selain ticket yang tidak transparan dan nama model. Aplikasi yang menawarkan satu mode tidak terpengaruh dan hanya melakukan inisialisasi sekali.
Saat travel sedang berlangsung, panggilan initialize() yang berulang akan diabaikan dengan peringatan — lakukan end() terlebih dahulu.

Langkah 7: Interaksi Real-Time (Pilih Satu Berdasarkan Mode)

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 melakukan throttle secara internal pada siklus menang-terbaru 42ms (24FPS), sehingga host dapat memanggilnya pada frekuensi tinggi:
  • Tindakan sekali jalan seperti melompat, menyerang, membungkuk, dan berlari dikirimkan satu kali.
  • Pergerakan dan rotasi tampilan dikirimkan secara terus-menerus selama ditahan, dan Anda cukup berhenti memanggilnya saat dilepaskan.
  • Anda tidak perlu mengirim none atau memanggil flushCommands() saat dilepaskan; SDK tidak pernah menghasilkan perintah berhenti secara mandiri, dan server mengakhiri tindakan setelah saluran real-time menjadi sepi.

Langkah 8: Jeda / Lanjutkan / Putar Ulang (Bergantung pada Mode)

_ = try await travel.pause()           // supported by directing and acting
_ = try await travel.resume()
_ = try await travel.rewind(toSec: 10) // directing only
adventure tidak mendukung none dari fitur ini; acting mendukung jeda/lanjutkan tetapi tidak mendukung putar ulang — sembunyikan titik masuk putar ulang pada mode tersebut. Panggilan yang tidak cocok akan ditolak secara lokal oleh SDK (103003 / 103002).

Langkah 9: Mengakhiri Pengalaman

Memutus koneksi, menghentikan polling, dan melepaskan semua sumber daya sesi; ticket telah terpakai. Bersifat idempoten — setiap jalur keluar harus bermuara padanya.
_ = try? await travel.end()
eventTask.cancel()
Panggil await engine.cleanup() saat membongkar SDK atau mengganti gateway.

4. Contoh Lengkap (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. Praktik Terbaik

  • Siklus hidup: Panggil OysterStream.register() + initialize() sedini mungkin saat peluncuran aplikasi, masing-masing sekali secara global; selalu panggil end() saat meninggalkan halaman pengalaman untuk memastikan koneksi real-time dan sumber daya dilepaskan. OysterTravel bersifat sekali pakai — buat yang baru melalui createTravel setelah mencapai status terminal.
  • Pembaruan token: Pastikan token HTTP masih baru sebelum memulai pengalaman; saat Anda menerima callback kesalahan terkait autentikasi, ambil token baru dan panggil updateToken (tidak fatal, tidak mengakhiri pengalaman).
  • Tingkat keparahan kesalahan: Untuk kesalahan fatal, SDK secara otomatis mengakhiri pengalaman saat ini, menampilkannya melalui .error dari events, dan beralih ke status terminal failed; Anda harus kembali ke layar yang ditampilkan sebelum "mulai pengalaman". Kesalahan non-fatal hanya ditampilkan dan dapat dicoba ulang. Lihat tabel kode kesalahan lengkap di Referensi API.
  • Adaptasi mode: Mode directing dan acting menampilkan input teks (sendInstruct); mode adventure menampilkan widget kontrol (sendCommand). Hanya directing yang menampilkan titik masuk putar ulang. Untuk dunia acting, pilih orientasi pemutar dari aspectRatio (potret 9:16 secara default).

6. Langkah Selanjutnya

  • API Lengkap (pause() / resume() / rewind(toSec:), model data, kode kesalahan) → Referensi API iOS SDK
Pembuatan Gambar
  • FAQ
Video Generation
Model Dunia
Audio
  • Pembuatan audio
Realtime API
Penyematan Teks
TokenPlan
Model production
Panduan Integrasi HappyOyster iOS SDK - Alibaba Cloud Model Studio