🚀 クイックスタート

SkyWay のメディア通信を体験できるシンプルなサンプルアプリケーションを作成します。

本チュートリアルでは SFU でのメディア通信を使用します。

ここでは、iPhone のマイク音源とカメラ映像を SFU サーバーに送信し、それぞれを自分が受信して音声を再生と映像の描画するというアプリケーションを作ります。

このクイックスタートの実行には iPhone 実機が必要です。

完成品は https://github.com/skyway/ios-sdk/tree/main/Tutorial にあります。

開発環境

  • Xcode 26.0.1
  • iOS 26.0.1

アプリケーション ID とシークレットキーの取得

※SkyWay への登録がまだの方はこちらから

SkyWay コンソールへログインし、以下の 3 つを行います。

  1. 「アプリケーションを作成」ボタンを押す Peer
  2. アプリケーション名を入力して作成ボタンを押す
  3. アプリケーション一覧からアプリケーション ID とシークレットキーをコピーする

SkyWay Auth Token の作成

SkyWay を利用するためには、初めに JWT(JSON Web Token)を用いて Context を初期化します。

SkyWay Auth Token は本来サーバーサイドで生成するため、 iOS SDK にはトークンの生成機能はございません。

クイックスタートでは、 Dev 環境専用の API である Context.setupForDev(withAppId:secretKey:options:completion:) を用いて初期化するため、 SkyWay Auth Token の作成は省略します。

認証認可について、詳しくはこちらをご覧ください。

XcodeProjectの作成・フレームワークの設定

新規で Xcodeproject を作成してください。

作成時、Interface は Storyboard を選択してください。

SelectStoryboard

パーミッションの設定

info.plistPrivacy - Microphone Usage DescriptionPrivacy - Camera Usage Description を追加して、value にはユーザーに利用許可を確認するプロンプトのメッセージを登録してください。

ここでは、マイクを利用しますカメラを利用します と登録します。

InfoPlist

SDKのダウンロード

今回は Swift Package Manager にてダウンロードします。

Xcode から Project を選択し、 Package Dependencies を選択します。

左下 + ボタンからパッケージ検索のモーダルを表示させ、右上の URL 検索ボックスに https://github.com/skyway/ios-sdk.git と入力します。

Package Product SkyWayRoom がチェックされていることを確認し、 Add Package を押下します。

映像ビューのセットアップ

今回は、カメラからキャプチャしている Local の View (プレビュー)と SFU サーバーから受信した Remote の View を描画します。

Storyboard を使って解説します。

まず、View コンポーネントを作成し、ViewController 直下の View に追加します。

StoryBoardView

View の名前を Local View にリネームします。

StoryBoardLocalView

この View の Custom Class の設定で Class 名を SKWCameraPreviewView に設定します。

この View を複製して、Remote View にリネームします。

StoryBoardRemoteView

この View の Custom Class の設定で Class 名を SKWVideoView に設定します。

適宜 Auto Layout を利用して View の constraint を設定します。

上側を LocalView, 下側を RemoteView に配置します。

StoryBoardConstraints

IBOutlet を利用して Local ViewRemote View をそれぞれ変数宣言します。この時、Type は SKW を消した CameraPreviewViewVideoView 宣言してください。

デフォルトでは`SKW`プレフィックスがついているのでご注意ください。

IBOutletLocalView

IBOutletRemoteView

Contextのセットアップ

ここでは、簡易的に Initial ViewController の viewDidLoad に SkyWay のロジックを記述していきます。

class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Impl here } }

import SkyWayRoom でフレームワークを import してください。

