Вымышленный рассказ о том, как был создан этот экспериментальный проект.

15 марта.

Получил справку из штаба. Они хотят что-то увидеть в React Native. Swift и Java устарели. Они на исходе. Нам нужна новая жара. Реагировать нативно. Вы пишете JavaScript и бум. Вы получаете приложение для iOS и Android. Вы получаете мгновенную перезагрузку кода. Вы получаете горячее сообщество открытого исходного кода, поддерживаемое крупными игроками. Фейсбук. Airbnb. Какой-то парень в Украине. Большие пушки.

Так что загрузите несколько руководств, скопируйте и вставьте пример кода и просто начните что-то создавать. Что-либо.

Я нативный разработчик приложений с опытом работы с Objective-C, Swift, Java и даже Android NDK для многоплатформенного кода C++. Когда HTML5 изо всех сил пытался проникнуть в игру приложений, я смеялся и высмеивал его тщетные попытки. Но React Native вызывает серьезную шумиху — я имею в виду какого-то парня в Украине — и большие шишки чего-то хотят. Что-либо. О, я дам тебе что-нибудь хорошее.

16 марта.

Я ничего не получил.

17 марта.

Удалось получить эквивалент приложения Hello World в React Native для загрузки как на симуляторе iOS, так и на эмуляторе Android. Однако это было не так просто, как просто нажать кнопку Play в Xcode. Приходилось открывать командную строку в корне каталога проекта и вводить одну инструкцию запуска для iOS и отдельную инструкцию для Android (для чего требовалось, чтобы эмулятор уже был открыт).

Говоря об IDE, я решил использовать Atom с Nuclide поверх него. Напоминает мне плагин Android поверх Eclipse дней. Я не хочу вспоминать о тех днях. Но пока не будет выпущена какая-то React Native Studio, так и будет. Также установил менеджер пакетов Yarn вместо npm просто потому, что я думал, что yarn add и yarn remove — это простые и понятные команды, которые нужно помнить при установке сторонних компонентов React Native. Оказалось, что запуск и установка пряжи были заметно быстрее, чем у npm. Это абсолютно критично, потому что пятница.

команда npm

npm install --save [package]
npm uninstall --save [package]yarn add [package]
yarn remove [package]

20 марта.

Что касается сторонних разработчиков, я обнаружил, что у меня намного больше компонентов React Native, чем зависимостей iOS CocoaPods или Gradle. В нативных приложениях я обычно просто включаю AFNetworking/Alamofire для управления HTTP-запросами в iOS и все. Может быть, Google Maps SDK, если этого требует проект. Аналогичная история в Android с Retrofit. Для всего остального, включая элементы пользовательского интерфейса и постоянное хранилище, я смог выполнить большинство задач с помощью классов, встроенных в iOS и Android.

В React Native я добавлял пряжу для таких простых вещей, как кнопки и ListViews! Однако, как только каждый компонент был добавлен в файл package.json, их было действительно легко начать реализовывать. С синтаксисом JSX React создание пользовательского интерфейса для экрана было довольно простым, особенно если вы уже знакомы с XML-подходом Android к проектированию для каждого возможного размера экрана. Оба эти решения гораздо проще использовать для адаптивных макетов, чем интерфейсный конструктор Xcode и даже обновление iOS Auto Layout, такое как SnapKit.

Реакция JSX

< Button onPress={onButtonPress} title=”Press Me”

/>

< Button android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:text=”@string/press_me” android:onClick=”onButtonPress”

/>

