- PVSM.RU - https://www.pvsm.ru -

Приложение для iOS и Android за 24 часа

Приложение для iOS и Android за 24 часа

Что можно успеть за новогодние праздники? Как я смог выяснить, очень много. Даже если у тебя двое детей и куча родни, которую хочется навестить. Не получается только закончить статью для хабра. Она, зараза, растягивается на весь январь.

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

В App Store нашлось приложение "Транжира [1]". Это небольшая программа для записи своих трат и доходов. В конце месяца по ним можно сделать вывод почему опять не хватило до зарплаты. На 5ое число оно входило в Топ-10 в разделе «Финансы». На iphones.ru нашлась dev-story [2].

В своей статье ребята пишут: «После сдачи проекта у нашей команды обычно есть 3-4 полусвободных дня (таковы правила компании), и уже через час над приложением нашего дизайнера работали 4 человека. Первым в “бой” ринулся product manager, подсказав несколько ключевых моментов по позиционированию приложения. За ним программисты и даже office manager.». Это меня обнадежило и я стал думать как уложиться в 2 дня.

UPD 1: приложение обновилось во второй половине января. Сравнивая с моим приложением стоит ориентироваться на скриншоты и функционал из dev-story.

UPD 2: иконка «Транжиры» удивительно похожа на иконку платных постов из «4 правил автора» на хабре:
картинка [3].

У меня уже был опыт мобильной разработки [4] на C# и Cocoa. Посколько время тратил личное, хотелось использовать его с максимально пользой. Даже если успеть приложение не получиться, то хотя бы «вкусить» нового фреймворка или языка.

Я работал в DevExpress с 2006 по 2011 года и с тех пор следил за их анонсами. От них до сих пор приходит What's New на мою почту. Соблазнить на покупку что-ли хотят? У них появился мобильный js-фреймворк [5] на базе Cordova/PhoneGap. Писать его начали уже после моего увольнения, поэтому что это за зверь я не знал и было интересно попробовать.

Исследовательская компания Gartner пишет [6], что по состоянию на август 2013 большинство корпоративных мобильных приложений было написано на PhoneGap или продуктах на его основе (вроде Kony [7]). Мой личный, потребительнский опыт подсказывает что это не так. Но вдруг я заблуждаюсь?

С JavaScript и HTML я знаком понаслышке. Верстаю со stackoverflow подмышкой, умею писать простенькие jQuery селекторы. Знаю, где находится её документация. В общем, это огромная дыра в моих знаниях. Очень хотелось если не закрыть её, то хотя бы получить какой-то опыт.

В итоге у меня должно было получиться кросс-платформенное приложение. Это позволило бы мне получить преимущество над разработчиками Транжиры и произвести больше добавленной пользы за доступное время. У меня была всего одна пара рук и использовать её хотелось наиболее эффективно. На одной чаше весов была потенциальная эффективность фреймворка, на другой — отсутствие опыта в js-разработке у меня. Я понадеялся, что фреймворк перевесит и решил попробовать.

Мне удобно писать с VCS, поэтому сейчас я попытаюсь восстановить и описать по часам мой прогресс.

Готовые приложения вы можете скачать здесь:
Приложение для iOS и Android за 24 часа

iOS версия на ревью

Не уверен, что могу выложить в публичный доступ репо. В нем лежат фото, купленные на стоке и сторонние библиотеки каждая со своей лицензией. Написанный мною код, вы можете увидеть ниже, либо на свой риск заглянув внутрь бандла приложения.

Место действия: Тула, Дата: 5 января 2014

+20 минут Ушло на установку node.js и Cordova CLI

+10 минут Скачал шаблон приложения от Cordova. Добавил шаблон из PhoneJS. Создал Git-репозиторий, зарегистрировал его в WebStorm. Добавил запись в httpd.conf, чтобы была возможность отлаживать и проверять работу приложения в браузере.

+38 минут Поменял неймспейс приложения на io.nikitin.ThriftBox. Настроил навигацию. PhoneJS является MVC-фреймворком. Каждый экран приложения представлен набором из html разметки (вью) и функцией-фабрикой вью-модели. В простейшем случае, это

<div data-options="dxView : { name: 'home', title: 'Home' } ">
    <div data-options="dxContent : { targetPlaceholder: 'content' } ">
    // Содержимое вью
    </div>
</div>

плюс

