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

Andrew Smith @andrewsmith1996

About: Developer specialising in web development and mobile app development, with a keen interest in JavaScript technologies, cross-platform & native mobile application development and Dev-Ops.

Location:
Nottingham, UK
Joined:
Oct 7, 2017

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

Publish Date: Sep 19 '18
160 16

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

Screenshots of finished app

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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-elements

npm install react-native-vector-icons --save
Enter fullscreen mode Exit fullscreen mode

Then we'll want to link our new library up

react-native link react-native-vector-icons
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

Firstly we need to setup the ForecastCard's class


export default class ForecastCard extends Component {


}
Enter fullscreen mode Exit fullscreen mode

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>
);

Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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:''
    };
Enter fullscreen mode Exit fullscreen mode

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 },
    );
}

Enter fullscreen mode Exit fullscreen mode

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
        }));
        })
    }

Enter fullscreen mode Exit fullscreen mode

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} />} />
    );
}

Enter fullscreen mode Exit fullscreen mode

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} />}
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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"}} />
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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}} />
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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'
    }
});
Enter fullscreen mode Exit fullscreen mode

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!

Comments 16 total

  • Oksana Borukh
    Oksana BorukhSep 23, 2018

    Thanks for sharing!

  • Taylor Short
    Taylor ShortSep 23, 2018

    Great tutorial. Thank you! One major issue on my end however, I can't get app to show up on emulator. It is only showing the default iPhone home screen. Any suggestions?

    • Andrew Smith
      Andrew SmithSep 25, 2018

      Are you running the app in Xcode?

      • Taylor Short
        Taylor ShortSep 25, 2018

        Yes. As far as I can tell. I’ve got the emulator open and can see an iPhone home screen. Maybe I missed something

        • Andrew Smith
          Andrew SmithSep 26, 2018

          Hmm. And you're running it via the Play button in Xcode too?

  • BlagojeV93
    BlagojeV93Nov 16, 2018

    First of all nice tutorial, very easy to track and implement, nice work!

    I have one issue.
    On my emulator following these steps works fine. But on the real device I get blank screen when I start the app. Have I missed some step or what?

    • Andrew Smith
      Andrew SmithNov 16, 2018

      Hey, have you specified the permissions in your Info.plus for iOS or your Android Manifest for Android?

  • surendrayalakala
    surendrayalakalaDec 5, 2018

    hai bro , last 5 days means last 5 days

  • shabnam8950
    shabnam8950Dec 20, 2018

    Hello, I downloaded the code from github. But when i'm running the app in android real device and emulator I'm getting blank screen. make sure your bundle is packaged correctly or you're running a packager server" Do you have any idea about this to resolve

  • Kevin Vu
    Kevin VuFeb 19, 2019

    Hi, I downloaded your code, replaced with my API key for openweather. However, when I launch the app from iOS simulator, I am getting blank black screen. Do you have any idea?

    Thanks,

  • miguelito
    miguelitoMar 23, 2019

    Hello @ andrewsmith1996
    Congratulations for your work, I've met you by chance and I like your articles and tutorials. I hope to learn by reading your work
    I have a question about the Open Weather Map API.
    Where should I put exactly my Open Weather Map Key?
    Thank you

    I've discovered where to put my Open Weather Map KEY, but I'm having an error running the application on Android, I'll show it to you if you can help me. Thank you

    The development server returned response error code: 500

    URL: 10.0.2.2:8081/index.delta?platform...

    Body:
    <!DOCTYPE html>



    Error

    Error: Unable to resolve module `./index` from `/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/react-native/.`: The module `./index` could not be found from `/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/react-native/.`. Indeed, none of these files exist:
     * `/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/react-native/index(.native||.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)`
     * `/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/react-native/index/index(.native||.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)`
       at ModuleResolver.resolveDependency (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:163:15)
       at ResolutionRequest.resolveDependency (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:52:18)
       at DependencyGraph.resolveDependency (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/node-haste/DependencyGraph.js:283:16)
       at /home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/lib/transformHelpers.js:261:42
       at Server.<anonymous> (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/Server.js:1038:41)
       at Generator.next (<anonymous>)
       at asyncGeneratorStep (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/Server.js:99:24)
       at _next (/home/miguel/todo-codigos/proyectos-2019/react-native/vienelloviendo/node_modules/metro/src/Server.js:119:9)

    processBundleResult
    BundleDownloader.java:296
    access$200
    BundleDownloader.java:37
    onResponse
    BundleDownloader.java:174
    execute
    RealCall.java:206
    run
    NamedRunnable.java:32
    runWorker
    ThreadPoolExecutor.java:1162
    run
    ThreadPoolExecutor.java:636
    run
    Thread.java:764

  • Maricruzfer
    MaricruzferMar 24, 2019

    Hi.
    Congratulations for your work and thanks for sharing it with us.
    Is there any way to translate the application to another language, for example Spanish?

    I mean, the user interface is displayed in Spanish
    Thank you

  • miguelito
    miguelitoMar 25, 2019

    Your obligation is not to respond, but I see the little personal category that is in you.
    I hope you do very well

  • Vux Lee
    Vux LeeDec 11, 2019

    Great tutorial. Thank bro!
    Note: geolocation has been extracted from React Native 0.60 version. If you need an alternative solution install react-native-community/geolocation
    stackoverflow.com/a/56909007/8613436

  • Drukey
    DrukeyNov 8, 2021

    Hi thanks, such a great tutorial. What if I need to display the current weather of other cities, like not based on my current location but for other cities.

Add comment