How to auto-update App Store description in Google Sheets and Fastlane

** I want to leave the difficult things to the machine as much as possible. ** **

Obviously, in order to update the iOS app, it is necessary to update the content of the update as well.

Until now, I used to copy and paste the app wording that the operator entered in Google Sheets one by one into App Store Connect.

Occasionally there is no problem, but as the number increases, it becomes difficult. I want to automate and make it easier. I want to make it easier. It was easy!

So, it worked, ** I'll write a procedure to automate the update of the App Store description using Google Sheets and Fastlane. ** **

1. What to prepare

-[Google Spreadsheet with App Store description](https://qiita.com/drafts/0f15157e93b8b9339656/edit#2-app-store Create a google spreadsheet with description)

2. Create a Google spreadsheet with the App Store description

This time, I will write it as an example of getting the wording of the App Store from the spreadsheet with this content.

Separate sheets by language (sheet names are "ja" for Japanese and "en-US" for English)

Japanese

app name subtitle promotion Details Update contents keyword
Fully automatic shoulder massager Please improve blood flow Please use it. This is a very healthy app. Made minor corrections Fully automatic, Massine

English

app name subtitle promotion Details Update contents keyword
Full Auto Shoulder massager Please improve blood flow. Please use it. It's a very healthy app. bugfix Full Auto、 Let's

Screen_Shot_2020-09-08_at_18_52_32.png

3. Set up Google Cloud Platform to use "Google Drive API" and "Google Spreadsheet API"

To get the wording from Google Sheets, you need to hit "Google Drive API" and "Google SpreadSheet API".

And to hit these APIs from CI, you need to create a service account on GCP.

Go to Google Cloud Platform and create a project.

Screen_Shot_2020-09-04_at_14_28_51.png

Go to APIs & Services> Credentials, click Create Credentials, and click Service Account.

Screen_Shot_2020-09-04_at_14_29_56.png

Fill in your name, ID and service account description and click the Create button.

Screen_Shot_2020-09-04_at_14_32_46.png

Determine the permissions of the service account. This time, I want to load Google Sheets, so ask the viewer (choose the appropriate permissions in a timely manner) and click the Continue button.

Screen_Shot_2020-09-04_at_14_33_07.png

Click the Finish button.

Screen_Shot_2020-09-04_at_14_37_24.png

A new item has been added to the service account column. Click.

Screen_Shot_2020-09-08_at_19_03_24.png

Click Add Key, then click Create New Key.

Screen_Shot_2020-09-08_at_19_03_48.png

A dialog like this will appear. Specify JSON and click Create. ** Rename the saved JSON file to config.json. ** **

Screen_Shot_2020-09-08_at_19_04_03.png

After that, search for "Google Drive API" and "Google Spreadsheet API" from API Services> API Library, enable each API, and the setting is completed.

Screen Shot 2020-09-08 at 19.12.03.png

4. Create your own Fastlane Action

You can write it in the Fastfile, but in order not to make the Fastfile bloated, create your own Action and divide the processing.

Hit the following command.

bundle exec fastlane new action

You will be asked for a name, so guess an appropriate name. Should I say ** metadata **?

[20:28:11]: Name of your action: metadata

A file called fastlane / action / metadata.rb will be generated.

5. Use "google-drive-Ruby" to load the App Store description from Google Sheets

Preparation

Use the gem google-drive-ruby to load Google Sheets.

Add the following to the Gemfile:

gem "google_drive"

Installation.

bundle install

Put the service account key (config.json) generated by Google Cloud Platform in fastlane / action / (Jump here if you don't want to put the service account key / items / 0f15157e93b8b9339656 # 7-fastlane-plugin and published))

Write code

Add the following to the beginning of fastlane / action / metadata.rb.

require "google_drive"

Write the following code in self.run (params). Specify the contents of the spreadsheet written at the beginning as the following constants.

--LANGUAGES specifies the sheet name --COLUMNS specifies the Fastlane text file name for each item from the left (Refer to deliver for each text file name)

LANGUAGES = ["ja", "en-US"]
COLUMNS = ["name", "subtitle", "promotional_text", "description", "release_notes", "keywords"]

Load the spreadsheet, specifying the service account key file path and spreadsheet ID.

session = GoogleDrive::Session.from_config("config.json")
spreadsheet = session.spreadsheet_by_key("Spreadsheet ID")

All you have to do is pull the text for each column from the last row of the sheet for each language and save it in each text file.

LANGUAGES.each do |language|
  spreadsheet.worksheet_by_title(language).rows.last.each_with_index do |text, i|
    File.open("#{FastlaneCore::FastlaneFolder.path}metadata/#{language}/#{COLUMNS[i]}.txt", mode = "wb") do |f| f.write(text) end
  end
end

Full code

require "google_drive"

module Fastlane
  module Actions
    class MetadataAction < Action
      def self.run(params)
        LANGUAGES = ["ja", "en-US"]
        COLUMNS = ["name", "subtitle", "promotional_text", "description", "release_notes", "keywords"]

        session = GoogleDrive::Session.from_config("config.json")
        spreadsheet = session.spreadsheet_by_key("Spreadsheet ID")

        LANGUAGES.each do |language|
          spreadsheet.worksheet_by_title(language).rows.last.each_with_index do |text, i|
            File.open("#{FastlaneCore::FastlaneFolder.path}metadata/#{language}/#{COLUMNS[i]}.txt", mode = "wb") do |f| f.write(text) end
          end
        end
      end

      def self.description
        "A short description with <= 80 characters of what this action does"
      end

      def self.details
        "You can use this action to do cool things..."
      end

      def self.available_options
        []
      end

      def self.authors
        ["Your GitHub/Twitter Name"]
      end

      def self.is_supported?(platform)
        platform == :ios
      end
    end
  end
end

6. Run with Fastfile

After that, if you execute metadata on Fastfile, the items read from Google Spreadsheet will be saved in each text file.

Then run deliver (skip_metadata: false) to update the App Store description.

lane :deploy_appstore do
  metadata
  deliver(skip_metadata: false)
end

By the way, if you make a pull request when a diff appears, it is recommended because the difference between the previous versions will be obvious at a glance.

7. I made it a Fastlane Plugin and published it

What I explained so far, I tried Fastlane Plugin.

There were other plugins for the same purpose, but ** this allows me to specify an environment variable because I didn't want to Git manage the service account key file. ** Also, if you specify text file name that deliver does not use for metadata update in colims, the column there will be ignored. I have.

You can use it like this.

fetch_metadata_from_google_sheets(
  languages: ["ja", "en-US"],
  columns: ["version", "name", "subtitle", "release_notes", "promotional_text", "description", "keywords"],
  spreadsheet_id: ENV["TEST_APP_STORE_METADATA_SPREADSHEET_ID"],
  project_id: ENV["TEST_GCP_PROJECT_ID"],
  service_account_private_key_id: ENV["TEST_GCP_SERVICE_ACCOUNT_PRIVATE_KEY_ID"],
  service_account_private_key: ENV["TEST_GCP_SERVICE_ACCOUNT_PRIVATE_KEY"],
  service_account_client_email: ENV["TEST_GCP_SERVICE_ACCOUNT_CLIENT_EMAIL"],
  service_account_client_id: ENV["TEST_GCP_SERVICE_ACCOUNT_CLIENT_ID"],
  service_account_auth_uri: ENV["TEST_GCP_SERVICE_ACCOUNT_AUTH_URI"],
  service_account_token_uri: ENV["TEST_GCP_SERVICE_ACCOUNT_TOKEN_URI"],
  service_account_auth_provider_x509_cert_url: ENV["TEST_GCP_SERVICE_ACCOUNT_AUTH_PROVIDER_X509_CERT_URL"],
  service_account_client_x509_cert_url: ENV["TEST_GCP_SERVICE_ACCOUNT_CLIENT_X509_CERT_URL"]
)

For details, please refer to the following ribodigries.

kurarararara/fastlane-plugin-fetch_metadata_from_google_sheets https://github.com/kurarararara/fastlane-plugin-fetch_metadata_from_google_sheets

8. Finally

Until now, I was confused about making mistakes every time I copied and pasted, but after the automatic update, I made no mistakes and it became much easier.

If you have any problems, please try it.

Please also read this article.

Note in detail the procedure for hitting the Qiita API in GAS and auto-filling the results in Google Sheets

Recommended Posts

How to auto-update App Store description in Google Sheets and Fastlane
Save in Japanese to StringProperty in Google App Engine data store
How to update Google Sheets from Python
How to use Google Test in C
How to use is and == in Python
How to generate permutations in Python and C ++
How to display videos inline in Google Colab
How to write async and await in Vue.js
How to plot autocorrelation and partial autocorrelation in python
How to run AutoGluon in Google Colab GPU environment
How to define Decorator and Decomaker in one function
How to use a library that is not originally included in Google App Engine
How to store Python function in Value of dictionary (dict) and call function according to Key
How to load files in Google Drive with Google Colaboratory
How to install Google Test / Google Mock in Visual Studio 2019
[Note] How to write QR code and description in the same image with python
[Python] How to sort dict in list and instance in list
How to use Django on Google App Engine / Python
How to use Decorator in Django and how to make it
How to use Spacy Japanese model in Google Colaboratory
How to get RGB and HSV histograms in OpenCV
How to swap elements in an array in Python, and how to reverse an array.
Foreigners talk: How to name classes and methods in English
How to extract any appointment in Google Calendar with Python
[Google Colab] How to interrupt learning and then resume it
How to use pyenv and pyenv-virtualenv in your own way
[Introduction to Udemy Python 3 + Application] 36. How to use In and Not
How to create and use static / dynamic libraries in C
Comparison of how to use higher-order functions in Python 2 and 3
How to get all the keys and values in the dictionary
[Blender] How to handle mouse and keyboard events in Blender scripts
[TF] How to load / save Model and Parameter in Keras
How to ssh into Azure App Service and enable venv
How to execute external shell scripts and commands in python
How to create dataframes and mess with elements in pandas
How to log in to AtCoder with Python and submit automatically
How to develop in Python
How to use Google Colaboratory
[sh] How to store the command execution result in a variable
How to install OpenCV on Cloud9 and run it in Python
How to compare lists and retrieve common elements in a list
Repeated @ app.callback in Dash How to write Input and State neatly
How to display formulas in latex when using sympy (> = 1.4) in Google Colaboratory
How to give and what the constraints option in scipy.optimize.minimize is
How to use functions in separate files Perl and Python versions
Difference in how to write if statement between ruby ​​and python
How to use Google Colaboratory and usage example (PyTorch x DCGAN)
[ROS2] How to describe remap and parameter in python format launch
How to deploy a Django app on heroku in just 5 minutes
How to display bytes in the same way in Java and Python
[Rails 6] Embed Google Map in the app and add a marker to the entered address. [Confirmation of details]
How to copy and paste the contents of a sheet in Google Spreadsheet in JSON format (using Google Colab)
[Python] How to do PCA in Python
How to handle session in SQLAlchemy
[Rails] How to display Google Map
How to install and use Tesseract-OCR
How to use classes in Theano
How to write soberly in pandas
How to collect images in Python
How to update Spyder in Anaconda
How to use SQLite in Python