ThriftBox.home = function (params) { // Параметры запроса из uri
  return {};  // Объект вью-модели
}; 

В дальнейшем, одно с другим увязывается с помощью knockout-биндингов [8]. Времени в притык, поэтому оставляю два экрана: ввод трат и отчет трат по месяцам.

+4 часа 20 минут Случился первый «затык». Не получилось быстро сделать разметку цифровых кнопок для ввода трат.
В исходном приложении на главном экране огромная клавиатура, похожая на калькулятор или приложение-звонилку.
Приложение для iOS и Android за 24 часа

Оказалось, что даже «в лоб», с помощью тега table сделать такую клавиатуру не получается. На retina-iphone бордюры в 1 пиксел между кнопками меняли свой цвет после нажатия кнопок. Разница в цвете линий на хорошем экране телефона была очень заметна. Пришлось думать, как победить.
Приложение для iOS и Android за 24 часа
Изучал вопрос и опробовал вариант верстки на div'ах. С ними не получилось добиться, чтобы рамка была ровно 1 пиксел шириной и все кнопки были одинакового размера на экранах разного разрешения. Спустя 3 часа баг был оставлен как есть и я двинулся дальше.

+28 минут Убран индикатор нажатия ссылки на iOS. iOS отрисовывает серый индикатор нажатия вокруг ссылок и объектов с обработчиком onclick. Поскольку у меня был свой индикатор нажатия (кнопка становилась темнее), этот серый индикатор мне был не нужен. Проблема решилась с помощью события dxAction [9]:

было:
<td data-bind="click: function() { buttonPress('1') }">1</td>

стало:
<td data-bind="dxAction: function() { buttonPress('1') }">1</td>

Это событие — расширенная вариация события click: в качестве обработчика оно поддерживает uri навигации между вью и корректно отрабатывает в скроллируемой область.

+14 минут Обработчик buttonPress из предыдущего примера, валидация введенного числа.

    var number = ko.observable(null);
    var isValidNumber = ko.computed(function() {
        return  number() && parseFloat(number()) > 0;
    });

    ......

    function buttonPress(button) {
        if (button) {
            if (number())
                number(number() + button);
            else
                number(button);
        } else if (number())
            number(number().substr(0, number().length - 1));
    }

    var viewModel = {
        number: number,
        isValidNumber: isValidNumber,

        viewShowing: viewShowing,
        buttonPress: buttonPress
    };
    
    .....  

+8 минут Добавил FastClick.js [10], который убирает задержку перед срабатыванием события click на телефонах. По умолчанию, мобильный браузер откладывает вызов обработчика click, чтобы убедиться, что не будет double-tap. Внешне это проявляется как подтормаживающее приложение. Вы быстро нажимаете кнопками, а они реагируют с ощутимой задержкой на нажатия. Авторы FastClick.js вешаются на touchstart и выстраивают свою логику обработки нажатий.

Забегая вперед скажу, что добавление этой библиотеки было ошибкой. Почему — читайте далее.

+4 минуты Ввел ограничение на длину числа, которое может ввести пользователь. Подкорректировал размер шрифтов, чтобы все отлично выглядело и не вылезало за границы области ввода.

+58 минут Выбор категории траты. Под областью ввода добавил прокручивающуюся область с кнопками доступных категорий. Видео [11].
Заняло меньше, чем могло бы. Внутри PhoneJS нашел подходящий компонент dxTileView [12]. Из коробки получил нужный мне внешний вид и кинетик-прокрутку. С последней пришлось бы помучится, если делать её самому. Особенно здорово, что ребята из DevExpress включили её только для iOS, где она — системная, но выключили для Android, где такой прокрутки нет.

<div class="category" data-bind="dxTileView: {
                                      dataSource: categories,
                                      itemMargin: 0,
                                      baseItemHeight: 72,
                                      baseItemWidth: 72,
                                      listHeight: 72
                                    }">
    <div class="tile"
         data-options="dxTemplate: { name:'item' }"
         data-bind="css: { selected: $parent.category() == $data },
                    click: function() { $parent.category($data) }">
        <div><img data-bind="attr: { src: image }"/></div>
        <div data-bind="text: name"></div>
    </div>
</div>

Было уже 19:40 и я решил закончить.

Место действия: Тула, Дата: 6 января 2014

