Разработка
Как за месяц вернуть к жизни приложение после неудачной разработки
Что может быть обиднее: вложить много времени и сил в приложение, которое в итоге не работает? К нам часто попадают приложения, которые нужно «спасать» в прямом смысле слова: они «виснут» или не запускаются вовсе, регистрация не работает или база данных отказывает. «Горе-специалисты» сделали яркую игрушку, не позаботившись о том, чтобы она заводилась и работала.
Каждое приложение должно приносить пользу. Поэтому мы выработали целую систему по спасению приложений.
Разберем ее на примере.
Есть мобильный аудиогид для путешествий без интернета. Приложение содержит оффлайн карту, встроенные маршруты и экскурсии, каталог достопримечательностей, аудиоразговорник и контакты туристических центров. Изначально приложение работало нестабильно, «падало», карты не работали оффлайн. А это один из основных функционалов приложения.
Аудит
Самое главное — быстро разобраться, что работает не так. Для этого анализируем документацию, макеты и дизайн и сравниваем это с тем, что есть в коде приложения.
По итогам составляем тестовую документацию, в которой учтены все функциональные требования из технического задания. Сюда же добавляем тест кейсы по юзабилити и нефункциональным требованиям к мобильному приложению.
По новой документации мы протестировали работоспособность приложения и функционал. Несколько интересных фич при тестировании мы разбирали в статье Тестируем мобильные приложения: наш чек-лист, они оказались актуальными и здесь.
Итоги аудита
Приложение попало к нам, когда только появившаяся iOS 10 не «переваривала» его и «выплевывала» при каждом удобном случае, а исправление одних багов приводило к появлению других:
- Долгая загрузка изображений на главном экране
Фотографии были огромного размера, а на сервере не было функционала для их уменьшения, поэтому загрузка была очень долгой. Слайдер листался только в автоматическом режиме, если пользователь хотел посмотреть фото вручную, часть фотографий исчезала; - Проблема с памятью
Приложение не «чистило» оперативную память: пользователь «ходил» по приложению, загружал новые экраны, и информация о них сохранялась в оперативной памяти гаджета. В итоге каждая страница «весила» десятки мегабайт, что приводило к перегрузке и отключению приложения. Это один из наиболее сложных и неявных багов, который часто проскальзывает в релиз. Мы подключались к Xcode, чтобы отследить, сколько памяти потребляет приложение; - Проблема с оффлайн картами
При покупке экскурсии пользователь получает маршрут с аудиозаписями, которые включаются при приближении к достопримечательностям. Если пользователь после загрузки экскурсии выключал и включал приложение заново уже без Интернета, то карта не кэшировалась, вместо нее появлялся черный экран; - Медленная работа карт
Любая карта состоит из нескольких наложенных друг на друга изображений, количество которых зависит от масштаба карт. Изначально предусматривали увеличение карты в 12 раз, но разработчик загрузил карты только для 5, что тормозило масштабирование; - Проблема с библиотекой TestFairy
С помощью TestFairy разработчики отслеживали моменты, когда приложение «падало». Как ни странно, сама библиотека тоже приводила к падениям. В итоге мы заменили эту библиотеку на Crashlytics. Она помогает собирать логи при нерегулярных падениях приложения, что помогает исправлять эти падения.
Спасение
Выяснив основные причины нестабильной работы приложения, мы взялись за работу.
Хранение данных
Приложение уже было в App Store, поэтому после нашего обновления у пользователей любой ценой должны были остаться ранее загруженные данные. Структура хранилища была неплохая, но когда понадобилось добавить новые поля, ее пришлось менять. Все проблемы с хранением данных были решены в процессе изменения хранилища — это помогло спасти пользователей от потери ранее загруженных данных: местоположения, картинки, ссылки в базе и т.д. Еще одним вопросом, возникшим в процессе разработки, был вопрос об обязательных полях для заполнения в базе данных. Структура была сконфигурирована таким образом, что все поля являлись опциональными (использовался язык разработки Swift), поэтому приходилось добавлять большое количество проверок на наличие тех или иных данных.
Геолокация
Мы уже рассказывали, как устроены геолокационные приложения в статье Разработка геолокационных приложений. Для приложения нужен был алгоритм, который бы рассчитывал границы загружаемой зоны на основе экскурсионных мест (маркеров на карте). Мы могли бы использовать данные от готовых сервисов, например, Google Maps, но если экскурсионные места находились только в центре города, то нужно было загружать слишком много данных (см. проблема с памятью). Вместо этого мы создали алгоритм, позволяющий рассчитать самый подходящий загружаемый квадрат на карте. Это помогло нам снизить количество загружаемых тайлов (tile — единица информации, которая необходима для отрисовки растровой карты из векторных данных) до 5 000. Для сравнения: количество тайлов целиком для Рима — 12 000. Этот же алгоритм ограничивает передвижение и масштабирование карты в пределах загруженного квадрата.
Работа с загрузкой
Предыдущий разработчик сделал так, чтобы данные загружались последовательно, но сделал это «в своем стиле». В итоге получилось как всегда: пользователь отправлял запрос в систему, но при любой ошибке загрузка прекращалась и приходилось запускать все заново. Чтобы это исправить, мы изменили процесс загрузки до неузнаваемости:
- Все запросы выполнялись последовательно, но были независимы друг от друга. Даже если в середине цепочки какой-то запрос возвращал ошибку, следующие запросы продолжали выполняться;
- Добавили проверку загруженных данных, если данные уже были загружены (именно корректно!). Запрос сразу пропускался и запускался следующий — так мы сократили время ожидания. Даже если у пользователя по какой-то причине возникал сбой, загрузка продолжалась с этой точки;
- Скорректировали функцию полной загрузки данных. Пользователю не было смысла загружать те экскурсии, которые он не покупал. Вместо этого он видел информацию по местам не купленных экскурсий без загрузки маршрута и аудиофайлов.
Все это позволило сделать загрузку быстрее и с меньшими затратами ресурсов. Помним, что пользователь находится за границей, когда использует приложение.
Случайные падения
В приложении была библиотека TestFairy для сбора статистики по критическим ошибкам. Это должно было привести к скорейшему нахождению причин падений и их исправлению. Как бы это странно ни звучало, именно она стала одной из причин остановки работы приложения. Поэтому пришлось ее убрать. Возник вопрос: каким сервисом воспользоваться. В итоге мы выбрали Crashlytics. Он показал истинную причину проблем — неправильно налаженная работа хранилища. Одни и те же данные пытались сразу использоваться и перезаписываться, что приводило к падению приложения. Детально изучив проблему, мы переписали логику работы с хранилищем и данными, исключив случайные падения.
Миграция со Swift 2.3 на Swift 3
Для разработки под iOS 10 нужно было переводить приложение на Swift 3. Если планируете поддерживать приложение, это обязательное условие — Apple объявила о прекращении поддержки версии Swift 2.3. Это лишние затраты, но деваться некуда. Перевод приложения на новую версию языка всегда риск появления багов, поэтому мы исправили все баги, которые попали к нам с приложением, и протестировали его перед миграцией. Мигрировали на новую версию одновременно с полным тестированием. Таким образом, когда миграция завершилась, QA-специалисты знали, в каком состоянии приложение.
Результат
Релиз обновленного приложения состоялся в декабре 2016 года спустя месяц работ по восстановлению его работоспособности. После первой загрузки мы продолжили поддерживать приложение и выпустили несколько обновлений с новыми идеями заказчика.