Автоматизация выдачи AdHoc сборки приложения из Xcode для установки на девайс заказчика

в 7:31, , рубрики: amazon, amazon s3, Amazon Web Services, iOS, ios development, objective-c, testflight, xcode, разработка под iOS, метки: , , , , , , ,

Поясню для начинающих, что при разработке под iOS для установки на девайс большую часть времени вы собираете приложение в development режиме, т.е. только для себя.
Но в какой-то момент требуется начинать выдавать заказчику результат работы на «посмотреть».
Для этого используется особый вид сборки AdHoc Distribution. Нужно сходить к Apple'у и создать distribution provisioning profile. После чего собирать приложение, подписывая его этим профилем. В профиле прописываются все идентификаторы девайсов, на которые планируется это приложение устанавливать на этом этапе. В итоге при билде под AdHoc, XСode создает файл с расширением .ipa, который уже можно установить на все, прописанные в профиле, девайсы. Например через iTunes.

Возникает вопрос как лучше всего передать вашему клиенту получившуюся сборку. Да, можно просто отправить файл по почте например, или выложить на файлообменник и пусть бедняга сам устанавливает его через iTunes на свой девайс. Но если вы цените время своего клиента или вам лень объяснять ему как это сделать, ну или вы просто милый и приятный человек, то вам стоит задуматься, а нет ли другого, более удобного способа.

Об одном из таких способов, с автоматизацией выдачи из Xcode читаем под катом!

На этой стезе уже давно промышляет достаточно прихлебателей, которые, надо признать довольно не плохо справляются с этой задачей. Одним из заслуженных сервисов является например TestFlight. Суть в том что они дают возможность вашему клиенту (или бета тестерам) установить ваше приложение через их сервис. Для этого все бета тестеры должны установить на свои девайсы их приложение. И в дальнейшем, когда они получают по почте уведомление о выходе очередной вашей сборки — они запускают это приложение, находят там ваше обновленние и устанавливают его себе через их интерфейс.

Но и эта схема, я бы сказал, не идеальна.
Во-первых это подразумевает что вам все равно придется объяснять заказчику как ему зарегистрироваться в TestFlight, как установить их приложение себе (его нет в AppStore, установка происходит с их сайта).
Так же в этом случае мы не упрощаем себе жизнь на своей стороне — каждый раз нужно сначала собрать очередной AdHoc билд в Xcode, затем в его окне Organizer выбрать опцию AdHoc distribution, указать правильный профиль и сохранить .ipa файл на диск. Затем зайти на сайт TestFlight, и залить туда этот .ipa, написать комментарий, не забыть выставить разрешения для тех кому этот билд предназначен. Очень, очень много телодвижений.
К тому же часто сервис бывает не доступен, или просто скорость загрузки такова, что процесс установки приложения сваливается по таймауту.
Особенно не приятно бывает слышать от заказчика, что ваше приложение почему-то не устанавливается, когда ты точно знаешь что это не так, и ты проверил загрузку из TestFlight сам.
В итоге для меня последней каплей стал тот факт что они пока что не поддерживают установку на iOS7, под которую мы ведем разработку нашего последнего проекта.
И тогда мы пришли к решению, которое оказалось проще для обеих сторон.

Сейчас вся вышеописанная процедура сводится к двум шагам. Я, как и раньше, выбираю в Xcode archive build (так обычно собираются сборки под AdHoc). После чего пишу заказчику что приложение обновлено. Все.
Теперь заказчик просто заходит со своего девайса по определенному адресу в интернете (адрес не меняется и он может сохранить его в закладки). У него появляется всплывающее окно — не хотите ли установить приложение, он конечно же хочет, приложение устанавливается. Все счастливы.

И вот что нужно сделать, чтобы прийти к этой нирване.

Магия установки приложений через мобильный браузер.

Сначала немного о той магии, которая позволяет устанавливать приложения по URL.
Это уже давным давно существующая возможность, и на ней собственно и основаны все эти сервисы аля TestFlight.