let btn: UI Button = UIButton(frame: CGRect(x: 100, y: 200, width: 100, height: 50))btn.setTitle(title: “Press Me”, for: .normal)btn.addTarget(self, action: #selector(onButtonPress), forControlEvents: .touchUpInside)

self.view.addSubview(btn)

Как человек, который сразу перешел от Swift+Java (Swava? Jift?) к кодированию на JavaScript, я в значительной степени изучаю основы по ходу дела. Или когда я пишу блок кода, который получает красный экран ошибки. Или когда я пишу строку кода. Или когда я пишу что-нибудь.

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

import mainDoer from ‘./Doers’import { thingDoer } from ‘./Doers’export default function mainDoer() { code }

export function thingDoer() { code }

Что не так с синтаксисом стрелки?

someNumberArray.map(item => item + 1); // Add 1 to each number in the array

Ну, это просто более чистая альтернатива старому способу написания этой логики:

someNumberArray.map(function(item) { return item + 1;})

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

function doThinger(a, b, c) { code };var args = [0, 1, 2];doThinger.apply(null, args);function doThinger(a, b, c) { code };var args = [0, 1, 2];doThinger(...args);

Отличием от синтаксиса расширения является оператор rest, в котором вместо расширения массива до его элементов несколько элементов объединяются в один.

var [dope, ...wack] = [1, 2, 3, 4, 5]; // wack = [2, 3, 4, 5]

Так что все хорошо и весело. Но по мере того, как я продолжал писать все больше и больше JavaScript, язык оказался… слабым? Слабые в том смысле, что Swift и Java имеют строгие правила использования точек с запятой, но на самом деле это не правила, которые вам нужно запоминать или обдумывать, а скорее просто часть кода, который вы пишете, потому что он имеет полный и недвусмысленный смысл. Тот факт, что в JavaScript продолжаются споры об использовании точек с запятой и о том, что такие вещи, как запятые в конце являются необязательными (хотя и не разрешены в JSON), заставляет меня скучать по нативным языкам приложений, где вы не беспокоитесь ни о чем таком. и просто сосредоточиться на решении проблем.

21 марта.

Вместо того, чтобы бесцельно блуждать по миру React Native с учебными пособиями и примерами кода, приятно, наконец, иметь цель, на которой можно сосредоточиться и работать над ней. Решил создать приложение, которое заполняет карту маркерами достопримечательностей из Google Places API. Нажав на маркер определенного местоположения, вы получите фотографии этого местоположения из Instagram в виде эскизов GridView. Здесь задействовано множество концепций: HTTP-запросы, синтаксический анализ JSON, загрузка и кэширование изображений, карты и маркеры, а также забавный пользовательский интерфейс.

Убрал компонент Airbnb React-Native-maps. Очевидно, оригинальный встроенный компонент MapView был превзойден реализацией Airbnb, и поддержка первого была прекращена в январе с официальной рекомендацией использовать сторонние карты для карт. Это серьезная сила сообщества с открытым исходным кодом. Это тоже показалось хорошим решением, так как добавление Airbnb JSX (вместе с ключом API Google Maps) привело к функциональному представлению Apple Maps в симуляторе iOS и представлению Google Maps в эмуляторе Android гораздо быстрее, чем если бы я добавление карт изначально по одной для каждой платформы.

Чтобы заполнить карту маркерами данных о реальном местоположении, я использовал React Native’s Fetch API. Предоставляя Fetch URL-адрес Google Places, я получаю ответ JSON, который легко анализируется как объект или многомерный массив. Я также смог быстро добавить пряжу и использовать стороннюю библиотеку под названием traverse для оценки каждой пары ключ-значение в ответе, принимая во внимание ее глубину в структуре. То, что эта библиотека не обновлялась почти 4 года, но все еще работала прямо из коробки, казалось мне невероятным, и я не знаю, произойдет ли это когда-нибудь с библиотекой для iOS или Android.

Следующим шагом после извлечения соответствующих данных геолокации из ответа JSON было создание моделей — тот же шаг, что и для нативных — чтобы мы могли построить массив маркеров для размещения на карте. Или, может быть, это не должно было быть следующим шагом в том, что касается реактивного программирования? Есть ли правильный, функциональный подход, которому я должен был следовать? Ну что ж. Для меня это имело смысл. Я выражаю беспокойство только потому, что не было никаких стандартов или большого количества примеров для создания моделей в React Native, тогда как iOS и Android имеют довольно распространенные шаблоны, поскольку модели составляют большую часть MVC. Даже треть инициализма!

Я закодировал свою модель MarkerData следующим образом:

export default class MarkerData { constructor(locationId) { this._locationId = locationId; } getLocationId() { return this._locationId; } set name(name) { this._name = name; } get name() { return this._name; } set latitude(latitude) { this._latitude = latitude; } get latitude() { return this._latitude; } … // NOT CODE. Just saying etc. and so on and so forth...

Создание объектов этой модели будет выглядеть примерно так:

for (var i = 0; i var markerData = new MarkerData(idArray[i]); markerData.name = nameArray[i]; markerData.latitude = latArray[i]; markerData.longitude = lngArray[i]; markers.push(markerData);

}

А затем создание маркеров карты из моделей может происходить следующим образом:

getFeedMarkers() { return this.props.feed.map( (markerData, i) =>

key={ i } title={ markerData.name } coordinate={ {latitude: markerData.latitude, longitude: markerData.longitude} }>

{ markerData.name }

);

}

22 марта.

Переход от традиционного Swift и Android Java к React Native требовал большего, чем просто использование другого языка и инструментов. Мне также пришлось попробовать решать проблемы реактивным способом вместо того, как я привык подходить к ним нативно. Например, после создания массива объектов MarkerData моим следующим шагом в нативном коде был бы цикл по массиву и в каждой итерации прямой доступ к объекту карты для вызова его функции для добавления одного маркера Google или аннотации MapKit. Если наш набор маркеров изменился (например, когда пользователь перемещается по карте), мы должны очистить предыдущий набор от объекта карты, а затем снова запустить цикл. Никогда не задумывался над утомительностью этого императивного, объектно-ориентированного процесса, потому что так оно и есть.

Однако в реактивном программировании мы можем добиться того же результата (размещение текущего набора маркеров на карте в этом примере) с меньшими усилиями и, возможно, с более естественным мышлением. Что, если мы просто скажем нашему представлению карты: «Эй, вот массив маркеров карты. Если вы визуализируете себя на экране, просто представьте маркеры, находящиеся в массиве в данный момент времени. Не ленитесь и ждите, пока я раздам ​​вам маркеры один за другим — реагируйте на массив по мере его изменения». Кажется, это немного более естественно, нет? Помимо разговора с блоком кода.

Это изменение мышления также помогло мне решить использовать Redux в приложении. Любой новый разработчик React Native обязательно увидит это слово, когда будет читать все больше и больше статей и руководств. Я решил, что если мне нужен полный опыт React Native, мне нужно собрать весь набор. Из инструментов. И я рад, что сделал это. Для задачи обновления маркеров на карте это было полным излишеством. Но я получил гораздо больше от этого, чем это. Преодолев трудности обучения использованию Redux, я изменил свой образ мышления, чтобы стать более реактивным.

Шаг первый: дайте компоненту MapView массив для создания маркеров местоположения. На самом деле я уже говорил об этой части — это вчерашняя функция getFeedMarkers(). Redux позволяет вам использовать реквизит для вещей с состоянием, которые вы хотите наблюдать. В предыдущем примере это свойство «this.props.feed», над которым я запустил функцию карты, описывающую, как отображать маркер для каждого местоположения в массиве.

Как мы обновляем этот массив каналов геолокации всякий раз, когда получаем новый набор местоположений? Вот тут-то и начинается излишняя часть. В Redux мы можем сделать нашу ленту объектом, который является частью глобального состояния приложения, называемого store. Вы не сразу записываете в этот массив новые значения. Вы отправляете действие, которое в значительной степени говорит: «Эй, я только что успешно получил фид геолокации, вот он». Отправленное действие будет перехвачено чем-то, называемым редьюсером, который выполняет необходимую логику для обновления состояния Redux. Это действие отправляется после успешного вызова выборки.

fetch(url).then((response) => { if (!response.isOk) { throw Error(response.statusText); } return response;}).then(response => response.json()) // Extract the JSON body (if valid).then(feed => { dispatch(feedsFetchSuccess(feed)); // dispatch is a Redux keyword

})

