Особенности сборки и доставки iOS-приложений

от автора

В этой статье мы делимся опытом сборки и доставки пользователям приложений для iOS, который накопился у студии Plarium Krasnodar в процессе отладки CI/CD.

Подготовка

Каждый человек, так или иначе связанный с разработкой приложений для устройств Apple, уже успел оценить спорное удобство инфраструктуры. Сложности встречаются повсюду: начиная с меню профиля разработчика и заканчивая инструментами отладки и сборки.

Статей об «азах» предостаточно в сети, поэтому постараемся выделить главное. Вот что нужно для успешной сборки приложения:

  • аккаунт разработчика;
  • устройство на базе macOS, выступающее в роли билд-сервера;
  • сгенерированный сертификат разработчика, который будет далее использоваться для подписи приложения;
  • созданное приложение с уникальным ID (следует отметить важность Bundle Identifier, потому что применение wildcard ID делает невозможным использование многих функций приложения, например: Associated Domains, Push Notifications, Apple Sign In и прочих);
  • профиль подписи приложения.

Сертификат разработчика следует сгенерировать через Keychain на любом устройстве на базе macOS. Очень важным является тип сертификата. В зависимости от среды приложения (Dev, QA, Staging, Production) он будет различаться (Development или Distribution), так же как и тип профиля подписи приложения.

Основные типы профилей:

  • Development — предназначен для подписи приложения команды разработчиков, используется Development-сертификат (имя вида iPhone Developer: XXXXX);
  • Ad Hoc — предназначен для подписи тестового приложения и внутренней проверки QA-отделом, используется Distribution-сертификат разработчика (имя вида iPhone Distribution: XXXXX);
  • App Store — релизный билд для внешнего тестирования через TestFlight и выгрузки в App Store, используется Distribution-сертификат разработчика.

При генерации профилей Development и Ad Hoc также указывается список устройств, на которые можно установить билд, что позволяет дополнительно разграничить доступ для пользователей. В профиле App Store нет списка устройств, так как разграничением доступа при закрытом бета-тестировании занимается TestFlight, о котором будет рассказано позже.

Для наглядности можно представить профиль разработчика в виде таблички ниже. Так проще понять, какие параметры для сборки нам нужны и откуда их брать.

Сборка

Чтобы было проще разделять сборки по проекту и среде, используем имена профилей вида ${ProjectName}_${Instance}, то есть имя проекта + инстанс (зависит от среды приложения: Dev, QA, GD, Staging, Live и так далее).

При импорте на билд-сервер профиль меняет название на уникальный ID и перемещается в папку /Users/$Username/Library/MobileDevice/Provisioning Profiles (где $Username соответствует имени учетной записи пользователя билд-сервера).

Существует два способа сборки файла *.ipa — устаревший (PackageApplication) и современный (через создание XcAchive и экспорт). Первый способ считается устаревшим, так как с версии 8.3 модуль упаковки app-файла убран из дистрибутива Xcode. Для его использования надо скопировать модуль из старого Xcode (версии 8.2 и более ранних) в папку:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/

И затем выполнить команду:

chmod +x /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/*

Далее нужно собрать *.app-файл приложения:

xcodebuild \ -workspace $ProjectDir/$ProjectName.xcworkspace \ -scheme $SchemeName \ -sdk iphoneos \ build \ -configuration Release \ -derivedDataPath build \ CODE_SIGN_IDENTITY=”$DevAccName”\ PROVISIONING_PROFILE=”$ProfileId” DEPLOYMENT_POSTPROCESSING=YES \ SKIP_INSTALL=YES \ ENABLE_BITCODE=NO 

Где:

-workspace — путь к файлу проекта.

-scheme — используемая схема, указанная в проекте.

-derivedDataPath — путь выгрузки собранного приложения (*.app).

CODE_SIGN_IDENTITY — имя аккаунта разработчика, которое можно проверить в Keychain (iPhone Developer: XXXX XXXXXXX, без TeamID в скобках).

PROVISIONING_PROFILE — ID профиля для подписи приложения, который можно получить командой:

cd "/Users/$Username/Library/MobileDevice/Provisioning Profiles/" && find *.mobileprovision -type f | xargs grep -li ">${ProjectName}_${Instance}<" | sed -e 's/.mobileprovision//'

Если в приложении используется дополнительный профиль (например, для Push Notifications), то вместо PROVISIONING_PROFILE указываем:

APP_PROFILE=”$AppProfile” \ EXTENSION_PROFILE=”$ExtProfile” \

Далее полученный файл *.app следует упаковать в *.ipa. Для этого можно использовать команду вида:

/usr/bin/xcrun --sdk iphoneos PackageApplication \ -v $(find "$ProjectDir/build/Build/Products/Release-iphoneos" -name "*.app") \ -o "$ProjectDir/$ProjectName_$Instance.ipa"

Однако данный способ считается устаревшим с точки зрения Apple. Актуальным является получение *.ipa путем экспорта из архива приложения.

Для начала нужно собрать архив командой:

xcodebuild \ -workspace $ProjectDir/$ProjectName.xcworkspace \ -scheme $SchemeName \ -sdk iphoneos \ -configuration Release \ archive \ -archivePath $ProjectDir/build/$ProjectName.xcarchive \ CODE_SIGN_IDENTITY=”$DevAccName” \ PROVISIONING_PROFILE=”$ProfileId” ENABLE_BITCODE=NO \ SYNCHRONOUS_SYMBOL_PROCESSING=FALSE

Отличия заключаются в методе сборки и опции SYNCHRONOUS_SYMBOL_PROCESSING, которая отключает выгрузку символов во время сборки.

Далее нам надо сгенерировать файл с настройками экспорта:

ExportSettings="$ProjectDir/exportOptions.plist"  cat << EOF > $ExportSettings <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>compileBitcode</key> <false/> <key>uploadBitcode</key> <false/> <key>uploadSymbols</key> <false/> <key>method</key> <string>$Method</string> <key>provisioningProfiles</key> <dict> <key>$BundleID</key> <string>$ProfileId</string> </dict> <key>signingCertificate</key> <string>$DevAccName</string> <key>signingStyle</key> <string>manual</string> <key>stripSwiftSymbols</key> <true/> <key>teamID</key> <string>$TeamID</string> <key>thinning</key> <string><none></string> </dict> </plist> EOF 

Где:

$Method — метод доставки, соответствует типу профиля подписи приложения, то есть для Development значение будет development, для Ad Hoc — ad-hoc, а для App Store — app-store.

$BundleID — ID приложения, который указан в настройках приложения. Проверить можно командой:

defaults read $ProjectDir/Info CFBundleIdentifier

$DevAccName и $ProfileId — настройки имени разработчика и ID профиля подписи, которые использовались ранее и должны совпадать со значениями в настройках экспорта.

$TeamID — десятизначный ID в скобках после имени разработчика, пример: iPhone Developer: …… (XXXXXXXXXX); можно проверить в Keychain.

Далее с помощью команды экспорта получаем необходимый файл *.ipa:

xcodebuild \ -exportArchive \ -archivePath $ProjectDir/build/$ProjectName.xcarchive \ -exportPath $ProjectDir \ -exportOptionsPlist $ExportSettings

Доставка

Теперь собранный файл нужно доставить конечному пользователю, то есть установить на устройство.

Для распространения билдов Development и Ad Hoc существует множество сервисов вроде HockeyApp, AppBlade и прочих, однако в рамках данной статьи речь пойдет об автономном сервере для раздачи приложений.

Установка приложения для iOS проходит в 2 этапа:

  1. Получение манифеста установки приложения через Items Service.
  2. Установка файла *.ipa согласно информации, указанной в манифесте, через HTTPS.

Таким образом, нам для начала надо сгенерировать манифест установки (тип файла *.plist) командой:

cat << EOF > $manifest <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>$ipaUrl</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>$BundleID</string> <key>bundle-version</key> <string>$AppVersion</string> <key>kind</key> <string>software</string> <key>title</key> <string>$ProjectName_$Instance</string> <key>subtitle</key> <string>$Instance</string> </dict> </dict> </array> </dict> </plist> EOF

Как видим, манифест содержит практически все параметры, участвующие в сборке приложения.

Версию приложения ($AppVersion) можно проверить командой:

defaults read $ProjectDir/Info CFBundleVersion

Параметр $ipaUrl содержит прямую ссылку на скачивание файла *.ipa. С седьмой версии iOS приложение должно быть установлено через HTTPS. В восьмой версии немного изменился формат манифеста: были удалены блоки с настройками иконок приложения вида

<images>    <image>...</image> </images>

Таким образом, для установки приложения достаточно простой html-страницы со ссылкой вида:

itms-services://?action=download-manifest&url=https://$ServerUrl/$ProjectName/$Instance/iOS/$AppVersion/manifest.plist

Для нужд отделов разработки и тестирования компания Plarium создала свое приложение установки билдов, которое дает нам:

  • автономность и независимость,
  • централизацию управления доступом и безопасную установку приложений через «временные», динамически создаваемые ссылки,
  • расширяемый функционал (то есть команда разработки при необходимости может интегрировать недостающие функции в уже существующее приложение).

Тестирование

Теперь речь пойдет о предрелизном тестировании приложения с помощью TestFlight.

Обязательными условиями для загрузки являются тип профиля подписи App Store и наличие сгенерированных API-ключей.

Есть несколько способов загрузки приложения:

  • через Xcode (Organizer),
  • через altool,
  • через Application Loader для старых версий Xcode (теперь Transporter).

Для автоматической загрузки используется altool, в котором тоже есть два способа авторизации:

  • App-Specific Password,
  • API Key.

Более предпочтительной является загрузка приложения с помощью API Key.

Для получения API Key переходим по ссылке и генерируем ключ. Кроме самого ключа в формате *.p8, нам понадобятся два параметра: IssuerID и KeyID.

Далее скачанный ключ импортируем на билд-сервер:

mkdir -p ~/.appstoreconnect/private_keys mv ~/Downloads/AuthKey_${KeyID}.p8 ~/.appstoreconnect/private_keys/

Перед загрузкой приложения в TestFlight нужно выполнить валидацию приложения, делаем это командой:

xcrun altool \ --validate-app \ -t ios \ -f $(find "$ProjectDir" -name "*.ipa") \ --apiKey “$KeyID” \ --apiIssuer “$IssuerID” 

Где apiKey и apiIssuer имеют значения полей со страницы генерации API-ключа.

Далее при успешной валидации выполняем загрузку приложения командой --upload-app c теми же параметрами.

Приложение будет проверено Apple в течение одного-двух дней и после станет доступным внешним тестировщикам: им пришлют на почту ссылки для установки.

Другим способом загрузки приложения через altool является использование App-Specific Password.

Для получения App-Specific Password нужно перейти по ссылке и сгенерировать его в разделе Security.

Далее следует создать в Keychain запись билд-сервера с этим паролем. С 11 версии Xcode это можно сделать командой:

xcrun altool --store-password-in-keychain-item "Altool" -u "$DeveloperName" -p $AppPswd

Где:

$DeveloperName — имя аккаунта iOS-разработчика, используемое для логина в сервисы Apple.

$AppPswd — сгенерированный App-Specific Password.

Далее получаем значение параметра asc-provider и проверяем успешность импорта пароля командой:

xcrun altool --list-providers -u "$DeveloperName" -p "@keychain:Altool"

Получаем вывод:

Provider listing: - Long Name - - Short Name - XXXXXXX        XXXXXXXXX

Как видим, искомое значение Short Name (asc-provider) совпадает с параметром $TeamID, который мы использовали при сборке приложения.

Для валидации и загрузки приложения в TestFlight применяем команду:

xcrun altool \ --(validate|upload)-app \   -f $(find "$ProjectDir" -name "*.ipa") \ -u "$DeveloperName" \ -p "@keychain:Altool" \

В качестве значение параметра -p можно взять значение $AppPswd в незашифрованном (явном) виде.

Однако, как уже было сказано, с точки зрения работоспособности для авторизации altool лучше выбрать API Key, так как в разных версиях Xcode встречаются те или иные проблемы («не видит» Keychain, ошибки авторизации при выгрузке и прочее).

На этом, собственно, все. Желаю всем причастным успешных сборок и беспроблемных релизов в App Store.

ссылка на оригинал статьи https://habr.com/ru/company/plarium/blog/490698/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *