Skip to content

Force update feature

Пятница. Солнечный весенний день. До конца рабочего дня остается полчаса… В комнату врывается менеджер - “СРОЧНО! Бэкэнд закончил деплой, нужно релизить мобильный билд!!! Чтобы завтра у всех пользователей все было доступно!!!” Знакомая ситуация? Я бы хотел поговорить о причинах того и есть ли от такой болезни таблетка.

Причины

Как бы быстро не скакал прогресс, не все успевают за ним следовать. Причина появления таких “моментальных” запросов проста - менеджмент долгое время вел web проекты, где выпуск новой версии это подлитие кода на головной сервер и ваша свежая версия доступна пользователю по нажатию CMD+R. Мобильный разработке уже больше 15 лет, и нюансы поставки не всегда самая нужная и важная часть при ведении проекта. Именно по этой причине, такого рода запросы, пораждают facepalm у разработчиков.

Untitled

А из новшеств, появилось только дополнительное звено в виде магазинов приложений для основных мобильных платформ iOS и Android. Алгоритм выглядит следующим образом - вы публикуете свою новую версию приложения в магазине, далее, клиентское устройство видит, что появилась новая версия и готов предложить вам ее скачать. Не делать это мгновенно он может по ряду причин:

  • обновления по умолчанию доступны только при подключении к сети WiFi
  • обновления отключены полностью
  • версия устройства и приложения несовместимы
  • новая публикация запрещена в определенном регионе
  • магазин запрещен в вашем регионе
  • вы используете экстравагантный способ поставки приложения, когда операционная система не может взять на себя обязанности обновления и может сделать это только с помощью вмешательства пользователя

Untitled

А что же делать, если мы выпускаем новую версию приложения с раскрытием новых полезных фич, которая привлечет продажи? Мы же не можем сидеть сложа руки и ждать, когда пользователь сам догадается обновиться до последней версии?

Вообще-то причин может быть еще больше, чем просто промоушен:

  • выпуск версии без поддержки обратной совместимости(зачастую так дешевле)
  • юридические обоснования
  • особенности платформы
  • маркетинг и т.д.

Решение

Решить проблему поможет ForceUpdate. ForceUpdate - это такая фича, которая дает вам возможность блокировать действия пользователя в приложении, пока тот не обновиться до последней версии.

Untitled

Это не обязательно должно быть блокирующее окно. Запрос на обновление можно разбить на 3 приоритета:

  • низкий - если у пользователя установлена не последняя версия из доступной, такой вид приоритета будет показывать раз в N дней GUI с запросом на обновление.
  • средний - GUI с предложением на обновление будет показан каждый раз после запуска приложения.
  • высокий - блокирующий экран с описанием того, что приложение не может быть использовано до обновления.

У платформ есть свои внутренние библиотеки с решениями. Попробовав внедрить Google инструмент - он вызвал у меня ряд неудобств в настройке и использовании. Основной недостаток - то что механизм вызова триггера находиться на стороне Google магазина и вызвать его можно только с помощью залития новой версии с указанием типа приоритета в сборке.

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

Это означает, что при залитии новой версии вы всегда должны указать приоритет этого релиза. Логично, строго, удобно. Вам не нужно писать GUI, библиотека это все предоставляет. Вам не нужно заморачиваться с тем, где и какой будет механизм централизованного вызова триггера обновления - свой бэк, BAAS и так далее.

Вроде все есть, что вам еще не нравиться? А не нравиться то, что с таким подходом мы очень жестко подсаживаемся на иглу платформы магазина. А магазинов, как мы уже упомянули ранее, может быть множество и на всех мы хотим быть опубликованы. А с такими условиями, мы обязаны менять процесс в публикации для одной из платформ…. Менять процесс это всегда сложно - сложно = дорого. И дорого еще потому, что учитывая такой нюансы, мы должны быть уверены, что менеджмент сверху принял это во внимание, а не просит жать на кнопку релиза снова. Помимо этого, запрос на обновления на более новую версию может быть инициирован до публикации версии. Это может быть связано все с той же проблемой поддержки обратной совместимости.

Untitled

Рассмотрим эту схему и ее варианты. На ней указан “средний” жизненный цикл проектов. Есть 2 клиентские платформы и backend. Бэк идет впереди планеты всей, клиенты, по мере возможностей, должны синхронизироваться и решить “проблему” синхронного релиза. Когда один из клиентов отстает мы не можем провести рекламу приложения для всех, потому что это затронет часть аудитории.

Я “обзываю” это среднем случаем по ряду причин - ваша команда маркетинга готова к реакциям на платформах? у вас есть бюджет на поддержку такой команды? - если нет, идите в среднее звено и держите синхронность общую(так проще выстроить процесс). Если вы не уверены и хотите все просчитать сами - сложность схемы для 2х платформ плюс backend = 2^3 и выводите среднюю линию.

Библиотека

Так как в своих планах я замахнулся на более чем один проект, я решил сделать из этого library, которую буду внедрять не простым CTRL+C/CTRL+V, а с обновлениями. Чтобы это сделать, внедрение и обновления должны быть максимально простыми и незаметными для пользователей. По-этому прячем вызов за composable функцией для корневой активити и дальше все решения отдаем за доменный слой.

И за логикой принятия решения того, какой экран может быть показан сейчас, на уровне домена я прячу только один usecase:

interface GetForceUpdateEventFlowUseCase {
    val event: StateFlow<ForceUpdateType>
}

enum class ForceUpdateType {
    ABSENT,
        // LOW, not implemented yet
    MEDIUM,
    FORCE,
    ;
}

Все остальное гибкое и легко заменяемое. Базовая имплементация строиться на FirebaseRemoteConfig. У нее есть изъян того, что у нас нет гарантии моментального скачивания конфига на более ранних версиях и, у пользователя, может быть задержка в несколько суток. Для более безопасного варианта - свое решение. Свой backend, например. Вопрос решения проблемы упирается в ваш бюджет и внутренние условия.

Ранее, я писал о FeatureToggle, которые могут иметь один из узлов имплементации на том же добром Firebase. В комментариях я заметил, что часть людей используют ForceUpdate как составляющая множества тоглинга. Не мешайте эти фичи в одно целое. Это два независимых инструмента. А проблема может начаться с, например, выхода на новый рынок, где готовый инструмент тогглинга недоступен(хоть и может потерпеть), а обновление пользователей на новые версии терпеть не может. Вот и получиться завязка на решение менее приоритетной проблемы.

Заключение

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