SYSteen
  • Home
  • About
Home  /  iOS Development  /  Weather App Using Swift 3 and Alamofire 4 on Xcode 8

Weather App Using Swift 3 and Alamofire 4 on Xcode 8

SYSteen Founder November 24, 2016 iOS Development 8 Comments

In this tutorial we will build an iOS application that will fetch the weather data from an online weather API and display it to the user. The App will display the country name, temperature, weather state, current date, and an image showing the current weather state.

Let’s start by creating a new Xcode project.

screen-shot-2016-11-03-at-10-56-16-pm

 

screen-shot-2016-11-03-at-10-56-47-pm

 

This project uses Alamofire framework to create the GET request and fetch the data needed from the weather API.

We will install that framework using Cocoapods.

How to install Cocoapods:

1- Open terminal and type:

1
sudo gem install cocoapods

 

Gem will get installed in Ruby inside System library. Or try on 10.11 Mac OSX El Capitan, type:

1
sudo gem install -n /usr/local/bin cocoapods

 

If there is an error “activesupport requires Ruby version >= 2.xx”, then install latest activesupport first by typing in terminal.

1
sudo gem install activesupport -v 4.2.6

 

2- After installation, there will be a lot of messages, read them and if no error found, it means cocoapods installation is done. Next, you need to setup the cocoapods master repo. Type in terminal:

1
pod setup

And wait it will download the master repo. The size is very big (300MB++ at Sept 2016). So it can be a while. You can track of the download by opening Activity and goto Network tab and search for git-remote-https. Alternatively you can try adding verbose to the command like so:

1
pod setup --verbose

3- Once done it will output “Setup Complete”, and you can create your XCode project and save it.

Installing Alamofire 4:

First, open the Terminal window (on MAC) and browse to the directory of the Xcode project we created. I saved my project on the Desktop. So firs thing I’m going to do is “cd” to the Desktop. Then I’m going to “cd” to the project folder:

screen-shot-2016-11-03-at-11-04-58-pm

 

Next we need to do is create the Pod file. This file will be used by Cocoapods to run the installation of the Pods we specify for the project. In the terminal window type:

1
pod init

Next, go to the project folder and edit the “Podfile” using a text editor:

1
2
3
4
5
6
7
8
9
10
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
 
target 'SYSteenWeatherApp' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
 
# Pods for SYSteenWeatherApp
 
end

 

Replace everything with the below:

1
2
3
4
5
6
7
platform :ios, '10.0'
use_frameworks!
 
target 'SYSteenWeatherApp' do
 
    pod 'Alamofire', '~> 4.0'
end

 

The last step is to go back to the Terminal window and type : “pod install” to install the Pods required:

screen-shot-2016-11-03-at-11-19-17-pm

 

Now that Alamofire is installed, close Xcode and then browse to the project folder and run the file “SYSteenWeatherApp.xcworkspace” . From now on, we will open the project using the .xcworkspace extension to load the Pods.

screen-shot-2016-11-03-at-11-22-46-pm

 

Connect UIViews to ViewController:

Now we are ready to start designing the View Controller.

Go ahead and place those UIViews that will represent the following:

  • Image View as a blue background
  • Image View to show weather state
  • 1st Label: Today’s date.
  • 2nd Label: Current temperature
  • 3rd Label: Location
  • 4th Label: Weather state

You can mess with the look and feel of those views but this is what I got at the end. You can fill the labels with some dummy text for now.

Weather App Xcode Project

In order to get some nice weather icons, I chose http://garmahis.com/weather-icons-free-vector-weather-icon-pack/

weather_preview

Unpack the file and import the images to Xcode Assets.

Note: Rename the images to whatever status it shows. For example, the sun icon will symbol the clear sky status, so rename it to Clear. You will find out why later. Trust me.

 

Head over to the ViewController.swift file and let’s connect those UILabels and UIImageViews. Your file should look like this now after connecting the views:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import UIKit
 
class ViewController: UIViewController {
 
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var tempLabel: UILabel!
    @IBOutlet weak var locationLabel: UILabel!
    @IBOutlet weak var weatherImage: UIImageView!
    @IBOutlet weak var weatherLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}

 

Create the Data Model:

In order to correctly follow the MVC standard in our design, we will create a new swift file to manage our data model.

Create a new Swift file and add it to your project. Name it DataModel.swift.

In our DataModel file, we will use four variables to build our view. Open that file and add the following:

1
2
3
4
5
6
7
8
9
class DataModel {
    
    private var _date: Double?
    private var _temp: String?
    private var _location: String?
    private var _weather: String?
    typealias JSONStandard = Dictionary<String, AnyObject>
    
}

We notice that we chose _date to be a Double and not of type Date. The reason behind this is that the JSON data that we fetch has the date in EPOCH linux time which is a double. Fortunately, Swift has a function for us to do the conversion.

