Реклама ООО Таймвэб
Реклама ООО Таймвэб

Создаем мобильное приложение на React Native. Часть 2: Системные запросы и API

1 комментарий
Создаем мобильное приложение на React Native. Часть 2: Системные запросы и API

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

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

Предыдущий материал: Создаем мобильное приложение на React Native. Настройка окружения

Добываем местоположение пользователя 

В этот раз нам понадобится функциональность, встроенная в операционную систему Apple (как и в Android, на самом деле). В iOS есть специальный модуль API, отвечающий за работу с GPS. То есть для запроса информации о местоположении пользователя. Вы наверняка и сами не раз сталкивались с тем, что встроенные и сторонние приложения запрашивают доступ к геопозиции через специальное системное окно. Это окно нам как раз и понадобится.

Запрос на отслеживание геопозиции в iOS

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

Для доступа к подобным функциям операционной системы необходимо использовать соответствующие модули из набора API самой Apple. Так как при работе с React Native мы не можем получить доступ к API калифорнийской корпорации напрямую, нам нужно воспользоваться уже адаптированным под эту деятельность RN-модулем.

Раньше такой модуль был у Facebook, но он устарел и больше не поддерживается, так что придется обратиться за помощью к сторонним решениям от энтузиастов. Я использовал React-Native-Geolocation-Service, потому что ее создатель пытался сделать максимально простое решение, способное стать полноценной аналогом официальной геолокационной библиотеки. 

Устанавливаем react-native-geolocation-service

Чтобы установить сторонний RN-модуль в свое приложение:

  • Открываем командую строку в корневой директории нашего проекта и вводим команду npm install react-native-geolocation-service

  • После этого закрываем наш проект и переходим в папку ios, чтобы установить pod-компонент, необходимый для взаимодействия с системными API для получения информации с GPS-модуля устройства: cd ios

  • Затем вводим команду для установки пода. Если этого не сделать, программа будет жаловаться на отсутствие нужных утилит: pod install

  • Затем вновь возвращаемся в корневую директорию: cd ..

  • Снова запускаем наш проект при помощи команды npx react-native run-ios

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

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

Настраиваем запрос геопозиции

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

Это делается простой командой import, ранее мы ее уже использовали: 

import Geolocation from 'react-native-geolocation-service

Импорт модуля Geolocation в React Native

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

Комьюнити теперь в Телеграм
Подпишитесь и будьте в курсе последних IT-новостей
Подписаться

Запрашиваем геопозицию 

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

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

Нас интересуют такие параметры, как долгота и широта (они будут использоваться для запроса погоды в конкретном местоположении). В английском эти единицы называются latitude и longtitude. Поэтому создадим два соответствующих состояния. 

const [lat, setLat] = useState(0)
const [lon, setLon] = useState(0)

Для работы с useState необходимо импортировать useState следующим образом: import { useState } from 'react'

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

Теперь пишем функцию запроса к геопозиции:

Запрос геопозиции устройства в React Native

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

() => {}

В теле функции делаем асинхронный запрос к GPS-модулю с использованием аргумента ‘whenInUse’, так как мы хотим получить от пользователя разрешение на отслеживание местоположения, пока программа открыта. И если пользователь дал добро, и программа на запрос выдает статус ‘granted’, то можно смело просить текущую геопозицию пользователя и обрабатывать полученные данные. 