Действие, которое должно быть отправлено Redux, просто:

export function feedsFetchSuccess(feed) { return { type: FEEDS_FETCH_SUCCESS, feed // ES6 shorthand for “feed: feed” }

}

Поэтому, когда наш API-выборка получает ответ JSON, мы говорим Redux отправить действие в магазин, неся с собой фид геолокации в качестве полезной нагрузки. Редьюсер, запрограммированный на перехват действия с таким именем, заменит существующее хранилище новым хранилищем, содержащим фид. Одним из основных принципов функционального программирования является неизменность, поэтому Redux требует нового хранилища, а не обновления текущего.

export function feeds(state = [], action) { // Initialize the state to an empty array switch (action.type) { case CLEAR_FEED: // “Clear” the feed by replacing the state with an empty array return []; case FEEDS_FETCH_SUCCESS: var newState = Object.assign([], state); // Create a new state from the old state newState.push({ action.feed }); // Add to the state the feed attached to the action return newState; default: return state; }

}

Так что да, это очень окольный способ обновления фида геолокации, который может быть таким же тривиальным, как очистка массива и добавление к нему элементов в Swava. Но как выходец из этого мира и с головой погрузившийся в реактивное программирование, реализация Redux заставила меня придерживаться некоторых принципов функционального программирования: неизменяемых структур данных и чистых функций (которые при одних и тех же входных данных всегда будут возвращать одни и те же данные). вывод).

