-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move WebView init to main thread. #989
Move WebView init to main thread. #989
Conversation
|
||
if let result = result, self.userAgent.isEmpty { | ||
self.userAgent = "\(result)" | ||
if let webView = self.webView { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the webview has not initialized the completion block will not be called. Is that OK?
Since I initiated this issue myself I have reviewed the code. Here's my feedback There are some potential areas where improvements can be made to ensure thread safety, efficiency, and better management of the user agent string. Here are my suggestions:
I have made a solution in a branch in our own fork Vivino#1 tell me if you want a PR IMO it's a lot of work to fetch the string. Have you ever considered reverse engineering the string format and accept that it might not always reflect the string from WKWebView? |
Hi @weibel, thanks for the review! I like the idea to persist the ua string. However, we still should update the value because the user can update the OS, and the actual string will be changed as well. We want to avoid blocking operations since we had already faced deadlock solving this task with semaphores. So let @OlenaPostindustria propose the fix as well, and we will choose the most reliable variant. |
@YuriyVelichkoPI We could e.g. persist the string with the OS version as key. I tried this in the solution I linked to. It's still in review on our side. Did you consider the following?
|
It should be considered on the committee. I'm not sure how sensitive this info is. In some cases it should be precies. |
@@ -22,11 +22,13 @@ public class UserAgentService: NSObject { | |||
|
|||
public private(set) var userAgent: String = "" | |||
|
|||
private let webView = WKWebView() | |||
private var webView: WKWebView? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to keep the webView
instance the whole time, so I suggest deallocating it when the user agent is fetched. But as we use singleton it can be pretty tricky and can cause problems if we accidentally deallocate the web view instance when another fetchUserAgent
is in progress. My solution here would be to create the web view registry. Check out the code below for more info. Please, let me know your thoughts about this.
@objc(PBMUserAgentService) @objcMembers
public class UserAgentService: NSObject {
public static let shared = UserAgentService()
public private(set) var userAgent: String = ""
private var webViews = [WKWebView]()
override init() {
super.init()
fetchUserAgent()
}
public func fetchUserAgent(completion: ((String) -> Void)? = nil) {
// user agent has been already generated
guard userAgent.isEmpty else {
completion?(userAgent)
return
}
DispatchQueue.main.async {
let webView = WKWebView()
self.webViews.append(webView)
webView.evaluateJavaScript("navigator.userAgent") { [weak self] result, error in
guard let self = self else { return }
if let error {
Log.error(error.localizedDescription)
}
if let result = result, self.userAgent.isEmpty {
self.userAgent = "\(result)"
}
self.webViews.removeAll(where: { $0 == webView })
completion?(self.userAgent)
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should work well since the registry and userAgent
will be managed sequentially in the main thread. userAgent
should be safe for reading from any thread. Alternatively, the value passed to fetchUserAgent
completion handler should be used.
@weibel also mentioned the "warming up" of the WKWebView object before runing the JS code in it. Is it possible that the result of fetching the ua will be different depending on the js execution time in the WebView lifecycle?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OlenaPostindustria thank you so much for your proposed solution! I like this method as it bypasses the proposed issues with singletons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YuriyVelichkoPI No, I don't think so, the result should be the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure about the warm up time. I need to run a few tests. The problems i have had might be becasue the webview was deallocated before the block could return as i did not think about the singleton issue that was solved with registering the views
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Closes #988