Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to make function for getting current user coordinates in RN

I want to make one function for getting user coordinates and I somehow find that I am not sure my approach is good enough.
I would like to know how others write this function to work 100% sure on ios and on android.
Now I have something like this:

function getUserLocation(callback: func) {
  Permissions.check('location')
  .then(response => {
    if (response == 'authorized') {
      navigator.geolocation.getCurrentPosition(
        (location) => {
          callback({
              latitude: location.coords.latitude, 
              longitude: location.coords.longitude
            }, response);
        },
        (error) => {
          callback(null);
        },
        Platform.OS === 'android' ? null : {enableHighAccuracy: true, timeout: 100000, maximumAge: 1000}
      );
    } else {
      callback(null, response);
    }
  })
}

So you can see that I want to use this function so caller can know if locations usage is permitted and if it is get current coordinates.
What I am trying is to learn how to:

1. Make function like this to be awaitable.
2. How others make function like this (for both OS) and to work 100% same.

like image 626
1110 Avatar asked May 15 '18 18:05

1110


2 Answers

There are two parts to your question

1. Make function like this to be awaitable.

This answerable directly by providing you logic of how to do it. Awaitable is nothing but what is actually an async function or something that returns a promise. If you want to the async way then you need to first make a version of getCurrentPosition which returns a promise. That can be done like below

function getCurrentLocation(options) {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, ({code, message}) =>
      reject(Object.assign(new Error(message), {name: "PositionError", code})),
      options);
    });
};

PS: Credits to https://stackoverflow.com/a/44439358/2830850 for above code

Now you can rewrite your code like below

async function getUserLocation() {
  let response = await Permissions.check('location')
  let options = Platform.OS === 'android' ? null : {enableHighAccuracy: true, timeout: 100000, maximumAge: 1000}

  if (response == 'authorized') {
      return await getCurrentLocation(options)
  }

  return null;
}

In case you wan't to go the promise way it would be like below

function getUserLocation() {
    return new Promise((resolve, reject) => {
        Permissions.check('location')
            .then(response => {
                if (response == 'authorized') {
                    navigator.geolocation.getCurrentPosition(
                        (location) => {
                            resolve({
                                latitude: location.coords.latitude,
                                longitude: location.coords.longitude
                            });
                        },
                        (error) => {
                            reject(error);
                        },
                        Platform.OS === 'android' ? null : { enableHighAccuracy: true, timeout: 100000, maximumAge: 1000 }
                    );
                } else {
                    resolve(null);
                }
            })
    });
}

One key thing here is unlike callbacks, promise resolve a single value, which means you need to use objects to return multiple parameters.

2. How others make function like this (for both OS) and to work 100% same.

This is where you question becomes opinionated, I may do some different approach you may do something else and another person might do totally something else. But I will still discuss few possibilities.

Split into Multiple Function

function getUserLocation() {
   if (Platform.OS === 'android')
   {
       return getUserLocationAndroid();
   } else {
       return getUserLocationIOS();
   }
}

Maintain key value pairs

You can have code which automatically binds function based on the OS.

Utils = {
   getFunction(name) {
      if ((name + Platform.OS) in this)
          return this[name + Platform.OS]
      return this[name];
   }
   getLocation: function() {}
   //getLocationandroid: function() {}
}

So by default you will have getLocation to server both OS, but in case one of OS code deviates too much you will create a new function for the same. The code would look something like below

Utils.getFunction("getLocation")(...)

One function with branching

This is what your current approach is. I would stick with this as long there functions are easy to understand and there is clear intent what part of the code does platform specific stuff

Again there may 10 more different ways one can put here, but it would in the end depend one thing, what is easier for you to maintain, understand use. Some approaches are use case specific also, where you can mix multiple approaches based on the use case

like image 115
Tarun Lalwani Avatar answered Nov 16 '22 10:11

Tarun Lalwani


Make async version of getCurrentPosition, you can add this function to navigator.geolocation.

async function getCurrentPosition(options) {
    return new Promise((resolve, reject) => {
        navigator.geolocation.getCurrentPosition(
            position => resolve(position.coords),
            error => reject(error),
            options
        });
    });
}

Next function is what you want. Better to throw exception when not authorized, because anyway you need try / catch because of getCurrentPosition.

const locationOptions = Platform.OS === 'android' ? null : 
    { enableHighAccuracy: true, timeout: 100000, maximumAge: 1000 };

async function getUserLocation() {
    let permission = await Permissions.check('location');
    if (permission !== 'authorized') {
        throw new Error('Not authorized: ' + permission);
    }

    return await getCurrentPosition(locationOptions);
}

How to use:

try {
    let location = await getUserLocation(); // { latitude, longitude }
} catch (error) {
    Alert.alert(error.message);
}
like image 40
Alexander Danilov Avatar answered Nov 16 '22 09:11

Alexander Danilov