Generate screenshots for a Flutter app with golden testing and upload them to the stores (2/2)
This article is a follow up of the previous article, that you can find here.
In the first part, we managed to generate beautiful decorated screenshots to be uploaded to the Google Play Store and the App Store Connect.
How do you upload the illustrations to the stores?
To upload the screenshots to the stores, we will use the same strategy for both the Google Play Store and the App Store Connect: upload the screenshots while we deploy the app.
To achieve that, I use Fastlane. Fastlane is a set of open-source tools and scripts that automates building, testing, and deploying mobile apps for both iOS and Android.
You can easily install Fastlane on your local machine with the following command: sudo gem install fastlane
.
Once installed, we need to set it up (fortunately only once for the project). Open a terminal and go to the root of your project.
Android
- Go to the
android
directory:cd android
. - Run the following command:
sudo fastlane init
. - Follow the instructions with the minimum requirements.
- At that point, you might need to change the owners of the generated files, since we used
sudo
:
sudo chown your_id fastlane/
sudo chown -R your_id fastlane/*
sudo chown your_id Gemfile
sudo chown your_id Gemfile.lock
- Now, we need an API Key, which is a JSON. Just follow this short video to learn how to get that key. I named that file
google-play-store.json
. - For testing purposes, we can locally save that file in the
android
directory. But don’t forget to add it in your.gitignore
file! - (Optional) Now you can test that Fastlane can communicate with your store with the following command:
fastlane run validate_play_store_json_key json_key:google-play-store.json
- Edit your
Appfile
file so you can update it like the following:
# Path to the json secret file, relative to the "android" directory:
json_key_file("google-play-store.json")
# Package name (actually your application ID):
package_name("com.mistikee.mistikee")
- Finally, in order to locally get all the current screenshots and metadata with the right files and folders , run:
sudo fastlane supply init
. Here again, you might need to change the owners of the generated files as explained above.
iOS
- Go to the
ios
directory:cd ios
. - Run the following command:
sudo fastlane init
. - Follow the instructions with the minimum requirements: choose
Manual setup
, and continue. Be careful not to create an app on App Store Connect at this point! - Again, you might need to change the owners of the generated files, since we used
sudo
:
sudo chown your_id fastlane/
sudo chown -R your_id fastlane/*
sudo chown your_id Gemfile
sudo chown your_id Gemfile.lock
- Now, we need an API Key, which is a JSON. I named that file
app_store_connect.json
, and will look like the following:
{
"key_id": "D123SF789",
"issuer_id": "1234a5cd-12a3-4acb-56dd-123bb1234567",
"key": "-----BEGIN PRIVATE KEY-----\n[...]\n-----END PRIVATE KEY-----"
}
To get the different values, login to the App Store Connect, then go to My Apps > Users and access > Keys. Here you can generate a new API Key, which content will go into key
in the JSON above. On that same page, you can also find the key ID that you’ve just created, and the Issuer ID that you can find on the top of the list.
- For testing purposes, we can locally save that file in the
ios
directory. But don’t forget to add it in your.gitignore
file! - Edit your
Appfile
file so you can update it like the following:
app_identifier("com.mistikee.mistikee") # The bundle identifier of your app
apple_id("yourlogin@icloud.com") # Your Apple Developer Portal username
itc_team_id("123456789") # App Store Connect Team ID
team_id("123A4P567S") # Developer Portal Team ID
- In order to locally get all the current screenshots and metadata with the right files and folders , run:
sudo fastlane deliver init --use_live_version true
.
Here again, you might need to change the owners of the generated files as explained above. - Finally, if your app does not use encryption, in your
Info.plist
file, add theITSAppUsesNonExemptEncryption
key withfalse
for its value.
Back to root
At this point, I added the following in my .gitignore
file, at the root of my project:
/android/fastlane/metadata/android/fr-FR/images/
/android/fastlane/metadata/android/en-US/images/
/android/google-play-store.json
/ios/fastlane/screenshots
/ios/app_store_connect.json
Since the google-play-store.json
and the app_store_connect.json
files are not meant to be added to your repository, we need to provide them in the CI in a safe way.
For example, in Codemagic, you can store the content of the google-play-store.json
file in a encrypted environment variable named GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
, and run a script in your CI that will generate the google-play-store.json
in the right location, with the right content, by doing as follow:
echo $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS > android/mistikee-store.json
Then in my CI, I wrote a script that copies the generated illustrations in the right directories. For example, here is how I copy my illustrations for the French Android version of my app:
mkdir -p android/fastlane/metadata/android/fr-FR/images/phoneScreenshots
mkdir -p android/fastlane/metadata/android/fr-FR/images/sevenInchScreenshots
mkdir -p android/fastlane/metadata/android/fr-FR/images/tenInchScreenshots
mkdir -p android/fastlane/metadata/android/fr-FR/images/tvScreenshots
mkdir -p android/fastlane/metadata/android/fr-FR/images/wearScreenshots
cp test/screenshots/goldens/fr.android_smartphone.* android/fastlane/metadata/android/fr-FR/images/phoneScreenshots/
cp test/screenshots/goldens/fr.android_tablet_7.* android/fastlane/metadata/android/fr-FR/images/sevenInchScreenshots/
cp test/screenshots/goldens/fr.android_tablet_10.* android/fastlane/metadata/android/fr-FR/images/tenInchScreenshots/
Fastfile
One last step: the Fastfile
files, one for Android, one for iOS, that will each contain the detailed command to deploy everything to each store.
Here is what the Fastfile
file looks like for the Google Play Store (go to the supply documentation for more info):
default_platform(:android)
platform :android do
desc "Deploy app with screenshots to the Google Play Store"
lane :deployapp do |options|
supply(
package_name: "com.mistikee.mistikee", # put your own package name instead
aab: "../build/app/outputs/bundle/release/app-release.aab", # check if it's the right path for you
skip_upload_apk: "true",
skip_upload_aab: "false",
skip_upload_metadata: "false",
skip_upload_changelogs: "false",
skip_upload_images: "false",
skip_upload_screenshots: "false",
json_key: "google-play-store.json",
track: "production",
metadata_path: "./fastlane/metadata/android",
version_code: options[:versionCode].to_i
)
end
end
And here is what the Fastfile
file looks like for the App Store Connect (go to the deliver documentation for more info):
default_platform(:ios)
platform :ios do
desc "Deploy app with screenshots to App Store Connect"
lane :deployapp do |options|
deliver(
api_key_path: "./app_store_connect.json",
app_version: options[:versionName],
ipa: "../build/ios/ipa/mistikee.ipa",
submit_for_review: true,
skip_binary_upload: false,
skip_metadata: false,
skip_app_version_update: false,
skip_screenshots: false,
overwrite_screenshots: true,
metadata_path: "./fastlane/metadata",
screenshots_path: "./fastlane/screenshots",
languages: ['en-US','fr-FR'], # or any other languages according to your needs
precheck_include_in_app_purchases: false,
force: true,
submission_information: {
add_id_info_limits_tracking: true,
add_id_info_serves_ads: true, # or false, depending on your app having ads or not
add_id_info_tracks_action: true,
add_id_info_tracks_install: true,
add_id_info_uses_idfa: true,
content_rights_has_rights: true,
content_rights_contains_third_party_content: true,
export_compliance_platform: "ios",
export_compliance_compliance_required: false,
export_compliance_encryption_updated: false,
export_compliance_app_type: nil,
export_compliance_uses_encryption: false,
export_compliance_is_exempt: false,
export_compliance_contains_third_party_cryptography: false,
export_compliance_contains_proprietary_cryptography: false,
export_compliance_available_on_french_store: true
}
)
end
end
Now, in my CI, in order to run my deployapp
command above for the Google Play Store, I just need to run the following script:
cd android/
fastlane deployapp versionCode:25 # put your own version code here
And for the deployapp
command above for the App Store Connect, here is the script:
cd ios/
fastlane deployapp versionName:"2.0.1" # put your own version name here
Note that, as I write this article, the App Store Connect is sometimes buggy when it comes to deleting the previous screenshots. If that operation takes too much time (it should be done in a matter of seconds), don’t hesitate to interrupt the script and run your CI all over again.
Conclusion
To wrap it up, I would say that:
- Using that approach, the screenshots and illustrations can be quickly generated, on your machine, without any device or emulator.
- You can fully decorate your screenshots in Flutter only, which could be quite handy if you’re not a graphic designer.
- It’s not impossible that the screenshots generation explained in the previous article could not apply to some specific edge-cases. If there are any, feel free to share them in the comments section.
Don’t hesitate to let me know if you have any question.