چگونه یک برنامه بر پایه موقعیت مکانی را با استفاده از React Native توسعه دهیم؟

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 05 دی 1397
دسته بندی ها : react

وقتی که پنجره‌ای ظاهر می‌شود و درخواست می‌کند تا به موقعیت مکانی ما دسترسی داشته باشد، و ما بر روی اجازه دادن کلیک می‌کنیم چه اتفاق خوبی می‌تواند بیفتد؟ برخی برنامه‌ها تجربه بهتری را فراهم می‌کنند؛ مانند فیس‌بوک که افراد نزدیک را به شما پیشنهاد می‌دهد. برخی برنامه‌های دیگر مانند Uber یا Google Maps بدون دانستن موقعیت مکانی کاربر، نمی‌توانند به درستی کار کنند.

این برنامه‌های بر پایه موقعیت مکانی، از موقعیت مکانی دستگاه استفاده می‌کنند تا برخی امکانات را فعال کرده، و کنترل کنند. برنامه‌های بر پایه موقعیت مکانی مانند Find My iPhone در اعمال روزانه ما با دانستن این که ما کجا هستیم، کمک می‌کنند.

موقعیت مکانی می‌تواند در برنامه‌هایی مانند Tinder یک عملکرد اولیه باشد، یا می‌تواند در برنامه‌هایی مانند Instagram یک عملکرد کمکی باشد. وقتی که شما در Instagram یک عکس را آپلود می‌کنید، این برنامه یک مکان را به شما پیشنهاد می‌دهد تا بتوانید موقعیت مکانی خود را نیز بر روی آن قرار دهید. موقعیت‌ها چه یک عملکرد اصلی باشند یا نه، تجربه کاربری را پیشرفت می‌دهند.

در این مقاله، درباره کامپوننت‌های فناوری اصلی برنامه‌های بر پایه موقعیت مکانی به شما توضیح خواهم داد و شما نحوه توسعه‌دهی یک برنامه این چنینی با استفاده از React Native را یاد خواهید گرفت. در ابتدا، من به صورت خلاصه React Native را توصیف خواهم کرد و آن را با توسعه‌دهی برنامه بومی مقایسه خواهم کرد. سپس رویکردهایی را برای دریافت و نمایش موقعیت مکانی در یک برنامه با شما به اشتراک خواهم گذاشت. در آخر، در یک مسیر کوتاه برخی از چالش‌های طراحی و این که چگونه React از پس آن‌ها بر می‌آید را برای شما توصیف خواهم کرد.

ابزاری برای توسعه دهی برنامه بر پایه موقعیت مکانی: React Native

در این بخش، من به صورت خلاصه فریم‌وورک React Native، نکات مثبت و منفی آن و این که چرا برای ساخت برنامه‌های بر پایه موقعیت مکانی عالی است را توصیف خواهم کرد.

React Native یک فریم‌وورک JavaScript متن باز است که توسعه دهندگان را قادر می‌سازد تا برنامه‌های میان پلتفرمی را به همراه رفتارهای بومی آن‌ها بسازند.

«رفتار» در این توصیف چیست؟ بگذارید توضیح دهم. iOS و اندروید متفاوت هستند. (رابط‌های آن‌ها متفاوت هستند، انیمیشن‌های آن‌ها متفاوت هستند، و بسیاری چیزهای دیگر هم متفاوت هستند.) داشتن برنامه مشابه برای iOS و اندروید نیازمند توسعه‌دهی دو برنامه جدا خواهد بود. این فرایند قبلا یک روند دشوار بود، اما توسعه دهندگان با استفاده از React Native یک مجموعه کد می‌نویسند، و این کد به درستی بر روی هر دو پلتفرم کار می‌کند.

