Разработка
Уроки нового: Как переписывалось приложение Comedy Central для iOS
Нет слова страшнее в мире программирования, чем «переписать». Так что вы можете представить восторг в глазах руководства, когда наша техническая команда захотела сделать именно это.
Аурели Гаудри написал о том, как создавалось новое приложение Comedy Central. Мы предлагаем вам русский перевод его статьи.
Инвестиции в переписывание
Нет слова страшнее в мире программирования, чем «переписать». Так что вы можете представить восторг в глазах руководства, когда наша техническая команда захотела сделать именно это.
Но, как и картинка, эмодзи иногда стоят тысяч слов…
Мобильная разработка развивается быстро. Каждую осень разработчики встречают с новой версией операционной системой и новыми инструментами. Становится все более сложно поддерживать вашу кодовую базу на хорошем уровне. Команды разработчиков обнаруживают, что они тратят время на борьбу с изменениями, а не на экспериментирование с новыми функциями.
Некоторые из устаревших поставщиков кода, который мы поддерживаем, сидят на Restkit и XML фидах. Некоторые полагаются на NSURLConnection, класс, от которого отказались в пользу NSURLSession в iOS 9. Если вы планируете наперед и ваше приложение поддерживает хороший уровень абстракции, вы можете справиться с проблемами, вроде этих. Но это не всегда так.
Мы поставили перед собой цель – создать масштабируемую архитектуру мобильного приложения, которая удовлетворит наши будущие потребности. Мы пристально посмотрели на текущий каталог устаревших приложений и спросили себя – что работает, что не работает и что работает достаточно хорошо для того, чтобы это можно было улучшить и вновь использовать.
Как и в большинстве устаревшего ПО, функция накладывается на функцию. С этим приходит постоянно увеличивающийся технический долг. Наш фронт-энд достиг своей критической массы. Из-за этого пострадала и архитектура фидов.
Эти важные факторы, в сочетании с новым, современным и гибким API, укрепили нашу позицию. Мы получили «добро» от всех заинтересованных сторон для того, чтобы начать все с начала. Мы начали работать с разработчиками внутреннего API и продуктовыми менеджерами для создания чего-то, что будет устойчиво в будущем. Чего-то, что будет соответствовать нашим собственным технологическим, продуктовым и бизнес требованиям.
To Swift or Not to Swift?
Примерно неделю назад, в рассылке Swift Evolution мне попалась на глаза любопытный тред. Он вопил в своей теме: «Серьезно! Заморозьте Swift после выпуска версии 3.0».
Мы начинали наш проект на Swift 1.1. Сейчас есть Swift 2.3, а версия «больших переименований» 3.0 находится в бете.
Каждый релиз был болезненным. Изменения часто выбивали почву у нас из-под ног. Отладка и рефакторинг становились все более сложными. Время компилирования увеличивалось из-за удаления обращений к нативным методам
С хорошей стороны, Swift уменьшил количество файлов в проекте. Он краток. Это система типов и дженерики не дают нам писать много кода. Он также прекрасно подходит для функционального реактивного программирования. Стандартные библиотеки Swift обеспечивают большую часть из того, что вам может понадобиться.
И все мы можем согласиться, что Swift гораздо легче воспринимается на глаз, чем его монструозный предшественник Objective-C.
Эксперименты с современными концепциями и фреймворками
Большинство iOS приложений используют MVC архитектуру. Наши требования привели нас к MVVM (Model View ViewModel) шаблону.
Мы также ввели Функциональное Реактивное Программирование в наш код. По большей части, мы использовали его на уровне работы с данными. И мы использовали Обещания (Promises), концепцию, используемую в языках параллельного программирования, вроде Scala.
Две библиотеки для нас незаменимы — PromiseKit и ReactiveCocoa. PromiseKit это реализация и коллекция вспомогательных функций. Она привносит концепцию Обещаний в iOS и разъясняет намерения сложного кода. С ее помощью легко управлять последовательностью и/или параллельным выполнением единиц работы с помощью понятного синтаксиса. Библиотека безопасна для потоков и идет с прекрасными утилитами для управления параллелизмом.
ReactiveCocoa это реализация Функционального реактивного программирования для iOS. Мы использовали ее для связи данных и UI. И также мы использовали ее для упрощения абстрагирования от основных механизмов событий в iOS. Мы выбрали ReactiveCocoa, а не RxSwift, так как члены команды уже работали с ней. Кроме того, RxSwift был слишком молодым в то время.
При работе с замыканиями и добавлением слушателей, в PromiseKit могут возникать проблемы с управлением памятью. Мы кроме того обнаружили, что PromiseKit может создавать проблемы с отладкой.
Все эти концепции могут иметь крутую кривую обучения, но все они стоят инвестиций времени в долгосрочной перспективе.
Не слишком полагайтесь на других
Каждый раз, когда мы скачивали новую версию Xcode и пытались скомпилировать приложение, мы сталкивались с проблемами. Почти всегда это зависело не от нас. Инструмент Swift Migrator иногда работал хорошо, но чаще не очень, и тогда мы видели ошибки, типа этой:
Вы можете двигаться настолько быстро, насколько вам позволяют библиотеки, которые вы выбрали для интеграции в ваш код.
Вокруг библиотек, вроде PromiseKit и ReactiveCocoa, выстроились отличные сообщества. Их команды решают проблемы еще на стадии бета-версий Swift с помощью быстрых обновлений. Но такого не бывает у сторонних библиотек, которые вы выбрали или ваш бизнес выбрал для вас. Если решение зависит от вас, спросите, нужны ли они вам.
Кроме того, вы должны ограничить общее количество библиотек, которые вы включаете в проект. Мы поняли, что большое количество зависимостей преступно замедляет запуск приложения на старых устройствах. Apple рекомендует использовать только 6 внешних зависимостей.
Используйте инструменты, которые работают на вас
Визуальная разметка в конструкторе интерфейсов в Xcode и сторибордах продолжает улучшаться. Начинающие программисты могут легко создавать простые, но впечатляющие приложения и адаптивную разметку. Хотя это и не означает, что это правильное решение для всех.
Сториборды могут замедлить iOS разработку. Они вызывают проблемы в Xcode и конфликты при слиянии. Они путают действия пользователей, экраны и бизнес-логику в одном месте. А это плохо масштабируется.
Мы решили создать приложение, в котором большая часть UI определяется данными и динамична. Сториборды не были подходящим решением для этого. Мы использовали библиотеку PureLayout. Мы создаем наши экраны и модули в многократно используемых, динамичных кусках кода.
Знайте лучшие практики Apple
Бизнес-требования и другие ограничения иногда мешают делать вещи правильно. Но вы должны быть осведомлены о лучших практиках компании Apple и следовать им как можно чаще.
Ограничивайте обратную совместимость. Apple требует поддержки текущей версии и предыдущей. В настоящий момент это означает поддержку 9.3 и 8.4, а осенью, когда выйдет iOS 10, вы должны будете поддерживать только 9.3.
Устраняйте все предупреждения и ошибки до выпуска приложения. Это было доступно в Xcode для Objective-C и будет доступно в Swift для Xcode 8.
Профилируйте рано и потом профилируйте часто. Чем раньше вы найдете проблему, тем меньше времени вы потратите на ее решение. Всегда профилируйте релизный билд на устройстве, а не на эмуляторе. И отлаживайте на более старых и медленных устройствах, нежели ваше приложение будет поддерживать.
Переходите к Каталогам Ресурсов (Asset Catalog). Убедитесь, что вы включили изображения для всех разрешений, что вы поддерживаете. Масштабирование изображений в 3 раза для старых устройств может вызвать пиковые потребления памяти. А вам еще надо загрузить изображение, чтобы уменьшить его. Apple на WWDC в этом году наглядно показала как лучше использовать Каталоги Ресурсов (посмотрите «Улучшаем существующие приложения с новыми лучшими практиками»).
Переходите на TLS. До сих пор Apple давала разработчикам возможность выключить App Transport Security. Теперь его использование обязательно. К концу 2016 от разработчиков будут требовать объяснений по каждому случаю.
Следуйте вашим лучшим практикам
Мы создали приложение, которое мы считаем нашим лучшим произведением на сегодняшний день. И в ходе его создания мы выработали несколько наших лучших практик, которым мы и следуем, двигаясь вперед.
Используйте самый высокий уровень абстракции, доступный для вас. Управляйте асинхронностью с помощью абстракций типа Promises. Управляйте событиями, связями и потоками с помощью абстракций типа ReactiveCocoa. Разделяйте основную функциональность, используйте, например, слой данных. Разделяйте функции и реализации. Поддерживайте минимально возможное количество состояний и делайте каждый вид без состояний.
Будьте динамичными. Потоки данных обеспечивают работу большей части нашего приложения. Это дает нашим продюсерам невиданную гибкость и возможности управления.
Следуйте Gitflow. Используйте теги для ваших релизов и закрывайте pod-версию в pod-файле до релиза. Примите участие в обсуждении архитектуры дизайна до начала выполнения нетривиальных задач. Участвуйте в целенаправленной проверке кода после этого.
Примите правильную тактику развертывания. Используйте Crashlytics в качестве обзорного инструмента и тщательно рассматривайте все возможные проблемы. Любой простой сбой в вашем QA цикле потенциально может разнести всё тогда, когда приложение выйдет на рынок. Проверяйте повторные установки приложения для того чтобы убедиться, что настройки пользователя сохраняются, когда нужно.
И, конечно, тестируйте.
Тестируйте и автоматизируйте, чтобы вы могли делать новое
MVVM хорошо тестируемая архитектура. В ней легко создаются и глушатся объекты. Вы можете представлять экраны для автоматизированных UI тестов. А индивидуальные ViewModel методы для обработки данных и представления так же легко протестировать.
Наш уровень данных и работы с сетью сейчас по большей части покрыт тестами. Это гарантирует, что извлечение данных и их парсинг стабильны и безопасны.
Мы создали набор тестов на Python для проверки схемы данных бэкенда. Мы кроме того сделали простую автоматизацию для перехвата и воспроизведения данных сервисов. Далее, мы сделаем сравнение скриншотов текущего билда и последней хорошей версии. И используем DVR воспроизведение для удаления живых данных из уравнения.
Так как мы разделили приложение на секции и сделали многократно используемые библиотеки и pod-ы, мы сделали больше юнит-тестов. Цель в достижении большого покрытия для этих компонентов. Наконец, мы работаем над правильным отображением результатов для системных администраторов и программистов. Только после этого мы можем быть уверены, что все хорошо, и приступить к играм с расширением для iMessage. Это и другие вещи уже стоят в наших планах для iOS.
Вы сами можете проверить наше новое приложение в App Store.