A Safari extension for iOS that provides developer console functionality similar to desktop browser DevTools.
- Console: View
console.log,console.warn,console.error, andconsole.infooutput - JavaScript Execution: Run arbitrary JavaScript on the current page
- Error Tracking: Capture runtime errors and unhandled promise rejections
- Network Monitoring: Inspect fetch and XMLHttpRequest calls with request/response details
- In-App Log Viewer: View captured logs directly in the host app, organized by tab
- Host App: SwiftUI + SwiftData (iOS 17+)
- Popup UI: Vite + React + TypeScript + Tailwind CSS
- Icons: Lucide React
- Linting/Formatting: Biome
devtools/
├── Shared/ # Code shared between app and extension
│ ├── Settings.swift # Shared settings (App Group UserDefaults)
│ ├── DebugLog.swift # SwiftData model for console logs
│ └── NetworkLog.swift # SwiftData model for network requests
├── popup-app/ # Vite + React + TypeScript popup UI
│ ├── src/
│ │ ├── App.tsx # Main app with tab navigation
│ │ ├── index.css # Tailwind + custom DevTools styles
│ │ ├── components/
│ │ │ ├── Console.tsx # Console logs + JS execution
│ │ │ └── Network.tsx # Network requests viewer
│ │ ├── content.ts # Content script (bridges page ↔ extension)
│ │ ├── inject.ts # Injected script (intercepts console/network)
│ │ └── types.ts # TypeScript definitions
│ ├── vite.config.ts # Popup build config
│ ├── vite.content.config.ts # Content script build config
│ └── vite.inject.config.ts # Inject script build config
├── devtools Extension/
│ ├── Resources/ # Built extension files (gitignored)
│ │ ├── popup.html
│ │ ├── popup.js
│ │ ├── popup.css
│ │ ├── content.js
│ │ ├── inject.js
│ │ ├── background.js
│ │ └── manifest.json
│ ├── SafariWebExtensionHandler.swift # Native message handler
│ └── Info.plist
├── devtools/ # iOS SwiftUI app
│ ├── DevToolsApp.swift # App entry point
│ ├── ContentView.swift # Main TabView
│ ├── SetupView.swift # Extension setup instructions
│ ├── DebugView.swift # Console log viewer
│ ├── NetworkView.swift # Network request viewer
│ └── SettingsView.swift # App settings
└── devtools.xcodeproj/
- Xcode 16+
- iOS 17+ deployment target
- Node.js 18+
- pnpm
- Select the
devtoolstarget → Signing & Capabilities → + Capability → App Groups - Add:
group.co.za.stephancill.devtools - Repeat for the
devtools Extensiontarget with the same group name
cd popup-app
pnpm installcd popup-app
pnpm buildThis builds:
popup.html,popup.js,popup.css- React popup UIcontent.js- Content scriptinject.js- Page injection script
Open devtools.xcodeproj in Xcode and build. The Xcode build phase automatically runs pnpm build before compiling.
A test web app is included for testing the extension:
cd test-app
pnpm install
pnpm devThen open http://localhost:5173 in Safari and use the extension.
- Content Script (
content.ts): Injected into every page, bridges communication between the page and extension - Inject Script (
inject.ts): Runs in page context, interceptsconsole.*,fetch, andXMLHttpRequest - Background Script (
background.js): Stores captured logs/requests, relays messages, and sends to native handler - Popup (
App.tsx): React UI that displays logs and network requests
Communication flow:
Page → inject.js → (postMessage) → content.js → (runtime.sendMessage) → background.js → popup
↓
(sendNativeMessage)
↓
SafariWebExtensionHandler → SwiftData
↓
Host App (reads & displays)
- Storage: SwiftData with shared App Group container
- Limits: Configurable max logs/requests per tab (default 500/200)
- Cleanup:
- Immediate cleanup when tabs close
- Periodic sync every 5 minutes to catch missed events
- Time-based expiry (default 24 hours)