跳转到内容

SuperSDK iOS 接入文档

版本:dev | 稳定性:开发中 — 内容随时变更,仅供内部参考

SuperSDK iOS 版提供与 Android 版功能对齐的 WebView 容器能力,包括 JSBridge、离线包、云控配置等。

项目要求
iOS 版本iOS 14.0+
Swift 版本Swift 5.0+
WebViewWKWebView
架构支持arm64(真机)、arm64 + x86_64(模拟器)

方式一:远程 Swift Package Manager(第三方模板推荐)

Section titled “方式一:远程 Swift Package Manager(第三方模板推荐)”

第三方宿主和模板工程优先使用远程 SPM 包。发布时需要同时提供:

环境变量说明
SPM_PACKAGE_URL承载 generated Package.swift 的 HTTPS Git 仓库地址
SPM_BINARY_BASE_URL承载 SuperSDK-<VERSION>.xcframework.zip 的 HTTPS CDN 根地址
Package.swift
dependencies: [
.package(url: "<SPM_PACKAGE_URL>", exact: "<VERSION>")
]

发布脚本会生成:

packages/ios-sdk/release/
├── SuperSDK-<VERSION>.xcframework.zip
├── spm-package/Package.swift
└── spm-release.json

spm-release.json、spm-package/Package.swift 和 zip 文件 checksum 必须一致。只有 SPM_PACKAGE_URL 与 SPM_BINARY_BASE_URL 都配置为真实 HTTPS 地址后,才能作为公网远程 SPM 包发布。

  1. 从本目录获取 SuperSDK.xcframework
  2. 将 SuperSDK.xcframework 拖入 Xcode 项目
  3. 在 Target > General > Frameworks, Libraries, and Embedded Content 中设置为 Embed & Sign
SuperSDK.xcframework/
├── ios-arm64/ # 真机 (arm64)
└── ios-arm64_x86_64-simulator/ # 模拟器 (arm64 + x86_64)

方式三:本地 Swift Package Manager(SDK 开发调试)

Section titled “方式三:本地 Swift Package Manager(SDK 开发调试)”
Package.swift
dependencies: [
.package(path: "path/to/SuperSDK")
]

将 SuperSDK/ 目录作为本地 Swift Package 引入项目。

宿主必须在 App 的 Info.plist 填写 NSMicrophoneUsageDescription(SDK / xcframework 不会替宿主带上这条)。H5 getUserMedia 麦克风依赖该用途说明;缺或空字符串时,iOS 15+ 容器 Deny、不启动采集(避免 TCC 因缺说明杀进程)。

<key>NSMicrophoneUsageDescription</key>
<string>网页需要使用麦克风进行语音输入或录音。</string>

createPageView() / createPageViewController(url:) 无需再包一层 WKUIDelegate。iOS 15+ SuperWebView 已实现 requestMediaCapturePermissionFor(microphone 有说明则 Prompt;camera 恒 Prompt;cameraAndMicrophone 无 mic 说明则 Deny)。iOS 14 无该 API,保持系统默认。getUserMedia 需 https 安全源;离线命中改写的 super-offline:// 通常不是 secure context。


注意:模块名为 SuperSDK(import SuperSDK),入口类为 SuperSDKManager。 用法 SuperSDKManager.shared.xxx。(iOS 入口类名与 Android 的 SuperSDK 不一致,详见发布说明。)

在 AppDelegate 中初始化 SDK:

import SuperSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Replace ab12cd34 with the real spid issued by the SuperSDK console.
try! SuperSDKManager.shared.initialize(
spid: "ab12cd34",
provider: MySDKProvider()
)
return true
}
}

参数说明:

参数类型说明
spidString控制台签发的 8 位小写字母/数字项目标识
providerSuperSDKProvider宿主实现的服务提供者

项目环境对应控制台顶栏的 env(如 prod / staging),决定 SDK 拉取哪套云控配置(settings)以及埋点上报归属的环境。它不再是 initialize 参数,而是独立的运行时状态。

方法签名:public func setAPIEnvironment(_ environment: String) throws / public func getAPIEnvironment() -> String

// 设置项目环境(initialize 前后均可调用;非法值抛 SDKIdentityValidationError.invalidEnvironment 且不落地)
try SuperSDKManager.shared.setAPIEnvironment("staging")
// 读取当前项目环境(未设置时返回 "prod",不要求已 initialize)
let env = SuperSDKManager.shared.getAPIEnvironment()

