跳到主要内容
版本:v8

从 Capacitor 8.4 升级到 Capacitor 8.5

Capacitor 8.5 采用了 iOS UIScene 生命周期。Xcode 27 需要它,因此这作为破坏性小版本发布,而不是等待 Capacitor 9。这些更改仅影响 iOS。

核心库仍然支持 AppDelegate 路径,因此仅更新依赖本身不会破坏你的应用。不过,要使用 Xcode 27 构建,你的应用项目需要采用场景生命周期:一个新文件、一个 Info.plist 条目,以及 AppDelegate 中的一个方法。

@capacitor/ios 中的变更​

  • 新的 SceneDelegateProxy(位于 CAPSceneDelegateProxy.swift)镜像了现有的 ApplicationDelegateProxy。它转发 URL 打开、通用链接和场景连接相关的场景回调。
  • 旧的 .capacitorOpenURL 和 .capacitorOpenUniversalLink 通知仍会从场景路径以相同的数据载荷发出,因此使用它们的插件无需更改即可继续工作。Cordova 的 CDVPluginHandleOpenURL 通知也会照常发出。
  • 新增场景作用域的通知:.capacitorSceneWillConnect、.capacitorSceneOpenURL 和 .capacitorSceneOpenUniversalLink。它们将来源 UIScene 作为通知的 object 携带;URL 通知则将其数据载荷携带在 userInfo 中。它们的存在是为了在多窗口支持到来时让监听器可以按场景过滤。请注意,它们仅在 8.5 及更高版本中发出,因此同时支持更早 Capacitor 8 版本的插件应继续使用旧通知。
  • JS 的 resume 和 pause document 事件现在由 UIScene.willEnterForegroundNotification 和 UIScene.didEnterBackgroundNotification 驱动,并已过滤到桥接器自身的场景。未采用场景清单的应用仍会收到这些事件;iOS 会为旧版应用创建的兼容场景发出场景通知。
  • WebViewDelegationHandler 现在在决定将导航交给系统时检查 Web 视图的 windowScene.activationState,而不是 UIApplication.shared.applicationState。
  • ApplicationDelegateProxy.lastURL 会从场景路径填充,因此 App.getLaunchUrl() 继续正常工作。
  • 已移除:TmpViewController 以及早已弃用的 CapacitorBridge.tmpWindow 属性和 tmpViewControllerAppeared 通知。

更新你的 iOS 项目​

首先更新 Capacitor 包:

npm i @capacitor/core@^8.5.0 @capacitor/ios@^8.5.0
npm i -D @capacitor/cli@^8.5.0

1. 添加 SceneDelegate.swift​

创建 App/App/SceneDelegate.swift。同一个文件适用于 SPM 和 CocoaPods 项目,并与随附的模板一致:

import UIKit
import Capacitor

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }

window = UIWindow(windowScene: windowScene)
window?.rootViewController = CAPBridgeViewController()
window?.makeKeyAndVisible()

SceneDelegateProxy.shared.scene(scene, willConnectTo: session, options: connectionOptions)
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
SceneDelegateProxy.shared.scene(scene, openURLContexts: URLContexts)
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
SceneDelegateProxy.shared.scene(scene, continue: userActivity)
}
}

该委托在代码中创建窗口和根视图控制器;Main.storyboard 不再提供它们。如果你使用自定义的 CAPBridgeViewController 子类,请在这里实例化它,而不是在 storyboard 中设置。

2. 将场景清单添加到 Info.plist​

<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>

3. 在 AppDelegate.swift 中添加场景配置钩子​

func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let config = UISceneConfiguration(name: "Default Configuration",
sessionRole: connectingSceneSession.role)
config.delegateClass = SceneDelegate.self
return config
}

一旦场景清单就位,iOS 将不再在 AppDelegate 上调用 application(_:open:options:) 和 application(_:continue:restorationHandler:),而是将这些事件交付给场景委托。你可以删除旧方法或保留它们;它们不再运行。应用级回调仍然保留:didFinishLaunchingWithOptions、applicationWillTerminate 以及远程通知回调(推送令牌注册和送达)会像以前一样继续工作。前后台生命周期方法也不再被调用;请参阅下面的审查部分。

4. 在 Xcode 项目中注册该文件​

将 SceneDelegate.swift 添加到 App target。当你通过 IDE 添加该文件时,Xcode 会自动完成此操作。如果你手动编辑 project.pbxproj,该文件需要一个 PBXFileReference、一个 PBXBuildFile、App 组中的一个条目,以及 target 的 Sources 构建阶段中的一个条目。

然后运行 npx cap sync ios。

审查你的自定义代码和插件​

大多数应用除了上述步骤外不需要任何额外操作。请在你自己的原生代码和任何自定义插件中检查这些模式:

  • UIApplication.shared.applicationState:在单场景应用中仍然有效,但感知场景的代码应改为读取窗口场景的 activationState。请参阅 WebViewDelegationHandler 了解该模式。
  • application(_:open:options:) 或 application(_:continue:restorationHandler:) 内的自定义逻辑:一旦场景清单存在,这些方法就不再被调用。请将该逻辑移动到匹配的 SceneDelegate 方法中,与 SceneDelegateProxy 的转发调用放在一起。
  • AppDelegate 生命周期方法内的自定义逻辑(applicationDidBecomeActive、applicationWillResignActive、applicationDidEnterBackground、applicationWillEnterForeground):在场景生命周期下,这些方法也不再被调用。请将代码移动到匹配的 SceneDelegate 方法中,或监听仍然会触发的 UIApplication 通知。模板将这些方法作为空存根提供,因此这只会影响向其中添加了代码的应用。
  • .capacitorOpenURL 或 .capacitorOpenUniversalLink 的观察者:无需更改。场景路径上的数据载荷形状相同。
  • 对 tmpWindow 或 TmpViewController 的引用:这些已被移除。请删除接触它们的代码;桥接器的 viewController 是呈现锚点。

验证迁移​

迁移后,请在设备或模拟器上确认:

  • 应用正常启动并渲染。
  • 将应用置于后台和前台会触发 JS 的 pause 和 resume 事件。
  • 自定义 URL scheme 在冷启动(应用被杀死,然后点击链接)和热启动(应用正在运行)时都能到达应用。同时检查 appUrlOpen 监听器和 App.getLaunchUrl()。
  • 如果你的应用使用通用链接,请确认其能路由到应用中。

使用 CLI 进行迁移​

将 latest 版本的 Capacitor CLI 安装到你的项目中并运行迁移器:

npm i -D @capacitor/cli@latest
npx cap migrate

对于仍然符合 Capacitor 模板结构的应用,迁移器会应用上述项目更改(SceneDelegate、Info.plist 清单、AppDelegate 钩子、项目文件注册),并打印它无法自动完成的操作。使用手工编写的场景委托或自定义 AppDelegate URL 处理的项目,应改用手动步骤。

使用迁移技能​

对于 CLI 跳过的项目,ionic-team/capacitor-skills 中的 capacitor-uiscene-migrator 技能会借助 AI 代理走手动路径:它会审查你的项目和已安装的插件,在更改任何内容之前报告发现,在需要判断的地方询问(自定义深链逻辑、现有 SceneDelegate),并合并到你的文件中而不是覆盖它们。用“migrate my Capacitor app to UIScene”提示它。符合模板结构的项目会交回给 npx cap migrate;本指南仍然是它无法自动化的每个步骤的权威来源。