IPA 升级 URL 自动包成 itms-services:跳过 Safari、直接弹系统安装窗
【UX 目标】 点「确定」→ 直接弹系统安装确认窗(不跳 Safari)→ 用户点「安装」→ App 自然切后台、用户看到桌面图标下载进度,App 进程留在内存不退出。 【方案】 makeInstallURL 规范化 appDownload: - itms-services://... → 原样 - https?://...*.plist (mfst) → 包成 itms-services://?action=download-manifest&url=... - 其它(网页 / 短链) → 原样跳 Safari(后台需把 URL 改成上面两种之一才能跳过 Safari) 不再需要 exit(0);iOS 处理 itms-services 时本来就会把 App 切后台,自动满足用户 "回到桌面但 App 不退出"的需求。 文案改为"开始升级,系统会自动下载并安装"匹配新流程。 BuildProject 通过。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
692c849e0d
commit
befe205bf9
@@ -554,27 +554,48 @@ public final class WebContainerViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func showIPAUpgradeAlert(downloadURL: String) {
|
||||
// ⚠️ 不要在跳 Safari 后自动 exit(0):
|
||||
// iOS 的 itms-services:// 系统安装确认弹窗依赖 source app 仍在内存中
|
||||
// 才能弹出(系统安全机制);提前 exit 会让 Safari 收到 URL 但确认窗
|
||||
// 不显示,整个安装流程被悄悄中断。
|
||||
// 行为目标:点「确定」直接弹系统安装窗(不跳 Safari),用户点「安装」后
|
||||
// App 自然切后台、用户看到桌面图标下载进度,App 进程留在内存,无需主动 exit。
|
||||
//
|
||||
// 正确做法:跳 Safari 后让 App 自然留在后台。用户在 Safari 点「安装」
|
||||
// 后,iOS 开始下载 → 替换 IPA 时会自动结束旧 App 进程(或要求用户上滑),
|
||||
// 无需主动介入。
|
||||
// 实现依赖:URL 必须是 iOS 可直接接管的安装协议(itms-services 或 manifest plist 直链)。
|
||||
// 详见 makeInstallURL 注释。如果远端配的是 https 网页链接,仍会跳 Safari
|
||||
// 给用户看引导页 —— 这是预期外的情形,需后台把 appDownload 改成 itms-services
|
||||
// 或 .plist 直链才能跳过 Safari 中转。
|
||||
let alert = UIAlertController(
|
||||
title: "需要升级",
|
||||
message: "检测到新版本,点击「确定」前往下载。\n\n安装确认弹窗出现后请点击「安装」,然后从多任务列表上滑退出本 App,更新即可自动完成。",
|
||||
message: "检测到新版本,点击「确定」开始升级,系统会自动下载并安装。",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in
|
||||
if let url = URL(string: downloadURL) {
|
||||
if let url = Self.makeInstallURL(from: downloadURL) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
/// 把远端 `appDownload` 规范化为 iOS 系统可直接接管的安装协议,避免跳 Safari 中转。
|
||||
///
|
||||
/// 三种 raw URL 形态:
|
||||
/// - `itms-services://...` → 原样返回(iOS 直接弹安装窗)
|
||||
/// - `https?://...*.plist`(企业签 manifest 直链)→ 自动包成
|
||||
/// `itms-services://?action=download-manifest&url=<encoded>`,iOS 直接弹安装窗
|
||||
/// - 其它(网页 / 短链等)→ 原样跳转,由 Safari 接管。这种情形下后台需要把
|
||||
/// URL 改成上面两种之一才能跳过 Safari 中转
|
||||
private static func makeInstallURL(from raw: String) -> URL? {
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespaces)
|
||||
let lower = trimmed.lowercased()
|
||||
if lower.hasPrefix("itms-services://") {
|
||||
return URL(string: trimmed)
|
||||
}
|
||||
if (lower.hasPrefix("https://") || lower.hasPrefix("http://")),
|
||||
lower.hasSuffix(".plist") {
|
||||
let encoded = trimmed.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? trimmed
|
||||
return URL(string: "itms-services://?action=download-manifest&url=\(encoded)")
|
||||
}
|
||||
return URL(string: trimmed)
|
||||
}
|
||||
|
||||
/// 启动期错误的精确分类。先看 NWPath.unsatisfiedReason 区分"真断网"和"权限被拒"
|
||||
/// (iOS 14+ 两类都报 NSURLError -1009,但 path 里有 wifiDenied / cellularDenied
|
||||
/// 标记),fallback 看 NSURLError code。
|
||||
|
||||
Reference in New Issue
Block a user