Добрый день, меня зовут Антон, и я хочу в двух словах рассказать об истории создания Droopls — iOS клиента для DigitalOcean. История создания Droopls началась в 2014 году и, как обычно, довольно банально.
Многие не понаслышке знают и уважают облачный хостинг DigitalOcean (далее DO), я один из таких людей.
DO использую постоянно и много в основном для различных тестов и экспериментов, есть и дроплеты которые работают в режиме 24/7. Также у меня есть с десяток снапшотов с различными окружениями из которых я создаю дроплеты по мере необходимости.
Все необходимые действия выполняются через web панель DO, которая не сильно заточена под работу с телефона (возможно сейчас что-то изменилось). А очень часто нужно посмотреть в каком состоянии дроплет или выполнить какое-либо действие с телефона, что не очень удобно делать используя web панель.
Я попробовал использовать около 5-6 приложений из списка на сайте комьюнити DO https://www.digitalocean.com/community/projects/?type=apps и все по разным причинам меня не устраивали.
В общем, тут я решил сделать приложение для себя, которое будет выполнять необходимые мне задачи.
Первым делом ознакомился с API DO. У DO было две версии API — разрабатываемая версия 2.0 и стабильная, релизная 1.0. Выбор сразу был сделан в пользу версии 2.0, так как по возможностям она превосходит версию 1.0.
После изучения API были прикинуты задачи, которые должно было выполнять приложение:
- просмотр списка дроплетов и их текущие состояние (включен, выключен, залочен)
- просмотр IP адресов дроплета
- выполнение действий над дроплетом:
- включение/выключение
- пересоздание дроплета
- перезагрузка
- создание снапшота
- восстановление из снапшота
- создание нового дроплета
- удаление дроплета
На бумажке прикинута архитектура приложения, экраны и взаимодействие между ними.
За 6-8 выходных были реализованы: модели данных, маппер JSON -> Object, HTTP клиент, роутер запросов/ответов и всякие вспомогательные категории и классы. Все было написано на Objective-C и заточено под iOS 7. Никакой авторизации в приложение не было, API токен был в наглухо вшит в само приложение.
В последние дни июля прикрутил UI, никакого дизайна не было, только стандартные элементы управления. Приложение по дизайну или даже его отсутствию напоминало DigitalManager.
Как-то в ноябре мы встретились с приятелем Дмитрием за кружкой пива, речь зашла о каких-то проектах и DO. Я ему показал приложение и он попросил установить приложение на его телефон, так как ему нужно что-то подобное. На следующий день мы внесли его UDID в мой профайл, заменили в исходниках API token и залили ему на его iPhone.
Через пару недель он позвонил и говорит, что ему не хватает некоторых действий в приложении и было бы здорово их сделать. Но так как у меня и у него под конец года времени на это не было, отложили эту затею на неопределенный срок.
Так было до конца декабря, потом начались новогодние каникулы, появилось немного времени и мы допилили все действия, которых не хватало.
После очередной встречи решили, а почему бы не допилить оставшийся функционал, которые реализует API DO, но которого нет у нас и не выложить приложение в AppStore?!
Так как дизайн у нас отсутствовал полностью, я привлек к проекту еще одного моего знакомого из Германии — Эдуарда. Мы с ним проговорили по скайпу минут 40, я ему рассказал о приложении и идеи выложить его в AppStore. Он согласился в этом поучаствовать.
Работать вместе удавалось только 2-3 дня в неделю с вечера пятницы по воскресенье, да и то не всегда. Параллельно мы допилили функционал и работали над UI. Мы пробовали разные варианты (большое изображение по клику):

