Skip to content
lyftPublic

About

iOS touch synthesis library

Topics

Resources

Code of conduct

Contributing

Stars

708 stars

Watchers

182 watching

Forks

Repository files navigation

Hammer

If you can't touch this, it's Hammer time!

Demo

Hammer is a touch, stylus and keyboard synthesis library for emulating user interaction events. It enables better ways of triggering UI actions in unit tests, replicating a real world environment as much as possible.

  • Real events: touches and key presses go through UIKit like a user's, so gesture recognizers, controls and SwiftUI handle them as they would on a device.
  • Tests: tap, swipe, drag, pinch and type in XCTest or Swift Testing, finding views by accessibility identifier, label, value or traits.
  • Running apps: the hammer command line tool and AI agents can drive an app that runs Hammer's agent, see what's on screen and try view changes live.

⚠️ IMPORTANT: This library makes extensive use of private APIs and should never be included in a production app.

Installation

Hammer requires Swift 6.0 and iOS 17.0 or later. Add it with SwiftPM:

.package(url: "https://github.com/lyft/Hammer.git", from: "2.0.0")

Add the Hammer product to your test target, and HammerAgent to your app to drive it with the command line tool. CocoaPods is deprecated.

Writing tests

Hammer tests need a host application to send touches. Create an EventGenerator for the view, view controller, SwiftUI view or window you test, then send it events:

@MainActor
final class SignInTests: XCTestCase {
    func testSignIn() async throws {
        let eventGenerator = try await EventGenerator(viewController: SignInViewController())

        try await eventGenerator.fingerTap(at: "email_field")
        try await eventGenerator.keyType("me@example.com")
        try await eventGenerator.fingerTap(at: .label("Sign In", traits: .button))
        try await eventGenerator.waitUntilVisible("home_title")
    }
}

Locations can be an accessibility identifier, an AccessibilityQuery, a view or a point. A few more of what you can do:

try await eventGenerator.fingerSwipe(at: "list", direction: .up)
try await eventGenerator.fingerEdgeSwipe(direction: .right)
try await eventGenerator.fingerDrag(from: "card", to: "trash", duration: 0.5)
try await eventGenerator.fingerPinch(fromDistance: 100, toDistance: 50, duration: 0.5)
try await eventGenerator.keyPress(.returnOrEnter)
try await eventGenerator.waitUntilHittable("checkout_button")

Learn more:

Driving a running app

Start the agent in a debug build of your app. By default it only runs in the simulator and only accepts connections from the same Mac:

#if DEBUG
import HammerAgent

let agent = HammerAgent()
try await agent.start()
#endif

Install the prebuilt hammer tool from the latest release:

mkdir -p ~/.local/bin
curl -fsSL https://github.com/lyft/Hammer/releases/latest/download/hammer-macos.zip -o /tmp/hammer-macos.zip
unzip -o /tmp/hammer-macos.zip -d ~/.local/bin

Then see what's on screen and act on it:

hammer describe                      # Elements on screen, each with a ref such as e12
hammer tap --ref e12
hammer type-text --identifier email --text "me@example.com"
hammer wait-for --identifier home_title --condition visible
hammer screenshot --output screen.png
hammer help swipe                    # Arguments and examples of a command

To give an AI agent the same commands as tools, run hammer mcp as an MCP server. For example, with Claude Code:

claude mcp add hammer -- ~/.local/bin/hammer mcp

Learn more:

Documentation

Every guide is in docs.

License

Hammer is released under the Apache License. See LICENSE

About

iOS touch synthesis library

Topics

Resources

Code of conduct

Contributing

Stars

708 stars

Watchers

182 watching

Forks

Releases

Packages

Used by

Contributors

Languages