Геотрекинг в React Native

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



    Надежность, скорость и уровень зрелости React Native иногда вызывают критику со стороны разработчиков, и около трех лет назад такие сомнения можно было назвать обоснованными. Однако, за это время технология стала совершеннее, а React Native признали практичным инструментом для создания широкого круга приложений. В частности, React Native подходит для приложений, которые используют географические координаты – например, различные трекеры, приложения для курьеров и т.д.

    Также в сторах популярны разработанные на React Native приложения интернет-магазинов, меню ресторанов, новостные ленты, приложения для доставки, клиенты для соцсетей – таким образом, огромное количество постоянно использует React Native, даже не задумываясь об этом. Простые примеры – Facebook, Instagram, Skype, Uber Eats, Wallmart. По прогнозам, Microsoft может пополнить этот список и перевести свои мобильные приложения на React Native, особенно после того, как в нем реализовали поддержку платформы Windows. Есть поддержка React Native для Qt, macOS, «встроенных» приложений weechat, хотя поддержка и документация для этих платформ остается довольно скудной.

    React Native может практически все то же самое, что и нативное приложение. Например, получать информацию с датчиков устройства, как это делают приложения на Java/Kotlin или ObjC/Swift, или отправлять данные в фоновом режиме, используя Foreground Service или Background fetch. Отличие только в том, что в React Native необходимо реализовать интерфейс для взаимодействия между нативной частью и Javascript. Для повседневных задач эти интерфейсы зачастую уже реализованы. Это также касается отслеживания местоположения и работы Javascript в фоне: уже есть решения в виде самостоятельных библиотек с открытым кодом. Так сложилось, что комьюнити самостоятельно «тянет» Javascript вперед, как программисты, так и компании активно используют решения Open Source. Немногие языковые сообщества могут похвастать таким же количеством библиотек, фреймворков, качественных утилит. И все же в React Native пока ограничены некоторые возможности, которые существуют в нативной разработке, и об этом нужно помнить.

    Работа с React Native


    Рассмотрим несколько ключевых факторов, которые обеспечивают скорость разработки кроссплатформенных приложений на React Native:

    Общая кодовая база


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

    Оптимальный воркфлоу


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

    Hot Reload


    Разработчику не нужно каждый раз пересобирать приложение, чтобы увидеть изменения. Благодаря Hot Reload, хватает буквально пары секунд, чтобы обновить приложение до актуального состояния. А в версии 0.61 Live Reload (теперь это Fast Refresh) дополнительно довели до ума, теперь при редактировании верстки изменения подтягиваются «на лету», не сбрасывая текущее состояние компонентов.

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

    1) Javascript

    React Native нацелен на работу с Javascript, позволяя быстро описывать логику и интерфейсы. Есть много готовых модулей из мира Web и Node.js, которые можно использовать в обычном порядке (естественно, если они не связаны с DOM или с серверной частью). Несмотря на высокую скорость работы, Javascript иногда уступает нативным языкам. Здесь нет многопоточности. В React Native мы не можем напрямую обратиться к Native API, хотя в скором времени это ограничение может быть снято. Javascript в первую очередь описывает, что необходимо сделать и отрисовать в нативной части.

    2) Мост (Bridge)

    Шина, которая принимает вызовы Javascript через специальные интерфейсы, представленные библиотекой react-native. Вызовы представляют собой обычные сериализованные сообщения. Для каждого компонента – простого View, поля ввода или вызова системного диалога – есть интерфейс, описанный в Javascript, с реализованной в нативной части логикой. Шина – самая медленная часть, она асинхронна, передача данных между Javascript и «нативкой» занимает время (по всей видимости, Facebook может вообще избавиться от нее). Зачастую проблемы производительности приложений на React Native вызваны именно работой моста, но если использовать его правильно (свести передачу данных туда-сюда к минимально необходимому), то производительность в приложениях React Native не будет значительно отличаться.

    3) Нативная часть

    Приложение на React Native – это изначально обычное нативное приложение. Особенность в том, что в нем уже подключены все необходимые библиотеки для того, чтобы можно было писать всю логику на React JS. Здесь же мы можем писать свою нативную логику, чтобы использовать (или не использовать) её в JS, добавлять обычные нативные библиотеки (например, для поддержки TLS v1.2 на KitKat и ранее). В готовые нативные приложения можно добавить поддержку React Native.

    Работа с геотрекингом в React Native


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

    Для работы с геолокацией в React Native есть несколько инструментов. Например, это пакет от сообщества React Native @react-native-community/geolocation, который покрывает большинство вариантов использования, однако, не работает в фоне.

    Для реализации нашей задачи мы использовали несколько решений, в том числе библиотеку с говорящим названием @mauron85/react-native-background-geolocation. Первоначально эта библиотека представляла собой плагин для Cordova, но позднее автор отделил базу и сделал отдельные обертки для React Native. Также есть одноименная платная библиотека от transistorsoft, но на момент нашей работы над проектом она выигрывала разве что своей документацией.

    Библиотека позволила нам задать гибкие настройки для отслеживания местоположения. Например, с какой частотой опрашивать системный сервис, как именно работать в фоне, в каком радиусе позиция будет считаться неподвижной. Настройки и методы для отправки этих координат на сервер можно было использовать «из коробки», но мы решили не спамить запросами, а агрегировать полученные координаты в хранилище и отправлять их с большим интервалом. При этом библиотека позволила запустить наше приложение как foregroundActivity, избавив нас от лишней головной боли, поскольку на многих Android-устройствах приложения в фоне могут работать только с дополнительными настройками (Huawei, MIUI, Samsung с недавних пор).

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

    import BackgroundGeolocation, {
      Location,
    } from '@mauron85/react-native-background-geolocation';
     
    class ForegroundService extends PureComponent<Props> {
    //...
    startBackgroundService = (config: GeotrackerConfig) => {
         // Задаем настройки для сервиса
    	BackgroundGeolocation.configure({
      	desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY,
      	stationaryRadius: 50,
      	distanceFilter: config.distance,
      	notificationTitle: Strings.Geolocation.NOTIFICATION_TITLE,
      	notificationText: Strings.Geolocation.NOTIFICATION_DESCRIPTION,
      	debug: false,
      	startOnBoot: false,
      	stopOnTerminate: true,
      	locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER,
      	interval: this.runnerInterval,
      	fastestInterval: 10000,
      	activitiesInterval: 30000,
      	stopOnStillActivity: false,
      	notificationIconColor: Colors.primary,
      	notificationIconSmall: 'notification_icon',
    	});
     
         // Когда мы вызовем start() произойдет запрос на разрешение к геолокации
                        	BackgroundGeolocation.on('authorization', status => {
      	if (status !== BackgroundGeolocation.AUTHORIZED) {
        	// Пользователь отклонил разрешение
      	} else if (status.hasPermissions) {
            // hasPermission - незадокументированое свойство, работает на обеих платформах
      	}
    	});
     
         // Запуск сервиса
                        	BackgroundGeolocation.start();
    };
    


    Мы видим, что здесь можно задать и настройки для уведомления, иконку (из нативных asset-ов). А что с разрешениями? Для Android после установки пакета дополнительно ничего не требуется, запросами на доступ к системной геолокации на себя возьмет библиотека. А вот для нормальной работы на iOS в XCode дополнительно нужно включить Background processing и Location updates во вкладке Signing & Capabilities проекта.



    Основной метод для сохранения и отправки координат мы реализуем в событии on('location') в этом же методе. Он будет работать как при активном приложении, так и в свернутом состоянии:

    BackgroundGeolocation.on('location', location => {
        BackgroundGeolocation.startTask(async taskKey => {
         // Нам также необходимо узнать состояние аккумулятора устройства,
         // здесь же происходит отсев iOS-симуляторов
          const batteryLevel = this.isSimulator
            ? 100
            : (await DeviceInfo.getBatteryLevel()) * 100;
          const updateDelta = location.time - this.lastUpdateAt;
    
         // Обновим и добавим данные в хранилище
          this.props.locationUpdate(location);
          this.props.locationAddTrackerData({
            altitude: location.altitude,
            batteryChargeLevel: batteryLevel,
            latitude: location.latitude,
            longitude: location.longitude,
            course: location.bearing,
            accuracy: location.accuracy,
            speed: location.speed,
            timestamp: Number(String(location.time).substr(0, 10)),
            isAlertOn: false,
          });
    
         // Пора отправить?
          if (updateDelta / 1000 > config.sendInterval) {
            this.syncDataWithInterval();
            this.lastUpdateAt = location.time;
          }
    
         // Кастомный хелпер для AsyncStorage, сохраним последнюю известную позицию
          Storage.setLastKnownLocation(location);
          BackgroundGeolocation.endTask(taskKey);  // ОБЯЗАТЕЛЬНО завершить задачу!
        });
    });
    


    Здесь мы вызываем action из стора, куда передаем данные координат в кастомном формате, дополнительно сохраняя данные о показаниях аккумулятора, используя библиотеку react-native-device-info. Все это одинаково хорошо работает на Android и iOS, когда приложение активно либо находится в фоне.

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

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

    class ForegroundService extends PureComponent<Props> {
    	//...
    	componentDidMount() {
    		this.startBackgroundService(this.props.config);
    	}
    
    	componentWillUnmount() {
        BackgroundGeolocation.stop();
        BackgroundGeolocation.removeAllListeners();
      }
    
    	startBackgroundService = (config: GeotrackerConfig) => {
        BackgroundGeolocation.configure({
    		// ...
    	};
    
    	render() {
        return null;
      }
    } 


    Если используется react-navigation, можно создать навигацию для авторизованной части следующим образом:

    // Создаем StackNavigator в обычном порядке
    const Navigator = createStackNavigator(RouteConfigs, NavigatorConfig);
    
    
    class AppNavigator extends PureComponent<Props> {
      static router: NavigationRouter = Navigator.router;
    
      componentDidMount(): void {
        SplashScreen && SplashScreen.hide();
      }
    
      render() {
        return (
          <>
            <ForegroundService navigation={this.props.navigation} />
            <Navigator {...this.props} />
          </>
        );
      }
    }
    
    export default AppNavigator;


    Далее этот навигатор можно использовать обычным образом в корневом навигаторе:

    const RootNavigator = createSwitchNavigator(
      {
        Auth: AuthNavigator,
        App: AppNavigator,
      },
      {
        initialRouteName: 'Auth',
      },
    );
    
    export default createAppContainer(RootNavigator);


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

    Подводя итоги


    В этой статье мы рассмотрели особенности применения React Native в приложениях с геолокацией. Как мы выяснили, используя этот фреймворк, разработчики уже сейчас могут легко отслеживать географические координаты устройства – например, в велотрекерах, приложениях для курьеров и экспедиторов, приложениях для выбора недвижимости и т.д. Библиотека покрывает большинство кейсов, связанных с геотрекингом, в частности, позволяет настраивать интервалы и экономить заряд устройства, стабильно работает в фоне на обеих платформах, поддерживает автозапуск на Android. При этом для сложных приложений, как правило, больше подходит нативная разработка (например, для работы с вычислениями и виджетами, как мы уже писали в прошлой статье).

    Спасибо за внимание, надеемся, что статья была вам полезна!
    SimbirSoft
    Лидер в разработке современных ИТ-решений на заказ

    Комментарии 0

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

    Самое читаемое