Есть такой протокол itms-services:, с помощью которого на iOS в частности осуществляется установка приложений из браузера. Протокол подразумевает что будет загружен .plist файл c определенной структурой, где, в том числе, указана и ссылка на .ipa файл. В результате это обрабатывается iOS как запрос на установку приложения.
Т.е. нам первым делом понадобится создать какую-нибудь простенькую (или не очень, как хотите) HTML страницу. Рядом положить наш волшебный .plist файл и .ipa билд.

Здесь я подразумеваю что для такого хранения используется Amazon S3 хранилище. Завести свой аккаунт на Amazon Web Services проще простого и я не буду здесь это описывать. Хранение и раздача таких мизерных объемов выйдет вам в какие-то копейки, которые мне лениво подсчитывать здесь, сейчас не об этом.

Итак создаем наши файлы, вот так может выглядеть наш HTML. Назовем его, скажем index.html. Главное чтобы в нем были ссылки по itms-services: протоколу

<html>
<head>
<title>My Ad-Hoc Distribution Site</title>
<style>
    li{font-size:60pt margin:20px 0}
</style>
<meta name="viewport" content="width=device-width" />
<meta name="apple-mobile-web-app-capable" content="yes" />
</head>
<body>
<ul>
    <li><a href="itms-services://?action=download-manifest&url=http://mybucketname.s3.amazonaws.com/MyApp1.plist">
                Установить мое приложение MyApp1</a></li>
    <li><a href="itms-services://?action=download-manifest&url=http://mybucketname.s3.amazonaws.com/MyApp2.plist">
                Установить мое другое приложение MyApp2</a></li>
    </ul>
</body>
</html>

Как видите, здесь внедрен список, потому что мы не хотим ограничиваться только одним приложением, у нас их будет несколько. И каждое из них устанавливается по отдельной ссылке.

В самом адресе itms-services://... нужно изменить только mybucketname и название .plist файла. Т.е. замените их соответственно на название своего bucket на Amazon S3, куда вы все это сложите, и на имя вашего .plist (имя файла не имеет никакого значения и может не совпадать с названием приложения).

Сам .plist выглядит вот так:

<?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>http://mybucketname.s3.amazonaws.com/MyApp1.ipa</string>
				</dict>
			</array>
			<key>metadata</key>
			<dict>
				<key>bundle-identifier</key>
				<string>com.mycoolcompanyname.myapp1</string>
				<key>bundle-version</key>
				<string>1.0</string>
				<key>kind</key>
				<string>software</string>
				<key>title</key>
				<string>MyApp1</string>
			</dict>
		</dict>
	</array>
</dict>
</plist>

Понятное дело, здесь тоже нужно заменить название mybucketname в строке ключа software-package. И указать ваше название .ipa файла вместо MyApp1.ipa. И не забудьте поменять строку ключа bundle-identifier. Вместо com.mycoolcompanyname.myapp1 впишите свой bundle id, который вы использовали при билде этого приложения в Xcode.
Ну и укажите желаемое имя приложения в ключе title.

Отлично! Уже теперь вы можете собирать приложение под AdHoc distribution и ручками выкладываеть его на S3 в этот bucket. После чего его можно будет установить по ссылке mybucketname.s3.amazonaws.com/index.html открыв ее в мобильном браузере.

Кстати для ручной заливки на S3 рекомендую приложение CyberDuck. Сам остановился на нем, как на наиболее удобном варианте под MacOs.
Одна важная деталь, не забывайте выставлять права доступа к .ipa файлу на public-read. В CyberDuck это делается правой кнопкой на файле > Info и добавить группу Everybody с правами Read.

Но этот вариант тоже не для ленивых, поэтому идем дальше.

Автоматизация в Xcode. Пишем post-actions скрипт.