规则与行为:

  • 取值限 1–10 位小写字母/数字(^[a-z0-9]{1,10}$),不 trim、不做大小写转换
  • SDK 不持久化该值:进程启动默认 prod,宿主如需记忆用户选择,需自行持久化并在启动后调用 setAPIEnvironment
  • 运行时切换的副作用顺序:冲刷旧环境的存量上报事件 → 换值 → settings 清本地缓存并按新环境重拉 → 刷新存活 WebView 的 UA(仅影响后续导航)
  • 切换后 settings 拉取自动带 env 参数、上报 basic_data.env 与请求头 X-Super-API-ENV 均使用新值;H5 侧经 getEnv() 读到新值
  • 该环境与 HostConfigService.sdkApiBaseUrls(API 域名)是不同维度:切环境不需要也不应该改 API 域名

SuperSDKProvider 是宿主 App 向 SDK 提供能力的桥梁。所有方法均为可选实现(默认返回 nil)。

class MySDKProvider: SuperSDKProvider {
private let hostConfigService = MyHostConfigService()
func getHostConfigService() -> HostConfigService? {
return hostConfigService
}
func getEventService() -> EventService? { nil }
func getLoginService() -> LoginService? { nil }
func getHostMethodService() -> HostMethodService? { nil }
func getNavigationService() -> NavigationService? { nil }
func getNativeBlockService() -> NativeBlockProvider? { nil }
}

// 方式 1: 创建 PageView 嵌入到自己的视图中
let pageView = SuperSDKManager.shared.createPageView()
view.addSubview(pageView)
pageView.frame = view.bounds
pageView.load(url: "https://example.com")
// 方式 2: 创建 ViewController(推荐,内置导航栏和生命周期管理)
let vc = SuperSDKManager.shared.createPageViewController(url: "https://example.com")
navigationController?.pushViewController(vc, animated: true)
// 方式 3: 通过 PageController 打开全屏页面
SuperSDKManager.shared.getPageController().openPage(url: "https://example.com")
class MyViewController: UIViewController, SuperPageDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let pageView = SuperSDKManager.shared.createPageView()
pageView.delegate = self
view.addSubview(pageView)
pageView.frame = view.bounds
pageView.load(url: "https://example.com")
}
func pageDidStartLoading(_ page: SuperPageView) {
// 开始加载
}
func pageDidFinishLoading(_ page: SuperPageView) {
// 加载完成
}
func pageDidFailLoading(_ page: SuperPageView, error: Error) {
// 加载失败
}
func page(_ page: SuperPageView, didChangeTitle title: String?) {
// 标题变化
}
func page(_ page: SuperPageView, didChangeProgress progress: Double) {
// 加载进度变化 (0.0 ~ 1.0)
}
}

SDK 内置的用户反馈提交,与 H5 的 SuperSDK.submitFeedback 同一条链路:POST /superapi/v2/feedback → 控制台「业务运营 → 用户反馈」按项目 × 环境查看、搜索并标记处理状态。SDK 自动补齐 spid / env / device_id / platform / app_version / sdk_version / os_version / device_model,服务端记录提交时间与用户 IP;宿主只需提供业务字段。

let request = FeedbackRequest(
scene: "settings",
title: "打开设置页闪退",
content: "进入设置页后约 1 秒必现闪退",
subScene: "about", // 可选
userId: currentUserId, // 可选:业务用户 ID,SDK 不维护用户身份
extra: ["page": "settings"] // 可选:附加信息(≤4KB)
)
SFeedback.submit(request) { result in
switch result {
case .success(let feedbackId):
print("feedback stored: \(feedbackId)")
case .failure(let error):
// error.code: INVALID_ARGUMENT(本地校验失败,未发请求)/ NETWORK_ERROR / RATE_LIMITED /
// UNAVAILABLE / PROTOCOL_MISMATCH,或服务端业务 code(INVALID_REQUEST / NOT_FOUND …)
print(error.code, error.message)
}
}
字段必填限制
scene✓≤64 字符
title✓≤200 字符
content✓≤5000 字符
subScene≤64 字符
userId≤128 字符
extraJSON object,序列化后 ≤4KB

回调恒在主线程;所有字符串会被 trim,长度按字符数计(服务端为权威)。限流:每 IP 10 次/分钟、每项目 600 次/分钟。提交不是幂等的,SDK 不自动重试。

