An iOS build passes on a cloud Mac, and the confirmation button works in the simulator. That still does not mean the team has accepted the haptic feedback. The simulator can check whether the code prepares the right event, but it cannot let a tester feel the vibration. Treat these as separate checks: automate the first, and reserve the second for a physical device that supports haptics.
Define what the feedback should communicate
Take “Save complete” as an example. The feedback should fire once after the save succeeds, not as soon as the user taps the button. If the save fails, the interface needs to show an error, but it must not play success feedback. Before writing tests, define the contract in three parts: trigger condition, event type, and number of triggers.
Do not use haptics in place of text or a visible state. Users may turn off system haptics or use a device that does not support the chosen haptic capability. The save result should always have a visible confirmation; haptics add another cue to that result.
Automated tests establish “which feedback was requested.” Acceptance on a physical device answers “whether that feedback feels right.” Neither replaces the other.
Keep the event specification separate from playback code
If event parameters are scattered across button callbacks, tests can only infer what happened by tapping through the interface. Create a function that generates the events, then have the playback layer use it. Tests can then inspect event count, type, and relative timing directly, without depending on whether hardware vibrates.
Run event checks on a cloud Mac
The example below uses a single transient event to represent “Save complete.” Its parameters are a starting point, not recommended intensity settings for every product; confirm the final values by testing on a physical device.
import CoreHaptics
func confirmationEvents() -> [CHHapticEvent] {
let event = CHHapticEvent(
eventType: .hapticTransient,
parameters: [
CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.65),
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.35)
],
relativeTime: 0
)
return [event]
}
In the project’s test target, start with the contract that does not depend on a playback device. The test and application code should reference the same confirmationEvents() function. Do not recreate the event in the test: the two sets of parameters could drift apart.
import XCTest
import CoreHaptics
@testable import YourApp
final class ConfirmationHapticsTests: XCTestCase {
func testConfirmationIsOneImmediateEvent() {
let events = confirmationEvents()
XCTAssertEqual(events.count, 1)
XCTAssertEqual(events[0].relativeTime, 0)
XCTAssertEqual(events[0].eventType, .hapticTransient)
}
}
Replace YourApp with the actual module name, then run the test target on the cloud Mac through the project’s existing shared test entry point. This checks the event definition; it does not require the simulator to produce a real vibration. If the product later changes the feedback from one event to a two-part rhythm, update the contract and tests first, then evaluate how it feels on a physical device.
Playback failure must not change the business result
The playback layer should check CHHapticEngine.capabilitiesForHardware().supportsHaptics before creating the engine and player. Starting the engine, creating a pattern, or playing it can fail. Record those failures as internally diagnosable feedback errors; do not reclassify an already successful save as a failure. After an engine interruption, do not assume the old player remains usable. Prepare the playback state again as appropriate for the application lifecycle.
| Situation | What the application must guarantee | Haptic handling |
|---|---|---|
| Save succeeds and haptics are supported | Show the completed-save state | Request the success event |
| Save succeeds but haptics are unsupported | Show the same completed-save state | Skip playback |
| Save fails | Show an understandable error | Do not play the success event |
| Playback fails | Preserve the completed save result | Stop this feedback attempt and log diagnostic information |
Test when playback is called, not how the simulator feels
Put a replaceable interface around the playback action. In save-flow tests, inject an implementation that records call counts: assert one call after success and zero after failure. If business logic blocks repeated taps, they must not cause extra playback either. Add a test for the no-haptics branch to confirm that the visible result is unchanged. These checks are more reliable than asking whether the simulator vibrated, and they are closer to what the code can actually guarantee.
Reserve experience acceptance for a physical device
At minimum, test a successful save, a failed save, rapid consecutive actions, and the first action after returning from the background. Check whether the haptic cue and completed state occur together; whether repeated taps make the cues too dense to distinguish; and whether feedback can still be requested after a system audio interruption or a move to the background. Change only one parameter at a time, and record the test device, OS version, and observations so subjective differences are not mistaken for code regressions.
A cloud Mac can handle compilation, simulator tests, and event-contract checks. The team must still confirm the actual sensation on a haptics-capable device. A button animation seen over remote desktop is not evidence that the haptics passed acceptance.
Check the boundaries before merging
Before merging, verify four things: production code and tests use the same event-generation function; a failed save does not trigger success feedback; an unsupported device or playback error still leaves a clear visible result; and physical-device notes cover trigger timing and consecutive actions. If feedback from device testing leads to a parameter change, update the expected values in the event tests and rerun the automated checks.
With that division of work, tests on the cloud Mac protect repeatable facts, while physical-device checks address the experience questions the simulator cannot answer.
Frequently asked questions
Can an iOS simulator test confirm that haptics feel right?
No. It can verify event construction and call conditions, but perceived strength and synchronization with the screen require a supported physical device.
Should an action fail if Core Haptics is unavailable?
No. Complete the action and update its visible state; treat haptic playback as optional feedback and skip it when unsupported or unsuccessful.
Get a dedicated Mac for your next experiment
Choose a physical Mac mini, node, and billing period to suit your needs. Models and availability are shown in real time in the console.