Deploy iOS Application to AppCenter via Github Actions (Manual Code SignIn)
Tags: fastlane, appcenter, github, cicd
Published: 2022-03-28 18:10:09

🌅 Good Morning Guys ☕️

⏰ 25th March 2022 08:15 hrs IST

I know you have read lots of tutorials same like this. But for me, this is my latest achievement from the last 4 days to learn CI/CD which I have been procrastinating on for a long time. Here is the simplest way to automate uploading iOS build to **AppCenter** with **Fastlane** and **Github Actions**.

Let's make a list we needed.

1. Apple Account Mandatory for code signing. If you are in a team/organization, you ask for a testing bundle and certificates.

2. Xcode 13.3/13.2.1, well that is independent but I’m using both so just to mention for clarifying any confusion.

3. Github, AppCenter, Apple accounts.

4. Cocoapods, Fastlane lib. No worries I have added steps where ever required.

**P.S.: We are pushing build with a development profile to AppCenter and NOT for AppStore because of limited access 😅.**

Let's start with some lazy steps.

### Account Setup

![Sign Up](https://media.giphy.com/media/kHTXxbVYbYzGlhjKXs/giphy.gif)

**Github**: [https://github.com/signup](https://github.com/signup)

**AppCenter**: [https://appcenter.ms/create-account](https://appcenter.ms/create-account)

**Apple Account**: I feel that is already provided by your organization. But still here: [https://support.apple.com/en-in/HT204316](https://support.apple.com/en-in/HT204316)

**Xcode:** Download Xcode from here [https://developer.apple.com/xcode/](https://developer.apple.com/xcode/)
Open Xcode, go to top menu **Xcode > Preferences.** A dialogue will open, on the bottom left tap on the **'+'** button, Select Apple Id. Add your credentials and after that when you see your account tap on download profiles.

### AppCenter Setup

[Here](https://docs.microsoft.com/en-us/appcenter/dashboard/creating-and-managing-apps) is the helping doc for the dashboard. Login to AppCenter and you will see a dashboard like below.

![AppCenter](https://cdn-images-1.medium.com/max/7168/1*UdBjhlDc6lmLf4E3OR-ZUw.png)

Top right you will see **Add new app** button, A side menu will appear to ask below details

1. **App Name:** AppCenterDemo-iOS
2. **Release Type:** Beta
3. **OS:** iOS
4. **Platform:** Objective-C / Swift.

Tap on **Add new app** button on the bottom right and it will redirect you to the app’s dashboard page. There you will get app secret and cocoapods installation link as well. Save the app secret for the next section.

### Xcode Project Setup

Open Xcode, create a new project any name let's say **AppCenterDemo.** You can learn from [here](https://developer.apple.com/documentation/xcode/creating-an-xcode-project-for-an-app) as well just in case. Click on Project and you will general settings, tap on signing and capabilities, uncheck automatic sign in and

1. Choose your organization's team in the dropdown.

2. Setup your bundle id like **com.organization.appname.**

3. Update your **.gitignore** file before pushing your code to GitHub Like below. We are removing pods, DeriveData, Fastlane some files which are not required in the repo.

```gitignore
xcuserdata/
*.xcscmblueprint
*.xccheckout

build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

*.hmap
*.ipa
*.dSYM.zip
*.dSYM

timeline.xctimeline
playground.xcworkspace
.build/
Pods/
Carthage/Build/
Dependencies/
.accio/

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

iOSInjectionProject/
```

4. Download the provisioning profile and keep a soft copy of the same as well for later usage in Fastlane.

![Project Settings](https://cdn-images-1.medium.com/max/5608/1*bebACUkA8tbigd_l8bJycA.png)

5. Open Keychain, and check the Apple Development certificate provided by your organization or your developer account, tap on to see the dropdown.

6. You will see a private key. right-click on it and select the export option

![Keychain](https://cdn-images-1.medium.com/max/6008/1*XhrVSzApwxZf2zh5lScfRA.png)

7. Save the **.p12** file for later use. Here I have created **ios_dev_certificate.p12**. Use the password when prompted for a password and save it for later use in this tutorial.

![development p12](https://cdn-images-1.medium.com/max/5204/1*wHB99cvvgj9X86GYLvh8RA.png)

The hard part is done 😅

![ ](https://media.giphy.com/media/l0Iyl55kTeh71nTXy/giphy.gif)

8. Setup cocoapods from [here](https://guides.cocoapods.org/using/getting-started.html) if not done in past. Open podfile and add like below. Here we are integrating the [**AppCenter**](https://docs.microsoft.com/en-us/appcenter/sdk/getting-started/ios).

```swift
target 'AppCenterDemo' do
  pod 'AppCenter'
end
```

Go to the terminal inside the root directory and run **pod install** in the terminal. You will observe a workspace is created like a project file. Open workspace project.

9. In the **AppDelegate** file or in the main file for **SwiftUI projects**, add the code below. Declare a constant variable for **appSecret. AppSecret** is the same I mentioned at the end of the previous section.

```swift
AppCenter.start(withAppSecret: appSecret, services:[
    Analytics.self,
    Crashes.self
])
```

10. Run/Build your project in order to check everything is working.

### Fastlane Setup Part 1

For all further steps, I will recommend [**VS Code**](https://code.visualstudio.com/download) Editor. As we will get the project structure and terminal in a single window and we can access **.extension** files as well.

Install fastlane via running below command.

```script
sudo gem install fastlane 
```

Open terminal, navigate to project root directory and run

```script
fastlane init
```

[Helping Doc](https://docs.fastlane.tools/getting-started/ios/setup/) for iOS Setup.

You will see a Fastlane folder with Gemfile, AppFile, Fastfile and  plugin files.
Now add [AppCenter Plugin](https://github.com/microsoft/fastlane-plugin-appcenter) for Fastlane via command fastlane add_plugin appcenter.

Now initial Fastlane setup add these in **Gemfile** and run bundle install.
[Here](https://docs.fastlane.tools/getting-started/ios/setup/) is the **Gemfile** setup at the end of the doc.

```ruby
source "https://rubygems.org"

gem 'dotenv-rails', groups: [:development, :test]
gem "fastlane"
gem "xcode-install"
gem "cocoapods"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
```

These are libraries will be used by Fastlane as an initial setup. [Dotenv-rails](https://rubygems.org/gems/dotenv-rails/versions/2.1.1) is used to load the environment while running Fastlane.

To specify the ruby version, add **.ruby-version** file and inside that add a single line string `2.6.8`. This will be used by Fastlane.

Now add `.env` file like below

```ruby
# General
WORKSPACE=AppCenterDemo.xcworkspace
PROJECT_PATH=AppCenterDemo.xcodeproj
BUILD_CONFIGURATION=Debug
SCHEME=AppCenterDemo
EXPORT_METHOD=development
VERSION_BUMP_TYPE=patch
APP_AUTHOR=Abhishek Thapliyal

# Appcenter
APP_CENTER_OWNER_NAME="thapliyal-fueled.com"
APP_CENTER_APP_NAME="AppCenterDemo-iOS"
APP_CENTER_DISTRIBUTION_GROUPS="Collaborators"
APP_CENTER_NOTIFY_TESTERS=true
APP_CENTER_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
APP_CENTER_API_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

#CodeSignIn
PROVSIONING_PROFILE_REPO_PATH="./fastlane/provision/Dev_Profile.mobileprovision"
KEYCHAIN_NAME="CodeSignInData"
```

These are environment variables used in shell script and Fastfile later in the tutorial.

Check the [**Parameters**](https://docs.fastlane.tools/actions/gym/) section for more details of the export method, scheme, workspace, Project Path and build configuration.

To get the **APP_CENTER_APP_NAME** and **APP_CENTER_OWNER_NAME** here is the [link](https://github.com/microsoft/fastlane-plugin-appcenter#a-note-on-app-name):
> [https://appcenter.ms/users/{owner_name}/apps/{app_name](https://appcenter.ms/users/{owner_name}/apps/{app_name)}

**APP_CENTER_API_KEY:** This is the same used previously in **AppDelegate**. Update here as well.
**APP_CENTER_API_TOKEN:** [https://appcenter.ms/settings/apitokens](https://appcenter.ms/settings/apitokens)
Use this link to get API Token.

### Github Repo

You need to create a new repository in order to proceed further.
Beginner, please follow these docs for the Github repository setup.

1. [Create Repository](https://docs.github.com/en/get-started/quickstart/create-a-repo)

2. [Cloning](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository)

3. [SSH Setup](https://docs.github.com/en/authentication/connecting-to-github-with-ssh)

Now add a new folder name **provision** inside **fastlane** folder and move the earlier **Dev_Profile.mobileprovision** file inside. Now push the code to the repo.

### Github Secrets

Now we have to setup Github Secrets in order to secure the **.p12** file and **credentials**. Follow this [doc](https://docs.github.com/en/actions/security-guides/encrypted-secrets) for encrypted secrets and create as follow

Github Deploying Xcode Applications [Doc](https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development#creating-secrets-for-your-certificate-and-provisioning-profile)

1. Go to the directory where **ios_dev_certificate.p12** is saved earlier.
Open terminal in the same directory and
run

```script
base64 -i ios_dev_certificate.p12 | pbcopy
```

This will copy the base64 string in the pasteboard. You can check it via **cmd+v**. Save it in Github Secrets with the key name **IOS_DEVELOPMENT**.

2. The password we have saved while creating the above certificate will be saved in Github Secrets with the key name **P12_PASSWORD**.

3. Add one more value **KEYCHAIN_PASSWORD** for the keychain that will be used in Fastlane. I recommend make it strong.

### Github Actions

Now in the root directory of the project.

1. Add nested folder **.github/workflows.**

2. Add **yml** file name **gh_actions.yml** inside the **workflows** folder. There is no specific name you can choose any name.

3. Update your above **yml** file like below.

```yml
name: iOS App Center Deployment
on: [push]
jobs:
  build:
    runs-on: macos-11
    steps:
      - uses: actions/checkout@v2
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Run Fastlane for iOS
        env:
          IOS_DEVELOPMENT: ${{ secrets.IOS_DEVELOPMENT }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: bundle exec fastlane beta_app_upload
```

**name:** Workflow name.

**on:** on which [event](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#events) it will be triggered. Here I have added push i.e. on every push a build will be triggered.

**jobs:** Series of actions that will be executed.

**build:** Action Name.

**runs-on: OS Name** in which actions will be executed.

**uses: actions/checkout@v2:** Use to checkout branch

**uses: ruby/setup-ruby@v1:** Ruby setup with **bundler-cache: true** so that Gemfile libraries can be used.

**name:** Specifying name.

**run:** to run shell script command

**env:** Here we will mention environment variables that will be used in Fastlane and Github actions from Github Secrets.

[Here](https://docs.github.com/en/actions) is the reference doc for actions. More details for the workflow file are [here](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#understanding-the-workflow-file).

**Well, It’s afternoon 13:30 hrs IST. Have lunch and come back 😅**

### Fastlane Setup Part 2

Here is the most important section that will actually deploy our app.

Below are the **documented** [**fastlane actions**](https://docs.fastlane.tools/actions/). Please go through once before using.

Now open fastfile inside fastlane folder. Update the code like below.
from this [blog](https://dev.to/matt_catalfamo/how-to-build-and-manually-sign-an-ios-app-with-fastlane-2256) I have taken reference to certificates setup. Our agenda is divided in three setps.

1. Add action to generate **KeyChain** for code signIn.

2. Add action to generate **iOS Build**.

3. Add action to upload build to **AppCenter**.

```javascript
default_platform(:ios)

platform :ios do

  private_lane :setup_certificates do
    create_keychain(
      name: ENV["CodeSignInData"],
      password: ENV["KEYCHAIN_PASSWORD"],
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: false
    )
    install_provisioning_profile(path: ENV["PROVSIONING_PROFILE_REPO_PATH"])    
    setup_ios_profiles
    import_certificate(
      certificate_path: "fastlane/certificates/ios_dev_certificate.p12",
      certificate_password: ENV["P12_PASSWORD"],
      keychain_name: ENV["CodeSignInData"],
      keychain_password: ENV["KEYCHAIN_PASSWORD"]
    )
  end

  desc "AppCenter iOS App. Upload"
  lane :beta_app_upload do
    xcversion(version: "13.2.1")
    setup_certificates
    clear_derived_data
    xcclean(scheme: ENV["SCHEME"])
    cocoapods(repo_update: true)
    build_ios_app(
      workspace: ENV["WORKSPACE"],
      configuration: ENV["BUILD_CONFIGURATION"],
      scheme: ENV["SCHEME"],
      silent: true,
      clean: false,
      disable_xcpretty: false,
      export_options: {
        method: ENV['EXPORT_METHOD']
      }
    )
    appcenter_upload(
      api_token: ENV["APP_CENTER_API_TOKEN"],
      owner_name: ENV["APP_CENTER_OWNER_NAME"],
      owner_type: "user",
      app_name: ENV["APP_CENTER_APP_NAME"],
      notify_testers: ENV["APP_CENTER_NOTIFY_TESTERS"]
    )
  end
end
```

1. `create_keychain`: use to create keychain in github runner environment. [**Doc**](https://docs.fastlane.tools/actions/create_keychain/)

2. `install_provisioning_profile`: use to install provision profiles for code sign in. [**Doc**](http://docs.fastlane.tools/actions/install_provisioning_profile/#install_provisioning_profile)

3. `import_certificate`: use to import **.p12** certificate from github secrets mentioned in env file inside **github actions yml** file previously. [**Doc**](http://docs.fastlane.tools/actions/import_certificate/#import_certificate)

4. `setup_ios_profiles`: It’s the custom Fastlane action created by me. [**Doc**](http://docs.fastlane.tools/create-action/#local-actions) [Example](https://spin.atomicobject.com/2021/05/21/custom-bash-script-fastlane/)

5. Open terminal and run

```script
bundle exec fastlane new_action
```

It will ask you name for action. I have added **setup_ios_profiles**. A **setup_ios_profiles.rb** will be generated inside fastlane folder. Add below code.

```ruby
module Fastlane
  module Actions
    class SetupIosProfilesAction < Action
      def self.run(params)
        UI.message "Setup Keychain"
        sh "./fastlane/actions/setup_code_signin.sh"
      end

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

Now create new file `setup_code_signin.sh`, open terminal in same directory and run 

```script
chmod u+x ./setup_code_signin.sh
```

This command will make file executable for user. After that update code below

```script
DEV_CERTIFICATE_PATH=./fastlane/certificates/ios_dev_certificate.p12

mkdir -p ./fastlane/certificates

echo "$IOS_DEVELOPMENT" | base64 -d -o $DEV_CERTIFICATE_PATH
```

This script will convert saved base64 string from github secrets, mentioned in yml file, to **.p12** file.

6. `xcversion`: use to specify Xcode version.

7. `clear_derived_data`: use to clear derive data use to clear derive data

8. `xcclean`: use to clean project with specified scheme.

9. `cocoapods`: use to install cocoapods with repo update if specified.

Now code signIn and fastlane project in github action is almost setup. We have to configure rest two setps. As shown above fastfile code build_ios_app will generate build for iOS **([Reference Doc](http://docs.fastlane.tools/actions/build_ios_app/#build_ios_app))**. It will take values mentioned in **.env** file. Have a closer to export method, you can change to as per [**doc**](http://docs.fastlane.tools/actions/build_ios_app/#parameters)

```javascript
export_method // Method used to export the archive. Valid values are: app-store, validation, ad-hoc, package, enterprise, development, developer-id and mac-application.
```

And the last part upload to [**AppCenter**](https://github.com/microsoft/fastlane-plugin-appcenter#usage) i.e. `appcenter_upload`: This method will take **App. Name**, **API Token** and **Owner Name** from **.env** file. We have discussed earlier as well. As per doc

```javascript
owner_type: "user" // # Default is user - set to organization for appcenter organizations 

app_name: "<appcenter app name (as seen in app URL)>", 

notify_testers: true // # Set to false if you don't want to notify testers of your new release (default: `false`)
```

Now push your code and see the magic. If all goes well then in a sample logs for successful app upload to **AppCenter** will looks like below.

![Github Actions](https://cdn-images-1.medium.com/max/4564/1*tSeRiZuAbeMYBmNVK3VOTg.png)

You can **cmd+click** the link to navigate app center release page as well.

I hope you will get an idea **CI/CD** works. You can explore more yourself for TestFlight builds as well as managing several environments.

**HAPPY CODING 😃😃**

**Good Night 😴 🛌**