允许 H5 页面嵌入宿主 App 提供的原生视图(如广告、视频播放器等)。

class MyNativeBlockProvider: NativeBlockProvider {
var supportedBlockTypes: [String] {
return ["ad-banner", "video-player"]
}
func createBlockView(type: String, params: [String: Any]) -> UIView? {
switch type {
case "ad-banner":
let adView = UIView()
adView.backgroundColor = .systemBlue
return adView
case "video-player":
let player = UIView()
// 配置视频播放器...
return player
default:
return nil
}
}
func onBlockShow(blockId: String, view: UIView, type: String) {
// Block 进入可视区域
}
func onBlockHide(blockId: String, view: UIView, type: String) {
// Block 离开可视区域
}
func onBlockDetached(blockId: String, view: UIView, type: String) {
// Block 被移除,释放资源
}
}

通过 SuperSDKManager.shared.getOfflineController() 获取。

let offlineController = SuperSDKManager.shared.getOfflineController()
// 查询所有离线包状态
let packages = offlineController.getAllPackages()
// 查询指定离线包
let state = offlineController.getPackage(host: "example.com")
// 根据 URL 查询对应的离线包
let state2 = offlineController.getPackageForUrl("https://example.com/page")

SDK 支持从远程站点自动获取并安装最新离线包,无需宿主额外接入代码即可生效。

自动更新行为:

  • 探测时机:打开任意 http(s) 页面时,SDK 在页面加载过程中后台静默探测该 host 站点根路径下的 super_meta.json(站点侧发布规范见 OFFLINE_PKG_SPEC.md),不阻塞页面渲染,站点未接入(404)视为正常状态
  • 生效时机:探测到新版本并下载安装成功后,下次加载该 host 页面时生效,不会刷新当前已打开的页面
  • 大小上限:自动模式下单包 50MB 以内才会自动下载,超过则跳过(仅显式调用可下载)
  • 探测频控:同一 host 最小探测间隔 30 分钟,避免频繁开关页对站点造成请求压力

显式触发更新:宿主可主动调用 preloadPackage(host:listener:) / preloadAllPackages(listener:) 立即检查更新,绕过频控与大小上限:

class MyUpdateListener: OfflineUpdateListener {
func onProgress(host: String, progress: Int) {
// 下载进度 0-100
}
func onComplete(result: OfflineUpdateResult) {
if result.isSuccess {
// 更新成功,下次加载该 host 页面生效
}
}
}
// 立即检查并更新指定 host 的离线包(绕过频控与 50MB 上限)
offlineController.preloadPackage(host: "example.com", listener: MyUpdateListener())
// 立即检查并更新全部已装离线包
offlineController.preloadAllPackages(listener: MyUpdateListener())

首次调用时若本地尚未安装该 host 的离线包,只要远程清单探测成功,同样会走首装流程(首装同样在下次加载时生效,不影响本次页面渲染)。

站点侧要求:站点需在根路径发布符合 super_meta.json v1 规范的清单文件,并保证声明的 pkg_path 与站点同源。完整字段定义、zip 打包工具链约束(zip -r deflate,不支持流式 data-descriptor 产物)见 OFFLINE_PKG_SPEC.md。

离线页运行时约定(自 1.0.0.59 起)

Section titled “离线页运行时约定(自 1.0.0.59 起)”

离线命中时,SDK 通过自定义 scheme super-offline://{host}/… 请求级拦截整个离线包(对齐 Android,支持 base:'/' 绝对路径与 History 路由)。离线页在 iOS 上的 origin 因此是 super-offline://{host} 而非真实域名,接入方(尤其 H5 侧)需注意:

  • 非 secure context:离线页不满足安全上下文,Service Worker、crypto.subtle 等受安全上下文限制的 API 不可用。若离线包依赖这些能力,请勿以离线包形态分发。
  • API 跨域:离线页打自家后端为跨域请求,后端需返回 CORS 头;带 cookie 凭据时不能用 Access-Control-Allow-Origin: *,需回显具体 origin 并加 Access-Control-Allow-Credentials: true。
  • POST body:经 fetch/XHR 发往 super-offline:// 同源路径的 POST/PUT 请求体会被 WebKit 丢弃;API 请求一律走绝对 https URL。
  • 页内资源引用用相对路径或 base:'/' 绝对路径均可(都会命中本地包);跨域绝对 https URL 资源不经离线拦截,照常走网络。

