Как мы публиковали iOS приложение для видеочата в App Store


    В этой статье мы расскажем как собирали свое первое WebRTC-приложение для iOS и размещали его в App Store, с чем пришлось при этом столкнуться и что из этого вышло.

    Зачем


    Мы занимаемся тем, что пилим WebRTC медиасервер и три SDK для работы с сервером: Web, Android, iOS.

    С каждым SDK идет несколько примеров. Например для iOS SDK есть 11 примеров приложений, исходники которых доступны здесь.

    Разработчик берет пример по ссылке, убеждается, что пример работает и уже после этого собирает пример из исходников и начинает его видоизменять / встраивать SDK в свое приложение для работы с потоковым видео.

    Такой подход, как «посмотрел демку — собрал — внедрил» прекрасно работает практически везде, но не с Apple. Вы не можете сбросить ссылку любому желающему протестировать ваше приложение до тех пор, пока оно не будет опубликовано в App Store.

    Возможно мы пишем здесь простые истины, которые уже давно пройдены всеми iOS разработчиками и издателями, но для нас это было небольшим сюрпризом.

    Без публикации в App Store никто не сможет установить ваше приложение, пока UDID вашего устройства (iPhone) не будет внесен в список.

    Т.е. Для того, чтобы дать потестировать приложение кому-то, вы должны сначала попросить этого кого-то дать вам UDID устройства, внести его в список на сайте developer.apple.com, пересобрать приложение в Xcode, выложить, и только после этого давать ссылку на скачивание этому конкретному человеку.

    Такой метод распространения называется adhoc. В принципе это удобно, внутри команды — один раз вписать всех разработчиков и тестировщиков. Но это жутко неудобно, если вам нужно предоставить доступ к тестовому демо-приложению всем желающим.

    В результате, было принято решение опубликовать наше демо-приложение в App Store, дабы дать всем желающим возможность его потестить и понять как оно работает, не затрачивая времени на сборку из сурсов.

    Приложение


    В качестве пилотного приложения взяли Two Way Streaming, которое выглядит так:


    Это простое приложение позволяет делать всего три вещи:

    • Подключиться к серверу.
    • Захватить видеопоток с камеры и отправить на сервер.
    • Забрать видеопоток с сервера и проиграть на дисплее справа.

    Приложение представляет собой фейковый видеочат, в котором вы можете отправить видеопоток со своего iPhone и тут же посмотреть видеопоток другого человека. Т.е. При желании, приложение достаточно легко переделывается в видеочат на подобие Chatroulette.

    Исходный код приложения доступен по этой ссылке и требует WCS iOS SDK в качестве зависимости. Процесс сборки приложения и всех остальных примеров, с вытягиванием сурсов, описан на странице SDK. Поэтому на сборке мы заострять внимание не будем, а перейдем сразу к описанию публикации в App Store.

    Публикация


    1. Начнем с самого главного — с иконок. Делаем иконки с максимальным размером 1024x1024 и добавляем в Xcode.


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

    2. Создаем App Record — заполняем карточку приложения.

    Заполнение формы процесс не сложный., поэтому предлагаем ознакомиться с ним на сайте Apple.

    3. Добавляем скриншоты.

    Так как, у нас демо-приложение, мы не стали долго раздумывать и просто подфотошопили скриншоты под требуемые размеры:

    • 5.5-Inch Display (1242 x 2208)
    • 12.9-Inch Display (2048 x 2732)

    4. Загружаем сборку в iTunes Connect

    Это можно сделать с использованием Application Loader для выгрузки приложения в iTunes Connect.

    1) Открываем Xcode меню и кликаем Open Developer Tool / Application Loader
    2) Логинимся со своим Apple ID
    3) Нажимаем «Deliver Your App».
    4) Выбираем приложение ipa, кликаем Send и ждем окончания загрузки.

    5. Отправляем загруженное приложение на модерацию в iTunes Connect

    Здесь нужно будет ответить на три вопроса. Немного погуглив, мы вписали No во все три ответа:
    Is your app designed to use cryptography or does it contain or incorporate cryptography? No
    Does your app contain, display, or access third-party content? No
    Does this app use the Advertising Identifier (IDFA)? No

    На самом деле, наше приложение активно использует шифрование, а именно AES и HTTPS / Websocket. Но гугл дал ответ о том, что это про кастомное шифрование, выходящее за рамки стандартных технологий AES, HTTPS. Поэтому поставили No, как и для двух остальных вопросов.

    В результате приложение ушло на модерацию, а мы занялись своими делами.

    Траблы


    После того, как приложение отправилось на модерацию, на почту может прийти нотификация об отклонении сборки — Binary Rejected.

    1. Первый реджект не заставил себя долго ждать и буквально в этот же день пришло сообщение.

    We have started the review of your app, but we are not able to continue because we need access to a video that demonstrates your app in use on an iOS device.

    Пришлось записать короткое демонстрационное видео и выложить его на YouTube. Видеоролик записан с экрана мобильного устройства и показывает как функционирует данное мобильное приложение.

    2. Второй трабл, как мы и писали, был связан с иконкой. Мы установили в качестве крупной иконки такую:


    А в качестве мелких иконок такую:


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

    We noticed the app icon displayed on the device and the large icon displayed on the App Store do not sufficiently match, which makes it difficult for users to find the app they just downloaded.

    3. Разобраться с иконками и записать видео для демонстрации приложения было делом недолгим, и в этот же день мы снова отправили приложение на модерацию.

    Следующий реджект пришел через четыре дня и был, с первого взгляда, более непонятным:

    Your app declares support for audio in the UIBackgroundModes key in your Info.plist but did not include features that require persistent audio.

    The audio key is intended for use by apps that provide audible content to the user while in the background, such as music player or streaming audio apps. Please revise your app to provide audible content to the user while the app is in the background or remove the «audio» setting from the UIBackgroundModes key.

    Здесь утверждается, что наше приложение декларирует поддержку аудио в UIBackgroundModes, но при этом не имеет фич, которые бы требовали воспроизведения аудио в бэкграунде. Имеется в виду, что если при работе приложения переключиться на другое приложение либо нажать кнопку Home, то аудио будет продолжать играть, и это называется audio in the UIBackgroundModes, т.е. функция, актуальная например для воспроизведения музыки.

    Чтобы не спорить с модераторами Apple и не доказывать, что наше приложение Two Way Streaming тоже способно играть аудио / видеопотоки в бэкграунде, мы просто отключили декларацию этой фичи.

    4. Аналогичное требование, касающиеся работы в UIBackgroundModes было по функции VoIP. Наше приложение декларировало поддержку VoIP в бэкграунде, а модераторы заподозрили наше приложение в отсутствие VoIP.

    Your app declares support for VoIP in the UIBackgroundModes key in your Info.plist, but it does not include any Voice over IP services.

    To resolve this issue, please revise your app to either add VoIP features or remove the «voip» setting from the UIBackgroundModes key.

    Формулировка «revise your app to add VoIP features» нам не очень понравилась, т.к. пришлось бы доказывать и показывать модераторам, что у нас есть VoIP функции и им необходимо работать в UIBackgroundModes. Поэтому чтобы ускорить процесс, мы отключили VoIP in UIBackgroundModes, пересобрали приложение и снова отправили на модерацию.

    Отключение UIBackgroundModes


    Для отключения, мы проводили все манипуляции с файлом Info.plist, который для adhoc-раздачи выглядел так.

    <?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>method</key>
            <string>ad-hoc</string>
    </dict>
    </plist>
    

    После подготовки его для публикации в App Store, конфиг стал выглядеть так.

    <?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>CFBundleDevelopmentRegion</key>
    	<string>en</string>
    	<key>CFBundleExecutable</key>
    	<string>$(EXECUTABLE_NAME)</string>
    	<key>CFBundleIdentifier</key>
    	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    	<key>CFBundleInfoDictionaryVersion</key>
    	<string>6.0</string>
    	<key>CFBundleName</key>
    	<string>$(PRODUCT_NAME)</string>
    	<key>CFBundlePackageType</key>
    	<string>APPL</string>
    	<key>CFBundleShortVersionString</key>
    	<string>1.0</string>
    	<key>CFBundleSignature</key>
    	<string>????</string>
    	<key>CFBundleVersion</key>
    	<string>1</string>
    	<key>LSRequiresIPhoneOS</key>
    	<true/>
    	<key>NSCameraUsageDescription</key>
    	<string>Need camera access for publishing stream with video</string>
    	<key>NSMicrophoneUsageDescription</key>
    	<string>Need microphone for publishing stream with audio</string>
    	<key>UIBackgroundModes</key>
    	<array>
    		<string>audio</string>
    		<string>voip</string>
    	</array>
    	<key>UILaunchStoryboardName</key>
    	<string>LaunchScreen</string>
    	<key>UIMainStoryboardFile</key>
    	<string>Main</string>
    	<key>UIRequiredDeviceCapabilities</key>
    	<array>
    		<string>armv7</string>
    	</array>
    	<key>UISupportedInterfaceOrientations</key>
    	<array>
    		<string>UIInterfaceOrientationPortrait</string>
    		<string>UIInterfaceOrientationLandscapeLeft</string>
    		<string>UIInterfaceOrientationLandscapeRight</string>
    	</array>
    	<key>UISupportedInterfaceOrientations~ipad</key>
    	<array>
    		<string>UIInterfaceOrientationPortrait</string>
    		<string>UIInterfaceOrientationPortraitUpsideDown</string>
    		<string>UIInterfaceOrientationLandscapeLeft</string>
    		<string>UIInterfaceOrientationLandscapeRight</string>
    	</array>
    </dict>
    </plist>
    

    И сейчас, после удаления UIBackgroundModes, конфиг имеет такой вид:

    <?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>CFBundleDevelopmentRegion</key>
    	<string>en</string>
    	<key>CFBundleExecutable</key>
    	<string>$(EXECUTABLE_NAME)</string>
    	<key>CFBundleIdentifier</key>
    	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    	<key>CFBundleInfoDictionaryVersion</key>
    	<string>6.0</string>
    	<key>CFBundleName</key>
    	<string>$(PRODUCT_NAME)</string>
    	<key>CFBundlePackageType</key>
    	<string>APPL</string>
    	<key>CFBundleShortVersionString</key>
    	<string>1.0</string>
    	<key>CFBundleSignature</key>
    	<string>????</string>
    	<key>CFBundleVersion</key>
    	<string>1.1</string>
    	<key>LSRequiresIPhoneOS</key>
    	<true/>
    	<key>NSCameraUsageDescription</key>
    	<string>Need camera access for publishing stream with video</string>
    	<key>NSMicrophoneUsageDescription</key>
    	<string>Need microphone for publishing stream with audio</string>
    	<key>UIBackgroundModes</key>
    	<array/>
    	<key>UILaunchStoryboardName</key>
    	<string>LaunchScreen</string>
    	<key>UIMainStoryboardFile</key>
    	<string>Main</string>
    	<key>UIRequiredDeviceCapabilities</key>
    	<array>
    		<string>armv7</string>
    	</array>
    	<key>UISupportedInterfaceOrientations</key>
    	<array>
    		<string>UIInterfaceOrientationPortrait</string>
    		<string>UIInterfaceOrientationLandscapeLeft</string>
    		<string>UIInterfaceOrientationLandscapeRight</string>
    	</array>
    	<key>UISupportedInterfaceOrientations~ipad</key>
    	<array>
    		<string>UIInterfaceOrientationPortrait</string>
    		<string>UIInterfaceOrientationPortraitUpsideDown</string>
    		<string>UIInterfaceOrientationLandscapeLeft</string>
    		<string>UIInterfaceOrientationLandscapeRight</string>
    	</array>
    </dict>
    </plist>
    

    Ура


    Через пару дней пришло сообщение о том, что наше приложение заапрувили и теперь оно имеет статус Ready For Sale и скоро появится в App Store.

    The following app has been approved and the app status has changed to Ready for Sale:
    App Name: TwoWayStreaming
    App Version Number: 1.0
    App Type: iOS


    В результате публикация приложения заняла 1 неделю и потребовала корректировки следующих проблем:

    • Крупная иконка не должна отличаться от мелких рисунком
    • Должно быть обязательно добавлено демонстрационное видео, по которому видно как работает приложение.
    • Приложение должно явно показывать, что для его работы требуется воспроизведение аудио в бэкграунде, либо эта функция audio in the UIBackgroundModes должна быть отключена.
    • Приложение должно явно показывать, что для его работы требуется использование микрофона бэкграунде, либо эта функция VoIP in the UIBackgroundModes должна быть отключена.

    На данный момент приложение доступно в App Store по этой ссылке:
    https://appsto.re/ru/r1MEjb.i

    Теперь любой желающий сможет установить апп и провести тесты.

    Надеемся, что эта статья поможет при публикации вашего собственного приложения с аудио и видео функциями и ваше приложение попадет в App Store намного быстрее.

    Ссылки


    Two Way Streaming for iOS — демо приложение Live-стриминга для iOS
    Source — исходный код

    Two Way Streaming for Web — тоже самое приложение для Web
    Source — исходный код

    Two Way Streaming for Android — тоже самое приложение для Android
    Source — исходный код

    Web Call Server — WebRTC сервер, через который работает приложение Two Way Streaming

    WCS iOS SDK — описание процесса сборки примеров в Xcode для iOS.

    Подборка ссылок на iTunes Connect


    Работа с иконками


    iTunes Connect Properties
    App Distribution Guide, Adding App Icons

    Создание карточки приложения (App Record)


    iTunes Connect Developer Help, Add an app to your account
    iTunesConnect_Guide, Creating iTunes Connect Record for an App
    iTunes Connect Properties

    Требования к скриншотам


    Screenshot Properties

    Загрузка билда приложения на iTunes Connect


    App Distribution Guide, Uploading Your App to iTunes Connect
    Xcode Help, Upload an app to iTunes Connect
    Upload your application binary files with Application Loader
    Метки:
    Flashphoner 83,83
    Компания
    Поделиться публикацией
    Комментарии 11
    • +1
      почему я вспомнил про «пего-чат»?))
      • –1
        Потому что недавно вышел 4-й сезон Кремниевой долины?))
      • 0
        Согласен с модерацией — независимо от размера — иконки приложения должны быть похожи как минимум. У вас же общего был только цвет и малозаметные стрелки. И… все приглядывался: на упрощенной иконке у вас стрелки вверх сползли. Так задумано?
        • 0
          Без публикации в App Store никто не сможет установить ваше приложение, пока UDID вашего устройства (iPhone) не будет внесен в список.

          Это не совсем так. Для того чтобы отдать приложение на внешнее тестирование существует TestFlight Beta Testing
          • 0

            Тоже столкнулись с некоторыми вопросами при публикации. Наш сервис подразумевает подписку с ежемесячной оплатой. Так вот сейчас мы еще монитезацию не включили, а apple заинтересовало кто оплачивает сервис и взымается ли плата с пользователей. Пока ответили что все бесплатно, но как дальше быть пока не знаем. Да, там есть у них встроенная подписка, но она нам будет походу не очень удобно. Может кто вкурсе как правильно?

            • 0
              Если вы не продаёте offline-сервисы то Apple не даст сделать свой биллинг (карточный/смсный/..), придётся пользоваться их подписками. А подписки у них в техническом плане та ещё боль.
              Если у вас продукт действительно ограничен в использовании по времени (либо некие периодические издания журналов/книг/итп), то проблем при ревью возникнуть не должно.
              • 0
                У нас система учета с разными тарифами и понятное дело ежемесячно оплачивается. Можно ли через ихние подписки как-то разные тарифы сделать? Конкуренты наши не юзают подписки я так понял. Тогда как apple не банит их за нарушение правил?
                • 0
                  Разные тарифы сделать можно.
                  Конкуренты наши не юзают подписки я так понял. Тогда как apple не банит их за нарушение правил?

                  Не совсем понял. За что их банить, если нет подписок?)
                  • 0
                    Ну я не в теме может. Если они сами (конкуренты) внутри приложения блокируют доступ и мимо apple принимают платежи то это разве не запрещено?
                    • 0
                      Согласно App Store Review Guidelines п 3.1.1:
                      If you want to unlock features or functionality within your app, (by way of example: subscriptions, in-game currencies, game levels, access to premium content, or unlocking a full version), you must use in-app purchase. Apps may not include buttons, external links, or other calls to action that direct customers to purchasing mechanisms other than IAP.
                      • 0
                        Ну и в подтверждение про offline-сервисы п 3.1.5:
                        If your app enables people to purchase goods or services that will be consumed outside of the app, you must use purchase methods other than IAP to collect those payments ...

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

            Самое читаемое