跳到正文
起步应用与指南

Web 应用套壳 iOS

已有 Web 产品上 iOS 的路径设计:用 WKWebView 壳加载你的站点,再原生桥接 Embedded SDK 拿到设备能力。SDK 尚未开放发放。

本页描述的是 Embedded SDK 的接口形态设计,SDK 尚未开放发放。需要「用 iKho 硬件采集并绑定到你的 App」的合作方,请通过联系我们登记需求,我们会在 SDK 开放时通知。

当前已可用的是服务端三组接口:认证文件上传转写。可以先用你自己的音频跑通转写链路,见后端篇教程

开发者平台处于内测阶段,接口契约以联调时提供的正式文档为准。账号由对接工程师开通,详见联系我们

如果你已经有一个成熟的 Web 应用,不必推翻重做。这条路径是:用一个原生 iOS 壳(WKWebView)加载你的站点,再在壳里集成 Embedded SDK,通过 JS 桥把扫描、绑定、同步等设备能力暴露给 Web 侧,Web 应用继续是 Web 应用,同时具备原生设备能力。壳工程与 JS 桥可以先建起来,设备能力那一段要等 SDK 开放。

接入步骤(SDK 开放后)

第一步现在就能做;第二步起依赖 SDK 开放。

1
建原生壳工程
新建一个 iOS 工程,主界面放一个 WKWebView,加载你部署好的 Web 应用地址。这一步不依赖 SDK。
2
集成 Embedded SDK 并注册 JS 桥
SDK 开放后,按 iOS SDK 集成 IKhoEmbedded,并给 WKWebView 注册一个消息处理器(如名为 ikho)。
3
原生侧实现桥方法
在消息处理器里把 Web 的调用转发给 IKhoDeviceManager;设备事件在代理回调里通过 evaluateJavaScript 回传 Web。
4
Web 侧通过桥调用
在 Web 代码里判断处于原生壳中时,调用桥接口发起扫描 / 绑定 / 同步,并监听回传事件更新界面。

JS 桥接口示例

swift
import WebKit
import IKhoEmbedded

final class WebBridge: NSObject, WKScriptMessageHandler, IKhoDeviceDelegate {
    private weak var webView: WKWebView?

    func attach(to webView: WKWebView) {
        self.webView = webView
        webView.configuration.userContentController.add(self, name: "ikho")
        IKhoDeviceManager.shared.delegate = self
    }

    // 接收 Web 侧调用:{ method, args }
    func userContentController(_ controller: WKUserContentController,
                              didReceive message: WKScriptMessage) {
        guard let body = message.body as? [String: Any],
              let method = body["method"] as? String else { return }
        switch method {
        case "scanDevices": IKhoDeviceManager.shared.scanDevices()
        case "bindDevice":  IKhoDeviceManager.shared.bind(device: /* 解析 args */)
        case "syncRecordings": IKhoDeviceManager.shared.fetchRecordings()
        default: break
        }
    }

    // 设备事件回传 Web(payloadJSON 为已序列化的 JSON 字符串)
    func recordingListUpdated(_ recordings: [IKhoRecording]) {
        let js = "window.__ikhoBridge.emit('recordingList', " + payloadJSON + ")"
        webView?.evaluateJavaScript(js)
    }
}

蓝牙权限只能在原生侧申请:WKWebView 内的 Web 代码无法直接访问 CoreBluetooth,所有设备操作都必须经由 JS 桥转到原生。请把蓝牙、麦克风的权限声明放在原生壳的 Info.plist 里。

桥接口安全

JS 桥等于把原生设备能力开放给 WebView 中加载的页面,而 WebView 可能因外链跳转或内嵌 iframe 加载到你控制之外的页面。上线前先立三条规矩:

  • 方法白名单。WKScriptMessageHandler 只分发显式登记过的方法名,未知方法一律丢弃,不做任何基于字符串的动态调用。
  • 校验消息来源。每条消息先检查发送页面的来源(message.frameInfo.securityOrigin),只处理来自你自己域名的 https 页面;第三方 iframe 发来的消息直接忽略。
  • 令牌不进 Web 层。用户令牌只保存在原生侧,由原生代码直接交给 SDK;不通过桥回传给页面,也不写入 localStorage 或全局变量,Web 侧只拿到业务结果本身。
带来源校验与方法白名单的消息处理
final class WebBridge: NSObject, WKScriptMessageHandler {
    // 方法白名单:新桥方法必须在这里显式登记
    private let allowedMethods: Set<String> = ["scanDevices", "bindDevice", "syncRecordings"]
    // 只信自家域:替换为你的正式域名
    private let trustedHosts: Set<String> = ["app.example.com"]

    func userContentController(_ controller: WKUserContentController,
                               didReceive message: WKScriptMessage) {
        // 1. 校验消息来源:只处理来自可信域的 https 页面(含 iframe 场景)
        let origin = message.frameInfo.securityOrigin
        guard origin.protocol == "https",
              trustedHosts.contains(origin.host) else { return }

        // 2. 方法白名单:未知方法一律丢弃,不做动态派发
        guard let body = message.body as? [String: Any],
              let method = body["method"] as? String,
              allowedMethods.contains(method) else { return }

        dispatch(method: method, args: body["args"])
    }
}