使用 SwiftUI 搭建一个使用通用链接的视频通话平台

如果你想给视频通话开发一个分享链接,方便你的用户的朋友通过链接直接加入某个视频通话或直播推流,方法一是你的用户向朋友分享聊天室代码,让其朋友把这个代码敲到他们自己的应用上;方法二是让朋友通过用户分享的链接直接进入聊天室,这就需要通用链接。很明显方法二更好,而方法二就需要搭建通用链接。


什么是通用链接?

大部分情况下,应用视图都有等效的网页视图。例如,你可以在手机上的网页浏览器上打开 instagram 个人中心,也可以在 instagram 应用打开这个视图,而通用链接就是把网页浏览器上的内容显示到应用上的“通道”。

在有些情况下,我们不能通过通用链接打开网页,比如,有些复杂的游戏不能在网页浏览器上运行,如果点击专属链接,就会直达模板页面并将模板页面传递给应用,应用会渲染所有内容或加入游戏。

可查看 Apple 文档获取更多关于通用链接的信息:

2

通用链接也适用于安卓系统,但网站设置和设备略有不同。


前期准备

  • 声网开发者帐户(详细步骤可参考这篇文章
  • Xcode 12.3 或更高版本
  • 配置了 iOS 13.0 或更高版本的 iOS 设备
  • 配置了 SSL 证书的公共服务器(https)


设置通用链接

通用链接很好设置,主要分两步:

1.设置网站

2.设置应用

我们先从网站端设置开始。


设置网站

这一步应该是通用链接最简单的部分,只需要添加一个名为 apple-app-site-association 的文件,该文件包含了应用程序的正确值:

{
  "applinks": {
    "apps": [],
    "details": [{
        "appIDs": ["DEVELOPMENT_TEAM.PRODUCT_BUNDLE_IDENTIFIER"],
        "components": [
          {
            "/": "*"
          }
        ]
    }]
  }
}

上述示例会在网站的每个页面分别设置一个通用链接按钮,下文会教大家如何在自己的域名下设置特定的 URL。

只需要用你的应用的关联值替换 DEVELOPMENT_TEAM 和 PRODUCT_BUNDLE_IDENTIFIER 。要找到这些相关值,可以找到项目的根文件夹的终端,在终端中运行几个命令,例如:

grep -r -m 1 "DEVELOPMENT_TEAM" .; grep -r -m 1 "PRODUCT_BUNDLE_IDENTIFIER" .

你的终端与下列代码类似:

=> grep -r -m 1 "DEVELOPMENT_TEAM" .; grep -r -m 1 "PRODUCT_BUNDLE_IDENTIFIER" .
./AppName.xcodeproj/project.pbxproj:    DEVELOPMENT_TEAM = 278494H572;
./AppName.xcodeproj/project.pbxproj:    PRODUCT_BUNDLE_IDENTIFIER = io.agora.AppName;

本示例中,我们需要的值分别是 278494H572io.agora.AppName

为确保网站设置正确,可点击查看 branch.io’s validator。常见问题之一是找不到 content-type 标头,如果你也遇到这个问题,需要创建或添加以下代码到 .htaccess,与 apple-app-site-association 文件的位置相同:

<Files "apple-app-site-association">
  ForceType 'application/json'
</Files>

按照上述设置,网站的所有页面都可以链接到指定的应用。如果你只想把网站的特定部分链接到应用,可以修改组件数组对象或再添加一个组件数组。例如,我们用下面的代码替代当前的组件数组:

"components": [
  { "/": "/wwdc/news" },
  { "/": "/videos/wwdc/2015/*" }
]

那么所有的 WWDC 新闻文章和 2015 年之后的视频就可以被导入应用。

点击 Apple 文档 查看更多示例。

我们打算在应用中添加下列组件:

{
  "/": "/join*",
  "?": { "channel": "?*" }
}

我们用 example.com 作为域名地址,以下是完整 URL 的示例:

https://www.example.com/join?channel=dkvn3lw2

想查看更多用 apple-app-site-association 文件设置通用链接的信息,可以点击 这里 查看 WWDC 2019 的相关资源。

完整文件如下:

{
  "applinks": {
    "details": [
      {
        "appIDs": ["DEVELOPMENT_TEAM.PRODUCT_BUNDLE_IDENTIFIER"],
        "components": [
          {
            "/": "/join/*",
            "?": {
              "channel": "?*"
            }
          }
        ]
      }
    ]
  }
}

以上就是所有设置,接下来是对应用的设置。


设置应用

安装工具包

在 Xcode 中设置一个新的 SwiftUI 应用,通过以下 URL 把 Swift 安装包添加到你的项目中:

https://github.com/AgoraIO-Community/iOS-UIKit.git

截止 2022 年 3 月 16 日,UIKit 的稳定版本为 1.7.1。

如果不知道怎么添加 Swift 安装包,可查看 Apple 文档中的具体步骤:

3


设置视图

因为本文的主要内容是通用链接,所以这一部分我们就简单介绍一下。首先,添加一个用于创建和加入频道的按钮:

Button {
  // click action here
} label: {
  Text("Create Channel")
    .padding().background(.green).cornerRadius(30)
}​

4

这个按钮可以创建频道名称并显示 AgoraViewer。AgoraViewer 是 View 的子类,由 UIKit 提供,可通过下列代码块实现:

import SwiftUI
import AgoraRtcKit
import AgoraUIKit_iOS

struct ContentView: View {
  @State var channelName: String?
  @State var isShowingVideo: Bool = false

  var videoCallView: some View {
    VStack {
      HStack { /* Other buttons will be placed here */ }
      ContentView.agview
    }
  }
  static var agview: AgoraViewer = {
    AgoraViewer(
      connectionData: AgoraConnectionData(
        appId: <#Agora App Id#>, rtcToken: <#Agora Token#>
      ), style: .floating
    )
  }()

  var body: some View {
    NavigationView {
      ZStack {
        NavigationLink(
          destination: self.videoCallView
            .navigationBarHidden(true)
            .onAppear(perform: { /* appear action */ })
            .onDisappear(perform: { /* disappear action */ }),
          isActive: $isShowingVideo
        ) { EmptyView() }
        HStack {
          Button {
            self.channelName = "test"
            self.isShowingVideo = true
          } label: {
            Text("Create Channel")
            .padding().background(.green).cornerRadius(30)
          }
        }
      }
    }
  }
}

如上,频道被设置为静态字符串 "test",特性 isShowingVideo 被设置为 true。 在 GitHub 的最终产品里,“test”字符串会被替换为随机字符串生成器。

isShowingVideo 是一个布尔值,可以决定是否由 NavigationView 展示视频通话视图(videoCallView)。接下来,我们需要用 onAppear 和 onDisappear 方法加入或离开频道,并在摄像头视图上方添加一些按钮。

.onAppear(perform: {
  guard let channelName = self.channelName else {
    self.isShowingVideo = false
    return
  }
  ContentView.agview.join(
    channel: channelName, with: nil, as: .broadcaster
  )
})
.onDisappear(perform: {
  ContentView.agview.viewer.leaveChannel()
})

视频视图出现时,如果 channelName 是 nil,我们就再次关闭视频视图,因为如果没有频道名称,就说明之前的阶段出现了错误。然后,我们从 Agora UIKit 中调用 builtin 函数,以主播角色加入频道。

加入方法中的第二个参数用于令牌,我在应用测试中没有使用令牌,但如果用声网搭建生产级应用需要用令牌。

想了解更多用声网 SDK 使用令牌的信息,可查看以下内容:

UI 设置的最后一步是在上述 videoCallView 中设置其他按钮。

var videoCallView: some View {
  VStack {
    HStack {
      Button {
        self.isShowingVideo = false
      } label: {
        Text("Exit")
          .padding().background(.gray).cornerRadius(3)
      }
      Spacer()
      Button {
        guard let channelName = channelName,
        let urlShare = URL(
          string: "https://example.com/join?channel=\(channelName)"
        ) else { return }
        let activityVC = UIActivityViewController(
          activityItems: [urlShare],
          applicationActivities: nil
        )
        let scenes = UIApplication.shared.connectedScenes
        guard let windowScene = scenes.first as? UIWindowScene,
        let window = windowScene.windows.first else { return }
        window.rootViewController?.present(activityVC, animated: true, completion: nil)
      } label: {
        Text("Share")
          .padding().background(.gray).cornerRadius(3)
      }
    }
    ContentView.agview
  }
}

上述代码段中已添加了两个按钮,这两个按钮被放置在 HStack 中,相邻放置,中间放了 Spacer()

5

用户可以点击第一个按钮退出当前视图、离开通话并返回主屏幕。点击第二个按钮可以创建一个用频道作为参数的 URL (用 example.com 作为域名),生成的 URL 会打开默认的共享屏幕,并通过 iMessage、AirDrop 和 email 等发送默认共享屏幕。

共享屏幕如下:

6


解释通用链接

想让应用解释通用链接,必须让应用知道它能够与哪个域名沟通。这是一个非常简单的项目,可以直接用 Xcode 完成。

在 Xcode 中找到你的应用,选中 Signing & Capabilities,像下面这样添加关联域功能:

7


完成之后,在新功能中设置域名:

8


上述条目是 applinks:example.comapplinks:www.example.com。用托管 apple-app-site-association 的域名替代“example.com”。

最后,了解什么时候通过通用链接打开应用,并获取打开应用的完整 URL。

SwiftUI 提供了一个非常容易实现的方法来捕获打开应用程序的通用链接—— onOpenURL 方法。这个方法可以附加到任何 View 上,它唯一的参数是 URL 对象。我们可以把以下内容添加到 NavigationView 上:

NavigationView {
  // Navigation View content here
}.onOpenURL { url in
  print(url.absoluteString)
}

通过通用链接打开应用时,以上代码段会执行并打印完整的 URL 字符串。

我们只需从中获得 URL 的最后一部分——频道参数。

我们把 URL 的部分内容提取到字典中,从下列 Stack Overflow 回答中获取 queryDictionary 特性并调用该特性:

9

使用 queryDictionary 获得频道名称,再次触发并打开视频传送:

if let channel = url.queryDictionary?["channel"] {
  self.channelName = channel
  self.isShowingVideo = true
}

应用搭建完成!现在,通过终端 join?channel=test 进入你的域名地址,可以启动你的应用,直接到设置频道名称这一步。

测试

可以在 GitHub 上查看完整项目:

11

运行应用时,确保在 Signing and Capabilities 中更新域名(默认设置为 example.com)。改变 Bundle 和 Team ID 后,立即在终端运行下列命令,获得新的值:

grep -r -m 1 "DEVELOPMENT_TEAM" .; grep -r -m 1 "PRODUCT_BUNDLE_IDENTIFIER" .

其他资源

想了解更多使用声网 SDK 搭建应用的信息,请查看声网Agora Video Call Quickstart Guide声网 API Reference

原文作者:Max Cobb
原文链接:Universal Links and SwiftUI Video Calls
推荐阅读
相关专栏
SDK 教程
164 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。