通过 SuperSDKManager.shared.getShareController() 获取,提供短链生成能力(与 Android SuperSDK.getShareController() 对齐)。

let shareController = SuperSDKManager.shared.getShareController()
// 生成短链
shareController.requestShortUrl(url: "https://example.com/very/long/path?a=1") { code, msg, shortUrl in
if code == "SUCCESS", let shortUrl = shortUrl {
print("短链: \(shortUrl)")
}
}
// 通过 HTML 内容生成短链(访问短链时直接渲染 HTML,不做 302 跳转)
shareController.requestShortUrlWithHtml(html: "<html>...</html>") { code, msg, shortUrl in
// ...
}
// 两个方法均支持自定义短链前缀
shareController.requestShortUrl(url: "https://example.com/page", baseUrl: "https://s.a.com/") { code, msg, shortUrl in
// ...
}

回调 code 取值:SUCCESS 成功(shortUrl 为短链字符串)、BAD_REQUEST 参数为空、FAIL_API 服务端返回错误(msg 为错误信息)、FAIL_PARSE 响应解析失败、FAIL_REQUEST 网络请求失败。

H5 侧对应能力为 SuperSDK.requestShortUrl() / requestShortUrlWithHtml();系统分享面板由 JSBridge share 方法内置提供(UIActivityViewController),无需宿主注入。


提供宿主 App 的基本配置信息。

class MyHostConfigService: HostConfigService {
var appChannel: String { "AppStore" }
var appVersionCode: Int { 1 }
var appVersionName: String { "1.0.0" }
var sdkApiBaseUrl: String? { nil } // 自定义 API 地址,nil 使用默认
func generateDeviceId() -> String? { nil } // 宿主接管 deviceId 生成,nil 使用 SDK 默认逻辑
}
成员类型说明
appChannelString渠道标识(如 AppStore)
netEnvApiEnv⚠️ 已废弃(SDK 从未消费);项目环境请使用 setAPIEnvironment(_:)
appVersionCodeInt版本号
appVersionNameString版本名称
sdkApiBaseUrlString?自定义 API 地址
generateDeviceId()String?宿主接管 deviceId 生成(可选,默认 nil)

generateDeviceId - 宿主接管 deviceId 生成(可选):

  • 仅在 SDK 需要生成新 deviceId 时调用(首次安装,Keychain 无既有值),已持久化的 deviceId 继续沿用,不受影响(缓存优先)。
  • 返回非空字符串:SDK 采用该值作为 deviceId 并写入 Keychain;返回 nil/空串:走 SDK 默认生成逻辑(identifierForVendor → UUID 兜底)。
  • 实现要求:同步快速返回,且多次调用应返回稳定值。

接收 SDK 产生的事件,对接宿主的埋点平台。

class MyEventService: EventService {
func onEvent(_ event: Event) {
// 上报到自己的埋点平台
Analytics.track(event.eventName, params: event.params)
}
}

Event 属性:

属性类型说明
eventNameString事件名称
eventTypeInt事件类型
timestampInt64时间戳
params[String: Any]事件参数

当 H5 需要登录时,SDK 调用此服务。

class MyLoginService: LoginService {
func requestLogin(from viewController: UIViewController,
callback: @escaping (_ code: String, _ msg: String, _ loginInfo: HostLoginInfo?) -> Void) {
// 拉起宿主的登录流程
LoginManager.shared.login(from: viewController) { result in
if result.isSuccess {
let loginInfo = HostLoginInfo(
userId: result.userId,
token: result.token,
extra: nil
)
callback("SUCCESS", "登录成功", loginInfo)
} else {
callback("FAIL", "登录失败", nil)
}
}
}
}

HostLoginInfo:

属性类型说明
userIdString用户 ID
tokenString登录 Token
extra[String: Any]?扩展信息(可选)

允许 H5 调用宿主自定义方法,实现业务扩展。

class MyHostMethodService: HostMethodService {
func invokeAsync(methodName: String, params: [String: Any]?,
callback: @escaping (_ code: String?, _ errMsg: String?, _ result: [String: Any]?) -> Void) {
switch methodName {
case "getUserInfo":
callback("SUCCESS", nil, ["name": "张三"])
default:
callback("NOT_FOUND", "方法不存在", nil)
}
}
}
Section titled “NavigationService - 接管 openPage(URL 路由)”

