NAUMEN
Компания
40,98
рейтинг
13 декабря 2013 в 07:28

Разработка → Создаем свое AppStore для распространения по программе iOS Enterprise Distribution recovery mode

В 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, остальные «телодвижения» можно совершить без особых проблем на всех (большинстве) платформ/технологий и т.д.
Автор: @wlw
NAUMEN
рейтинг 40,98
Компания прекратила активность на сайте

Комментарии (8)

  • 0
    Enterprise Distribution — требует чтобы приложения распространнялось строго только сотрудникам организации или все же допускает распространнение среди клиентов, партнеров претприятия, как минимум в целях тестирования?
    • 0
      только внутри предприятия. Причем если будет замечена утечка, то блокируется и получить заново будет очень сложно, да и первый раз получить не очень просто. При каждой установке происходит проверка онлайн и если чего-то не так, то установить нельзя будет, то-есть бан действует мгновенно.
      • 0
        Есть еще мнение, что одновременно на одно устройство нельзя поставить приложения от разных Enterprise подписок — работать не будут, данная информация с просторов буржуйских интернетов, сейчас ссылки привести не могу. Возможности проверить это так же нет.
        При установке/обновлении приложения устройство и вправду шлет много хитрых GET'ов на адреса Apple, блокировка этих адресов не влияла на работоспособность приложения. Часть из этих запросов, как я понимаю, проверка сертификата, которым подписано приложение.
        • 0
          Два приложения с разными сертификатами ставил — работало, может в чем другом проблема.
          По поводу механизма запросов — всё конечно засекречено. Ибо нефиг :-) И этот механизм может меняться время от времени.
      • 0
        понятно — остается надеятся, что когданибудь айпл откроет доступ для нашей страны в Volume Purchase Program, но даже это не решит ситуацию когда требуется делать очень много маленьких приложений для большого количества клиентов которым в appstore приложение и не нужно, а нужна только установка на ~3-10 устройст.
  • 0
    Начиная с версии iOS 7.1 требутеся HTTPS соединение для установки приложения.
  • 0
    В связи с Wirelurker и Masque Attacks
    Скажите, а что происходит, когда Apple отзывает сертификат?

    — профиль не удаляется с девайса?
    (судя по тому, что профиль ставится без проверки Apple — подозреваю, что это так)

    — корпоративные приложения, установленные ранее остаются и действуют?

    — на девайсы, где что-то ставилось ранее с этим профилем можно ставить дальше?
    • 0
      поведение может отличаться от версии к версии iOS

      — профиль не удаляется с девайса?
      как я помню, профиль удаляется после перезагрузки устройства

      — корпоративные приложения, установленные ранее остаются и действуют?
      если профиль не валидный или отсуствует, он не дает запускать приложение
      приложение сразу после запуска на сплешскрине сворачивается

      — на девайсы, где что-то ставилось ранее с этим профилем можно ставить дальше?
      да установка других приложений была без проблем

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка