diff --git a/Sources/Segment/Plugins/Context.swift b/Sources/Segment/Plugins/Context.swift index cb5346b4..0698ef0c 100644 --- a/Sources/Segment/Plugins/Context.swift +++ b/Sources/Segment/Plugins/Context.swift @@ -108,9 +108,6 @@ public class Context: PlatformPlugin { "width": screen.width, "height": screen.height ] - // user-agent - let userAgent = device.userAgent - context["userAgent"] = userAgent // locale if Locale.preferredLanguages.count > 0 { context["locale"] = Locale.preferredLanguages[0] @@ -147,6 +144,13 @@ public class Context: PlatformPlugin { "wifi": wifi ] + // user-agent + // BKS: This use to be in the static section, however it was discovered that on some platforms + // there can be a delay in retrieval. It has to be fetched on the main thread, so we've spun it off + // async and cache it when it comes back. + let userAgent = device.userAgent + context["userAgent"] = userAgent + // other stuff?? ... } diff --git a/Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift b/Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift index 8736addd..edf7162b 100644 --- a/Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift +++ b/Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift @@ -19,6 +19,7 @@ import WebKit internal class iOSVendorSystem: VendorSystem { private let device = UIDevice.current + @Atomic private static var asyncUserAgent: String? = nil override var manufacturer: String { return "Apple" @@ -65,17 +66,15 @@ internal class iOSVendorSystem: VendorSystem { override var userAgent: String? { #if !os(tvOS) - var userAgent: String? - - if Thread.isMainThread { - userAgent = WKWebView().value(forKey: "userAgent") as? String - } else { - DispatchQueue.main.sync { - userAgent = WKWebView().value(forKey: "userAgent") as? String + // BKS: It was discovered that on some platforms there can be a delay in retrieval. + // It has to be fetched on the main thread, so we've spun it off + // async and cache it when it comes back. + if Self.asyncUserAgent == nil { + DispatchQueue.main.async { + Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String } } - - return userAgent + return Self.asyncUserAgent #else // webkit isn't on tvos return "unknown" @@ -198,6 +197,7 @@ import WebKit internal class MacOSVendorSystem: VendorSystem { private let device = ProcessInfo.processInfo + @Atomic private static var asyncUserAgent: String? = nil override var manufacturer: String { return "Apple" @@ -238,16 +238,15 @@ internal class MacOSVendorSystem: VendorSystem { } override var userAgent: String? { - var userAgent: String? - if Thread.isMainThread { - userAgent = WKWebView().value(forKey: "userAgent") as? String - } else { - DispatchQueue.main.sync { - userAgent = WKWebView().value(forKey: "userAgent") as? String + // BKS: It was discovered that on some platforms there can be a delay in retrieval. + // It has to be fetched on the main thread, so we've spun it off + // async and cache it when it comes back. + if Self.asyncUserAgent == nil { + DispatchQueue.main.async { + Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String } } - - return userAgent + return Self.asyncUserAgent } override var connection: ConnectionStatus { diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index e053bb8b..81d3d473 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -123,6 +123,14 @@ final class Analytics_Tests: XCTestCase { let outputReader = OutputReaderPlugin() analytics.add(plugin: outputReader) +#if !os(watchOS) && !os(Linux) + // prime the pump for userAgent, since it's retrieved async. + let vendorSystem = VendorSystem.current + while vendorSystem.userAgent == nil { + RunLoop.main.run(until: Date.distantPast) + } +#endif + waitUntilStarted(analytics: analytics) // add a referrer