let response = Geolocation.requestAuthorization('whenInUse').then(x => { if (x == 'granted') { } }

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

Geolocation.getCurrentPosition(position => { })

При получении объекта position, разбираем его на части и сохраняем в состояние (мы используем toFixed, чтобы хранить приблизительное значение без точности до метра). 

setLat(position.coords.latitude.toFixed(0))

Запрашиваем долготу:

setLon(position.coords.longtitude.toFixed(0))

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

В симуляторе геолокация равна тому значению, что укажет пользователь в соответствующем пункте меню. Нужно открыть раздел Features, потом перейти в пункт Location и выбрать Custom Location. После этого устройство будет думать, что находится в указанной зоне. 

Запрашиваем погоду по геолокации

Начнем с доступа к API-ключу, а закончим отрисовкой погоды в интерфейсе программы. 

Регистрируемся на openWeatherMap

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

Чтобы получить ключ:

  • Переходим на сайт openWeatherMap. 

  • Регистрируемся.

  • Заходим в свой профиль и находим там пункт API. Копируем значение этого пункта. 

API-ключ на сайте openWeatherMap

Готово. У нас есть ключ, и мы можем использовать его в своем приложении на бесплатной основе (при использовании бесплатных функций сервиса openWeatherMap).

Создаем точку доступа для общения с API

Сейчас мы напишем функцию, которая запрашивает данные о погоде с openWeatherMap и передает нам объект с соответствующими данными. Но эти данные необходимо где-то хранить, поэтому мы сделаем отдельное состояние с текущей температурой. Назовем его weatherData

const [weatherData, setWeatherData] = useState(0)

Теперь создадим функцию getWeather, обращающуюся к API и фиксирующую информацию о текущей температуре и других погодных параметрах. 

Функция getWeather

  1. Сначала мы объявляем асинхронную функцию getWeather:

    const getWeather = async ( ) => { }
  2. В теле функции необходимо создать переменную, в которой будет содержаться запрос к API и ответ от сервера openWeatherMap. Здесь будет использоваться спецссылка, в ней необходимо указать широту и долготу точки на Земле, для которой мы хотим узнать погоду. Также в ней указывается наш API-ключ. Широту и долготу возьмем из состояния программы. 

    const weather = await fetch([указываем ссылку на API в обратных кавычках, в полях lat и lon оставляем ссылки на состояние приложения]&appid=[API-ключ])
  3. Преобразуем ответ от API в формат JSON:

    const data = await weather.json()
  4. Записываем полученную температуру в состояние weatherData:

    setWeatherData(data.main.temp)

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

Настраиваем отображение погоды в интерфейсе

Наши цифры просто хранятся в состоянии, и пользователь приложения их увидеть не сможет. Нам нужно создать элемент интерфейса, показывающий текущую температуру. Для этого необходимо добавить в верстку компонент View с соответствующим содержимым. 

Текстовый компонент в React Native

  1. Внутри компонента View необходимо создать еще один компонент для отображения текста. Сразу стилизуем его, изменив размер текста и его шрифт (пока не особо симпатично, но уже что-то): 

    <Text style={{color: 'red', marginHorizontal: 'auto', fontSize: 40}}> </Text>
  2. Внутри будет текст на ваше усмотрение и информация о погоде, хранящаяся в состоянии weatherData.

    [какой-то текст] {weatherData.toFixed(0)}

toFixed(0) используется, чтобы показывать только целые значения и скрыть числа за точкой/запятой. 

Создаем отдельную функцию для запроса геопозиции и отображения погоды

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

Реализовать это легко. Мы просто вытащим уже существующую функцию из useEffect и поместим ее в отдельную переменную с другим названием, например getLocationWeather

Функция getLocationWeather

Теперь мы можем запускать эту функцию вручную. Например, по нажатию на кнопку. 

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

Вместо стандартного элемента Button воспользуемся компонентом TouchableOpacity, так как Button менее абстрактный и не позволяет себя стилизовать, а вот TouchableOpacity может принять любой облик на ваш вкус. И он может спокойно стать картинкой, если это понадобится. 

Кнопка TouchableOpacity в React Native

Создаем компонент TouchableOpacity, предварительно импортировав его в приложение. И одновременно с этим проводим базовую стилизацию, а также назначаем действие на нажатие по этой кнопке:

<TouchableOpacity style={{borderWithin: 2, borderRadius: 10, padding: 5}} onPress={getLocationWeather}> </TouchableOpacity>

Внутри можно указать любой другой компонент. В нашем случае это будет заранее загруженная картинка. Я оставил ее в корневой директории приложения. 

<Image style={{width: 20, height: 20}} source={require('./gps.png'} />

Готово. Теперь мы вручную запрашиваем геопозицию и погоду на ее основе. К сожалению, такой подход создает другую проблему. Из-за того, что useState – это асинхронная функция, мы не увидим результата ее работы сразу после нажатия на значок GPS. Это произойдет только после повторного нажатия. Будет складываться ощущение, будто приложение отстает на шаг от действий пользователя. Мы решим эту проблему позднее, когда вновь будем редактировать функцию useEffect ниже. 

Запрашиваем погоду по выбранному городу

Есть вероятность, что человек не захочет делиться с приложением геопозицией или просто захочет посмотреть погоду в каком-то другом городе, а не в своем. Для этого необходимо реализовать поиск широты и долготы других локаций при помощи какого-нибудь текстового поля и клавиши для ручного запроса погоды в конкретной локации. Это можно реализовать, используя API все той же openWeatherMap. 

Настраиваем поле для поиска города 

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

Текстовое поле в React Native

Добавляем базовый компонент Input, предварительно импортировав его в программу: <Input />

Внутри компонента нужно прописать такие атрибуты, как:

  • placeholder – чтобы пользователь видел, что в это поле можно ввести название города.
  • value – чтобы отображать в текстовом поле значение из состояния, в котором мы будем хранить название города (это так называемый контролируемый инпут-компонент): value={city}
  • onChangeText – чтобы передавать значение инпут-компонента в состояние, где хранится название города: onChangeText={setCity}

Теперь нужно организовать состояние, хранящее название города:

const [city, setCity] = useState('')

Остается добавить кнопку, активирующую API и добывающую для нас необходимую информацию о погоде. 

Кнопка Button в React Native

  1. Импортируем в программу компонент Button

    <Button>
  2. Добавляем ему свойство title, чтобы заменить текст на get weather

    title="get weather"
  3. И подключаем отслеживание нажатий с применением функции getWeatherInTheCity (мы создадим ее в следующей главе). 

    onPress={getWeatherInTheCity}
Мы используем тут обычную кнопку вместо TouchableOpacity, так как нет необходимости в какой-то глубокой стилизации или замене текста на картинку. Здесь нам подходит самая примитивная системная кнопка без изысков. 

Настраиваем точку доступа к API

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

В разделе Geocoding API на сайте openWeatherMap можно найти документацию конкретно по этой функции. Принцип работы у нее простой – указываем название города и получаем его широту с долготой, а их уж использовать мы умеем. 

Функция запроса погодных данных по API

  1. Объявим функцию с названием getWeatherInTheCity

    const getWeatherInTheCity = async () => { }
  2. В теле функции создадим переменную, хранящую в себе запрос к API и ответ от сервера openWeatherMap. 

    const geo = await fetch('https://api.openweathermap.org.geo/1.0/direct?q=${city}&limit=5&appid=[API-ключ]'
  3. Следом объявляем переменную, хранящую ответ сервера, преобразованный в формат JSON. 

    const data = await geo.json()
  4. Полученную информацию сохраняем в состояния lat и lon, как мы уже делали ранее при запросе геопозиции устройства. Например, так: 

    setLat(data[0].lat.toFixed(0))
  5. И здесь же вновь вызываем функцию getWeather, чтобы обновить информацию о погоде в интерфейсе:

    getWeather()

Готово! У нас есть механизм, использующий значение в переменной city для поиска города в базе openWeatherMap и последующего запроса погоды в выбранной локации. 

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

Решаем оставшиеся проблемы с useEffect

Напоминаю, что у нас еще осталась проблема – независимо от того, каким образом мы запрашиваем текущую температуру, мы все равно отстаем на шаг. 

Если зайти в программу и нажать на значок GPS, мы получим температуру, не соответствующую реальной, даже если указать другую геопозицию в настройках. Почему? Из-за базовых значений lat и lon. Оба значения равны нулю по умолчанию, и мы находим погоду для локации, расположенной в 0 и 0. 

Но если еще раз нажать на тот же значок, то информация обновится и вывод покажет уже предыдущие (обновленные) значения lat и lon. Так будет происходить всегда, потому что useState – асинхронная функция, не успевающая передать данные к моменту перерисовки интерфейса. 

Это можно исправить при помощи хука useEffect. Он может запускать какую-либо функцию при перерисовке интерфейса. Можно добавить туда функцию обновления ширины с долготой и параллельно функцию обновления данных о погоде. Но в таком случае мы всегда будет получать только погоду в текущем местоположении пользователя. Изменить это можно, если добавить в useEffect условие. 

Например, если поле lat уже заполнено и не равняется 0, то можно просто запросить погоду при перерисовке интерфейса. А если же поле lat равняется 0, то можно сначала запросить местоположение пользователя, а потом уже и погоду. 

Обновленный компонент useEffect

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

Чтобы сделать этот метод еще эффективнее, можно использовать хук useAppState, созданный сообществом React Native. С помощью него можно четко отследить факт открытия программы, а не перерисовки интерфейса. Получится использовать функции, связанные с API, более эффективно и не наживать себе проблемы, когда реально понадобится найти данные по нулевой долготе/ширине.

Вместо заключения

Мы научили нашу мобильную программу спрашивать разрешение на отслеживание геопозиции и показывать текущую температуру за окном. Это пока далеко не Carrot Weather и даже не стандартная погодная утилита для iOS, но уже рабочий проект, который можно довести до ума. Теперь вы знаете базовые аспекты работы с React Native.

Далее мы сделаем поиск по городам с подсказками и прогноз погоды на ближайшие несколько дней. 

Продолжение: Мобильное приложение на React Native. Часть 3: Интерфейс и запрос данных

Hello World! Гайды и обзоры для девелоперов разных мастей.

Комментарии

Дмитрий Пинчук 0
28 марта в 2023
После этого закрываем наш проект и переходим в папку ios, чтобы установить pod-компонент, необходимый для взаимодействия с системными API для получения информации с GPS-модуля устройства: cd ios

В папке проекта нет папки ios. Где ее можно найти?
С помощью соцсетей
У меня нет аккаунта Зарегистрироваться
С помощью соцсетей
У меня уже есть аккаунт Войти
Инструкции по восстановлению пароля высланы на Ваш адрес электронной почты.
Пожалуйста, укажите email вашего аккаунта
Ваш баланс 10 ТК
1 ТК = 1 ₽
О том, как заработать и потратить Таймкарму, читайте в этой статье
Чтобы потратить Таймкарму, зарегистрируйтесь на нашем сайте