Publish to AppStore
Uploading to AppStore is a little tricky to handle certificates, Make sure you do all these steps carefully
-- Note: You need a Mac environment for doing these steps .
1- Install fastlane
There are different ways of installing fastlane, but the recommended approach is to make a Gemfile with following content :
# fastlane/Gemfile
source "https://rubygems.org"
gem "fastlane"
Then run bundle install
This will create a Gemfile.lock
, upload both Gemfile
and Gemfile.lock
to your repo .
2- Create storage for Apple certifications
Fastlane has a nice implementation of codesigning.guide concept called match. It basically uploads all necessary keys and certificated in a storage of your choice(Private Git repo,Amazon S3,..) and then share it between your different development envs.
For using match :
Create a private git repository
Run following command :
fastlane match appstore
this will ask for your github repository and AppleId, and then upload your certificates to the private git repository.
-- Note: Make sure your AppleId have two-step Authentication and have enough access .
-- Note: If possible,It's also better to remove(after making a backup) all your certificates before doing it. Some times Match mess things up.
3- Add following fastlane files to your Fastlane folder
# fastlane/Matchfile
git_url(ENV["MATCH_URL"])
git_basic_authorization(ENV["GIT_TOKEN"])
type("appstore")
app_identifier(ENV["IOS_APP_ID"])
username(ENV["APPLE_CONNECT_EMAIL"])
# fastlane/Appfile
for_platform :ios do
app_identifier(ENV["IOS_APP_ID"])
apple_dev_portal_id(ENV["APPLE_DEVELOPER_EMAIL"])
itunes_connect_id(ENV["APPLE_CONNECT_EMAIL"])
team_id(ENV["APPLE_TEAM_ID"])
itc_team_id(ENV["APPLE_TEAM_ID"])
end
In the following file Change com.company.application to your bundle identifier .
# fastlane/Fastfile
keychain_name = "temporary_keychain"
keychain_password = SecureRandom.base64
platform :ios do
desc "Push a new release build to the App Store"
lane :release do
build
upload_to_app_store
end
desc "Submit a new Beta Build to Apple TestFlight"
lane :beta do
build
api_key = app_store_connect_api_key(
key_id: "#{ENV[APPSTORE_KEY_ID]}",
issuer_id: "#{ENV[APPSTORE_ISSUER_ID]}",
key_filepath: "#{ENV['APPSTORE_P8_PATH']}",
duration: 1200, # optional
in_house: false, # true for enterprice and false for individual accounts
)
upload_to_testflight(skip_waiting_for_build_processing:true,api_key: api_key)
end
desc "Create .ipa"
lane :build do
update_code_signing_settings(use_automatic_signing: false,path: "#{ENV['IOS_BUILD_PATH']}/iOS/Unity-iPhone.xcodeproj")
certificates
update_project_provisioning(
xcodeproj: "#{ENV['IOS_BUILD_PATH']}/iOS/Unity-iPhone.xcodeproj",
target_filter: "Unity-iPhone",
profile: ENV["sigh_com.company.application_appstore_profile-path"],
code_signing_identity: "Apple Distribution: #{ENV['APPLE_TEAM_NAME']} (#{ENV['APPLE_TEAM_ID']})"
)
increment_build_number(
xcodeproj: "#{ENV['IOS_BUILD_PATH']}/iOS/Unity-iPhone.xcodeproj"
)
gym(
project: "#{ENV['IOS_BUILD_PATH']}/iOS/Unity-iPhone.xcodeproj",
scheme: "Unity-iPhone",
clean: true,
skip_profile_detection: true,
codesigning_identity: "Apple Distribution: #{ENV['APPLE_TEAM_NAME']} (#{ENV['APPLE_TEAM_ID']})",
export_method: "app-store",
export_options: {
method: "app-store",
provisioningProfiles: {
ENV["IOS_APP_ID"] => "match AppStore #{ENV['IOS_APP_ID']}"
}
}
)
end
desc "Synchronize certificates"
lane :certificates do
cleanup_keychain
create_keychain(
name: keychain_name,
password: keychain_password,
default_keychain: true,
lock_when_sleeps: true,
timeout: 3600,
unlock: true
)
match(
type: "appstore",
readonly: true,
keychain_name: keychain_name,
keychain_password: keychain_password
)
end
lane :cleanup_keychain do
if File.exist?(File.expand_path("~/Library/Keychains/#{keychain_name}-db"))
delete_keychain(name: keychain_name)
end
end
after_all do
if File.exist?(File.expand_path("~/Library/Keychains/#{keychain_name}-db"))
delete_keychain(name: keychain_name)
end
end
end
-- Note: If you add libraries that need Podfile (e,g Firebase) to your project, Add this line in the beginning of build step :
cocoapods(
clean_install: true,
podfile: "#{ENV['IOS_BUILD_PATH']}/iOS/"
)
This will install pods and generate xcworkspace
for you .
Then change the gym section so that it use the new xcworkspace
:
gym(
workspace: "#{ENV['IOS_BUILD_PATH']}/iOS/Unity-iPhone.xcworkspace",
scheme: "Unity-iPhone",
clean: true,
skip_profile_detection: true,
codesigning_identity: "Apple Distribution: #{ENV['APPLE_TEAM_NAME']} (#{ENV['APPLE_TEAM_ID']})",
export_method: "app-store",
export_options: {
method: "app-store",
provisioningProfiles: {
ENV["IOS_APP_ID"] => "match AppStore #{ENV['IOS_APP_ID']}"
}
}
)
4- Add Github action
# .github/workflows/main.yml
BuildForiOSPlatform:
name: Build for iOS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: Library
key: Library-iOS
- uses: webbertakken/unity-[email protected]
with:
targetPlatform: iOS
- uses: actions/upload-artifact@v2
with:
name: build-iOS
path: build/iOS
ReleaseToAppStore:
name: Release to the App Store
runs-on: macos-latest
needs: buildForiOsPlatform
env:
APPLE_CONNECT_EMAIL: ${{ secrets.APPLE_CONNECT_EMAIL }}
APPLE_DEVELOPER_EMAIL: ${{ secrets.APPLE_DEVELOPER_EMAIL }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_TEAM_NAME: ${{ secrets.APPLE_TEAM_NAME }}
MATCH_URL: ${{ secrets.MATCH_URL }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }}
APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
APPSTORE_P8: ${{ secrets. APPSTORE_P8 }}
APPSTORE_P8_PATH: ${{ format('{0}/fastlane/p8.json', github.workspace) }}
IOS_APP_ID: com.company.application # Change it to match your unity bundle id
IOS_BUILD_PATH: ${{ format('{0}/build/iOS', github.workspace) }}
PROJECT_NAME: Your Project Name
RELEASE_NOTES: Your Release Notes
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Make App Store p8
run: echo "$APPSTORE_P8" > $APPSTORE_P8_PATH
- name: Download iOS Artifact
uses: actions/download-artifact@v2
with:
name: build-iOS
path: build/iOS
- uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-${{ hashFiles('**/Gemfile.lock') }}
- name: Install Fastlane
run: bundle install
- name: Prepare for Upload
run: find $IOS_BUILD_PATH -type f -iname "*.sh" -exec chmod +x {} \;
- name: Upload to the Test Flight
uses: maierj/fastlane-[email protected]
with:
lane: 'ios beta'
- name: Tidy up artifact to avoid storage limit
if: ${{ always() }}
uses: geekyeggo/delete-artifact@v1
with:
name: build-iOS
5- Add secrets to your Github repo
- APPLE_CONNECT_EMAIL : Apple connect email (usually same as APPLE_DEVELOPER_EMAIL)
- APPLE_DEVELOPER_EMAIL: Your AppleId
- APPLE_TEAM_ID: Team Id From developer.apple.com/MemberShip
- APPLE_TEAM_NAME: Team Name From developer.apple.com/MemberShip
- MATCH_URL: Address of private repository that you made in previous steps for storing certificates.
- GIT_TOKEN: Base64 of user@MATCH_URL e,g user@https://github.com/game-ci/documentation.git . You can use some online base64 encoder for this step
- MATCH_PASSWORD: The password you set when you use
fastlane match appstore
- APPSTORE_KEY_ID ,APPSTORE_ISSUER_ID,APPSTORE_P8: Because of limitation in using Apple accounts with 2fa ( 2-factor authentication ) in CI environments, you have to make special key for accessing appstore . Follow fastlane official guide to generate these values.
6- Unity Settings
- Set Signing Team Id and Bundle identifier in iOS player setting
- Add your application icon (Application with no icon generate error during uploading to test flight)