优化首次启动app的网络权限体验
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<key>agent</key>
|
||||
<string>veRa0qrBf0df2K1G4de2tgfmVxB2jxpv</string>
|
||||
<key>appversion</key>
|
||||
<string>43</string>
|
||||
<string>44</string>
|
||||
<key>other</key>
|
||||
<string></string>
|
||||
<key>appleconfig</key>
|
||||
|
||||
@@ -194,6 +194,21 @@ public final class WebContainerViewController: UIViewController {
|
||||
// 异步 snapshot NWPath 区分"真断网"和"权限被拒",文案和按钮语义按 kind 精确分流。
|
||||
let path = await Self.currentPathSnapshot()
|
||||
let kind = Self.classifyBootError(error, path: path)
|
||||
|
||||
// 首次启动权限授予竞争:iOS 弹权限对话框时,pipeline 的第一个网络请求
|
||||
// 已经在跑;如果用户没在请求 timeout 前点"允许",请求会 fail 且 NWPath
|
||||
// 报 wifiDenied / cellularDenied —— 但用户可能"正准备点允许"。此时立
|
||||
// 即弹"前往设置"会让用户困惑("我明明点了允许还弹")。
|
||||
//
|
||||
// 策略:只在 kind.isPermissionDenied 时,短暂观察 NWPath;5s 内若变
|
||||
// satisfied → 静默重跑 pipeline(等价用户点允许后我们自动重试);超时
|
||||
// 或仍拒绝 → 走原逻辑弹"前往设置"。真拒绝的长尾场景多等 5s 可接受。
|
||||
if kind.isPermissionDenied,
|
||||
await Self.waitForNetworkPermissionResolution(timeout: 5.0) {
|
||||
await runBootPipeline()
|
||||
return
|
||||
}
|
||||
|
||||
presentBootError(error, kind: kind)
|
||||
}
|
||||
}
|
||||
@@ -698,6 +713,34 @@ public final class WebContainerViewController: UIViewController {
|
||||
monitor.start(queue: DispatchQueue.global(qos: .userInitiated))
|
||||
}
|
||||
}
|
||||
|
||||
/// 等首次启动权限对话框有决定 —— 最长 `timeout` 秒内观察 NWPath 是否变
|
||||
/// `.satisfied`。用于消除"用户点允许太慢就弹前往设置"的误报体验。
|
||||
/// 返回 true 表示已授权(可静默重试 pipeline);false 表示超时或明确拒绝
|
||||
/// (应走原逻辑弹前往设置)。
|
||||
private static func waitForNetworkPermissionResolution(timeout: TimeInterval) async -> Bool {
|
||||
await withCheckedContinuation { (cont: CheckedContinuation<Bool, Never>) in
|
||||
let monitor = NWPathMonitor()
|
||||
nonisolated(unsafe) var resumed = false
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
|
||||
guard !resumed else { return }
|
||||
resumed = true
|
||||
monitor.cancel()
|
||||
cont.resume(returning: false)
|
||||
}
|
||||
|
||||
monitor.pathUpdateHandler = { path in
|
||||
guard !resumed else { return }
|
||||
if path.status == .satisfied {
|
||||
resumed = true
|
||||
monitor.cancel()
|
||||
cont.resume(returning: true)
|
||||
}
|
||||
}
|
||||
monitor.start(queue: DispatchQueue.global(qos: .userInitiated))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WKNavigationDelegate
|
||||
|
||||
Reference in New Issue
Block a user