宿主实现此协议即可接管 openPage:拿到 URL + 配置,自行决定如何打开(自有原生页 / 重定向 / 自有 WebView 等)。返回 true 表示已自行处理;返回 false 交回 SDK 默认容器打开(选择性拦截 + SDK 兜底)。

class MyNavigationService: NavigationService {
func openPage(url: String, config: PageConfig) -> Bool {
// 命中宿主路由 → 打开自己的原生页,返回 true 表示已接管
if let vc = MyRouter.matchNative(url) {
MyRouter.push(vc)
return true
}
// 其余交回 SDK 默认容器打开
return false
}
}

若你想用 SDK 的 WebView 容器但自己控制展示,在 openPage 里自取容器即可,无需走 SDK 默认页:

let v = SuperSDKManager.shared.getPageController().createPageView()
v.load(url: url)
// v.onClosePageListener = { ... } 可响应 H5 的 closePage
// 把 v 放进你自己的 VC 展示,然后 return true

PageConfig:

属性类型说明
showTitleBarBool是否显示标题栏
showBackButtonBool是否显示返回按钮
initialTitleString初始标题

容器行为 · target="_blank" / window.open:H5 中 <a target="_blank"> 链接点击与 window.open(url) 会被容器自动识别,统一通过 openPage 在端上以新页打开(等价于 appbridge.openPage),并走上面的 NavigationService。H5 无需额外适配;与 Android 行为一致。

SuperPageView 提供以下方法供宿主调用:

方法说明
load(url: String)加载 URL
reload()重新加载
canGoBack() -> Bool是否可以后退
goBack()后退一页
handleBackPressed(exitAction: @escaping () -> Void)处理返回键,优先级:前端拦截 > 页面历史回退 > exitAction
setSafeArea(statusBarHeight: Int, navigationBarHeight: Int)设置安全区域(物理像素)
setImmersiveBottom(_ immersive: Bool)底部是否沉浸(默认 true=内容延伸到底部安全区下,现有行为)。置 false(浏览器场景):WebView 内容缩短到底部安全区之上 + 页面 env(safe-area-inset-bottom)=0 + UA SafeAreaBottom=0,第三方站点底栏紧贴系统栏、无多余白边。仅作用底部(顶部恒沉浸);应在注入安全区/首次 load 前调用
sendBroadcast(eventKey: String, jsonData: String?)向当前 WebView 发送广播
onShow()通知页面进入前台
onHide()通知页面进入后台
onDestroy()销毁页面

HostNetworkExtensionService - 网络请求扩展(可选)

Section titled “HostNetworkExtensionService - 网络请求扩展(可选)”

允许宿主为 SDK 内部网络请求(settings / 事件上报 / 归因 / 短链等)追加 query 参数与 request header。协议方法全部有默认实现,不实现即不生效,既有接入方无需改动。

class MyProvider: NSObject, SuperSDKProvider {
private let networkExtension = MyNetworkExtension()
func getHostNetworkExtensionService() -> HostNetworkExtensionService? {
return networkExtension
}
}
class MyNetworkExtension: HostNetworkExtensionService {
func getExtraQueryParams(_ context: SuperRequestContext) -> [String: String]? {
// 开启响应混淆:SDK 自身不会默认追加该参数
return ["suenc": "1"]
}
func getExtraHeaders(_ context: SuperRequestContext) -> [String: String]? {
return ["X-Host-Trace": traceId()]
}
}

SuperRequestContext 提供 url / urlString / path / host / method,宿主可据此按请求条件化返回。

合并规则(SDK 侧强制,宿主无法绕过):

  1. SDK 自有 query / header 恒胜——query 名精确匹配,header 名大小写不敏感匹配。例如 SDK 已设 X-Super-API-ENV,宿主返回 x-super-api-env 也不会覆盖它
  2. Authorization、Cookie 等认证凭据 header 不接受本接口注入
  3. 非法项被单项跳过,其余照常生效;日志只记 key 与拒绝原因,不记 value
  4. 实现抛出的异常一律被捕获,不阻断 SDK 主流程
  5. 每次请求实时读取,SDK 不缓存返回值
  6. 方法在发起请求的线程上同步调用,实现必须快速返回、不得阻塞

服务端在请求带 ?suenc=1 时会用自定义 Base64 表编码 JSON 响应体并回 X-Su-Enc: b64t1。SDK 内部请求会自动解码(含非 2xx 的错误响应),宿主无需介入——只要按上文注入 suenc=1 即可。