23 марта.

Пришло время показать фотографии из Instagram для местоположения Google Places, на которое нажал пользователь. Время пряжи добавить еще один сторонний компонент. В итоге получился RNFetchBlob, у которого почти такой же API, как у Fetch. RNFetchBlob также поставляется с общей структурой каталогов, которая абстрагирует изолированные каталоги, в которые вашему приложению разрешено размещать файлы, которые различаются в iOS и Android. В любом случае, каждый раз, когда изображение загружается, мы снова проходим через поток Redux, чтобы добавить путь к файлу изображения в массив, который содержит все пути к файлам изображений для выбранного местоположения. Когда на экране нажимается новый маркер местоположения, этот массив очищается и заполняется заново.

В этот момент мне пришла в голову идея думать об этом картографическом приложении как о диджейском микшере с двумя каналами. Первый канал — это лента локаций, которая наполняет карту маркерами. Второй канал — это набор данных обогащения для местоположения, выбранного на Первом канале, в данном случае для фотографий из Instagram. Наконец, имя для приложения. Миксер карт!

И еще напоследок. Настоящий пользовательский интерфейс! У нас есть основные функциональные возможности, и мы подумали, что было бы неплохо увидеть некоторые из них на экране. Также я понял, что не хочу тратить слишком много времени на создание пользовательских компонентов пользовательского интерфейса прямо сейчас и надеялся, что кто-то уже приложил значительные усилия для создания библиотеки кнопок plug and play, текстовых меток и тому подобного. Элементы пользовательского интерфейса, которые автоматически имеют собственный стиль для платформы, на которой работает приложение, будь то iOS или Android. NativeBase делает это очень хорошо и даже упрощает разделение элементов между панелью навигации и основным макетом с помощью простого XML:

render() { return (

{/* Anything within Header is placed in the nav bar! */}


< Button onPress={ () => this.onMenuPress() } iconCenter light>
{/* Anything in here is the main layout! */}
style={ styles.map } initialRegion={ this.state.mapRegion } onRegionChangeComplete={ this.onRegionChangeComplete }> { this.getFeedMarkers() } {/* Add map markers as child elements of MapView! */} {/* If the Channel Two array is not empty, display Channel Two on top of the map! */} { (this.props.channelTwo.length > 0) && this.renderChannelTwo() }

);

}

