iOS Safari Web Extension: Communication with Apple Watch App fails

Summary

The issue at hand involves a Safari Web Extension on iOS that is unable to communicate directly with an Apple Watch App using WatchConnectivity. Despite successful communication between the main iOS app and the Apple Watch, attempts to initiate this connection from the Safari Web Extension result in an XPC interruption, indicating a potential sandbox limitation imposed by Apple.

Root Cause

The root cause of this issue appears to be the sandboxed environment of the Safari Web Extension, which restricts its ability to connect to the Watch Connectivity Daemon (com.apple.wcd). This limitation prevents the extension from activating a WCSession, necessary for communication with the Apple Watch.

Why This Happens in Real Systems

This issue arises due to the security and privacy measures Apple has put in place for Safari Web Extensions. These extensions run in a sandboxed environment to protect user data and prevent malicious activities. However, this sandboxing also limits their ability to interact with other system services, such as WatchConnectivity, without explicit permissions or workarounds.

Real-World Impact

The impact of this limitation is significant for developers aiming to create seamless interactions between Safari Web Extensions and Apple Watch Apps. It restricts the development of features that rely on real-time communication between these components, forcing developers to seek alternative, often less direct, methods of data transfer.

Example or Code

import SafariServices
import WatchConnectivity
import os.log

class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling, WCSessionDelegate {
    func beginRequest(with context: NSExtensionContext) {
        // Extracting message from JS...
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate() // Activation fails due to sandboxing
        }
    }

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if activationState ==.activated {
            session.transferUserInfo(["time": "12:00"])
        }
    }
}

How Senior Engineers Fix It

Senior engineers address this issue by implementing workarounds such as using App Groups for data sharing between the Safari Web Extension and the main iOS app. The extension writes data to a shared UserDefaults suite, and the main app, running in the background, observes these changes to forward the data to the Apple Watch using WatchConnectivity. However, this approach requires the main app to be running, which may not always be the case if the app is force-quit or suspended by the system.

Why Juniors Miss It

Junior engineers might overlook the sandboxing limitations of Safari Web Extensions and the implications for system service interactions. They may not fully understand the restrictions on accessing certain frameworks like WatchConnectivity from within an extension, leading to attempts to directly activate WCSession without considering the necessary workarounds or alternative approaches.