OpenPanel.initialize blocks main thread / crashes on background thread due to WKWebView + DispatchSemaphore deadlock in getiOSUserAgent

未关闭
#6 0 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
48/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
冷清
技术栈
swift
领域
mobile

调研方向

Start with DeviceInfo.getiOSUserAgent and trace how OpenPanel.initialize obtains and uses the user agent. Verify the behavior when initialization is called on both the main and a background thread, then make completion or fallback handling non-blocking while preserving the expected user-agent value.

由索引模型根据 Issue 内容生成。

描述

OpenPanel.initialize is not safe to call from either the main thread or a background thread due to a deadlock in the internal getiOSUserAgent implementation. This makes it impossible to initialize the SDK without causing either a crash or a ~1 second UI freeze.
Root cause
Inside DeviceInfo.getiOSUserAgent():

let webView = WKWebView(frame: .zero)           // (1) must be on main thread
var userAgent = ""
let semaphore = DispatchSemaphore(value: 0)

DispatchQueue.main.async {                      // (2) queued on main thread
    webView.evaluateJavaScript("navigator.userAgent") { (result, error) in
        if let agent = result as? String { userAgent = agent }
        semaphore.signal()
    }
}

_ = semaphore.wait(timeout: .now() + 1.0)      // (3) blocks calling thread

This creates two mutually exclusive failure scenarios:
Scenario A — Called from main thread:
Step (1) is fine.
Step (3) blocks the main thread via semaphore.wait.
Step (2)'s main.async block is queued but can never execute because the main thread is blocked.
The semaphore always times out after 1 full second, freezing the UI every time.
Scenario B — Called from a background thread:
Step (1) crashes immediately with Main Thread Checker: UI API called on a background thread: -[WKWebView .cxx_construct].

Observed crash (background thread):

Main Thread Checker: UI API called on a background thread: -[WKWebView .cxx_construct]
Queue name: com.apple.root.utility-qos

#5  OpenPanel.DeviceInfo.getiOSUserAgent()
#6  OpenPanel.DeviceInfo.getUserAgent()
#7  OpenPanel.OpenPanel.initialize(options:)

Observed freeze (main thread): OpenPanel.initialize always blocks the calling thread for exactly 1 second (the semaphore timeout), regardless of device speed, because the JS evaluation can never signal the semaphore while the thread is waiting on it.
Expected behavior
OpenPanel.initialize should be safe to call from the main thread without blocking it. The user agent should be fetched asynchronously and either:
Cached lazily on first use after a non-blocking async fetch, or
Fetched during initialize without blocking (e.g. completion-based), or
Simply use getBasicUserAgent() as the default and upgrade to the WKWebView-based UA once it becomes available asynchronously.

Suggested fix:

private static func getiOSUserAgent(completion: @escaping (String) -> Void) {
    DispatchQueue.main.async {
        let webView = WKWebView(frame: .zero)
        webView.evaluateJavaScript("navigator.userAgent") { result, _ in
            let agent = (result as? String) ?? getBasicUserAgent()
            completion(agent + " OpenPanel/\(OpenPanel.sdkVersion)")
        }
    }
}
主要语言
Swift
星标
5
派生
8
PR 合并指标
30 天内没有已合并 PR

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

Openpanel-dev/swift-sdk 的其他 Issue

查看 Openpanel-dev/swift-sdk 的全部 Issue

相似的 Issue

更多 Swift Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。