Just like we use git to be able to audit changes to our code over time, doing so with our app’s AppStore metadata lets us keep an eye on changes made to our AppStore page as well.

The first step in getting more control of our metadata is by having it live in our project’s repository. Keeping a copy of our metadata in git lets us keep track of changes to our app’s description, keywords, and even screenshots over time.

To do so, we use Fastlane’s deliver command with the download_metadata subcommand:

fastlane deliver download_metadata

Here’s what a run of this for Little Countdown looks like:

% fastlane deliver download_metadata
[✔] 🚀
[10:52:02]: Login to App Store Connect (appstore@nerdtower.com)
[10:52:03]: Login successful

+--------------------------------------+-------------------------+
|                    deliver 2.148.1 Summary                     |
+--------------------------------------+-------------------------+
| screenshots_path                     | ./fastlane/screenshots  |
| metadata_path                        | ./fastlane/metadata     |
| username                             | appstore@nerdtower.com  |
| app_identifier                       | com.nerdtower.Countdown |
| platform                             | ios                     |
| edit_live                            | false                   |
| use_live_version                     | false                   |
| skip_binary_upload                   | false                   |
| skip_screenshots                     | false                   |
| skip_metadata                        | false                   |
| skip_app_version_update              | false                   |
| force                                | false                   |
| overwrite_screenshots                | false                   |
| submit_for_review                    | false                   |
| reject_if_possible                   | false                   |
| automatic_release                    | false                   |
| phased_release                       | false                   |
| reset_ratings                        | false                   |
| team_id                              | [hidden]                |
| dev_portal_team_id                   | [hidden]                |
| run_precheck_before_submit           | true                    |
| precheck_default_rule_level          | warn                    |
| individual_metadata_items            | []                      |
| ignore_language_directory_validation | false                   |
| precheck_include_in_app_purchases    | true                    |
+--------------------------------------+-------------------------+

[10:52:15]: Writing to './fastlane/metadata/en-US/description.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/keywords.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/release_notes.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/support_url.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/marketing_url.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/promotional_text.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/name.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/subtitle.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/privacy_url.txt'
[10:52:15]: Writing to './fastlane/metadata/en-US/apple_tv_privacy_policy.txt'
[10:52:15]: Writing to './fastlane/metadata/copyright.txt'
[10:52:15]: Writing to './fastlane/metadata/primary_category.txt'
[10:52:15]: Writing to './fastlane/metadata/secondary_category.txt'
[10:52:15]: Writing to './fastlane/metadata/primary_first_sub_category.txt'
[10:52:15]: Writing to './fastlane/metadata/primary_second_sub_category.txt'
[10:52:15]: Writing to './fastlane/metadata/secondary_first_sub_category.txt'
[10:52:15]: Writing to './fastlane/metadata/secondary_second_sub_category.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/trade_name.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/first_name.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/last_name.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/address_line1.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/address_line2.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/address_line3.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/city_name.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/state.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/country.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/postal_code.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/phone_number.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/email_address.txt'
[10:52:15]: Writing to './fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/first_name.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/last_name.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/phone_number.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/email_address.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/demo_user.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/demo_password.txt'
[10:52:15]: Writing to './fastlane/metadata/review_information/notes.txt'
[10:52:15]: Successfully created new configuration files.
[10:52:15]: Successfully downloaded large app icon

After a successful download, you should now have your metadata in your project’s ./fastlane/metadata. Great!

Something to notice above is that things like the app’s description, keywords, and release notes live inside of localized subdirectories for each locale such as en-US. If we had AppStore metadata localized into other locales, we’d see other subdirectories for each of those as well.

At this point, you can consider:

  • Having your ./fastlane/metadata be the source of truth of your release notes so that they’re bound directly to a commit or tag in your repository
  • Using deliver’s subcommand submit_build to submit your builds for review with changes you make to metadata in your repository. This opens the door to have our CI/CD pipeline manage submission of our build instead of having to login to AppStore Connect’s website and “pressing the button.”

With your AppStore metadata downloaded and stored in your project’s repository, you gain visibility and control of your presence in the AppStore and the data on your app’s page. Furthermore, you open up the path to automating the management of this metadata as well.