+3 часа 9 минут Хранение данных на диске. В PhoneJS входят классы для работы с данными: выборка, фильтрация, сортировка, группировка. Есть несколько вариантов хранения данных: OData и LocalStorage. Делать серверную часть для бесплатного приложения мне совершенно не хотелось, поэтому обратил внимание на LocalStorage. Довольно быстро выяснилось, что хранить в LocalStorage не самая здравая идея. Например, при обновлении на iOS 5.1 терлись пользовательские данные [13], другие жалуются, что LocalStorage трется раз в неделю на Android, третьи, что трется при выключении питания. Я решил не рисковать пользовательскими данными и использовать File API из Cordova/PhoneGap.

Документация [14] утверждает, что это API основано на W3C File API. По факту это означает, что это API — разное в Chrome и Safari на Mac OS, реализации в Cordova для iOS и реализации для Android. Реализации для iOS и Android — разные. Функции себя ведут по разному и разный набор классов и констант. Например, в реализации для Android отсутствует класс Blob и константа window.PERMANENT. Зато есть класс LocalFileSystem с классом LocalFileSystem.PERSISTENT. В браузере на ноутбуке есть дополнительное API для запроса квоты на хранение данных, которое отсутствует на мобильных телефонах.

Дополнительных проблем создает доступная в сети документация по этому API. Я читал несколько статей, которые я смог найти по запросу html5 file api. Мне ни разу не встретилась статья, которая дала бы мне ответы на все мои вопросы. В итоге, методом проб и ошибок, родился класс для работы с File API, поддерживающий Cordova 3.3 на iOS и Android, Chrome 32 на Mac OS и Windows 8. Вы можете найти его здесь: https://github.com/chebum/fileStorage-for-Phone.JS/blob/master/fileStorage.js [15]

Использовать его можно так:

//  В этом примере создастся файл data/records в папке Documents приложения.
FS.initFileAPI(1000000, true)
    .then(function () {
        var records = new FS.FileArrayStore({ key: "id", fileName: "records" });
        return records.insert({ customer: "Петя" })
     })
     .then(function () {
        alert("Запись добавлена!");
     });

// Или использовать низкоуровневое API:
FS.initFileAPI(100000, true)
    .then(function() {
        return FS.writeFile("file1", "Содержимое файла")
    })
    .then(function() {
        alert("Файл записан!");
    });

+33 минуты Сохранение введенных записей в сторедж. Список категорий вынесен в ArrayStore [16], чтобы упростить выборки.

+26 минут Собственный layout для вьюшек приложения. PhoneJS предлагает набор Layout [17], которые являются своего рода обрамлением для вью, которые они содержат. Поскольку в моем приложении главная страница не укладывалась ни в один из доступных layout'ов, я выбрал EmptyLayout. У него оказался минус: отсутствовала анимация переходов между экранами приложения. Я скопировал код EmptyLayout и вставил аттрибут, добавляющий анимацию при переключении экранов.

+1 час 51 минута Экран About из шаблона приложения переделан в экран отчетов, пока пустой. Сделана viewModel с выборкой данных текущего месяца. Добавлено локализованное форматированные даты для заголовка экрана.

+59 минут Группировка и вывод трат по группам в текущем месяце.

+28 минут Открытие списка выбора месяца для отчета по нажатию на заголовок экрана.

+1 час 20 минут Добавил Cordova-плугин Statusbar, который отказался работать «из коробки». Выяснилось, что в шаблоне приложения от PhoneJS по какой-то причине была закомментирована ссылка на cordova.js:

<!--<script type="text/javascript" src="cordova.js"></script>-->

В результате нативная часть приложения работала некорректно.

+39 минут На экране с отчетами верхняя часть заменена на dxToolbar [18].

+22 минут Выяснял почему не работает обработчик нажатия dxButton. Убирание FastClick.js решило проблему, но появилась ощутимая задержка при нажатии кнопок клавиатуры. Подписка на dxAction переделана на touchstart.

+25 минут Форматирование вывода строк в отчете (были косяки).

Всю ночь мне снились дрянные кнопки на главном экране приложения.

Место действия: Тула, Внуково, Москва, Дата: 7-8 января 2014

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