Чтобы уж совсем ничего не пришлось делать мы напишем маленький скрипт.
В Xcode существует возможность напихивать свои скрипты практически во все этапы при сборке приложения.
Нас, в данном случае, интересует только сборка в архив (именно так мы всегда собираем приложение для AdHoc Distribution).
Поэтому добавим post-action на завершающем этапе для этой схемы.
Заходим в Xcode, открываем диалог редактирования схем. Далее будет несколько поясняющих скриншотов. Они сделаны в beta версии Xcode 5, но Xcode 4.6 эта возможность тоже присутствует, и интерфейс в этой части практически не отличается.

Итак кликаем название вашего таргета в том месте где оно указано в левой верхней части главного окна Xcode. В появившемся диалоге выбираем пункт Edit Scheme…
В диалоге редактирования схем находим схему Arhive и кликаем маленький треугольник рядом с ее названием, развернутся несколько подпунктов. Последний из них и будет нужный нам Post-actions.

Автоматизация выдачи AdHoc сборки приложения из Xcode для установки на девайс заказчика

В выпадушке в этом окне выберите название вашего таргета. Это означает что все переменные в скрипте, вроде ${PRODUCT_NAME} например, будут браться соответствующие ему.

Вот такой скипт используем мы:

SIGNING_IDENTITY="iPhone Distribution: MyCoolCompanyName (G4DHGXDY2)"
PROVISIONING_PROFILE="${HOME}/Library/MobileDevice/Provisioning Profiles/20DB9849-4CFE-4005-81F6-1A594119839B.mobileprovision"
LOG="/tmp/post-build-upload-to-s3.log"

DATE=$( /bin/date +"%Y-%m-%d" )
ARCHIVE=$( /bin/ls -t "${HOME}/Library/Developer/Xcode/Archives/${DATE}" | /usr/bin/grep xcarchive | /usr/bin/sed -n 1p )
DSYM="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/dSYMs/${PRODUCT_NAME}.app.dSYM"
APP="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/Products/Applications/${PRODUCT_NAME}.app"

/usr/bin/open -a /Applications/Utilities/Console.app $LOG

echo -n "Creating .ipa for ${PRODUCT_NAME}... " > $LOG

/bin/rm "/tmp/${PRODUCT_NAME}.ipa"
/usr/bin/xcrun -sdk iphoneos PackageApplication "${APP}" -o "/tmp/${PRODUCT_NAME}.ipa" --sign "${SIGNING_IDENTITY}" --embed "${PROVISIONING_PROFILE}" >> $LOG


echo "Created .ipa for ${PRODUCT_NAME}" >> $LOG

echo -n "Uploading to S3... " >> $LOG

/opt/local/bin/s3cmd put --acl-public --force --guess-mime-type /tmp/${PRODUCT_NAME}.ipa "s3://mybucketname/MyApp.ipa" >> $LOG

echo "Done." >> $LOG
/usr/bin/open "https://basecamp.com/path-to-your-project-to-let-people-know-of-new-build"

Итак в открывшемся окне видим /bin/sh в первой строке ввода. Оставляем там это значение.
В пустое поле внизу копипастим этот скрипт.

Теперь нам предстоит отредактировать пару значений в нем под вашу конфигурацию. А именно, надо прописать правильное значение в переменную SIGNING_IDENTITY и прописать правильную ссылку на ваш текущий provisioning profile в переменной PROVISIONING_PROFILE.

Я делаю это так. Cохраняемся здесь — жмем OK внизу, выходим в Build Settings проекта и находим там раздел Code Signing Identity.
В нем есть все нужные значения. Но «услужливый» Xcode показывает их в человеко-читаемом виде и к тому же не дает выделять прямо там.

Кликаем правой кнопкой на название вашего signing identity (что-то вроде iPhone Distribution:...). Откроется поп-ап, в котором в самом низу будет опция Other… Выбираем ее и вот он весь наш набор параметров в текстовом виде и даже заботливо выделен.

В предыдущих версиях Xcode это выглядело примерно вот так:

iPhone Distribution: MyCoolCompanyName (G4DHGXDY2)
20DB9849-4CFE-4005-81F6-1A594119839B

Соответственно первая строка вписывается в переменную SIGNING_IDENTITY в скрипте.
А вторая — это код вашего профиля — просто замените им выделенную часть в адресе для переменной PROVISIONING_PROFILE, как показано ниже.

