SPEC-0015: Safari Web Extension — Apple Distribution and CI Pipeline
Overview
This spec defines the requirements for packaging the existing Manifest V3 Web Extension (SPEC-0008) as a native iOS and macOS Safari Web Extension, distributing it via TestFlight, and automating that distribution through the existing GitHub Actions CI pipeline. See 📝 ADR-0015 for the distribution strategy decision and 📝 ADR-0012 for the extension architecture decision.
Requirements
Requirement: Xcode Project Generation
The integrations/extension/ source directory SHALL be convertible to a valid Xcode project using
xcrun safari-web-extension-converter without requiring modifications to the existing
JavaScript or manifest files beyond what the converter applies automatically. The generated
Xcode project MUST be committed to the repository under integrations/apple/ and MUST target both iOS 15+
and macOS 12+ from a single project with two targets (iOS app and macOS app).
Scenario: Converter runs without errors
- WHEN a developer runs
xcrun safari-web-extension-converter integrations/extension/ --app-name "joe-links" --bundle-identifier net.joestump.joe-links --swift --macos-only false - THEN an Xcode project is created at
integrations/apple/with no conversion errors and the existingbackground.js,popup.html,popup.js,options.html,options.js, andmanifest.jsonare referenced intact
Scenario: Both platform targets present
- WHEN the generated Xcode project is opened
- THEN it contains an iOS app target (
joe-links (iOS)) and a macOS app target (joe-links (macOS)) sharing the same extension bundle
Requirement: iOS Safari Compatibility
The extension MUST function correctly in iOS 15+ Safari. Capabilities that are unavailable in
iOS Safari service workers MUST degrade gracefully without breaking extension startup or keyword
interception. Specifically, OffscreenCanvas (used for programmatic icon drawing) MUST NOT
cause a fatal error when unavailable; the extension MUST fall back to the PNG icons declared
in manifest.json.
Scenario: Extension loads on iOS Safari
- WHEN the TestFlight app is installed on an iPhone running iOS 15 or later and the extension is enabled in Settings → Safari → Extensions
- THEN the extension activates without errors and keyword interception is operational
Scenario: OffscreenCanvas unavailable
- WHEN the service worker attempts to call
setActionIcon()on iOS whereOffscreenCanvasis not supported - THEN the
try/catchinsetActionIcon()silently swallows the error and the extension continues operating with the PNG fallback icons
Scenario: declarativeNetRequest rules applied on iOS
- WHEN the extension starts on iOS 16 or later
- THEN
updateRedirectRules()successfully registers dynamicdeclarativeNetRequestrules for each keyword host; on iOS 15, the call is silently skipped if the API is unavailable
Requirement: macOS Safari Compatibility
The extension MUST function correctly in macOS 12+ Safari. The macOS app wrapper MUST be notarized using the Apple Developer Program distribution certificate so that Gatekeeper accepts it on any Mac without requiring the user to bypass security settings.
Scenario: Extension installs on macOS without Gatekeeper bypass
- WHEN a user downloads the macOS app from TestFlight or a direct link and opens it
- THEN macOS Gatekeeper accepts the app without displaying a security warning, and the extension appears in Safari → Settings → Extensions