Platforms

iOS simulator

PointfixKit adds long-press capture, a composer and a progress pill to Debug builds running in the iOS simulator. Release builds compile it out.

Prefer to start from working code? The ios sample is set up with the published SDK.

Add the Swift package

In Xcode, choose File → Add Package Dependencies…, enter https://github.com/pointfix-dev/pointfix-ios, and add the PointfixKit library to your iOS app target.

In a Package.swift:

dependencies: [
    .package(url: "https://github.com/pointfix-dev/pointfix-ios", from: "0.2.0")
],
targets: [
    .target(name: "App", dependencies: [
        .product(name: "PointfixKit", package: "pointfix-ios")
    ])
]

The SDK requires iOS 26 or later, Xcode 27 or newer (Swift 6.4).

Install the host

Apply .pointfixHost() once, to the root view inside your main window:

import PointfixKit

@main
struct ExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .pointfixScreen("Home")
                .pointfixHost()
        }
    }
}
Host modifiers
ModifierUse
.pointfixHost()Connects to the bridge on this Mac at http://127.0.0.1:4747. The simulator shares the Mac’s loopback interface.
.pointfixHost(port: 4848)A different local port, 1024–65535. Match the bridge’s port.
.pointfixHost(url: URL(string: "http://studio.local:4747")!)A bridge on another Mac, using a URL from that bridge’s Connections page. It needs remote access. The URL must be http or https with a host.

Keep the host on the window root so its composer and progress pill cover navigation and tab bars. Install only one host per window; a host on a nested view can clip the composer. The agent and model are chosen in Pointfix’s Preferences, not in your app.

Name screens: .pointfixScreen

.pointfixScreen("Checkout") gives reports a readable screen name. Put it on a screen’s root view; the name updates when the value changes. The screen name is also used to match the Proposed screenshot to the screen you reported.

Element context and accessibility identifiers

Individual elements need no Pointfix code. When you long-press, the SDK looks for the element in this order:

  1. The smallest .pointfixable marker under your finger (see below).
  2. A UIKit view under the touch with an accessibilityIdentifier, then SwiftUI accessibility elements, preferring ones with an identifier and the smallest frame. In simulator Debug builds the SDK turns on the app’s accessibility tree so SwiftUI identifiers are visible.
  3. An accessibility label or label text.

Existing identifiers are picked up automatically:

Button("Continue", action: continueCheckout)
    .accessibilityIdentifier("checkout.continue")

The bridge then searches your repository for the identifier (or the label) and gives the agent the matches. Identifiers are search hints, not guaranteed source locations, and you don’t need to add one to every element.

If the AXe simulator inspector is installed (on your PATH, or set POINTFIX_AXE to its executable), the bridge also resolves the touch against the simulator’s accessibility tree and adds parent context and nearby labels. Reports with an explicit source marker skip this lookup. Without any element, a report still has the screenshot and touch point.

Exact source: .pointfixable

Text("Continue to checkout")
    .pointfixable("checkout.continue")

.pointfixable(_ name:, file:, line:) records its call site’s file and line (defaults #filePath and #line) and the view’s frame. Markers take precedence over automatic identification, and nested markers use the smallest frame under the touch. Mark text for copy or color changes, and containers for layout changes.

Long-press capture

Run a Debug build in the simulator and hold an element for 0.6 seconds. The recognizer is attached to the host’s window and works alongside your app’s own gestures; once the hold is recognized, the original touch is cancelled. The SDK takes the screenshot first, outlining the element (or ringing the touch point when there is no element), then opens the composer. Pointfix’s own views are never reported.

The composer

The captured screen stays visible behind the composer, dimmed, with the selected element lit.

  • The header shows the identifier (in monospace) or the element’s name, with the screen, platform and source file.
  • Category chips (Spacing, Color, Text, Size, Layout, Other) are optional; tap again to clear.
  • Type the change in What should change?. Send is enabled once there is text.
  • Context shows a read-only preview of the Context Packet, refreshed as you type. The choice is remembered.
  • ✕ cancels.

Progress and review actions

After you send, a progress pill at the top of the app shows the status, your request, the elapsed time and four steps: Sent, Agent, Relaunch, Review. It polls the bridge every 2 seconds and shows Bridge unreachable after three failed attempts.

When the report reaches review, the pill offers:

  • Commit fix: commit the agent’s files, as in Review & commit.
  • I’ll commit: mark it reviewed and commit yourself.
  • The camera button, Retake proposed screen: send the current screen as the Proposed screenshot, for example after navigating back to the reported screen.

Finished reports leave the screen after 5 seconds; failures stay on screen; dismiss them with ✕. With a bridge on another Mac, use the review actions on that Mac: remote devices can only send reports.

The SDK saves the latest report ID. When the app is relaunched, it reports the launch to the bridge and, once the reported screen is showing, sends it as the Proposed screenshot.

Release builds

All PointfixKit modifiers are compiled only in Debug builds. In Release they return the view unchanged, and the capture, network and UI code is excluded. Capture is also attached only when running in the simulator.

Limitations

  • Physical iOS devices are not supported; they don’t share the Mac’s loopback interface.
  • Sheets and full-screen covers are presented above the root host. Showing the composer while one is open needs app-specific checking; dismiss it before capturing if they conflict.
  • Install one host per window root.
  • The long-press can compete with long-press gestures in your app.

For every public symbol, see the Swift API reference. To report without adding the SDK, use the Simulator panel.