typealias is used to make our code easier to read. So instead of writing  Dictionary<String, AnyObject> all over the place, we can simplify it with JSONStandard

Next, we will validate those values just in case we get any “nil” when downloading the JSON file, or in case we weren’t able to download any file.

Add the following to the DataModel file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import Alamofire
 
class DataModel {
    
    private var _date: Double?
    private var _temp: String?
    private var _location: String?
    private var _weather: String?
    typealias JSONStandard = Dictionary<String, AnyObject>
    
    let url = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=Portland&appid=a7bbbd5e82c675f805e7ae084f742024")!
    
    var date: String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .long
        dateFormatter.timeStyle = .none
        let date = Date(timeIntervalSince1970: _date!)
        return (_date != nil) ? "Today, \(dateFormatter.string(from: date))" : "Date Invalid"
    }
    
    var temp: String {
        return _temp ?? "0 °C"
    }
    
    var location: String {
        return _location ?? "Location Invalid"
    }
    
    var weather: String {
        return _weather ?? "Weather Invalid"
    }
    
}

For the variables _temp, _location, _weather, we return an “Invalid” string in case their values are nil. As for the date, we are going to return a nil in case the date is not valid, and also will return a formatted date in case the date was valid, using the DateFormatter.

Notice how we convert the date from linux time to Date() using the function timeIntervalSince1970.

Our DataModel is not ready yet. Now we need to define the URL that we will use to fetch the JSON data from.

I used the openweathermap.org Weather API. Go to that website and Sign up. Once you sign up, go to your profile and write down your API key. You will use that key to connect to the OpenWeather API.

You will have to use the following URL, with some slight changes, to fetch the needed JSON data:

1
http://api.openweathermap.org/data/2.5/weather?q=Portland&appid=a7bbbd5e82c675f805e7ae084f742024

You can replace your APPID in the above link. In addition, feel free to choose the location you want. Notice the bold text above.

Head over to DataModel.swift and add the URL variable:

1
2
3
4
5
6
7
8
9
10
class DataModel {
    
    private var _weather: String!
  ....
    
    let url = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=Portland&appid=a7bbbd5e82c675f805e7ae084f742024")!
    
    var date: String {
        let dateFormatter = DateFormatter()
  ....

We converted that string into a URL in order to use with Alamofire.

Fetching using Alamofire 4:

Our URL is ready. Now we will create a GET request using Alamofire to get the JSON data. Add the following function to your DataModel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    func downloadData(completed: @escaping ()-> ()) {
        
        Alamofire.request(url).responseJSON(completionHandler: {
            response in
            let result = response.result
            
            if let dict = result.value as? JSONStandard, let main = dict["main"] as? JSONStandard, let temp = main["temp"] as? Double, let weatherArray = dict["weather"] as? [JSONStandard], let weather = weatherArray[0]["main"] as? String, let name = dict["name"] as? String, let sys = dict["sys"] as? JSONStandard, let country = sys["country"] as? String, let dt = dict["dt"] as? Double {
                
                        self._temp = String(format: "%.0f °C", temp - 273.15)
                        self._weather = weather
                        self._location = "\(name), \(country)"
                        self._date = dt
                    }
 
            completed()
        })
    }

I know this is alot of code but trust me it works!

Alamofire.request is used to make a GET request to fetch the required data. The response we get is in a completion handler.

To make things clear for you, find below our JSON data we fetched:

screen-shot-2016-11-24-at-8-58-22-pm

 

1
if let dict = result.value as? Dictionary<String, AnyObject>

This states that the fetched JSON file will be a dictionary of key of type String and value of AnyObject.

 

1
if let main = dict["main"] as? Dictionary<String, AnyObject>

We use this line to fetch the “main” dictionary of keys and values.

 

To fetch the temperature, we use the following

1
2
3
if let temp = main["temp"] as? Double {
                        self._temp = String(format: "%.0f °C", temp - 273.15)
                    }

The reason why we subtracted 273.15 from the fetched temperature is to convert it from Kelvins to Celsius.

Finally, we are done with our DataModel. Time to head over to the ViewController.swift.

 

Finalizing the View Controller:

Inside ViewController file, add the variable weather of type DataModel:

1
2
3
4
5
6
7
    @IBOutlet weak var weatherLabel: UILabel!
    .....
    var weather = DataModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    .....

 

Create a new function inside ViewController, name it updateUI() . We will use that function to update the UIViews once the fetching of data is complete:

1
2
3
4
5
6
7
    func updateUI() {
        dateLabel.text = weather.date
        tempLabel.text = "\(weather.temp)"
        locationLabel.text = weather.location
        weatherLabel.text = weather.weather
        weatherImage.image = UIImage(named: weather.weather)
    }

Notice the text in bold. We will let Xcode choose the image based on the weather state we fetched. So a “Clear” state shall give us the image Clear (Now you know why I asked you to rename the images based on their state).

The last step is to add the downloadData() function into the ViewDidLoad method and initialize the weather object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import UIKit
 
class ViewController: UIViewController {
 
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var tempLabel: UILabel!
    @IBOutlet weak var locationLabel: UILabel!
    @IBOutlet weak var weatherImage: UIImageView!
    @IBOutlet weak var weatherLabel: UILabel!
    
    var weather = DataModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        weather.downloadData {
            self.updateUI()
        }
 
    }
    
    func updateUI() {
        dateLabel.text = weather.date
        tempLabel.text = "\(weather.temp)"
        locationLabel.text = weather.location
        weatherLabel.text = weather.weather
        weatherImage.image = UIImage(named: weather.weather)
    }
 
    
    
}

 

Thats it! Run the project. You will notice how the data will change based on the JSON file we fetched. Check the output below:

Weather App

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)