این مسئله کسب و کارها را قادر می‌‌سازد تا برنامه‌های خود را هم برای iOS و هم برای اندروید ارائه دهند، و این یعنی یک بازار بزرگ‌تر. به همین علت است که بسیاری از شرکت‌ها استفاده از React Native را ترجیح می‌دهند؛ زیرا نمی‌توانند هزینه توسعه‌دهی دو برنامه جداگانه را بدهند، یا این که مطمئن نیستند کاربرانشان از اندروید استفاده می‌کنند یا iOS. و با در نظر گرفتن این که بازار میان پلتفرمی احتمال دارد تا سال ۲۰۲۰ به مقدار ۸۰ میلیارد دلار رشد کند، این ابزار یک انتخاب منطقی برای تازه‌کاران به نظر می‌رسد.

حال بیایید نکات مثبت و منفی React Native را در زمینه توسعه‌دهی برنامه‌های بر پایه موقعیت مکانی توضیح دهیم.

نکات مثبت React Native

  • میان پلتفرمی بودن. به جای این که یک کد جداگانه برای هر سیستم (iOS و اندروید) بنویسید، می‌توانید یک کد بنویسید تا هر دو سیستم عامل را به عمل در بیاورد. و شما مجبور نیستید رابط کاربری و تجربه کاربری متفاوتی طراحی کنید.
  • کارایی بالا. React Native از کنترل‌ها و ماژول‌های بومی استفاده می‌کند. کد مورد نظر با کامپوننت‌های iOS و اندروید متناظر تعامل می‌کند و کد را به APIهای بومی رندر می‌کند. تمرکز بر روی API بومی است. این ابزار با استفاده از یک thread رابط کاربری جداگانه، کارایی برنامه را ارتقا می‌دهد.
  • متن باز بودن. جامعه React Native، و همچنین تعداد کامپوننت‌های متن باز هر روز رشد می‌کنند. این مسئله شما را قادر می‌سازد تا تجربه خود را میان اعضای جامعه به اشتراک بگذارید، فریم‌وورک را ارتقا دهید و راه حل‌هایی برای باگ‌های موجود پیدا کنید. تمام این موارد در ترکیب، روند توسعه‌دهی را تسریع می‌دهند.
  • این ابزار از هدر رفتن پول جلوگیری می‌کند. سه نکته قبلی به یک برتری قابل ملاحظه ختم می‌شوند: React Native از هدر رفتن پول شما جلوگیری می‌کند. این ابزار از ساخت دو برنامه جداگانه سریع‌تر است و در نتیجه زمان کمتری برای آزمایش و انتشار MVP مورد نیاز است.

گرچه، برخی موارد هم هستند که شاید نخواهید در آن‌ها از React Native استفاده کنید. مانند:

  • وقتی که شما نیازی به یک برنامه میان پلتفرمی ندارید. اگر می‌دانید که مخاطب شما یک پلتفرم خاص را ترجیح می‌دهد، پیشنهاد می‌کنم که به دنبال توسعه‌دهی بومی باشید. اول این که برنامه به گونه‌ای طراحی خواهد شد که با مشخص‌های سیستم عامل مورد نظر تطابق داشته باشد، و دوم این که شما می‌توانید از امکانات مختص یک پلتفرمی مانند ARKit برای iOS استفاده کنید.
  • وقتی شما نیاز به APIهای بیشتری از آنچه که React Native فراهم می‌کند دارید. یک مسئله به خصوص که من از آن خوشم نمی‌آید، این است که React Native تعداد محدودی API بومی پشتیبانی شده دارد. گرچه، APIهای کافی برای ساخت یک برنامه بر پایه موقعیت مکانی در آن وجود دارند. اگر به موارد دیگری نیاز دارید، می‌توانید آن‌ها را با استفاده از کد بومی متصل کنید.

چگونه موقعیت مکانی کاربر را جمع‌آوری کرده و نمایش دهیم؟

این بخش درباره جمع‌آوری و نمایش داده‌های موقعیت مکانی است. شما بر حسب مشخصه‌های برنامه خود، به دنبال یک راه خاص خواهید بود.

