Последние несколько месяцев я работаю над новым способом создания кросс-платформенных приложений для Android и iOS под названием Jasonette. Он позволяет написать приложение от начала до конца, используя только разметку JSON.



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

Больше нет необходимости в том, чтобы приложение было захардкорено на девайсе, Вы можете обновлять его по желанию посредством обновления разметки JSON на серверной стороне. При каждом запуске ваше приложение будет заново загружаться с сервера.

Посмотрите видео, чтобы получить общее представление.

Jasonette способен осуществлять различные операции. Вы можете прописывать функции, шаблоны стили и многое другое, используя JSON-разметку. Таким образом становится возможно написать нативного мобильного приложения любой сложности по схеме "Модель-Представление-Контроллер"

В этой статье я раскрою только звено «Представление»:

  1. Каким образом Jasonette реализует различные кросс-платформенные UI-модели в формате JSON.
  2. Как он реализует переходы из JSON к Native.


Базовая структура


При детальном рассмотрении Jasonette работает как веб-браузер. Однако вместо интерпретации HTML-разметок и отрисовки представления веб-страниц Jasonette извлекает JSON-разметку и на лету отрисовывает нативное представление. Разметка при этом является всего лишь JSON-файлом, который следует заданным алгоритмам. Прежде всего, он запускается с ключом $jason с двумя дочерними элементами: head и body. Выглядит это следующим образом:

{
  "$jason": {
    "head": {
      .. metadata about the document ...
    },
    "body": {
      .. actual content to be displayed on the view ..
    }
  }
}

Философия дизайна


Когда я только начинал заниматься разработкой синтаксических конструкций JSON для представления нативных структур, я столкнулся с некоторыми препятствиями:

  1. Нативная система разметки. iOS и Android не случайно вышли на рынок со своими собственными нативными интерфейсами. Макеты, созданные в эпоху компьютеров, не всегда удается приспособить для карманных девайсов. Синтаксис должен отображать макет в виде, максимально совместимом с нативной мобильной системой.
  2. Кроссплатформенность. И вместе с тем, он должен быть кроссплатформенным. К примеру, для продуктов iOS есть такая штука, как autolayout, а также визуальный язык форматов, которые, однако, не подойдут под Android, а значит, не годятся в качестве решения.
  3. Простота написания. Приложение должно легко переводиться в простую разметку JSON и также легко трансформироваться в сложную структуру.

Если посмотреть, как построено большинство мобильных приложений, вы увидите, что все они сделаны по одним и тем же шаблонам проектирования интерфейса.

  1. Вертикально прокручиваемый список
  2. Горизонтально прокручиваемый список
  3. Абсолютное позиционирование
  4. Сетка

Давайте подробнее рассмотрим первые три пункта, так как они применяются наиболее широко.

1. Разделы — Построение прокручиваемых списков


Наиболее часто применяемый шаблон пользовательского интерфейса — прокручиваемый список. В Jasonette данные шаблоны называются sections. Они бывают двух типов: горизонтальные и вертикальные, и прокручиваются горизонтально и вертикально соответственно.



Реализация — Вертикальные разделы

Этот UI-шаблон используется для отображения данных на мобильных устройствах, пожалуй, чаще всего. На iOS, Jasonette он реализуется с помощью UITableView. На Android — посредством RecyclerView.

{
  "body": {
    "sections": [{
      "items": [
        {"type": "label", "text": "Item 1"},
        {"type": "label", "text": "Item 2"},
        {"type": "label", "text": "Item 3"}
      ]
    }]
  }
}

На iOS вышеупоянутая разметка JSON создает UITableView с тремя UITableViewCells, каждая из которых содержит UILabel с соответствующими параметрами text.

На Android создается RecyclerView с тремя элементами, каждый из которых представляет собой TextView и отображает соответствующие параметры text.

Всё это прописано в коде без использования Storyboards (iOS) или XML-файлов разметки (Android), чтобы обеспечить возможность программировать каждый элемент в динамическом режиме.

Реализация — Горизонтальные разделы

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

{
  "body": {
    "sections": [{
      "type": "horizontal",
      "items": [
        {"type": "label", "text": "Item 1"},
        {"type": "label", "text": "Item 2"},
        {"type": "label", "text": "Item 3"}
      ]
    }]
  }
}

Примечание: Синтакс горизонтального раздела кажется достаточно простым, но на самом деле это не так. Горизонтальные разделы на iOS реализуются с помощью UICollectionView. Это хорошо известный механизм, но, по сути, горизонтальная прокрутка UICollectionView встраивается в исходный UITableView (который отвечает за вертикальную прокрутку). На Android, принцип реализован схожим образом, только с использованием составного RecyclerViews.