Также начал баловаться с некоторыми анимациями. Хотелось, чтобы Channel Two был GridView миниатюрных изображений, которые изначально выглядывают из нижней части экрана. Когда пользователь нажимает на GridView, он анимируется и в конечном итоге заполняет чуть более половины экрана, располагаясь над представлением карты. Удалось реализовать действие касания пользователя с помощью компонента TouchableHighlight и беспокойное кочевое представление с помощью компонента Animated.View. Но ждать! TouchableHighlight работает только на iOS, а не на Android. После некоторого переполнения стека обнаружил, что мне нужно использовать аналогичный TouchableNativeFeedback, если приложение обнаруживает среду Android. Слабый. Баллы вычтены, React Native.

toggleChannelTwo() { var toValue = (screen.height - 250) * -1; // Negative value moves view up the screen if (isChannelTwoExpanded) { toValue = 0; // Return the view to its original starting position } Animated.spring( this.state.bounceValue, { toValue: toValue, velocity: 3, tension: 2, friction: 8 } ).start(); isChannelTwoExpanded = !isChannelTwoExpanded;}

24 марта.

Глядя на проект в целом, я понял, что большинство компонентов, из которых состоит приложение, являются сторонними. Вещи, которые сделали другие люди. Вещи, которые я не могу контролировать с точки зрения возможности сборки и функциональности. Это достаточно пугающе для независимого разработчика, публикующего рабочие приложения. Это совершенно другой уровень страха для устоявшейся компании, которая должна нести ответственность не только перед отдельными пользователями, но, возможно, и перед клиентами. Я думаю, что если кто-то пойдет по пути React Native для своего следующего приложения, ему нужно будет сделать как минимум одну из двух вещей:

Номер A: Создайте как можно больше компонентов React Native своими силами. Компания, разрабатывающая свои собственные продукты (вместо аутсорсинга консалтинговых фирм) и достаточно крупная, чтобы выделять ресурсы на создание компонентов (вместо использования библиотек с открытым исходным кодом на Github), может воспользоваться преимуществами React Native, сохраняя при этом полный контроль над стабильность и ремонтопригодность своих опубликованных приложений.

Номер B: Ограничьте использование сторонних компонентов теми, которые, по-видимому, имеют большую вовлеченность и частые последние обновления в своих общедоступных репозиториях git. Когда просто не хватает времени или людей для создания пользовательских компонентов, из которых состоит приложение, определенный уровень доверия должен быть установлен для библиотек, которые обеспечивают ваши представления карты и ваши навигационные ящики, ваши модальные диалоги и т. д. В чем проблема? тестовое покрытие библиотеки? Насколько это перспективно? Сколько версий ОС он поддерживает? Удовлетворительные ответы на подобные вопросы с большей вероятностью можно найти в библиотеке Airbnb, которая была разветвлена ​​1000 раз, а не в той, которая не объединила запрос на вытягивание с тех пор, как смартфоны могли помещаться в карманах брюк.

В своем нынешнем состоянии Map Mixer довольно сильно зависит от компонентов с открытым исходным кодом. Я полагаю, что мог бы потратить время на создание своей собственной панели навигации с общим интерфейсом, но оформленной в соответствии с платформой, на которой она работает, но тогда мне, возможно, придется обновлять ее код каждый раз, когда выпускается новая версия iOS ИЛИ Android. Возьмите эту дилемму и умножьте ее на все кнопки, представления списков, загрузчики изображений, штуковины и отвратительные книды. Хочу ли я тратить столько времени, когда у NativeBase 46 участников на Github? Начиная с версии 2.0, я могу просто указать тег заголовка в JSX и получить красивую панель приложений в Android и эквивалентную панель навигации в iOS. Вместо того, чтобы беспокоиться обо всех вещах более низкого уровня, я могу сосредоточиться на объединении всех этих отдельных компонентов, чтобы сделать одно крутое приложение.

27 марта.

Сейчас эта версия устарела.

Программист. Юморист. Обналичьте его, работая над разработкой мобильных приложений в ustwo™ New York.

Первоначально опубликовано на ustwo.com.