Были и другие варианты. В итоге не стали сильно заморачиваться и остановились на более светлых и легких тонах (справа на скриншотах). Пока определялись и пробовали разные варианты UI вся функциональность была реализована. Не хватало только иконок в приложении и основной иконки.
Так как иконок для приложения требовалось пару десятков, то решили не заморачиваться с их отрисовкой а купить готовый пакет. Выбор пал на Streamline Icons. Мы купили Developer Pack за 69$.
Иконку приложения рисовал Эдуард, варианты были разные:
Когда определились с иконкой, приложение было практически закончено, оставались некоторые мелкие детали и немного багов.
Тестирование
Исправив все явные баги и мелкие недочеты мы решили выложить бета версию в TestFlight и позвать всех желающих поучаствовать в тестировании. В середине апреля мы выложили приложение и отправили его на ревью, примерно через сутки мы прошли ревью и приложение стало доступно в TestFlight App. На сайте droopls.com мы сделали форму для сбора apple id желающих принять учатие и стали приглашать людей через доступные нам каналы. За неделю мы набрали примерно 15 человек, не густо, но большего мы и не ждали.
Первого мая в официальном твиттере DigitalOcean упомянули о нашем приложении. За первые два дня после упоминания к нам пришло еще человек 30.
Мы стали получать фидбэк, пожелания по функционалу и багрепорты. Так по просьбам нескольких человек мы добавили поддержку TouchID и 1Password, так же исправили несколько фатальных ошибок.
За все время у нас набралось 50-60 бета тестеров, перед релизом мы просто перестали добавлять новых тестеров. Всего у нас было 5-6 бета-сборок.
Ревью
К первому мая мы уже отправили приложение на ревью, но после того как к нам пришли новые тестеры, благодаря DigitalOcean, нам сообщили о нескольких багах. Сборку пришлось отозвать. Исправив ошибки, мы заодно реализовали поддержку TouchID и 1Password. Залили новую сборку на ревью и в TestFlight.
Через 7 дней мы получаем reject от Apple с причиной «Apps that link to external mechanisms for purchases or subscriptions to be used in the App, such as a «buy» button that goes to a web site to purchase a digital book, will be rejected», пункт 11.13.
Проблема была в ссылке «Don’t have an account? Sin Up» на самом первом экране. Кликнув по которой пользователь попадал на страницу регистрации, где одним из шагов было нужно ввести данные карты для оплаты + показались тарифы.
Убрали ссылку и отправили новый билд на ревью, через сутки снова Reject, с той же причиной: «Apps that link to external mechanisms for purchases or subscriptions to be used in the App, such as a «buy» button that goes to a web site to purchase a digital book, will be rejected».
Начали разбираться, оказалось в этот раз проблема была в другом месте.
Для OAuth авторизации мы загружаем страницу с формой с сайта DO. Под формой есть ссылка «Don’t have an account? Sign Up», при переходе по которой мы снова попадаем на шаг с формой ввода данных карты/оплаты.
Мы несколько дней пытались объяснить, что страница не наша и ничего править мы на ней не можем. Ревьювер попался вредный 🙂 Решили не тратить на него время и программно ссылку вырезать.
Изначально страница сразу грузилась в webView, теперь же она загружается через NSURLConnection, мы вырезаем ссылку и отдаем ее в webView…
Новый билд с исправлениями залили 12го числа, снова неделю ожидали ревью и утром 19го числа получили одобрение.
Немного технических подробностей
Весь код написан на Objective-C и немного на C. Кода на Swift нет. Основная IDE — AppCode. Также в проекте нет ни одного xib и Storyboard. Хотя нет, один xib все же есть — LaunchScreen.xib.
Для отправки push-уведомлений мы пользуемся сервисом XtremePush (XtremePush.com), XtremePush также собирает немного аналитики: кол-во новых пользователей, сессия, потерянные пользователи, тип устройства и версия iOS. Также мы трекаем статистику использования различных котроллеров. Никакие другие системы аналитики не используются, вся аналитика анонимна.
Для сбора ошибок и краш-логов мы используем Sentry. OAuth-токены пользователей хранятся в KeyCachin и между устройствами не синхронизируются.
В приложении используются следующие сторонние компоненты:
- AFNetworking (http://afnetworking.com). Думаю в представлении не нуждается
- CocoaLumberjack (https://github.com/CocoaLumberjack/CocoaLumberjack). Тоже многим известный фреймворк для логирования
- SWTableViewCell (https://github.com/CEWendel/SWTableViewCell) Реализует ячейки для UITbaleView со сдвигаемым контентом, под которым реализованы кнопки действий
- REFrostedViewController (https://github.com/romaonthego/REFrostedViewController) реализует меню
- AMWaveTransition (https://github.com/andreamazz/AMWaveTransition) анимация переходов
- Raven (https://github.com/getsentry/raven-objc) фреймворк для интеграции с Sentry
- 1Password (https://github.com/AgileBits/onepassword-app-extension) для интеграции с одноименным приложением
В общей сложности на приложение мы потратили примерно 100$ и полгода на разработку. Сейчас на ревью находится версия 1.0.1 с незначительными исправлениями.
Приложение доступно в AppStore https://itunes.apple.com/us/app/id986857527.