2. Элементы — Построение разметки внутри каждого компонента прокрутки


Теперь, когда стало понятно, как построен самый верхний уровень, давайте взглянем на items. Каждый раздел состоит из нескольких компонентов прокручиваемых items. Обратите внимание, что каждый элемент имеет фиксированный размер и ничего внутри самого элемента не прокручивается.

Каждый элемент может представлять собой:

  • Только один компонент — такой, как label, image, button, textarea и т.д.
  • Сочетание любых из этих компонентов

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

К счастью, iOS и Android имеют очень похожие нативные системы разметки, UIStackView и LinearLayout соответственно, которые в свою очередь аналогичны CSS Flexbox. Так что можно считать это максимально возможным приближением к кроссплатформенности.

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



Для того, чтобы создать вертикальную разметку, вы должны установить соответствующий тип, а затем получить его компоненты:

{
  "items": [{
    "type": "vertical",
    "components": [
      {
        "type": "label",
        "text": "First"
      }, 
      {
        "type": "label",
        "text": "Second"
      }, 
      {
        "type": "label",
        "text": "Third"
      }
    ]
  }]
}

То же самое и для горизонтальной разметки. Просто установите горизонтальный тип.

{
  "items": [{
    "type": "horizontal",
    "components": [
      {
        "type": "image",
        "url": "http://i.giphy.com/LXONhtCmN32YU.gif"
      }, 
      {
        "type": "label",
        "text": "Rick"
      }
    ]
  }]
}

Чтобы встроить одну разметку в другую, достаточно прописать ее как один из компонентов.

{
  "items": [{
    "type": "horizontal",
    "components": [
      {
        "type": "image",
        "url": "http://i.giphy.com/LXONhtCmN32YU.gif"
      }, 
      {
        "type": "vertical",
        "components": [{
          "type": "label",
          "text": "User"
        }, {
          "type": "label",
          "text": "Rick"
        }]
      }
    ]
  }]
}

Ради краткости я не стал говорить о стилях. Вы можете оформлять каждый компонент в отдельности, а также сам макет, пока не придадите им точно такой вид, какой вы хотите. Всё, что для этого необходимо сделать — добавить объекты style, описывающие font, size, width, height, color, background, corner_radius, opacity и т.д.

3. Уровни — «Абсолютное позиционирование»


Иногда вам может захотеться закрепить компоненты в определенных частях экрана без возможности прокрутить их. В терминологии CSS мы назвали бы это «абсолютным позиционированием». Jasonette поддерживает эту возможность через инструмент под названием layers.

В настоящее время layer поддерживает два типа дочерних элементов: image и label. Вы можете поместить их в любую область на экране, какую пожелаете. Вот пример:


В этом примере на экране мы видим две метки (температура и сообщения о погоде) и изображение (значок камеры), координаты которых были заданы, чтобы они оставались на месте во время прокрутки. Разметка будет выглядеть примерно следующим образом:

{
  "$jason": {
    "body": {
      "style": {
        "background": "camera"
      },
      "layers": [
        {
          "type": "label",
          "text": "22°C",
          "style": {
            "font": "HelveticaNeue-Light",
            "size": "20",
            "top": "50",
            "left": "50%-100",
            "width": "200",
            "align": "center"
          }
        },
        {
          "type": "label",
          "text": "few clouds",
          "style": {
            "font": "HelveticaNeue",
            "size": "15"
          }
        },
        {
          "type": "image",
          "url": "https://s3.amazonaws.com/.../camera%402x.png",
          "style": {
            "bottom": "100",
            "width": "30",
            "color": "#ffffff",
            "right": "30"
          }
        }
      ]
    }
  }
}

Как ни удивительно, это все, что вам нужно знать, чтобы создать любой головоломный интерфейс, какой только можете себе представить, на мобильных устройствах.

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

Вот несколько примеров, на 100% составленных из вышеупомянутых UI-элементов:


После просмотра


На данном этапе вы скорее всего думаете одно из двух:


  • «Вау! Класс! Надо попробовать» или
  • «Для каких-нибудь игрушечных приложений это, может, и годится, но построить что-нибудь функциональное таким образом не получится»

Как я уже упоминал выше, здесь описана только часть «Представление» — самая простая из трех. Но сильной стороной Jasonette является то, что вы можете пойти гораздо дальше и с помощью JSON полностью написать декларативную программу.

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

Если вы сумеете описать через JSON логику не только «Представления», но также «Модели» и «Контроллера» — ваши возможности безграничны.

Возможности


Все, что вам нужно — это сервер для отправки JSON, а значит, Jasonette совместим с любой платформой. Вам не придется зависеть от технологии с закрытым исходным кодом. Достаточно разметки JSON.

