Создаем свое AppStore для распространения по программе iOS Enterprise Distribution

от автора

В developer подписках Apple есть возможность разрабатывать приложения для внутрекорпоротивного использования. При этом приложения не проходят проверку со стороны Apple и не обязаны «светится» в AppStore. Всё выше сказанное относится и к Ad Hoc приложениям, они принципиально ничем не отличаются, разве что временем жизни после создания.

Для распространения приложения хотелось решить задачи:

  1. пользователь самостоятельно мог установить приложение на свое устройство
  2. только авторизированный пользователь мог устанавливать приложение
  3. простая публикация приложения на сервере


Опишу, как реализовывали и какие были трудности.

1.

С первым пунктом всё понятно, есть информация на эту тему. Т.е. создаем правильную ссылку на plist файл, а в нем должна быть волшебная ссылка на ipa архив приложения.

2.

Второй пункт казался не такой и сложной проблемой: создаем страницу аутентификации и авторизируем пользователя. Далее выдаем ему ссылку на установку/обновление приложения. PROFIT.

На деле оказалось всё не так просто. Сессия между Safari (я не говорю уже о других браузерах) и AppStore не шарится. Немного подумав можно и этот вопрос решить – сделать сессионные ключи и передавать их в параметрах ссылки. Просто делаем аналог куки, которые передаются GET’ом и имеют ограничения по времени жизни/количество раз использования. Далее меня ждал еще один маленький нюанс. По задумке ссылка на plist выглядела примерно так: httр://example.com/getapp?key=F00DBEE&type=plist, для получения самого приложения параметр type=ipa. Оказалось, что при обработке ссылок устройство учитывает только первый параметр, остальные попросту отбрасывает – обидно. Но где наше не пропадало – все интересующие данные можно и в один параметр сложить.

Стоит обратить внимание еще на одну мелочь – перед тем как получить приложение AppStore делает запрос на сервер методом HEAD, а только потом скачивает сам фал GET’ом. В связи с этой хитрой хитростью у меня сессия «протухала» тогда когда не надо. Да само приложение выплевывалось два раза, т.к. привык для контента использовать HEAD === GET.

3.

Уменьшение количества данных для публикации = уменьшение ошибок третьей стороны. В идеальном случае, что бы в обновлении фигурировал один файл приложения — ipa, но пользователю, зачастую, хочется знать, какие изменения были в приложение. Так что остановились на варианте ipa + описание изменений.

Для установки приложения нужен plist, в котором описаны предустановочные свойства. Этот XML файл выглядит абсолютно одинаково (с разницей в значениях параметров) для разных приложений.

пример 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>__URL__</string>                      </dict>                      <!-- не обязательный, иконка, которая показывается                      в момент скачивания приложения.                      При отсутствии будет серый квадрат -->                      <dict>                          <key>kind</key>                          <string>display-image</string>                          <key>needs-shine</key>                          <false/>                          <key>url</key>                          <string>__SMALL_IMAGE__</string>                      </dict>                  </array>                  <key>metadata</key>                  <dict>                      <!-- обязательный. внутренний                      идентификатор приложения -->                      <key>bundle-identifier</key>                      <string>__IDENTIFIER__</string>                      <!-- обязательный. Версия сборки приложения -->                      <key>bundle-version</key>                      <string>__VERSION__</string>                      <key>kind</key>                      <string>software</string>                      <!-- обязательный. Наименование приложения -->                      <key>title</key>                      <string>__TITLE__</string>                  </dict>              </dict>          </array>      </dict>  </plist>

Данные параметры можно получить из другого plist, находящегося в недрах ipa-файла (который является zip архивом) приложения — Info.plist. Недостаток только в том, что он бинарный. В MacOS есть хорошая консольная утилита plutil для конвертации форматов plist’ов, но не у всех на серверах стоят MacOS.

Решить эту проблему можно путем написания своего конвертера, либо собрать нативное приложение, либо воспользоваться Perl скриптом, Windows можно поставить приложения от Apple и найти консольную утилиту по адресу «C:\Program Files (x86)\Common Files\Apple\Apple Application Support\». Вариант со своей реализацией заманчив, но дорог. Я воспользовался Perl’ом. После конвертации бинарного plist получаем примерно такой XML:

пример bplist

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">    <plist version="1.0">       <dict>          <key>BuildMachineOSBuild</key>          <string>14D73</string>          <key>CFBundleDevelopmentRegion</key>          <string>en</string>          <key>CFBundleDisplayName</key>          <string>MyApp</string>          <key>CFBundleExecutable</key>          <string>MyApp</string>          <key>CFBundleIcons</key>          <dict>             <key>CFBundlePrimaryIcon</key>             <dict>                <key>CFBundleIconFiles</key>                <array>                   <string>i57.png</string>                   <string>i114.png</string>                </array>             </dict></dict>             <key>CFBundleIdentifier</key>             <string>com.company.MyApp.ios</string>             <key>CFBundleInfoDictionaryVersion</key>             <string>6.0</string>             <key>CFBundleName</key>             <string>MyApp</string>             <key>CFBundlePackageType</key>             <string>APPL</string>             <key>CFBundleShortVersionString</key>             <string>1.0</string>             <key>CFBundleSupportedPlatforms</key>             <array>                <string>iPhoneOS</string>             </array>             <key>CFBundleVersion</key>             <string>1.0.3</string>             <!--                еще много ключей и их значений, который в данной статье не интересны             -->          </dict>       </plist> 

Из описания нам интересны: CFBundleVersion, CFBundleIdentifier, CFBundleDisplayName. Это наименование, версия, идентификатор приложения соответственно. Можно бонусом еще и иконку вытянуть. Для получения значений используем такой XPath /plist/dict/key[contains(text(),’__KEY__‘)]/following-sibling::string[1]/text() где __KEY__ необходимые ключи.

Таким образом можно распространять не только Enterprise, но и AdHoc без особых проблем.

Обновление приложения.

Следить за обновлением приложения должно само приложение, т.к. оно не является частью AppStore. Да и вообще за приложением b2b нужно следить очень внимательно, оно еще может не перенестись с одного устройства на другое при дампе через iTunes. Для непосредственного обновления приложения из самого приложения необходимо открывать в Safari волшебную ссылку на манифест (plist) файл, которая начинается так itms-services://?action=download-manifest&url=.

В качестве заключения.

В данной реализации, быть может, только один костыль — использование стороннего приложения для чтения info.plist, остальные «телодвижения» можно совершить без особых проблем на всех (большинстве) платформ/технологий и т.д.

ссылка на оригинал статьи http://habrahabr.ru/post/205780/


Комментарии

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

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