easy-frame
is a lightweight Swift command-line tool to create fully customizable framed App Store screenshots using SwiftUI. Built for automation, this open-source Swift package efficiently frames screenshots for all localizations and devices. While it follows a workflow similar to fastlane’s frameit, you are in full controll. Simply fork the reporsitory with just about 400 lines of swift code and get as creative as you desire.
This Swift package is ideal for indie Swift developers who value:
- Automation over manual screenshot creation
- Full design control over relying on premade templates with limited customization options
- A free and open-source solution over paid alternatives
To generate screenshots like in the example above - using the implementation as-is, just follow the below steps:
- Set up the directory structure as shown in the example below. Place your unframed raw screenshots in the
raw-screenshots
directory, organized by locale.parent-folder/ raw-screenshots/ EasyFrame.json de-DE/ iPhone 15 Pro Max-1-calendar.png iPhone 15 Pro Max-2-list.png iPhone 15 Pro Max-3-add-entry.png iPad Pro (12.9-inch) (6th generation)-1-calendar.png iPad Pro (12.9-inch) (6th generation)-2-list.png iPad Pro (12.9-inch) (6th generation)-3-add-entry.png en-GB/ iPhone 15 Pro Max-1-calendar.png iPhone 15 Pro Max-2-list.png iPhone 15 Pro Max-3-add-entry.png iPad Pro (12.9-inch) (6th generation)-1-calendar.png iPad Pro (12.9-inch) (6th generation)-2-list.png iPad Pro (12.9-inch) (6th generation)-3-add-entry.png
- If you use Fastlane to generate unframed screenshots, its output directory structure already meets
easy-frame
's requirements. Just ensure that theoutput_directory
is set toraw-screenshots
.capture_ios_screenshots( devices: ["iPhone 15 Pro Max", "iPad Pro (12.9-inch) (6th generation)"], languages: ["de-DE", "en-GB"], scheme: ENV["XCODE_SCHEME_UI_TEST"], output_directory: "./fastlane/raw-screenshots" )
- If you use Fastlane to generate unframed screenshots, its output directory structure already meets
- Create an
EasyFrame.json
configuration file based on the below Example EasyFrame.json. The config defines each App Store page with:- localized texts, which will be displayed on the framed screenshot
- a screenshot name, which must partially match the raw screenshot filenames
- Run the local
easy-frame
swift package command to generate framed screenshots into theparent-folder/screenshots/
directory:cd path/to/EasyFrameCommandProject swift run easy-frame path/to/parent-folder
See Sources/EasyFrameCommand/Model/SupportedDevice.swift. The device frames have been taken from https://github.com/fastlane/frameit-frames.
See Sources/EasyFrameCommand/View/ScreenshotDesignView.swift
parent-folder/
raw-screenshots/ (the locale folders contains the unframed raw screenshots)
EasyFrame.json
de-DE/
iPhone 15 Pro Max-1-calendar.png
iPhone 15 Pro Max-2-list.png
iPhone 15 Pro Max-3-add-entry.png
iPad Pro (12.9-inch) (6th generation)-1-calendar.png
iPad Pro (12.9-inch) (6th generation)-2-list.png
iPad Pro (12.9-inch) (6th generation)-3-add-entry.png
en-GB/
iPhone 15 Pro Max-1-calendar.png
iPhone 15 Pro Max-2-list.png
iPhone 15 Pro Max-3-add-entry.png
iPad Pro (12.9-inch) (6th generation)-1-calendar.png
iPad Pro (12.9-inch) (6th generation)-2-list.png
iPad Pro (12.9-inch) (6th generation)-3-add-entry.png
screenshots/ (this folder will be generated by easy-frame with the framed screenshots)
de-DE/
iPhone 15 Pro Max-1-calendar.jpg
iPhone 15 Pro Max-2-list.jpg
iPhone 15 Pro Max-3-add-entry.jpg
iPad Pro (12.9-inch) (6th generation)-1-calendar.jpg
iPad Pro (12.9-inch) (6th generation)-2-list.jpg
iPad Pro (12.9-inch) (6th generation)-3-add-entry.jpg
en-GB/
iPhone 15 Pro Max-1-calendar.jpg
iPhone 15 Pro Max-2-list.jpg
iPhone 15 Pro Max-3-add-entry.jpg
iPad Pro (12.9-inch) (6th generation)-1-calendar.jpg
iPad Pro (12.9-inch) (6th generation)-2-list.jpg
iPad Pro (12.9-inch) (6th generation)-3-add-entry.jpg
{
"pages": [
{
"languages": [
{ "locale": "en-GB", "title": "Title 1 EN", "description": "Description" },
{ "locale": "de-DE", "title": "Title 1 DE", "description": "Description" }
],
"screenshot": "1-calendar"
},
{
"languages": [
{ "locale": "en-GB", "title": "Title 2 EN", "description": "Description" },
{ "locale": "de-DE", "title": "Title 2 DE", "description": "Description" }
],
"screenshot": "2-list"
},
{
"languages": [
{ "locale": "en-GB", "title": "Title 3 EN", "description": "Description" },
{ "locale": "de-DE", "title": "Title 3 DE", "description": "Description" }
],
"screenshot": "3-add-entry"
}
]
}
Thanks to FrameKit and fastlane frameit, which both inspired me and made this swift package possible.
There is no planned roadmap or a guarantee of ongoing maintenance. This Swift package is intended to be forked and serves as a solid starting point for further customization 😉