IPA 升级 itms-services 路径补主动 suspend:用户回桌面 + 看到下载进度

【背景修正】
上个 commit (befe205) commit msg 里说"iOS 调 itms-services 时会自动把 App 切后台"
不准确 —— 实测系统弹窗只是模态覆盖在本 App 上,用户点「安装」后 App 回到 active,
桌面图标下载进度需要用户手动按 Home 才能看到。

【方案】
调 UIApplication.open(itms-services URL) 后,0.3s 用私有 selector "suspend"
把 App 主动切后台(等价按 Home / 上滑):
- 用户立即看到桌面 + 系统安装确认窗(弹窗是系统级,覆盖在桌面上)
- 用户点「安装」→ 弹窗 dismiss → 用户已经在桌面 → 看到 App 图标下载进度
- App 进程留在内存,IPA 替换时由 iOS 自动结束

【私有 API 风险评估】
suspend selector 是 iOS 私有 API,AppStore 审核会拒。本项目企业签 / 渠道分发,
不上架 AppStore,可以用 (CLAUDE.md 第 6 节稳定>一切原则下接受)。
UIControl().sendAction 比 perform() 更安全:selector 不响应时静默忽略,不 crash。

【仅 itms-services 路径触发 suspend】
Safari 路径(普通 https URL)不调 suspend:用户已经在 Safari,主动切后台反而
让 Safari 也消失,糟糕的 UX。

BuildProject 通过。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-27 22:12:59 +08:00
co-authored by Claude Opus 4.7
parent befe205bf9
commit 960adc5491
@@ -554,26 +554,44 @@ public final class WebContainerViewController: UIViewController {
}
private func showIPAUpgradeAlert(downloadURL: String) {
// Safari
// App App exit
// + App
// App App 退
//
// URL iOS itms-services manifest plist
// makeInstallURL https Safari
// appDownload itms-services
// .plist Safari
// iOS itms-services App inactive
// App dismissApp active
// **** App selector
// `suspend` Home /
// / AppStore API
//
// URL itms-services / .plist / Safari makeInstallURL
// Safari suspend Safarisuspend Safari
let alert = UIAlertController(
title: "需要升级",
message: "检测到新版本,点击「确定」开始升级,系统会自动下载并安装。",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in
if let url = Self.makeInstallURL(from: downloadURL) {
UIApplication.shared.open(url)
guard let url = Self.makeInstallURL(from: downloadURL) else { return }
UIApplication.shared.open(url)
if url.scheme?.lowercased() == "itms-services" {
// 0.3s itms-services URL schedule
// App
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
Self.suspendAppToHomeScreen()
}
}
})
present(alert, animated: true)
}
/// App App
/// selector `suspend` Home /
/// `UIControl.sendAction` selector `perform()`
private static func suspendAppToHomeScreen() {
let sel = NSSelectorFromString("suspend")
UIControl().sendAction(sel, to: UIApplication.shared, for: nil)
}
/// `appDownload` iOS Safari
///
/// raw URL