И JSON могут поступать откуда угодно — с локальных устройств, с удаленных серверов, да хоть с raspberry.pi!

  • Есть веб-приложение? Если у вас уже имеется веб-приложение, вы можете мгновенно создать мобильную нативную версию для ваших приложений Node.js, Rails, Django, PHP или любого другого веб-приложения, просто отправляя запросы на ваш API.
  • Вам даже не нужен сервер: Так как весь контроллер «Модель-Вид-Управление» можно поместить в один-единственный автономный JSON-файл, фактически вы можете выбирать любое местоположение, чтобы хранить данные и работать с ними. Можно даже создать приложение, используя статический файл JSON, который работает из Pastebin или Github!
  • Превратите любой HTML веб-сайт в приложение: У Jasonette сильный парсер HTML-JSON при поддержке cheerio library, которая позволяет превратить любой HTML-объект в объект JSON. А что можно сделать после этого преобразования, вы уже знаете: создать нативный интерфейс из преобразованного JSON-файла! Таким образом, вы можете превратить веб-сайт, у которого даже нет API, в нативное приложение. Конечно, предпочтительнее использовать JSON при любой возможности, но все равно это круто.

Можно продолжать до бесконечности, вот лишь несколько примеров:

1) Приложение для размещения фотографий, которое позволяет сделать снимок с помощью камеры и загрузить его в S3, а затем опубликовать запись на своём собственном сервере, создавая веб-канал:

2) Приложение Eliza Chatbot для iOS и Android на Node.js


3) Приложение Microblog, оснащённый функцией управления сеансами



4) Удаленное управление ботами Slack



5) Приложение-образец, которое превращает веб-страницы в файлы JSON, а затем в нативное приложение.


Заключение


Jasonette — молодой проект. Я выложил версию для iOS в открытый доступ в конце 2016-го года, а версию для Android — месяцем позже.

Однако он уже обзавелся сообществом пользователей, принимающих активное участие в его развитии, и находится на стадии стремительного роста. Я надеюсь, что эта технология позволит расширить возможности любого пользователя (не только разработчиков) и позволит создавать приложения без особых усилий.

Звучит заманчиво? Посмотрите веб-сайт проекта здесь. И последнее: вы можете найти репозитории Github здесь: iOS и Android (Лица, готовые сделать пожертвования проекту — радушно приветствуются!)
Поделиться с друзьями
-->

