# 实现音视频互动 (/zh-CN/realtime-media/rtc/get-started/quick-start/ios)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

本文介绍如何集成声网实时互动 SDK，通过少量代码从 0 开始实现一个简单的实时互动 App，适用于互动直播和视频通话场景。
首先，你需要了解以下有关音视频实时互动的基础概念：

- 声网实时互动 SDK：由声网开发的、帮助开发者在 App 中实现实时音视频互动的 SDK。
- 频道：用于传输数据的通道，在同一个频道内的用户可以进行实时互动。
- 主播：可以在频道内**发布**音视频，同时也可以**订阅**其他主播发布的音视频。
- 观众：可以在频道内**订阅**音视频，不具备**发布**音视频权限。

更多概念详见[关键概念](/zh-CN/realtime-media/rtc/reference/key-concept)。

下图展示在 App 中实现音视频互动的基本工作流程：

<DocsImage alt="实现音视频互动" src="https://web-cdn.agora.io/docs-portal/img/rtc/quick-start-tech.png" displayWidth="60%" />

1. 所有用户调用 `joinChannel` 方法加入频道，并根据需要设置用户角色：

   - 互动直播：如果用户需要在频道中发流，则设为主播；如果用户只需要收流，则设为观众。
   - 视频通话：将所有用户的角色都设为主播。
2. 加入频道后，不同角色的用户具备不同的行为：

   - 所有用户默认都可以接收频道中的音视频流。
   - 主播可以在频道内发布音视频流。
   - 观众如果需要发流，可在频道内调用 `setClientRole` 方法修改用户角色，使其具备发流权限。

## 前提条件 [#前提条件-1]

在实现功能以前，请按照以下要求准备开发环境：

