How to Test Playwright Apps on Real iOS Devices
Playwright’s iPhone profiles and WebKit help with fast feedback, but neither is a physical iPhone. Here’s how to add real iOS Safari runs to your test workflow.
Short answer: Playwright’s built-in iPhone profiles simulate mobile browser settings; they do not run tests on a physical iPhone. Playwright’s WebKit project is useful for browser-engine coverage, but it is not the same as released iOS Safari on iPhone hardware. To run Playwright against real iOS Safari, use a hosted device service that supports Playwright, such as BrowserStack Automate, and select a real device and iOS version. Playwright’s emulation guide, browser guide, and BrowserStack’s iOS instructions document these separate approaches.
Choose the right kind of iOS coverage
| Method | What it covers | Good use | Limit |
|---|---|---|---|
| Playwright iPhone emulation | Configured viewport, user agent, screen dimensions, touch behavior, and related settings | Fast responsive layout and interaction checks in the regular Playwright suite | Not physical iPhone hardware or iOS Safari |
| Playwright WebKit | Playwright’s WebKit browser engine | Browser-engine checks, including Safari-oriented investigation | Playwright WebKit comes from WebKit source builds and may contain changes ahead of Safari. It is not a real iOS run. |
| Hosted real iOS Safari | Tests on a selected physical iPhone or iPad and iOS version | Release checks and cases where device or Safari behavior matters | Requires service credentials; check the provider’s current supported commands and capabilities. |
| Personally owned iPhone | Manual Safari interaction on that handset | Exploratory checks and reproducing a report | Owning a handset does not by itself provide a documented local Playwright connection to automate it. |
Use emulation for broad, quick feedback, WebKit as a distinct browser-engine project, and real iOS devices for representative critical flows. The right physical device matrix depends on your audience and risk areas. Keep it focused instead of running every test across every device by default.
1. Add an iPhone emulation project for fast feedback
Install Playwright Test and its browser binaries:
npm init playwright@latest
npx playwright install
Add a mobile project to playwright.config.ts. The profile supplies simulated device parameters such as viewport, user agent, and touch support. The test can be run by itself with --project.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
projects: [
{
name: 'mobile-safari-emulation',
use: { ...devices['iPhone 13'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
Create a basic application check in tests/home.spec.ts:
import { test, expect } from '@playwright/test';
test('home page works at a mobile size', async ({ page }) => {
await page.goto('https://example.com');
await expect(page.locator('h1')).toBeVisible();
});
Run either project independently:
npx playwright test --project=mobile-safari-emulation
npx playwright test --project=webkit
Playwright projects let you group browser and device configuration and run projects together or individually. The device profile and viewport can be customized, but customizing them still does not make a physical-device test. See Playwright projects and emulation options.
What emulation settings can and cannot tell you
Profiles simulate browser parameters including user agent, screen size, viewport, and touch. Playwright also supports emulating locale, timezone, geolocation, permissions, color scheme, and high-DPI scale. Use these to test application responses to those settings. They do not validate physical-device Safari rendering, hardware behavior, or the complete iOS environment.
When overriding a profile value, define the override after spreading the profile so it wins. For example, set viewport: { width: 390, height: 844 } after ...devices['iPhone 13']. For a screen-specific case, Playwright also offers page.setViewportSize(). A custom viewport remains emulation.
2. Run tests on real iOS Safari with BrowserStack
BrowserStack’s documented Node.js SDK route requires a BrowserStack username and access key, Node.js 16 or later, and Playwright 1.50.1 or later. These minimums and supported device combinations can change, so check the current provider instructions before adopting the setup. Its quickstart uses a project configuration named browserstack.yml with Safari, iOS version, and device name entries.
Follow the documented SDK quickstart
- Get BrowserStack credentials from your account settings. Keep them out of committed source files; use your CI secret store or another approved secret manager.
- Use Node.js 16+ and Playwright 1.50.1+ as listed in the current documentation.
- Clone the provider’s sample project and install its dependencies:
git clone https://github.com/browserstack/node-js-playwright-browserstack.git
cd node-js-playwright-browserstack
npm i
Configure the device matrix in browserstack.yml. This follows the device and OS combinations in the provider’s quickstart; verify availability and supported OS versions in your account and current device list.
userName: YOUR_USERNAME
accessKey: YOUR_ACCESS_KEY
platforms:
- browserName: safari
osVersion: '17'
deviceName: iPhone 15 Pro Max
- browserName: safari
osVersion: '18'
deviceName: iPhone 14
- browserName: safari
osVersion: '14'
deviceName: iPhone 12 Pro
Run the sample build from the project root:
npm run sample-test
The sample repository supplies the project scripts and integration code. Start by running that quickstart unchanged, then adapt its tests to your app and keep the provider configuration aligned with its current documentation. The command above is the provider’s documented sample command, not a universal command for every Playwright repository.
For the documented legacy integration, the provider’s page gives a separate sample branch and environment variable names. The SDK route currently supports Node.js; the documentation directs other language frameworks to the legacy workflow.
git clone -b playwright-legacy https://github.com/browserstack/node-js-playwright-browserstack.git
cd node-js-playwright-browserstack
npm i
export BROWSERSTACK_USERNAME="YOUR_USERNAME"
export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"
npm run ios-test
Do not put real credentials in a checked-in YAML file or fixture. If credentials are accidentally committed, rotate them and remove them from the repository history according to your team’s procedure.
Provider capabilities to check before porting tests
BrowserStack’s iOS Playwright page lists these capabilities as unsupported: playwrightLogs, consoleLogs, resolution, and geoLocation. Its supported iOS commands reference is the right place to check specific operations that your suite depends on. Do not assume every desktop Playwright command behaves identically in a hosted iOS session.
3. Shape a useful real-device test matrix
- Start with user impact. Identify critical flows such as sign-in, checkout, navigation, forms, and any touch interaction with a history of failures.
- Select representative device and OS pairs. Choose based on your users and known risk areas. The provider quickstart’s named devices are examples, not a universal recommendation.
- Keep the fast suite broad. Run routine cases under emulation and browser projects, then execute a smaller selection against real iOS Safari in CI or at release time.
- Record what each result means. Label emulation, WebKit, and physical iOS runs separately so a simulated pass is not mistaken for device coverage.
- Revisit the matrix. Device inventories, available OS versions, service capabilities, and your own audience can change.
For CI, follow the sample command and integration for the provider workflow you selected, store credentials as secrets, and decide whether real-device runs belong on every change or a smaller release gate. No pricing or plan limits are quoted here because they were not established by the source material for this guide.
4. Debug differences with evidence
- Keep the failing test name, selected device and OS, app build identifier, and run URL or session reference together.
- Reproduce the same flow locally in emulation and in WebKit to narrow whether the issue appears only on physical iOS.
- Use only logs and artifacts supported by the provider’s current iOS integration. Do not design a required diagnostic around an unsupported capability.
- Reduce a failure to one navigation and one relevant interaction before expanding the device matrix.
- Check supported commands when an API call behaves differently from local Playwright. A passing desktop project does not prove that the command is supported in the iOS integration.
Performance, reliability, and cost considerations
Emulation and local browser projects fit ordinary rapid feedback because they run within the normal Playwright workflow. Hosted real-device runs add service setup, credentials, and selected device availability to the workflow. Keep the real-device matrix focused on critical cases to control run volume and make failures easier to investigate.
Device availability, session limits, pricing, and execution behavior depend on the service and current account plan; confirm those details directly with the provider before estimating CI cost or release time. The research for this guide establishes the documented setup, but not comparative benchmarks or current plan prices. Retries can help distinguish transient infrastructure failures from repeatable app defects, but avoid using retries to conceal stable failures.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| A run says iPhone but does not match a physical handset | The Playwright device profile is emulation. | Use a hosted real iOS device integration for physical Safari execution; label emulation results accurately. |
| WebKit passes, but iOS Safari fails | Playwright WebKit and released iOS Safari or device behavior can differ. | Reproduce on a real iOS Safari session and use that result for the device-specific investigation. |
| Authentication fails before the cloud session starts | Missing, invalid, or incorrectly named credentials. | Set the documented username and access key through CI secrets or environment variables; verify the account values without printing secrets into logs. |
| The sample command is missing in your project | The command belongs to the provider sample repository’s package scripts. | Run it from the cloned sample root, or use the integration and scripts documented for your own setup. |
| A capability or command is rejected or has no effect | The iOS integration may not support it. | Check the current supported iOS commands and unsupported capabilities list, then adapt or isolate that test. |
| The requested device or OS pair cannot start | The pair may be unavailable or named differently in the current inventory. | Confirm the device name and OS version in the provider’s current documentation and account. |
| Tests are slow or the CI queue grows | The matrix may run too many cases on hosted devices, or selected devices may have limited availability. | Keep broad coverage in local projects and reserve real iOS sessions for high-risk cases; check current device availability and plan limits. |
| Secrets appear in source control | Credentials were hard-coded in configuration or fixtures. | Rotate the key, move credentials to a secret manager, and remove exposed values from the repo. |
Or skip the browser setup
For screenshot capture of a web page, ScreenshotNeo offers a one-call screenshot API and MCP server. This does not run your Playwright test suite on an iPhone or replace real-device Safari testing. It is useful when the task is to capture a page image without maintaining browser setup. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the request was billed. Its MCP server has take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month with no card.
FAQ
Can Playwright test Safari on an actual iPhone?
Yes, through a supported hosted real-device integration such as the BrowserStack Automate workflow described here. A local iPhone profile or local WebKit run is not an actual iPhone Safari session.
Does Playwright iPhone emulation use a real iPhone?
No. It configures simulated mobile browser properties. Use it for fast responsive checks, not as proof of physical-device coverage.
Can I automate a locally owned iPhone with this setup?
The cited Playwright documentation describes emulation, WebKit, and a hosted real-device route. It does not document connecting a personally owned handset as a local Playwright device target.
Can Python or another language use the BrowserStack iOS SDK quickstart?
The current iOS SDK page describes its SDK workflow as Node.js-only and directs other language frameworks to the legacy workflow. Confirm current language support and setup in the provider documentation before porting a suite.
Does ScreenshotNeo provide real iPhone testing?
No. ScreenshotNeo captures website screenshots through an API or MCP server; it is not a Playwright real-device execution service.
Source links: Playwright emulation, Playwright browsers, Playwright projects, BrowserStack real iOS setup, and BrowserStack supported iOS commands.


