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-elements
npm 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:
- The longitude of the user's location
- The latitude of the user's location
- The forecast returned from the API
- 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 }℃</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
℃
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!
Top comments (16)
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
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
Hey, the API key should go in the following line in the
App.js let url = 'api.openweathermap.org/data/2.5/fo...' + this.state.latitude + '&lon=' + this.state.longitude + '&units=metric&appid=YOUR_KEY_HERE';
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
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
Thanks for sharing!
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?
Hey, have you specified the permissions in your Info.plus for iOS or your Android Manifest for Android?
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
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?
Are you running the app in Xcode?
Yes. As far as I can tell. I’ve got the emulator open and can see an iPhone home screen. Maybe I missed something
Hmm. And you're running it via the Play button in Xcode too?
Your obligation is not to respond, but I see the little personal category that is in you.
I hope you do very well
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,
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.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.