import UIKit import SkyWayRoom class ViewController: UIViewController {

アプリケーション ID とシークレットキーをメンバー変数で宣言します。

Context.setupForDev(withAppId:secretKey:options:completion:) で SkyWay のセットアップを行います。 completion にて失敗した場合は Error が返ります。ここでは async/await 形式で記述します。

Context.setupForDev(withAppId:secretKey:options:completion:) は Dev 環境での利用が想定されている API です。 本番環境では、SecretKeyを秘匿するため Context.setup(withToken:options:completion:) をご利用ください。

override func viewDidLoad() { super.viewDidLoad() Task { let appId = "アプリケーションIDを入力してください" let secretKey = "シークレットキーを入力してください" // SkyWayのセットアップ try? await Context.setupForDev(withAppId: appId, secretKey: secretKey, options: contextOpt)

Roomの作成

SkyWay のセットアップが完了したら Room を作成します。

Room.create(with:completion:) で Room を作成できます。

let roomInit: Room.InitOptions = .init() guard let room: Room = try? await .create(with: roomInit) else { print("[Tutorial] Creating room failed.") return }

Roomへの参加

Room.join(with:completion:) でルームに参加し、メンバーを作成します。

let memberInit: Room.MemberInitOptions = .init() memberInit.name = "Alice" // Memberに名前を付けることができます guard let member = try? await room.join(with: memberInit) else { print("[Tutorial] Join failed.") return }

マイク音源のAudioStreamの作成とRoomへのPublish

マイクを音声入力として Stream を作成します。

Publish をするための LocalStream を作成するためには、Source が必要です。

MicrophoneAudioSource のイニシャライザから Source を作成し、createStream() で LocalStream を作成します。

通信方式は P2P とメディアサーバーを介する SFU がありますが、今回は SFU を利用します。

通信方式は RoomPublicationOptionstype プロパティで指定できます。

LocalRoomMember.publish(_:options:completion:) で Room に Publish します。

// AudioStreamの作成 let audioSource: MicrophoneAudioSource = .init() let audioStream = audioSource.createStream() let audioPublicationOptions: RoomPublicationOptions = .init() audioPublicationOptions.type = .SFU guard let audioPublication = try? await member.publish(audioStream, options: audioPublicationOptions) else { print("[Tutorial] Publishing failed.") return }

AudioStreamのSubscribe

先ほど Publish した Stream を LocalRoomMember.subscribe(publicationId:options:completion:) を利用して、Subscribe して Stream を受け取ります。

PublicationID は RoomPublicationid で取得できます。

※P2P方式では、自分のPublishしたStreamをSubscribeできませんのでご注意ください
// Audioの場合、subscribeした時から音声が流れます guard let _ = try? await member.subscribe(publicationId: audioPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing audio stream successfully.")

カメラの設定とプレビューの描画

カメラ映像ソースの場合はカメラの設定とキャプチャ操作が必要です。

CameraVideoSource.supportedCameras() で SkyWay がサポートしているカメラの一覧を取得できます。

今回は前面カメラデバイスを取得します。

// Cameraの設定 guard let camera = CameraVideoSource.supportedCameras().first(where: { $0.position == .front }) else { print("Supported cameras is not found."); return }

CameraVideoSource.shared().startCapturing(with:options:completion:) でキャプチャーを開始します。

// キャプチャーの開始 try! await CameraVideoSource.shared().startCapturing(with: camera, options: nil)

確認用に CameraVideoSource.shared().attach(localView) でキャプチャリングをしているカメラ映像を描画します。

// Previewの描画 CameraVideoSource.shared().attach(localView)

カメラ映像ソースのVideoStreamの作成とRoomへのPublish

Stream は CameraVideoSource.shared().createStream() で作成できます。

AudioStream 同様、publish(_:options:completion:) で Publish を行います。

// VideoStreamの作成 let localVideoStream = CameraVideoSource.shared().createStream() let videoPublicationOptions: RoomPublicationOptions = .init() videoPublicationOptions.type = .SFU guard let videoPublication = try? await member.publish(localVideoStream, options: videoPublicationOptions) else { print("[Tutorial] Publishing failed.") return }

VideoStreamのSubscribe

AudioStream 同様、subscribe(publicationId:completion:) を利用して、Subscribe して Stream を受け取ります。

guard let videoSubscription = try? await member.subscribe(publicationId: videoPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing video stream successfully.")

RemoteViewの描画

Subscription の stream を取得し、RemoteVideoStream にキャストします。

attach(_)remoteView を指定することで、SFU から受信した映像を描画できます。

let remoteVideoStream = videoSubscription.stream as! RemoteVideoStream remoteVideoStream.attach(remoteView)

Tutorial完成コード

import UIKit import SkyWayRoom class ViewController: UIViewController { @IBOutlet weak var localView: CameraPreviewView! @IBOutlet weak var remoteView: VideoView! override func viewDidLoad() { super.viewDidLoad() Task { let appId = "アプリケーションIDを入力してください" let secretKey = "シークレットキーを入力してください" // SkyWayのセットアップ let contextOpt: ContextOptions = .init() contextOpt.logLevel = .trace try? await Context.setupForDev(withAppId: appId, secretKey: secretKey, options: contextOpt) let roomInit: Room.InitOptions = .init() guard let room: Room = try? await .create(with: roomInit) else { print("[Tutorial] Creating room failed.") return } let memberInit: Room.MemberInitOptions = .init() memberInit.name = "Alice" // Memberに名前を付けることができます guard let member = try? await room.join(with: memberInit) else { print("[Tutorial] Join failed.") return } // AudioStreamの作成 let auidoSource: MicrophoneAudioSource = .init() let audioStream = auidoSource.createStream() let audioPublicationOptions: RoomPublicationOptions = .init() audioPublicationOptions.type = .SFU guard let audioPublication = try? await member.publish(audioStream, options: audioPublicationOptions) else { print("[Tutorial] Publishing failed.") return } // Audioの場合、subscribeした時から音声が流れます guard let _ = try? await member.subscribe(publicationId: audioPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing audio stream successfully.") // Cameraの設定 guard let camera = CameraVideoSource.supportedCameras().first(where: { $0.position == .front }) else { print("Supported cameras is not found."); return } // キャプチャーの開始 try! await CameraVideoSource.shared().startCapturing(with: camera, options: nil) // Previewの描画 CameraVideoSource.shared().attach(localView) // VideoStreamの作成 let localVideoStream = CameraVideoSource.shared().createStream() let videoPublicationOptions: RoomPublicationOptions = .init() videoPublicationOptions.type = .SFU guard let videoPublication = try? await member.publish(localVideoStream, options: videoPublicationOptions) else { print("[Tutorial] Publishing failed.") return } guard let videoSubscription = try? await member.subscribe(publicationId: videoPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing video stream successfully.") let remoteVideoStream = videoSubscription.stream as! RemoteVideoStream remoteVideoStream.attach(remoteView) } } }

実行

iPhone(実機)にて Run します。

iPhone Simulatorではカメラが利用できないので実機にて実行してください。

iPhone 実機に声をかけたり音声を鳴らしてみてください。音声が出力され、LocalView と RemoteView に映像が描画されれば成功です。

次のステップ

今回は SFU サーバーを介して自分が Publish したメディアを Subscribe するというシンプルなものでした。

次は、他のクライアントと映像・音声・データをやりとりしてみましょう。他のクライアントと疎通できるサンプルアプリケーションを用意しています。

サンプルコード

また、開発の前に開発ドキュメントもご一読ください。

iOS SDKの開発ドキュメント