+2 часа 5 минут Утром, еще дома, решил разделить кнопки, чтобы избавиться от границ между ними. В качестве образца взял клавиатуру набора номера из iOS7.
Приложение для iOS и Android за 24 часа
Клавиатуры получилось три. В зависимости от габаритов экрана меняется размер кнопок: для 3.5'', 4'' и 5'' телефонов. В каждую ячейку таблицы был положен div и настроено выравнивание.

Из-за отсутствия недоделаного выравнивание текста по вертикали в HTML, получился достаточно сложный css-стиль для кнопок:

.home-view .buttons td div {
    color: #4a5360;
    border: 1px solid #4a5360;
    text-align: center;

    position: absolute;
    left: 50%;

    /* Small buttons - default */
    font-size: 26px;
    padding: 13px 0 13px 0;
    width: 52px;
    line-height: 26px;
    border-radius: 26px;

    margin-left: -27px;
    margin-top: -27px;
}

+1 час 50 минут Купил на Fotolia набор векторных иконок. Вырезал нужные и конвертировал в PNG формат. Возможно, так долго получилось из-за того, что было пол второго ночи:)

+1 час 10 минут Сплеш-скрин загрузки приложения

+36 минут Иконка приложения в 3х размерах. Локализация названия для iOS.

+20 минут Скрывание сплеш-скрина после полной загрузки приложения.

+2 часа Фикс многочисленных багов

+2 часа Экраны для Play Store

+30 минут Экраны для App Store

+30 минут Описание приложения для 2х магазинов

+1 час 30 минут Сабмит в App Store — возникли непонятные проблемы с подписью приложения.

Моя Бухгалтерия

Сложим потраченное время и разобъем его по категориям.

Разработка: 21 час 37 минут

Графика и тексты: 8 часов 26 минут

Всего: 30 часов 3 минуты

В итоге за это время получилось минимально работающее приложение, которое сильно уступает последней версии «Транжиры». Я не успел разбиение трат по дням, ввод доходов. В интерфейсе программы срезано несколько «углов».

Если проанализировать их трудозатраты, то получается следующее. В своей статье [2] они пишут про 3-4 дня 4 человек. Это 96-128 человеко-часов. У меня получилось чуть больше 30 часов и приложение под 3 платформы. iOS и Android уже в магазинах. Windows 8 потребует переделки интерфейса: текущий очень чужероден.

Можно гордиться собой.

Приложение для iOS и Android за 24 часа

Автор: ivann

Источник [19]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/news/53884

Ссылки в тексте:

[1] Транжира: https://itunes.apple.com/ru/app/tranzira-kontrol-budzeta/id637562972

[2] dev-story: http://www.iphones.ru/iNotes/299602

[3] картинка: http://habrastorage.org/storage3/41c/027/c54/41c027c543b0d11b45800edcce7c100c.jpg

[4] мобильной разработки: http://macradar.ru/interview/kak-dobitsya-uspexa-v-app-store-intervyu-s-razrabotchikami-prilozheniya-avoska-rozygrysh-kodov/

[5] мобильный js-фреймворк: http://phonejs.devexpress.com

[6] пишет: http://www.sap.com/pc/tech/mobile/featured/offers/gartner/reports/madp.html

[7] Kony: http://www.kony.com

[8] knockout-биндингов: http://knockoutjs.com/documentation/introduction.html

[9] dxAction: http://phonejs.devexpress.com/Documentation/Tutorial/Getting_Started/Data-Bound_Application#Add_Actions

[10] FastClick.js: https://github.com/ftlabs/fastclick

[11] Видео: http://vimeo.com/85335639

[12] dxTileView: http://phonejs.devexpress.com/Documentation/ApiReference/Widgets/dxTileView

[13] пользовательские данные: http://stackoverflow.com/questions/9664392/phonegap-ios-5-1-and-localstorage

[14] Документация: http://docs.phonegap.com/en/3.3.0/cordova_file_file.md.html#File

[15] https://github.com/chebum/fileStorage-for-Phone.JS/blob/master/fileStorage.js: https://github.com/chebum/fileStorage-for-Phone.JS/blob/master/fileStorage.js

[16] ArrayStore: http://phonejs.devexpress.com/Documentation/ApiReference/Data/ArrayStore

[17] Layout: http://phonejs.devexpress.com/Documentation/Howto/Views_and_Layouts

[18] dxToolbar: http://phonejs.devexpress.com/Documentation/ApiReference/Widgets/dxToolbar

[19] Источник: http://habrahabr.ru/post/210686/