جمع‌آوری داده‌های موقعیت مکانی

من سه روش را برای جمع‌آوری موقعیت مکانی دستگاه جدا خواهم کرد.

نکته: این یک بررسی اجمالی برای شما است تا درک کنید که چه زمانی به سراغ کدام مورد بروید و تفاوت میان آن‌ها را درک کنید.

استفاده از React Native API

یک API بومی JavaScript وجود دارد که موقعیت مکانی یک دستگاه را مشخص می‌کند. نصب و استفاده از آن ساده است، ولی یک «اما» در آن وجود دارد: این ابزار در پس‌زمینه کار نمی‌‌کند و فراهم کننده موقعیت مکانی (3G، Wi-Fi، GPS) را نشان نمی‌دهد.

react-native-background-geolocation

این مورد یک پکیج است که موقعیت مکانی یک دستگاه را از ۰ تا ۱۰۰۰ متر مشخص می‌کند. این ابزار انرژی باتری بیشتری مصرف می‌کند، اما در سمت دیگر بستگی به شما دارد که چند وقت یک بار موقعیت مکانی را ردگیری کنید. این پکیج همچنین با SQLite ادغام می‌شود و شما می‌توانید داده‌های موقعیت ضبط شده را ذخیره کنید و آن را از طریق HTTP با دیتابیس خود همگام‌سازی کنید.

import { NativeModules, DeviceEventEmitter, PermissionsAndroid } from 'react-native' 
import Config from 'react-native-config' 
import get from 'lodash/get' 
const { GeoLocation } = NativeModules

class BackgroundGeoLocation { 
  constructor(token, user_id) {
    this.state = null
  }
  start(dispatch, nextState) {
    this.dispatch = dispatch
    const token = get(nextState, 'session.data.token')
    const user_id = get(nextState, 'user.data.user_id')
    const id = get(nextState, 'user.data.id')
    this.state = {
      user_id,
      token,
    }

    return PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION)
      .then(is_granted => is_granted === PermissionsAndroid.RESULTS.GRANTED
        ? is_granted
        : PermissionsAndroid.requestMultiple([
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
          PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
        ])
      )
      .then(_ => PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION))
      .then(is_granted => is_granted === PermissionsAndroid.RESULTS.GRANTED ? true : new Error())
      .then(_ => setTimeout(() => GeoLocation.startService(token, user_id, id, `${Config.API_URL}/live/car-tracking/gps-pos/`), 300))
      .catch(e => console.log(e))
  }
  stop() {
    return GeoLocation.stopService()
      .then(_ => console.log(_))
  }

  handleLocationChange(geo) {
    console.log(geo)
  }
}

export default BackgroundGeoLocation

با توجه به این که این ابزار یک پکیج است، نیازمند نگهداری و بروزرسانی سر وقت می‌باشد، اما یک کانال پشتیبانی بر روی Github از سازنده پکیج وجود دارد.

نکته: هزینه پکیج برای iOS: رایگان - برای اندروید: ۳۰۰ دلار به ازای هر برنامه.

کد native را به JavaScript API متصل کنید

مشکل اصلی در رویکرد اول، (استفاده از JavaScript API) می‌تواند با اضافه کردن کد بومی‌ای که یک سرویس پیش‌زمینه را در یک thread جداگانه شروع می‌کند، حل شود.

package com.djangostars.azyan;

import android.app.Notification; 
import android.app.NotificationChannel; 
import android.app.NotificationManager; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.PackageManager; 
import android.location.Criteria; 
import android.location.Location; 
import android.location.LocationManager; 
import android.os.Build; 
import android.support.v4.content.ContextCompat;

import com.facebook.react.bridge.Arguments; 
import com.facebook.react.bridge.Promise; 
import com.facebook.react.bridge.ReactApplicationContext; 
import com.facebook.react.bridge.ReactContextBaseJavaModule; 
import com.facebook.react.bridge.ReactMethod; 
import com.facebook.react.bridge.WritableMap; 
import com.facebook.react.bridge.WritableNativeMap;


/**
 * Created by AGulchenko on 5/7/18.
 */

public class GeoLocationModule extends ReactContextBaseJavaModule { 
    public static final String CHANNEL_ID = "ExampleService_Channel";
    public GeoLocationModule(ReactApplicationContext reactContext) {
        super(reactContext);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,"testName", NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = reactContext.getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    @Override
    public String getName() {
        return "GeoLocation";
    }

    @ReactMethod
    public void startService(String token, String user_id, String id, String url_string, Promise promise) {
        WritableMap result = Arguments.createMap();
        result.putString("ststus", "success");
        try {
            Intent serviceIntent = new Intent(getReactApplicationContext(), GeoLocationService.class);
            serviceIntent.putExtra("token", token);
            serviceIntent.putExtra("user_id", user_id);
            serviceIntent.putExtra("id", id);
            serviceIntent.putExtra("url_string", url_string);
            getReactApplicationContext().startService(serviceIntent);
            promise.resolve(result);
        } catch (Exception e) {
            e.printStackTrace();
            promise.reject("rrrrr",e);
            return;
        }

    }

    @ReactMethod
    public void stopService(Promise promise) {
        String result = "Success";
        try {
            Intent serviceIntent = new Intent(getReactApplicationContext(), GeoLocationService.class);
            getReactApplicationContext().stopService(serviceIntent);
        } catch (Exception e) {
            promise.reject(e);
            return;
        }
        promise.resolve(result);
    }

    @ReactMethod
    public void getLocation( Promise promise) {
        WritableMap res = Arguments.createMap();
        try {
            LocationManager locationManager = null;

            locationManager = (LocationManager) this.getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE);
            int permissionCheck = ContextCompat.checkSelfPermission(this.getReactApplicationContext(),
                    android.Manifest.permission.ACCESS_FINE_LOCATION);
            if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
                Criteria criteria = new Criteria();
                String bestProvider = locationManager.getBestProvider(criteria, false);
                Location location = locationManager.getLastKnownLocation(bestProvider);
                if(location != null) {
                    res.putDouble("latitude", location.getLatitude());
                    res.putDouble("longitude", location.getLongitude());
                    promise.resolve(res);
                }

            }
        } catch (Exception e) {
            promise.reject(e);
            return;
        }
    }
}

دریافت مجوز برای دسترسی به داده‌های موقعیت مکانی گاهی اوقات باعث بروز مشکلات می‌شود، اما مشکلات قابل تحمل هستند. React Native بدون زحمت زیاد این مشکل را حل می‌کند.

سیستم‌های مختلف نیازمند مجوز برای دسترسی به داده‌های موقعیت مکانی در سطوح مختلف هستند: iOS اولین باری که برنامه را اجرا می‌کنید آن را درخواست می‌کند، و اندروید در هنگام دانلود این کار را انجام می‌دهد. اگر از کد بومی استفاده کنیم، می‌تواند باعث بروز مشکل شود. گرچه، React Native این روند را با استفاده از تیک دسترسی به ماژول داده موقعیت مکانی حل می‌کند. این مورد دسترسی به داده‌های موقعیت مکانی را بدون نمایش هشدار مجوز، ممکن می‌کند.

نمایش موقعیت مکانی

داده‌های موقعیت مکانی همیشه دقیق نیستند. شما باید در گذشته چیزی به مانند این تصویر را دیده باشید:

علت بروز آن می‌تواند این باشد: دستگاه داده‌های موقعیت مکانی را از سه منبع جمع‌آوری می‌کند: Wi-Fi، داده‌های سیم کارت و GPS، که آخرین مورد کمترین دقت را دارد. دستگاه‌های ما در یک وضعیت دائم بررسی ارتباط اینترنت خوب هستند. اگر هیچ ارتباطی وجود نداشته باشد، دستگاه GPS را فعال خواهد کرد. و اگر یک جهش سریع از 4G به GPS وجود داشته باشد، دستگاه «گم می‌شود».

برای رفع این مشکل، من Fused Location Client ساخته Google را پیشنهاد می‌کنم. این ابزار شما را قادر می‌سازد تا زمان و فاصله‌ای که داده‌های موقعیت مکانی در آن بروزرسانی شده‌اند را تنظیم کنید: برای مثال، داده‌ها را هر ۵۰ متر و هر ۱۰ ثانیه بروزرسانی کن. با توجه به این که این API با موقعیت‌های مکانی تمام دستگاه‌ها در جاده‌ها و پیاده روها تطابق دارد، از تداخل داده‌ها جلوگیری خواهد شد. گرچه اگر دستگاه از هر کدام دور باشد، دیگر این روش موثر نخواهد بود.

چند کلمه درباره طراحی

در این بخش کوتاه، برای شما درباره موانعی که ممکن است در ساخت برنامه‌های بر پایه موقعیت مکانی با آن‌ها مواجه شوید و نحوه رفع آن‌ها با استفاده از React Native صحبت خواهم کرد.

React Native راه‌های ساده‌ای برای نمایش نقشه‌ها را فراهم می‌کند. کامپوننت‌های رابط کاربری آن برای طراحی متریال، کار مهندسان را ساده‌تر می‌کنند. ما از طراحی متریال برای ساخت یک جمع‌کننده Google Maps استفاده می‌کنیم، و سپس React Native آن‌ها را با امکانات مشخص هر پلتفرم تنظیم می‌کند.

Infinite List یکی از امکانات React Native است که یک لیست بی نهایت از نتایج جستجو را می‌سازد. در Uber، اگر شما شروع به تایپ کردن آدرس نمایید، تمام مقاصدی که با آنچه که شما وارد کردید شروع می‌شوند را دریافت خواهید کرد. لیست‌های این چنینی واقعا هم بی نهایت نیستند، بلک فقط همینطور که به پایین اسکرول می‌کنیم، نتایج بیشتری را بارگذاری می‌کنند.

Flat List (یک کامپوننت رابط کاربری داخلی دیگر) شامل هِدِر و فوتر ثابت است و حائل جستجو می‌شود. اگر می‌خواهید چنین لیست‌هایی را از ابتدا بسازید، زمان بیشتری از ساخت یک برنامه React Native کامل خواد برد.

<FlatList 
        data={get(props, 'order.data.items', [])}
        renderItem={listItem}
        keyExtractor={({ id }) => id}
        style={style.list}
      />

function listItem({ item: { quantity, name } }) { 
  return (
    <View style={style.main}>
      <Text style={[style.count, style.listItemText]}>{quantity}</Text>
      <Text style={[style.listItemText, style.name]}>{name}</Text>
    </View>
  )
}

کلام آخر

اگر می‌خواهید یک برنامه بر پایه موقعیت مکانی بسازید، React Native می‌تواند انتخاب صحیحی برای شما باشد. اگر اکثر برتری‌های توصیف شده برای شما صحیح هستند، تحقیق بیشتری بر روی این فناوری و ظرفیت‌های آن انجام دهید.

حال شما را تشویق می‌کنم تا توجه بیشتری نسبت به برنامه‌هایی که استفاده می‌کنید داشته باشید. اگر بدانید که اکثر آن‌ها از شما می‌خواهند که به موقعیت مکانی شما دسترسی داشته باشند، شگفت‌زده خواهید شد. برای اکثر شرکت‌ها، این که موقعیت مکانی کاربر را بدانند تا کیفیت بهتر و سرویس‌های مربوطه‌تری به کاربر ارائه دهند، حیاتی است. چرا شما این کار را امتحان نمی‌کنید؟

منبع

مقالات پیشنهادی