Комментарии (25)


  1. MaximChistov
    20.02.2017 14:52
    +1

    Насколько мне известно, правила App Store напрямую запрещают делать приложения, подгружающие код со стороны во время исполнения.


    1. kekekeks
      20.02.2017 14:55
      +2

      Там кода нет. Только разметка. Этакий аналог HTML, только с нативными виджетами вместо элементов. По сути это тонкий клиент с интерфейсом на нативных виджетах.


  1. kekekeks
    20.02.2017 15:03
    +4

    Надо только понимать, что это не "мобильное приложение", а "мобильный клиент к веб-сайту, гвоздями приколоченый к наличию интернета".
    Если надо сделать красивый отзывчивый интерфейс к веб-сервису (а большое число приложений для мобилок, за которые берут немаленькую денежку, именно таковыми и являются), то это — незаменимая штука. Если нужна какая-то логика, которая должна работать без интернета, начинаются проблемы.


    1. zakhej
      21.02.2017 17:29

      Судя по всему, многие читатели не поняли суть.
      Disclaimer: я сам iOS-разработчик со стажем и не люблю ненатив, а даже немного побаиваюсь, что со временем останусь без работы из-за него :)
      По делу:
      Это не клиент. Это нативное приложение (фреймворк по сути), которое строит интерфейс исходя из конфигурации, которая хранится в JSON.
      Все, однажды пришедшие, данные приложение может сохранить на девайсе и в дальнейшем ничего не мешает запускать в оффлайне. То, что тут в примере строго забиты данные для отображения, ни о чем не говорит. Ведь ничто не мешает вместо этого сделать конфигурацию для слоя работы с сетью — в JSON будет храниться информация, к каким эндпоинтам обращаться за данными.
      Тут же можно дописывать кастомные элементы UI, которых не хватает.
      Можно сделать обертку для работы с БД, которая также будет конфигурироваться из JSON.
      В общем, полная свобода действий, но с оверхедом. Зато натив и кроссплатформа.


      1. kekekeks
        21.02.2017 18:26

        Окей, если это не клиент, а «полноценное приложение», поведайте, как на нём решить вот такую задачу (пример не из головы, я это дело в декабре лепил).
        Есть база из полугигабайта вордовских документов с разбивкой по разделам и темам. Нужно сделать оффлайн-читалку, которая при наличии интернета выкачивает документы из выбранных разделов и тем, отслеживая при этом появление новых и обровлённых документов, позволяет их в дальнейшем читать В оффлайн режиме, а так же обеспечивает оффлайн полнотекстовый поиск по оным. Поиск не должен длиться часами (в моём случае он по полной базе искало секунды за полторы).


      1. kekekeks
        21.02.2017 18:28

        Если же использовать его для кроссплатформенного гуя к нормальному коду, то я не вижу никаких преимуществ перед решениями типа Xamarin.Forms, которые точно так же предоставляют переносимую декларативную разметку для нативных виджетов.


  1. 4dmonster
    20.02.2017 15:41

    Я правильно понимаю, что можно сказать: «Поздравляю, вы только что изобрели XUL»?


    1. kekekeks
      20.02.2017 16:11

      XUL же мозилловцы вроде сами же и закопали после осознания его ненужности, нет?


      1. KAndy
        20.02.2017 16:13
        +5

        Вот вот, я бы задумался б


      1. 4dmonster
        20.02.2017 16:27

        Да, только, кажется, закопали в рамках стратегии по снижению количества пользователей Firefox.


    1. KAndy
      20.02.2017 16:12
      +3

      Я бы скзал даже html в json нотации и специальный браузер для него


    1. Zordhauer
      20.02.2017 22:07
      +1

      Я бы сказал: "Поздравляю, вы изобрели QML"


  1. impwx
    20.02.2017 17:00
    +2

    Статья состоит из маркетологического бреда чуть менее, чем полностью.

    Во-первых, «это очень просто, потому что это JSON» — откровенная манипуляция. Как если бы для свободного разговора на английском достаточно было выучить 26 букв латинского алфавита.

    Во-вторых, зачем мучать JSON и пытаться выразить на нем логику, если для этого гораздо лучше подходят классические ЯП? Я понимаю, когда для решения задачи выбирают самую неподходящую технологию в качестве эзотерического упражнения, но тут вроде как всё на полном серьезе.

    В-третьих, есть же Ionic Framework, React Native, Apache Cordova на куда более известном наборе технологий — в чем преимущество перед ними?


    1. koeshiro
      21.02.2017 05:35
      +1

      Поддерживаю (Поставить стрелку вверх не могу).


  1. HeaTTheatR
    20.02.2017 22:41
    +3

    О, мсье, знает толк в извращениях!


  1. Frankfurt
    21.02.2017 00:28

    А где приложения в аппсторе?


  1. D01
    21.02.2017 05:35

    Кэшируются ли изображения, которые указаны в разметке?

    Может быть стоит добавить какой-нибудь простой визуальный конструктор? (если его нет конечно))



  1. asushko
    21.02.2017 09:31

    Спасибо за статью! Для разработчиков бекенда — это просто независимость от всевозможных фронт-заморочек, не надо париться где найти толкового фронтиста, который выведит приложение собственно на экран. Остается скорректировать респонс в нужном формате и о чудо, REST-бек превращается в бек с фронтом!

    А как с браузерной версией? Или я что то пропустил…


    1. impwx
      21.02.2017 10:53
      +1

      Остается скорректировать респонс в нужном формате и о чудо

      Дьявол в деталях. Фронтенд, формально — это тоже всего лишь «респонс в нужном формате». И есть подозрение, что найти толкового фронтендера все-таки проще и быстрее.


      1. asushko
        21.02.2017 13:33

        Дьявол в деталях.

        как же без этого — везде есть!

        ключевое слово «независимость», даже пусть и от «толкового фронтендера»


  1. newkamikaze
    21.02.2017 17:25

    Ничего подобного раньше не пробовал. Начал делать проектик — пока получается, значит порог вхождения невысокий. Скажите, какова максимальная длина текста для type:label?


  1. Superiorr
    21.02.2017 18:40

    Порог вхождения действительно невысокий, но чем дальше в лес тем больше дров, стандартные примеры пусть и уникальны в своем роде, но мало чем отличаются по дизайну. Для меня минусом стало то, что нельзя взаимодействовать с внутренним кодом (если это не так, то подскажите как это можно сделать). К примеру при парсинге сайта нужно как то воспроизводить ifame видео.
    Еще немного огорчило что на тех же ios все смотрится куда красивее, но скорее всего это из-за моего древнего аппарата.


  1. Slepetc
    22.02.2017 05:48

    То есть это отказ от XML разметки и Java(в случае Android) логики в пользу Json разметки и Json логики? Выглядит очень круто для крайне узкого круга проектов. Я буквально не могу быстро придумать, где это может быть удобно. А вот для перевода HTML в натив может быть интересно.


  1. Doctorrr
    22.02.2017 14:36

    Дело полезное!

    В чём преимущество перед Cordova (помимо «фатального недостатка»)? В двух словах?