- Xcode 12.0 或以上版本。
- Apple 开发者账号。
- 如需使用 Cocoapods 集成 SDK，则确保已安装 Cocoapods，否则请参考 [Getting Started with CocoaPods](https://guides.cocoapods.org/using/getting-started.html#getting-started) 进行安装。
- 两台 iOS 9.0 或以上版本的设备。
- 可以访问互联网的计算机。如果你的网络环境部署了防火墙，参考[应对防火墙限制](/zh-CN/realtime-media/rtc/build/setup-and-access/firewall)以正常使用声网服务。
- 一个有效的声网账号以及声网项目。请参考[开通 RTC 服务](/zh-CN/realtime-media/rtc/build/setup-and-access/enable-service)从声网控制台获得 App ID 和临时 Token。

:::warning[注意]
      临时 Token 的有效期是 24 小时。Token 过期会导致加入频道失败。
      :::

## 创建项目 [#创建项目-1]

按照以下步骤，在 Xcode 中创建一个项目：

1. 参考 [Create a project](https://help.apple.com/xcode/mac/current/#/dev07db0e578) 创建一个新的项目。**Application** 选择 **App**，**Interface** 选择 **Storyboard**，**Language** 选择 **Swift**。

    :::info[信息]
    如果你没有添加过开发团队信息，会看到 **Add account…** 按钮。点击该按钮并按照屏幕提示登入 Apple ID，点击 **Next**，完成后即可选择你的 Apple 账户作为开发团队。
    :::

2. 为你的项目设置[自动签名](https://help.apple.com/xcode/mac/current/#/dev23aab79b4)。

3. 设置部署你的 App 的[目标设备](https://help.apple.com/xcode/mac/current/#/deve69552ee5)。

4. 添加项目的设备权限。

    在项目导航栏中打开 `info.plist` 文件，编辑[属性列表](https://help.apple.com/xcode/mac/current/#/dev3f399a2a6)，添加实时互动所需的录音和摄像头权限。

    :::info[信息]
    - 下表中列出的权限均为可选权限。但如果不添加所列权限，你将无法使用麦克风和摄像头进行实时音视频互动。
        - 如果你的项目中需要添加第三方插件或库（例如第三方摄像头），且该插件或库的签名与项目的签名不一致，你还需勾选 **Hardened Runtime** > **Runtime Exceptions** 中的 **Disable Library Validation**。
        - 更多注意事项，可以参考 [Preparing Your App for Distribution](https://developer.apple.com/documentation/xcode/preparing_your_app_for_distribution)。
    :::

    | Key | Type | Value |
    | -------------------------------------- | ------ | ------------------------------------------------------------ |
    | Privacy - Microphone Usage Description | String | 使用麦克风的目的，例如：for a call or live interactive streaming。 |
    | Privacy - Camera Usage Description | String | 使用摄像头的目的，例如：for a call or live interactive streaming。 |

## 集成 SDK [#集成-sdk-1]

根据实际情况，在以下集成方式中任选一种，在你的项目中集成 SDK。

<Tabs defaultValue="cocoapods" groupId="integrate-sdk" persist>
<TabsList>
  <TabsTrigger value="cocoapods">通过 Cocoapods 集成</TabsTrigger>
  <TabsTrigger value="manual">手动集成</TabsTrigger>
  <TabsTrigger value="swift-package">通过 Swift Package Manager 集成</TabsTrigger>
</TabsList>

<TabsContent value="cocoapods">

1. 在终端里进入项目根目录，并运行 `pod init` 命令。项目文件夹下会生成一个 `Podfile` 文本文件。
2. 打开 `Podfile` 文件，修改文件为如下内容。注意将 `Your App` 替换为你的 Target 名称。

    ```ruby
    platform :ios, '9.0'
    target 'Your App' do
        # 对于 4.6.0 及之后的版本
        # x.y.z 请填写具体的 SDK 版本号，如 4.6.0。可通过发版说明获取最新版本号
        # 集成 Full SDK
        pod 'ShengwangRtcEngine_iOS', 'x.y.z'
        # 集成 Lite SDK
        pod 'ShengwangLite_iOS', 'x.y.z',

        # 对于 4.6.0 之前的版本
        # x.y.z 请填写具体的 SDK 版本号，如 4.0.1。可通过发版说明获取最新版本号
        # 集成 Full SDK
        pod 'AgoraRtcEngine_iOS', 'x.y.z'
        # 或集成 Lite SDK
        pod 'AgoraLite_iOS', 'x.y.z'
    end
    ```

   :::warning[注意]
   Lite SDK 默认无法与 [RTC 小程序 SDK](/zh-CN/realtime-media/rtc) 互通。如果需要，请[联系技术支持](https://ticket.shengwang.cn/)。
   :::

3. 在终端内运行 `pod install` 命令安装声网 SDK。成功安装后，Terminal 中会显示 `Pod installation complete!`。

4. 成功安装后，项目文件夹下会生成一个后缀为 `.xcworkspace` 的文件，通过 Xcode 打开该文件进行后续操作。

</TabsContent>

<TabsContent value="manual">

1. 前往[下载](/zh-CN/realtime-media/rtc/reference/downloads)页面，获取最新版的 SDK，然后解压。
2. 将 SDK 包内 `libs` 路径下的文件，拷贝到你的项目路径下。
3. 打开 Xcode，[添加对应动态库](https://help.apple.com/xcode/mac/current/#/dev51a648b07)，确保添加的动态库 **Embed** 属性设置为 **Embed & Sign**。

    :::info[信息]
    声网 SDK 默认使用 libc++ (LLVM)，如需使用 libstdc++ (GNU)，请联系 sales@shengwang.cn。SDK 提供的库是 FAT Image，包含 32/64 位模拟器、32/64 位真机版本。
    :::

</TabsContent>

<TabsContent value="swift-package">

1. 在 Xcode 中，进入 **File > Swift Packages > Add Package Dependencies...**，粘贴如下 URL：

    ```text
    // 对于 4.6.0 及之后的版本
    github:git@github.com:Shengwang-Lab/ShengwangRtcEngine_iOS.git

    // 对于 4.6.0 之前的版本
    https://github.com/AgoraIO/AgoraRtcEngine_iOS.git
    ```

2. 在 **Choose Package Options** 中指定你想集成的 SDK 版本。你也可以参考 [Apple 官方文档](https://help.apple.com/xcode/mac/current/#/devb83d64851)进行设置。

</TabsContent>
</Tabs>

:::warning[注意]
自 4.5.0 起，RTC SDK 和 RTM SDK (2.2.0 及以上版本) 都包含 `aosl.xcframework` 库。如果你通过 CDN 手动集成 RTC SDK 且同时集成了 RTM SDK，为避免冲突，请手动删除版本较低的 `aosl.xcframework` 库。4.6.0 RTC SDK `aosl.xcframework` 库版本为 1.3.0。你可以通过查看 `aosl.xcframework` 文件的属性来得知库的版本信息。
:::

## 创建用户界面 [#创建用户界面-1]

在 `ViewController.swift` 文件中，为你的项目创建一个用户界面，包括本地视频窗口和远端视频窗口，分别用于展示本地视频和远端视频。如下所示：

<DocsImage alt="预览视图" src="https://web-cdn.agora.io/docs-portal/img/rtc/ios-quick-start-preview.png" displayWidth="30%" />

用以下代码替换 `ViewController.swift` 文件中的内容，即可实时预览你创建的用户界面。

<Accordions>
<Accordion title="创建用户界面示例代码">

```swift
import UIKit
import AgoraRtcKit

class ViewController: UIViewController {
    // 本地视频视图
    var localView: UIView!
    // 远端视频视图
    var remoteView: UIView!
    // RTC 引擎
    var agoraKit: AgoraRtcEngineKit!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 设置本地视频视图
        localView = UIView(frame: UIScreen.main.bounds)
        localView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.1) // 设置背景颜色
        self.view.addSubview(localView)

        // 设置远端视频视图
        remoteView = UIView(frame: CGRect(x: self.view.bounds.width - 135, y: 50, width: 135, height: 240))
        remoteView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5) // 设置背景颜色
        remoteView.layer.borderColor = UIColor.white.cgColor // 设置边框颜色
        remoteView.layer.borderWidth = 2.0 // 设置边框宽度
        self.view.addSubview(remoteView)

    }
}

// 开启视图预览
#Preview("VC", body: {
    let vc = ViewController()
    return vc
})
```

</Accordion>
</Accordions>

## 实现步骤 [#实现步骤-1]

下图展示了使用声网 RTC SDK 实现音视频互动的基本流程。

<DocsImage alt="实现流程" src="https://web-cdn.agora.io/docs-portal/img/rtc/quick-start-sequence.svg" displayWidth="70%" />

下面列出了一段实现实时互动基本流程的完整代码以供参考。复制以下代码到 `ViewController.swift` 文件中替换原有内容即可。

:::info[信息]
在 `appId`、`token` 和 `channelName` 字段中传入你在控制台获取到的 App ID、临时 Token，以及生成临时 Token 时填入的频道名。
:::

<Accordions>
<Accordion title="实现音视频互动示例代码">

```swift
//  ViewController.swift
import UIKit
import AgoraRtcKit

class ViewController: UIViewController {
    // 本地视频视图
    var localView: UIView!
    // 远端视频视图
    var remoteView: UIView!
    // RTC 引擎
    var agoraKit: AgoraRtcEngineKit!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 设置本地视频视图
        localView = UIView(frame: UIScreen.main.bounds)
        localView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.1) // 设置背景颜色
        self.view.addSubview(localView)

        // 设置远端视频视图
        remoteView = UIView(frame: CGRect(x: self.view.bounds.width - 135, y: 50, width: 135, height: 240))
        remoteView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5) // 设置背景颜色
        remoteView.layer.borderColor = UIColor.white.cgColor // 设置边框颜色
        remoteView.layer.borderWidth = 2.0 // 设置边框宽度
        self.view.addSubview(remoteView)

        // 初始化 RTC 实例
        agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId:<#Your App ID#>, delegate: self)

        // 开启视频模块（音频模块默认开启）
        agoraKit.enableVideo()

        // 开启本地摄像头预览
        startPreview()

        // 加入频道
        joinChannel()
    }

    deinit {
        agoraKit.stopPreview()
        agoraKit.leaveChannel(nil)
        AgoraRtcEngineKit.destroy()
    }

    func startPreview(){
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.view = localView
        videoCanvas.renderMode = .hidden
        agoraKit.setupLocalVideo(videoCanvas)
        agoraKit.startPreview()
    }

    func joinChannel(){
        let options = AgoraRtcChannelMediaOptions()
        // 设置频道场景为直播
        options.channelProfile = .liveBroadcasting
        // 设置用户角色为主播；如果要将用户角色设置为观众，保持默认值即可
        options.clientRoleType = .broadcaster
        // 发布麦克风采集的音频
        options.publishMicrophoneTrack = true
        // 发布摄像头采集的视频
        options.publishCameraTrack = true
        // 自动订阅所有音频流
        options.autoSubscribeAudio = true
        // 自动订阅所有视频流
        options.autoSubscribeVideo = true
        // 使用临时 Token 加入频道，在这里传入你的项目的 Token 和频道名
        // uid 为 0 表示由引擎内部随机生成; 成功后会触发 didJoinChannel 回调
        agoraKit.joinChannel(byToken: <#Your Token#>, channelId: <#Your Channel Name#>, uid: 0, mediaOptions: options)
    }
}

extension ViewController: AgoraRtcEngineDelegate{
    // 成功加入频道回调
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
        print("didJoinChannel: \(channel), uid: \(uid)")
    }

    // 远端用户或主播加入当前频道回调
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int){
        // 当远端用户加入频道后，显示指定 uid 的远端视频流
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = uid
        videoCanvas.view = remoteView
        videoCanvas.renderMode = .hidden
        agoraKit.setupRemoteVideo(videoCanvas)
    }

    // 远端用户或主播离开当前频道回调
    func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {

        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = uid
        videoCanvas.view = nil
        agoraKit.setupRemoteVideo(videoCanvas)
    }
}

// 开启视图预览
#Preview("VC", body: {
    let vc = ViewController()
    return vc
})
```

</Accordion>
</Accordions>

### 导入声网组件 [#导入声网组件]

在 `ViewController.swift` 文件中引入 `AgoraRtcKit`。

```swift
import AgoraRtcKit
```

### 初始化引擎 [#初始化引擎-1]

调用 `sharedEngineWithConfig` 方法，创建并初始化 `AgoraRtcEngineKit`。

:::warning[注意]
在初始化 SDK 前，需确保终端用户已经充分了解并同意相关的隐私政策。
:::

```swift
// 在这里输入你在声网控制台中获取的 App ID
agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId:<#Your App ID#>, delegate: self)
```

### 启用视频模块 [#启用视频模块-1]

按照以下步骤启用视频模块。

1. 调用 `enableVideo` 方法，启用视频模块。
2. 调用 `setupLocalVideo` 方法初始化本地视图，同时设置本地的视频显示属性。
3. 调用 `startPreview` 方法，开启本地视频预览。

```swift
// 启用视频模块
agoraKit.enableVideo()

let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.view = localView
videoCanvas.renderMode = .hidden
// 设置本地视图
agoraKit.setupLocalVideo(videoCanvas)
// 开始本地视频预览
agoraKit.startPreview()
```

### 加入频道并发布音视频流 [#加入频道并发布音视频流-1]

调用 `joinChannelByToken`[2/4] 方法、填入你在控制台获取的临时 Token，以及获取 Token 时填入的频道名加入频道，并设置用户角色。

```swift
let options = AgoraRtcChannelMediaOptions()
    // 设置频道场景为直播
    options.channelProfile = .liveBroadcasting
    // 设置用户角色为主播；如果要将用户角色设置为观众，保持默认值即可
    options.clientRoleType = .broadcaster
    // 发布麦克风采集的音频
    options.publishMicrophoneTrack = true
    // 发布摄像头采集的视频
    options.publishCameraTrack = true
    // 自动订阅所有音频流
    options.autoSubscribeAudio = true
    // 自动订阅所有视频流
    options.autoSubscribeVideo = true
    // 使用临时 Token 加入频道，在这里传入你的项目的 Token 和频道名
    // uid 为 0 表示由引擎内部随机生成; 成功后会触发 didJoinChannel 回调
    agoraKit.joinChannel(byToken: <#Your Token#>, channelId: <#Your Channel Name#>, uid: 0, mediaOptions: options)
```

### 设置远端用户视图 [#设置远端用户视图]

调用 `setupRemoteVideo` 方法初始化远端用户视图，同时设置远端用户的视图在本地显示属性。你可以通过 `didJoinedOfUid` 回调获取远端用户的 `uid`。

```swift
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int){
    // 当远端用户加入频道后，显示指定 uid 的远端视频流
    let videoCanvas = AgoraRtcVideoCanvas()
    videoCanvas.uid = uid
    videoCanvas.view = remoteView
    videoCanvas.renderMode = .hidden
    agoraKit.setupRemoteVideo(videoCanvas)
}
```

### 实现常用回调 [#实现常用回调-1]

根据使用场景，定义必要的回调。以下示例代码展示了如何实现 `didJoinChannel` 和 `didOfflineOfUid` 回调。

```swift
// 成功加入频道回调
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
    print("didJoinChannel: \(channel), uid: \(uid)")
}
// 远端用户或主播离开当前频道回调
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
    let videoCanvas = AgoraRtcVideoCanvas()
    videoCanvas.uid = uid
    videoCanvas.view = nil
    agoraKit.setupRemoteVideo(videoCanvas)
}
```

### 开始音视频互动 [#开始音视频互动-1]

当视图加载完成后，在 `viewDidLoad` 中调用一系列方法加入频道，开始音视频互动。

```swift
override func viewDidLoad() {
    super.viewDidLoad()
    // 当加载视图后，你可以进行其他其他设置
    // 当调用声网 API 时，以下函数会被调用
    agoraKit.enableVideo()
    startPreview()
    joinChannel()
}
```

### 结束音视频互动 [#结束音视频互动-1]

1. 调用 `leaveChannel` 方法离开频道，与会话相关的资源也会被释放。

    ```swift
    agoraKit.stopPreview()
    agoraKit.leaveChannel(nil)
    ```

2. 调用 `destroy` 销毁引擎，并释放声网 SDK 中使用的所有资源。

    ```swift
    AgoraRtcEngineKit.destroy()
    ```

    :::error[警告]
    - 调用该方法后，你将无法再使用 SDK 的所有方法和回调。如需再次使用实时音视频互动功能，你必须重新创建一个新的引擎。详见[初始化引擎](#初始化引擎-1)。
        - 该方法为同步调用。需要等待引擎资源释放后才能执行其他操作，因此建议在子线程中调用该方法，避免主线程阻塞。
    :::

## 调试 App [#调试-app-1]

参考以下步骤来测试你的 App：

1. 将 iOS 设备连接至计算机。
2. 点击 **Build** 来运行你的项目，需等待几秒至 App 安装完成。
3. 允许 App 访问设备的麦克风和摄像头权限。
4. （可选）如果设备上弹出**不受信任的开发者**提示，则首先点击**取消**关闭该提示，然后在 iOS 设备上打开**设置 > 通用 > VPN 与设备管理**，在**开发者 APP** 中选择信任该开发者。
5. 使用第二台 iOS 设备，重复以上步骤，在该设备上安装 App，并打开 App 加入频道，观察测试结果：

   - 如果两台设备均作为主播加入频道，则可以看到对方并且听到对方的声音。
   - 如果两台设备分别作为主播和观众加入，则主播可以在本地视频窗口看到自己；观众可以在远端视频窗口看到主播、并听到主播的声音。

<DocsImage alt="iOS 加入频道后的音视频互动界面" src="https://web-cdn.agora.io/docs-portal/img/rtc/ios-quick-start-inchannel.png" displayWidth="30%" />

## 后续步骤 [#后续步骤-1]

- 本文的示例使用了临时 Token 加入频道。在测试或生产环境中，为确保通信安全，声网推荐使用 Token 服务器来生成 Token，详见[使用 Token 鉴权](/zh-CN/realtime-media/rtc/build/setup-and-access/token-authentication)。
- 如果你想要实现极速直播场景，可以在互动直播的基础上，通过修改观众端的延时级别为低延时 (`AgoraAudienceLatencyLevelLowLatency`)实现。详见[实现极速直播](/zh-CN/realtime-media/rtc/build/advanced-channel/ls-quick-start)。

## 参考信息 [#参考信息-1]

### 示例项目 [#示例项目-1]

声网提供了开源的实时互动示例项目供你参考，你可以前往下载或查看其中的源代码。
<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">Agora-RTC-QuickStart-iOS</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/Agora-RTC-QuickStart/tree/main/iOS/Agora-RTC-QuickStart-iOS/Agora-RTC-QuickStart-iOS" />
  <Card title={<RepositoryCardTitle provider="github">Agora-RTC-QuickStart-iOS</RepositoryCardTitle>} href="https://github.com/AgoraIO-Community/Agora-RTC-QuickStart/tree/main/iOS/Agora-RTC-QuickStart-iOS" />
</Cards>

### 常见问题 [#常见问题-1]

- [直播场景下，如何监听远端观众角色用户加入/离开频道的事件？](/zh-CN/reference/faq/integration/audience_event)
- [如何处理视频黑屏问题？](/zh-CN/reference/faq/quality/video_blank)
- [如何处理 CocoaPods 常见问题？](/zh-CN/reference/faq/integration/cocoapods_problems)
- [为什么我无法打开摄像头？](/zh-CN/reference/faq/quality/video_camera)
- [如何处理频道相关常见问题？](/zh-CN/reference/faq/integration/channel)
- [如何设置日志文件？](/zh-CN/reference/faq/integration/set_log_file)
- [如何处理无声问题？](/zh-CN/reference/faq/quality/audio_noaudio)
- [为什么部分 iOS 版本 App 锁屏或切后台后音视频采集无效？](/zh-CN/reference/faq/quality/ios_background)
- [为什么 SDK 中使用的是动态库而不是静态库？](/zh-CN/reference/faq/integration/dynamic_or_static_library)
- [如何增加隐私清单？](/zh-CN/reference/faq/other/ios_privacy_manifest)

### 相关文档 [#相关文档-1]

- [SDK 错误码](/zh-CN/realtime-media/rtc/reference/error-code)
- [频道连接状态管理](/zh-CN/realtime-media/rtc/build/initialize-and-channel/channel-connection)
























