●MAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — Rork Max unlocks native features: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, HealthKit, and Core ML●STACK — Rork builds native iOS and Android apps with React Native (Expo) from a plain-English description●GROWTH — Rork now attracts over 743,000 monthly visits, growing at an 85% rate●PRICE — Rork is free to start, with paid plans from $25/month●TREND — Gartner projects 75% of new apps will be built with low-code/no-code by the end of 2026●MAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — Rork Max unlocks native features: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, HealthKit, and Core ML●STACK — Rork builds native iOS and Android apps with React Native (Expo) from a plain-English description●GROWTH — Rork now attracts over 743,000 monthly visits, growing at an 85% rate●PRICE — Rork is free to start, with paid plans from $25/month●TREND — Gartner projects 75% of new apps will be built with low-code/no-code by the end of 2026
You Only Need to Capture the 6.9-inch iPhone for App Store Screenshots
App Store Connect only requires two screenshot sizes: the 6.9-inch iPhone and the 13-inch iPad. Building on Apple auto-downscaling for smaller devices, here is how I use fastlane snapshot and frameit to cut the screenshot count for a multi-language app.
Every time a new iPhone shipped, I re-shot my store screenshots. When you publish wallpaper apps in 16 languages, adding a single device size multiplies the work by the number of languages. I still remember the small sense of dread when the iPhone Air landed and I assumed I was in for the same grind again.
Then I re-read what App Store Connect actually asks for. The only required sizes are the 6.9-inch iPhone and the 13-inch iPad. Every smaller device is generated by Apple, scaled down automatically. The extra sizes I had been dutifully re-shooting had not been necessary for years.
This article is the record of turning that fact into an actual workflow: narrow the capture target to a single largest device, automate per-language capture with fastlane, and composite device frames and translated captions with frameit — all shown with configs that run.
App Store Connect really only asks for two
Let me get the premise exact. As of 2026, the screenshot display sizes App Store Connect marks as required are these two. Every other device is downscaled from them.
Device class
Reference device
Resolution (px, portrait)
Aspect ratio
iPhone (6.9-inch)
iPhone 17 Pro Max class
1320 × 2868
19.5 : 9
iPad (13-inch)
iPad Pro 13-inch (M4) class
2064 × 2752
4 : 3
Two conditions come with this. An app that supports both iPhone and iPad needs separate screenshots for each device class. And each shelf needs at least three screenshots — App Store Connect will not accept a single image.
The lever here is the auto-downscale rule. Every current notch / Dynamic Island iPhone — 6.9-inch, 6.3-inch, 6.1-inch — shares the same 19.5:9 aspect ratio. So a shot captured on the largest device downsizes without breaking layout. The iPad sizes line up at 4:3 the same way. Keeping every size on hand is a habit left over from an era that no longer exists, and it is simply paying for the work twice.
Why "capture the largest device" is the correct answer
There was a time when both 6.5-inch and 5.5-inch were required, and I shot both. The 5.5-inch (iPhone 8 Plus) is 16:9, a different aspect ratio from 6.5-inch, so a plain downscale left letterboxing or clipped edges. Once you live through that, "shoot each size" gets wired into your muscle memory.
But the 5.5-inch requirement was retired, and the only iPhone shelf left is the 6.9-inch. As noted, the required device and its downscale targets share an aspect ratio. The reduction is proportional, so relative text size and composition are preserved.
The call I settled on: fix capture to the 6.9-inch iPhone and the 13-inch iPad, and leave smaller-device previews to App Store Connect. Checking how the downscale looks on a real device is something I do once, after release, on the iPhone in my pocket. Chase "perfect on every device" and the verification work multiplies by the number of languages until it collapses.
The premise is identical for apps built with Rork or Rork Max. Store artwork ultimately follows the image spec App Store Connect handles, so whatever the generation tool, everything converges onto these two shots.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Anyone re-shooting every device size will be able to ship an App Store submission by capturing just the 6.9-inch iPhone
✦You will get working fastlane snapshot and frameit configs that run for both Rork/Expo apps and Rork Max (SwiftUI)
✦You can compress the language x device screenshot count, with a clear rule for what is safe to drop
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Capture the 6.9-inch in one command with fastlane snapshot
I automate capture with fastlane's snapshot. It launches a UI test, calls snapshot(...) on the screens you specify, and grabs a simulator screenshot per language.
First, Snapfile narrows the devices and languages. Writing only the 6.9-inch and 13-inch here is the whole point of this article.
# fastlane/Snapfiledevices([ "iPhone 17 Pro Max", # 6.9-inch = the required App Store reference device "iPad Pro 13-inch (M4)" # 13-inch iPad])languages([ "en-US", "ja", "de-DE", "fr-FR", "zh-Hans"])scheme("MyAppUITests")output_directory("./fastlane/screenshots")clear_previous_screenshots(true) # wipe stale images on every re-run
Next, the UI test. A testID you set in React Native / Expo becomes the accessibility identifier on iOS, so it works directly in XCUITest element queries. Not knowing this is where people get stuck wondering why an element cannot be found.
// ScreenshotUITests.swiftimport XCTestfinal class ScreenshotUITests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false let app = XCUIApplication() setupSnapshot(app) // fastlane injects language and output path app.launch() } func testCaptureStoreScreens() throws { let app = XCUIApplication() // Home right after launch. Wait for load before shooting. XCTAssertTrue(app.otherElements["homeScreen"].waitForExistence(timeout: 10)) snapshot("01_Home") // testID="browseTab" becomes the iOS accessibility identifier app.buttons["browseTab"].tap() snapshot("02_Browse") app.buttons["settingsTab"].tap() snapshot("03_Settings") }}
Finally, tie it together in a lane. capture_screenshots reads the Snapfile; frame_screenshots reads the frameit config below.
# fastlane/Fastfileplatform :ios do desc "Capture localized store screenshots and composite frames" lane :screens do capture_screenshots # shoot device x language from Snapfile frame_screenshots(white: true) # composite device frames per Framefile.json endend
Now fastlane screens produces 2 devices x 5 languages x 3 screens = 30 images in one command. Back when I shot each size, the same content meant managing 3 sizes x 5 screens x 16 languages = 240 images by hand. The image count matters, but being freed from manual capture matters more.
Composite device frames and translated captions with frameit
Raw captures look weak if you ship them as-is. frameit places them in a device frame with a translated headline on top, which makes them work as store artwork.
Framefile.json sets one shared style across all languages. Standardizing on a Noto font is the safe move — one font handles Japanese, Chinese, and German umlauts without glyphs falling apart.
For caption text, place title.strings and keyword.strings in the same language folder as the images, and frameit swaps them per language.
# fastlane/screenshots/en-US/title.strings
"01_Home" = "A different view every morning";
"02_Browse" = "Today's pick from 5,000 images";
"03_Settings" = "Quiet, and ad-free";
Here is where I draw the line between "safe to drop" and "do not drop," as an operational decision.
Element
Droppable?
Reasoning
iPhone sizes below 6.9-inch
Yes
Apple downscales proportionally; same aspect ratio holds
iPad sizes at or below 11-inch
Yes
Generated from the 13-inch
The listing languages themselves
No
Unspecified languages fall back to the default image and lose impact
Count per shelf
Not below 3
App Store Connect rejects the submission
iPad shots for iPad-supporting apps
No
Required separately per device class
Across my wallpaper apps, this cut the images per app from 240 to 80 (6.9-inch only, 5 screens, 16 languages) — about a 66% reduction. Running several apps in parallel as an indie developer, stopping that double payment adds up quietly. An app without iPad support drops the entire iPad set on top of that. The easy thing to miss is that language is not droppable: you can leave devices to the downscaler, but trimming listing languages means the fallback (usually English) shows in the unspecified slots, and local readers reliably lose interest.
What changes between Rork Max (SwiftUI) and Rork (Expo)
Even with the same "capture at 6.9-inch," how you attach the UI test differs by generation base.
For Rork (React Native / Expo), run npx expo prebuild to generate the ios/ project, then add a UI test target in Xcode. Element targeting uses the testID-derived accessibility identifier described above. Since preview and production builds can diverge, the angle in fixing preview-vs-device inconsistencies in Rork Max applies directly.
For Rork Max (native SwiftUI), it is an Xcode project from the start, so adding a UI test target is straightforward. Set .accessibilityIdentifier("browseTab") explicitly on SwiftUI views so XCUITest can grab them reliably. Generated views often carry no identifier, so adding them only to the elements you capture is the practical route.
Either way, the exit — the two-size App Store Connect spec — is shared. The convergence point does not change with the tool.
Pitfalls that are easy to hit
Three non-obvious traps I stepped in, with the fix for each. Walk them in order and most generation mishaps are avoided.
If you call snapshot(...) before the screen finishes rendering, you capture a loading skeleton or splash. Wrapping waitForExistence(timeout:) around it, as in the code above, is the fix. For image-heavy apps especially, shooting without waiting gives you sad store artwork full of empty grids.
Missing per-language font embedding. Leave frameit's font as a Latin-only face and Japanese or Chinese captions render as tofu (□) boxes. Specifying one font with broad coverage like Noto Sans CJK and running every language through it was the reliable workaround.
Reusing images captured from a preview build directly in production listings. Fonts and padding can diverge between preview and production, and noticing after the listing is live means rework. Capture on a simulator with the same build configuration you ship.
If you are tired of re-shooting screenshots, start by rewriting your app's Snapfile to the single 6.9-inch iPhone line and running fastlane screens once. You will likely find the range you can leave to the downscaler is wider than you expected.
I have come to see store presentation as something you keep adjusting after release. I am still searching for the best composition myself, but automating away the capture effort let me pour that time back into sharpening the copy instead. Thank you for reading.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.