Related

Previous Article
Next Article

About Author

SYSteen Founder

Tech savvy and explorer. With over 15 years of IT and Systems experience under his sleeves, SYSteen Founder decided to launch his own blog to fill in the gaps of the lack of robust tech how-to's online.

Related Posts

  • Stop Audio Player from Another View Controller Using NotificationCenter Swift 3

    December 2, 2016
  • How to Add UITextField to UIAlertController in Swift 3

    November 29, 2016
  • How to use Protocols and Delegates in Segues Swift 3

    November 26, 2016

8 Comments

  1. zhenja Reply
    November 27, 2016 at 7:05 pm

    Hi great tutorial. What font has you used? thx

    • SYSteen Founder Reply
      November 27, 2016 at 7:14 pm

      Hey zhenja thanks!

      font name is Arial Rounded MT Bold

      Cheers!

  2. Steve Reply
    March 2, 2017 at 10:07 pm

    I’m gonna try this out tonight. Thank you and I’m glad I found your site

  3. Steve Reply
    March 3, 2017 at 2:02 am

    If I’ve done pod setup before for another project do I have to do that step again for this project?

  4. Steve Reply
    March 3, 2017 at 2:03 am

    Just need a clarification. If I’ve done pod setup before for another project do I have to do that step again for this project?

    • SYSteen Founder Reply
      May 16, 2018 at 4:06 pm

      yes

  5. Frankie Reply
    May 16, 2018 at 2:38 pm

    Hi,
    I’m having one problem.
    The updated data is not displaying on the app screen. It displays on the output section but not on the app screen. How to fix it?

    • SYSteen Founder Reply
      May 16, 2018 at 4:06 pm

      I need to see the project

Leave a Reply Cancel reply

Help SYSteen Stay Online!

$
Select Payment Method
Personal Info

Donation Total: $5.00

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Today's Poll

What Type of Content Would You Like to See More?

View Results

Loading ... Loading ...

Facebook Page

Facebook Page

SYSteen Calendar

November 2016
M T W T F S S
« Sep   Dec »
 123456
78910111213
14151617181920
21222324252627
282930  

Recent Posts

  • How to Install Graylog 2.4 on CentOS 7 and Configure Graylog Sidecar August 17, 2018
  • How to Install Nagios + Centreon + Nagvis on Debian 6 July 19, 2017
  • What Type of Content Would You Like to See More? May 4, 2017
  • Monitor IIS Application Pools Using PowerShell May 3, 2017

Recent Comments

  • Ric on Monitor Network Device Configurations with RANCID on CentOS 7
  • Dennis on Bash Script to Monitor CPU, Memory and Disk Usage on Linux
  • Aaron Axvig on Forward Rsyslog Logs to Graylog
  • HOW TO INSTALL RSYSLOG AND LOGANALYZER ON ClearOS 6.3 – KitDEE Studio on How to Install RSYSLOG v8 and LogAnalyzer v4 on CentOS 7
  • Tony on How to Install TeamPass 2.1 on CentOS 7

Archives

  • August 2018
  • July 2017
  • May 2017
  • April 2017
  • December 2016
  • November 2016
  • September 2016
  • August 2016
  • May 2016
  • April 2016

Categories

  • General Articles
  • How To's
  • iOS Development
  • Life Strategies
  • SQL Server
  • Study & Work Music
  • Tips and Tricks
  • Uncategorized
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy

Social Media

Recent Posts

  • How to Install Graylog 2.4 on CentOS 7 and Configure Graylog Sidecar August 17, 2018
  • How to Install Nagios + Centreon + Nagvis on Debian 6 July 19, 2017
  • What Type of Content Would You Like to See More? May 4, 2017
  • Monitor IIS Application Pools Using PowerShell May 3, 2017