Подготовка
Каждый человек, так или иначе связанный с разработкой приложений для устройств 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 этапа:
- Получение манифеста установки приложения через Items Service.
- Установка файла *.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.
Shiny2
спасибки
Plarium Автор
Рады, если будет полезно.