How to build an Geolocation Weather Forecast app in React Native in 30 minutes

Andrew Smith
8 min readOct 28, 2019

--

Following on from my last guide on how to create an Image Recognition app in React Native (https://dev.to/andrewsmith1996/how-to-build-an-image-recognition-app-in-react-native-m6g) I’m going to write a guide on how to build a simple Geolocation Weather Forecast app in React Native (in under 30 minutes, ofcourse)

We’ll be building a simple app that uses a mobile phone’s Geolocation functionality to take a user’s location, then pass the latitude and longitude of the location to Open Weather Map’s Weather API, which will give us a 5 day weather forecast (split into 3 hour chunks) for that location.

The Weather API is free, and you’ll need to grab your key to use the app at https://openweathermap.org/api

This tutorial presumes you have NodeJS and React Native installed. If you don’t then head over to https://facebook.github.io/react-native/docs/getting-started.html to get started. It also presumes you have a basic understanding of React and NodeJS.

What we’ll build

We’ll only actually be creating 1 extra React component here, and that’s the actual card that will show each 3 hour block of weather forecast on.

Let’s begin

Firstly, you’ll need to initialise a new React Native app.

react-native init geolocationWeatherReactNative

Then CD into your new React Native projects directory, and run the following command to boot up the iOS simulator.

cd geolocationWeatherReactNative
react-native run-ios

Next we’ll want to install React Native Elements, which is a React Native UI Toolkit that’ll provide us with a Card component often seen in mobile apps. We’ll also install the vector icons library that’s needed to use the Card elements.

npm install --save react-native-elementsnpm install react-native-vector-icons --save

Then we’ll want to link our new library up

react-native link react-native-vector-icons

We’ll also need to add a NSLocationWhenInUseUsageDescription in the Info.plist file otherwise the app will crash. This is just a small description where you state how your app is going to use the location services. So add the following to your Info.plist file in the iOS folder for the project.

<key>NSLocationWhenInUseUsageDescription</key>
<string>YOUR DESCRIPTION HERE</string>

We’ll also need to add the following to your AndroidManifest.xml for the same reasons

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Now you’re pretty much all setup.

Firstly, we want to build our card component which will be reused to display the forecasted weather details for every 3 hours.

ForecastCard.js

So create a folder called ‘components’ and inside this create a ForecastCard.js file.

At the top of the page, we’ll want to import React, as well as the StyleSheet, View and Image modules from React Native, as we’ll be using these later on.

We also need to import the Card component from the React Native Elements library we installed.

import React, {Component} from 'react';
import { StyleSheet, View, Image } from 'react-native';
import { Text, Card, Divider } from 'react-native-elements';

Firstly we need to setup the ForecastCard’s class

export default class ForecastCard extends Component {
}

We’re not using any state in this component, it’ll just render props that we pass to it from the App parent component, so no need to add a constructor here.

Inside the render function of the ForecastCard’s class we’ll want to add the following code to render a blank card for the time being.

return (
<Card containerStyle={styles.card}>
</Card>
);

Then add the following style to the card, or feel free to add your own.

card:{
backgroundColor:'rgba(56, 172, 236, 1)',
borderWidth:0,
borderRadius:20
}

App.js

Now let’s head back to App.js and start working on the App’s functionality.

So let’s import all the modules we need:

import React, {Component} from 'react';
import { FlatList } from 'react-native';

Notice we’re importing FlatList, this is a React Native component that we’ll be using later on to render a list of items (the ForecastCards)

We’ll be using 4 variables of state:

  1. The longitude of the user’s location
  2. The latitude of the user’s location
  3. The forecast returned from the API
  4. An error string indicating if there’s been an error in the API response

And then initialise these in the constructor for the class

constructor(props){
super(props);
this.state = {
latitude: 0,
longitude: 0,
forecast: [],
error:''
};

Next we’ll create the function that’ll user Geolocation to return a user’s position. So setup a getLocation() function with the following code.

getLocation(){    // Get the current position of the user
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState(
(prevState) => ({
latitude: position.coords.latitude,
longitude: position.coords.longitude
}), () => { this.getWeather(); }
);
},
(error) => this.setState({ forecast: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
);
}

This code simply uses the built in Geolocation services to get the user’s current position and then sets the state of the latitude and longitude to the response. Then as setState() is an asynchronous operation, we’ve added a callback that calls the getWeather() function, which we’ll set up next.

So now that we’ve got the location of the user stored in the state of the application, we’ll use this data to pass it the Weather API to get the forecast for that area.

So setup a getWeather() function:

getWeather(){        // Construct the API url to call
let url = 'https://api.openweathermap.org/data/2.5/forecast?lat=' + this.state.latitude + '&lon=' + this.state.longitude + '&units=metric&appid=YOUR API KEY HERE';
// Call the API, and set the state of the weather forecast
fetch(url)
.then(response => response.json())
.then(data => {
this.setState((prevState, props) => ({
forecast: data
}));
})
}

In the above, we’re constructing a URL string that calls the Weather API’s forecast service, and then we’re appending the latitude and longitude that we’ve got stored in the state of the class. After that we’re appending the units parameter, to specify that we want the units to be metric, and then we’re appending our API key to the end.

Now that we’ve got a URL to call, we’ll call it using the fetch() method, and using the JSON data to set the state of the forecast variable.

This will set the state of the forecast to be an array containing 5 days worth of forecast entries for that location.

Next we’ll be using React Native’s FlatList component to render a list of cards down the mobile screen:

render() {
return (
<FlatList data={this.state.forecast.list} style={{marginTop:20}} keyExtractor={item => item.dt_text} renderItem={({item}) => <ForecastCard detail={item} location={this.state.forecast.city.name} />} />
);
}

The FlatList component (https://facebook.github.io/react-native/docs/flatlist) takes multiple props, firstly we’ll provide it with ‘data’ which is the forecast that we’ve got stored in state, then we’ll point it the ‘list’ part of the JSON response as this contains each 3 hour block of forecast. Then we’ll push the list down by 20px by using the style props, then the keyExtractor props forces the list to use the ids for the keys, rather than the default ‘key’ props we see in lists (in this case we’re giving it the timestamp of the weather forecast item as a unique identifier)

The following line is where we actually tell React what we want the FlatList to render:

renderItem={({item}) => <ForecastCard detail={item} location={this.state.forecast.city.name} />}

Here we’re telling it to render the list with our ForecastCard components we’ve created.

However first we need to import it at the top of the App.js file:

import ForecastCard from './components/ForecastCard';

We’re passing it 2 props, detail and location. Detail is basically each iteration of 3 hour weather forecast that we’ve got from the JSON response from the API call, this means we can access each block of data in each card. Then location is the part of the JSON response that contains the city that the weather forecast is for.

Now we’ve got the FlatList setup so we can simply pass all the props through to the ForecastCard.js component we’ve created.

ForecastCard.js

Now we’ll add into each card a title, containing the location. For this we’ll use the React Native text element, and display the props we’re passing to it.

<Text style={styles.notes}>{this.props.location}</Text>

Then we’ll add the image and time using a View component, and Flexbox to position them on each side:

<View style={{flexDirection:'row', justifyContent:'space-between', alignItems:'center'}}>
<Image style={{width:100, height:100}} source={{uri:"https://openweathermap.org/img/w/" + this.props.detail.weather[0].icon + ".png"}} />
<Text style={styles.time}>{time}</Text>
</View>

Notice how we’re using the Image Component and passing it the props of the image URL picked out from the JSON response.

<Image style={{width:100, height:100}} source={{uri:"https://openweathermap.org/img/w/" + this.props.detail.weather[0].icon + ".png"}} />

For displaying the time, we’re using a variable. We’re doing this so we can turn the datestamp into a format that’s more user friendly and just has the time. So inside the render function, just before the return statement we’ll add this:

let time;// Create a new date from the passed date time
var date = new Date(this.props.detail.dt*1000);
// Hours part from the timestamp
var hours = date.getHours();
// Minutes part from the timestamp
var minutes = "0" + date.getMinutes();
time = hours + ':' + minutes.substr(-2);

This will just format our date stamp into a nice easy to read hour format.

Next to add out divider line we’ll use the Divider component, and give it a colour and a little bit of spacing.

<Divider style={{ backgroundColor: '#dfe6e9', marginVertical:20}} />

Then the final part of our Card component will be the description and the temperature:

<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text style={styles.notes}>{this.props.detail.weather[0].description}</Text>
<Text style={styles.notes}>{Math.round( this.props.detail.main.temp * 10) / 10 }&#8451;</Text>
</View>

Again we’ll be using flexDirection and justifyContent to space them at either side of the card. We’ll be using 2 Text components, the first to display the part of the JSON response that has the text description in, then the second Text element contains the temperature part of the JSON response, rounded to 1 decimal place to get a nice formatted temperature. Then we’ll add the HTML entity

&#8451;

to add the Celsius symbol.

Then to style it we’ll add the following:

const styles = StyleSheet.create({
time:{
fontSize:38
},
notes: {
fontSize: 18,
textTransform:'capitalize'
}
});

So overall, we’ve covered how you can use a FlatList to render a list of Cards, and how you can use Geolocation to get coordinates and how to use this with a Weather API to get a JSON response with the weather forecast for that given location.

We’ve also utilised a number of new React Native components, such as Images and FlatLists, as well as an introduction to the React Native Elements library, and how to use Cards and Dividers.

So simply connect up your phone, and open the Xcode project in Xcode to get it onto your device to give it a test.

The source code for this app is available here on Github https://github.com/andrewsmith1996/geolocationWeatherReactNative, and is also showcased on my portfolio here https://andrewsmithdeveloper.com

I hope you enjoyed this post, and if you have any questions at all or feedback on my post, code or anything then let me know!

--

--