Today WebView Bridge
Native Reference

macOS

The same WKWebView handler as iOS, hosted in AppKit.

macOS uses the same WKWebView and the same WKScriptMessageHandlerWithReply as iOS — WebKit is shared across Apple platforms. The bridge implementation is identical; only the hosting layer (AppKit instead of UIKit) and the platform tag differ. Read the iOS page for the full handler; this page only calls out the deltas.

Register the channel

import WebKit

let config = WKWebViewConfiguration()
config.userContentController.addScriptMessageHandler(
  TodayWebViewBridge(),
  contentWorld: .page,
  name: "todayWebViewBridge"
)
let webView = WKWebView(frame: .zero, configuration: config)
// hosted by an NSViewController / NSWindow rather than a UIViewController

The TodayWebViewBridge handler — the track / headers / refreshToken / establishSession dispatch, resolve-vs-reject rules, refreshToken coalescing, and the WKHTTPCookieStore cookie mint — is byte-for-byte the iOS handler. Share one type between the two targets.

Inject the platform tag

The only behavioural difference the web can observe is the platform value. Inject 'macos' at document start so the SDK does not have to fall back to user-agent sniffing:

let tag = WKUserScript(
  source: "window.__todayWebView = { platform: 'macos' };",
  injectionTime: .atDocumentStart,
  forMainFrameOnly: true
)
config.userContentController.addUserScript(tag)

Without the tag the SDK still resolves macos from a non-touch Mac user-agent, but the tag is the contract — do not rely on the heuristic. See Environment detection.

Contract notes specific to macOS

  • Shared handler. Do not fork the dispatch logic. The iOS and macOS targets compile the same WKScriptMessageHandlerWithReply; only injection and the tag differ.
  • Platform tag. 'macos' — the one signal that distinguishes this host from iOS, since both expose an identical webkit.messageHandlers channel.
  • client_plat. Enrich track events with client_plat: "macos", matching the cloud closed enum.
  • Main-frame + trusted surface. headers and refreshToken are token-bearing — serve them to the main document frame only on an HTTPS trusted surface; a cross-origin sub-frame, or an untrusted / cleartext main-frame URL, MUST receive undefined. refreshToken shares the FeedWebHeaders.isTokenTrustedSurface gate with the headers bearer — the shared withCommonHandlers() wiring is identical to iOS. See Capabilities.
  • establishSession. Mints the embed_bearer cookie into the shared WKHTTPCookieStore exactly as on iOS — the bearer is never returned to JS. No macOS-specific delta.
  • Everything else (trust-domain headers, refreshToken coalescing, content world .page, navigation-injection limitation) is exactly as documented for iOS.

On this page