PROVISIONING_PROFILE="${HOME}/Library/MobileDevice/Provisioning Profiles/20DB9849-4CFE-4005-81F6-1A594119839B.mobileprovision"

В новом Xcode 5 — эти два значения разнесены на отдельные записи в секции Code Signing Identity. Поэтому то же действие нужно проделать два раза. Сначала скопировать название identity, затем код для provisioning profile.

Как видим, скрипт выполняет два простых действия. Сначала получившийся в результате сборки .app файл превращает в .ipa. Затем этот .ipa заливает на S3. В процессе работы скрипта так же создается .log файл куда пишется все что происходит. Этот .log файл кладется во временную папку (туда же куда и .ipa) и открывается системной утилитой Console чтобы мы могли видеть ход выполнения скрипта.

Осталась только одна тайна — как же работает заливка на S3.

Заливаем сборку на Amazon S3 с помощью скрипта

На самом деле не совсем, не будем мараться с ручной заливкой на S3, это хлопотно, воспользуемся для этого готовой утилиткой (S3cmd).

Мы с вами на маке, поэтому ставим ее через MacPorts. Кто не в курсе, это инструмент, который позволяет ставить на мак всякие не присущие ему линуксовые изыски. Если у вас не установлен MacPorts — ставим по одной из ссылок для вашей версии MacOS (Mountain Lion, Lion или Snow Leopard).

Далее ставим саму утилиту s3cmd. С MacPorts это делается предельно просто.

В терминале набираем:

sudo port install s3cmd

Появится запрос ввести пароль, введите пароль администратора и начнется установка. Она занимает около минуты.
Все, утилита установлена, теперь давайте ей скажем наш пароль от S3 (когда вы регистрируетесь на Amazon Web Services) вам дадут ключ и секретный код (не спрашивайте в чем разница между этими двумя понятиями, но нужны оба).

Итак набираем в терминале:

s3cmd --configure

и нас попросят ответить на несколько вопросов касаемо нашей желаемой конфигурации, в том числе попросят ввести ключ и секретный код.

Например это выглядит вот так:

Enter new values or accept defaults in brackets with Enter.
Refer to user manual for detailed description of all options.

Access key and Secret key are your identifiers for Amazon S3
Access Key: XXXXXздесь должен быть ваш ключXXX
Secret Key: YYYYздесь должен быть ваш секретYYY

Encryption password is used to protect your files from reading
by unauthorized persons while in transfer to S3
Encryption password: 
Path to GPG program: 

When using secure HTTPS protocol all communication with Amazon S3
servers is protected from 3rd party eavesdropping. This method is
slower than plain HTTP and can't be used if you're behind a proxy
Use HTTPS protocol [No]: No

On some networks all internet access must go through a HTTP proxy.
Try setting it here if you can't conect to S3 directly
HTTP Proxy server name: 

New settings:
  Access Key: XXXXXпоказывают снова ваш ключXXX
  Secret Key: YYYYпоказывают снова ваш секретYYY
  Encryption password: 
  Path to GPG program: None
  Use HTTPS protocol: False
  HTTP Proxy server name: 
  HTTP Proxy server port: 0

Test access with supplied credentials? [Y/n] Y
Please wait...
Success. Your access key and secret key worked fine :-)

Now verifying that encryption works...
Not configured. Never mind.

Save settings? [y/N] y
Configuration saved to '/Users/st/.s3cfg'

Ну вот и все, теперь в скрипте нужно только поправить название вашего S3 bucket и название приложения в строке:

/opt/local/bin/s3cmd put --acl-public --force --guess-mime-type /tmp/${PRODUCT_NAME}.ipa «s3://mybucketname/MyApp.ipa» >> $LOG

После чего — можно просто запустить билд под archive (Xcode > Product > Archive) и все должно работать. Ваше приложение окажется залитым по указанному адресу.

Автор: OlegKlk

Источник

Поделиться

* - обязательные к заполнению поля