Syncing deviceUUID Between iOS mobile SDK and JS SDKs in app Using a WebView
Expected result of the “Syncing deviceUUID Between iOS mobile SDK and JS SDKs in app Using a WebView” step:A single customer profile is created in Maestra, combining actions from both the mobile SDK and the website using the JS SDK.
If your app uses a WebView that loads a website with the JS tracker, installing the mobile app will result in two separate customer profiles in Maestra, each with a different deviceUUID: one from the JS SDK and one from the mobile SDK.
To prevent this and keep all data in a single customer profile, follow the steps below:
- Add code to your app that syncs the deviceUUID with the JS SDK when it is received from the Mobile SDK.
func syncMindboxDeviceUUIDs(with webView: WKWebView) {
Mindbox.shared.getDeviceUUID { uuid in
guard !uuid.isEmpty else {
Mindbox.logger.log(level: .error, message: "[WebView]: Device UUID is empty or invalid")
return
}
let script = """
document.cookie = "mindboxDeviceUUID=\(uuid); path=/";
window.localStorage.setItem('mindboxDeviceUUID', '\(uuid)');
"""
DispatchQueue.main.async {
webView.evaluateJavaScript(script) { _, error in
if let error = error {
Mindbox.logger.log(level: .error, message: "[WebView]: Error setting cookies and localStorage: \(error)")
} else {
Mindbox.logger.log(level: .default, message: "[WebView]: Cookies and localStorage set successfully.")
}
}
}
}
}- When implementing
WKNavigationDelegatefor yourWKWebView, call this method inwebView(_:didCommit:).
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
viewModel.syncMindboxDeviceUUIDs(with: webView)
let message = "[WebView]: \(#function): Content started arriving for: \(webView.url?.absoluteString ?? "Unknown URL")"
Mindbox.logger.log(level: .debug, message: message)
}Implementation Example
import Observation
import WebKit
import Mindbox
@Observable final class ViewModel {
/// Syncing deviceUUID
func syncMindboxDeviceUUIDs(with webView: WKWebView) {
Mindbox.shared.getDeviceUUID { uuid in
guard !uuid.isEmpty else {
Mindbox.logger.log(level: .error, message: "[WebView]: Device UUID is empty or invalid")
return
}
let script = """
document.cookie = "mindboxDeviceUUID=\(uuid); path=/";
window.localStorage.setItem('mindboxDeviceUUID', '\(uuid)');
"""
DispatchQueue.main.async {
webView.evaluateJavaScript(script) { _, error in
if let error = error {
Mindbox.logger.log(level: .error, message: "[WebView]: Error setting cookies and localStorage: \(error)")
} else {
Mindbox.logger.log(level: .default, message: "[WebView]: Cookies and localStorage set successfully.")
}
}
}
}
}
/// Use this method to clear WebView data.
func clearAllWebsiteData() {
let dataStore = WKWebsiteDataStore.default()
let dataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
dataStore.removeData(ofTypes: dataTypes, modifiedSince: Date.distantPast) {
Mindbox.logger.log(level: .default, message: "[WebView]: All web data cleared")
}
}
}
extension ViewModel {
/// Can be used to view and verify the deviceUUID.
func viewCookiesAndLocalStorage(with webView: WKWebView) {
print("\n" + #function)
Mindbox.shared.getDeviceUUID { uuid in
let message = "[WebView]: Mobile SDK UUID: \(uuid)"
print(message)
Mindbox.logger.log(level: .default, message: message)
}
let script = """
JSON.stringify({
cookies: document.cookie || "No cookies found",
localStorage: window.localStorage.getItem('mindboxDeviceUUID') || "No value found"
})
"""
DispatchQueue.main.async {
webView.evaluateJavaScript(script) { result, error in
if let error = error {
let message = "[WebView]: Error retrieving cookies and localStorage: \(error)"
print(message)
Mindbox.logger.log(level: .error, message: message)
} else {
let message = "[WebView]: Cookies and LocalStorage: \(result ?? "nil")"
print("Start===============")
print("Cookies and LocalStorage: \(result ?? "nil")")
print("===============End\n")
Mindbox.logger.log(level: .default, message: message)
}
}
}
}
}import SwiftUI
import WebKit
import Mindbox
struct WebView: UIViewRepresentable {
static var currentWebView: WKWebView?
let url: URL?
var viewModel: ViewModel
func makeCoordinator() -> Coordinator {
Coordinator(viewModel: viewModel)
}
func makeUIView(context: Context) -> WKWebView {
let wkWebView = WKWebView()
wkWebView.navigationDelegate = context.coordinator
#if DEBUG
wkWebView.isInspectable = true
#endif
guard let url else { return wkWebView }
let request = URLRequest(url: url)
wkWebView.load(request)
WebView.currentWebView = wkWebView
return wkWebView
}
func updateUIView(_ uiView: WKWebView, context: Context) { }
final class Coordinator: NSObject {
var viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
}
}
// MARK: - WKNavigationDelegate
extension WebView.Coordinator: WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
viewModel.syncMindboxDeviceUUIDs(with: webView)
let message = "[WebView]: \(#function): Content started arriving for: \(webView.url?.absoluteString ?? "Unknown URL")"
Mindbox.logger.log(level: .debug, message: message)
}
}import SwiftUI
import WebKit
struct ContentView: View {
let webViewModel: ViewModel
private let url = URL(string: "https://your-website.com/")
var body: some View {
VStack {
WebView(url: url, viewModel: webViewModel)
Button("Print in console Cookies and LocalStorage") {
guard let webView = WebView.currentWebView else {
print("WebView is not initialized yet")
return
}
webViewModel.viewCookiesAndLocalStorage(with: webView)
}.padding()
}
}
}Updated 7 months ago