⚠️ 只用于降低通用关键字扫描的有效性,不是加密:不提供机密性、防篡改、防重放。

若宿主自己发起了 suenc 请求,可调用 SDK 的解码能力:

// 查询本端支持的 codec,决定是否发送 suenc=1
let capabilities = SuperSDKManager.shared.getSuEncCapabilities()
// ["supported": true, "codecs": ["b64t1"]]
// codec 必须直接使用响应 X-Su-Enc header 的原始值,不接受自定义编码表
do {
let json = try SuperSDKManager.shared.decodeSuEnc(encodedBody, codec: codec)
} catch let error as SuEncError {
// .unsupportedCodec / .decodeFailed / .invalidUTF8
}

注意事项:

  1. 解码失败一律视为请求失败,绝不回落读取明文——那正是该协议要防的降级
  2. 若请求带了 suenc=1 却收到无 X-Su-Enc 的响应,SDK 判定该线路失败并自动切换下一个 baseUrl
  3. 编码表在 SDK 发布后永不变更;新表一定使用新的 codec key,因此 codec 必须取自响应 header 而非硬编码
Service说明必要性
HostConfigService宿主配置(渠道、版本、环境等)推荐实现
EventService事件上报回调可选
LoginService登录服务可选
HostMethodService宿主自定义方法调用可选
NavigationService自定义导航服务可选
NativeBlockProviderNative Block 创建和生命周期可选
HostNetworkExtensionServiceSDK 内部请求的 query / header 扩展可选

前端通过 window.appbridge 调用原生能力,与 Android 保持一致。详见 JSSDK 文档。

// 获取环境信息
const env = await appbridge.getEnvAsync();
// 打开页面
appbridge.openPage('https://example.com', { title: '标题' });
// 调用宿主方法
const result = await appbridge.callHostMethod('methodName', { key: 'value' });

// 1. AppDelegate 初始化
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
try! SuperSDKManager.shared.initialize(
spid: "ab12cd34",
provider: MySDKProvider()
)
return true
}
}
// 2. Provider 实现
class MySDKProvider: SuperSDKProvider {
private let hostConfig = MyHostConfigService()
func getHostConfigService() -> HostConfigService? { hostConfig }
func getLoginService() -> LoginService? { nil }
func getEventService() -> EventService? { nil }
func getHostMethodService() -> HostMethodService? { nil }
func getNavigationService() -> NavigationService? { nil }
func getNativeBlockService() -> NativeBlockProvider? { nil }
}
class MyHostConfigService: HostConfigService {
var appChannel: String { "AppStore" }
var appVersionCode: Int { 1 }
var appVersionName: String { "1.0.0" }
var sdkApiBaseUrl: String? { nil }
// func generateDeviceId() -> String? { MyDeviceIdManager.deviceId } // 可选:宿主接管 deviceId 生成
}
// 3. 使用 ViewController 方式打开页面
class HomeViewController: UIViewController {
func openWebPage() {
let vc = SuperSDKManager.shared.createPageViewController(url: "https://example.com")
navigationController?.pushViewController(vc, animated: true)
}
}
// 4. 使用 PageView 方式嵌入
class EmbedViewController: UIViewController, SuperPageDelegate {
private var pageView: SuperPageView?
override func viewDidLoad() {
super.viewDidLoad()
let pv = SuperSDKManager.shared.createPageView()
pv.delegate = self
view.addSubview(pv)
pv.frame = view.bounds
pv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
pv.load(url: "https://example.com")
self.pageView = pv
}
func pageDidFinishLoading(_ page: SuperPageView) {
print("页面加载完成")
}
func page(_ page: SuperPageView, didChangeTitle title: String?) {
self.title = title
}
}

  1. 初始化时机:必须在使用任何 SDK 功能前调用 SuperSDKManager.shared.initialize()
  2. 线程安全:UI 操作在主线程执行,JSBridge 回调已自动切换到主线程
  3. 内存管理:WKScriptMessageHandler 的循环引用已在 SDK 内部处理
  4. ATS:如需访问 HTTP 地址,需在 Info.plist 中配置 App Transport Security
  5. 离线包:SDK 内置离线包支持,加载 URL 时会自动匹配本地资源
  6. 麦克风:宿主 Info.plist 必填 NSMicrophoneUsageDescription;不要替换 SDK 的 uiDelegate