Create an app that uses the weather API to determine if you need an umbrella.

Introduction

Beginners have created a portfolio for both learning and portfolio. It's been about 1 to 2 months since I started creating it, so I would like advice on refactoring and coding.

App overview

If you are a salaried worker, I thought it would be convenient if there was an app that could instantly check if you needed an umbrella today.

I press the button ↓ Determine the latitude and longitude of your current location from GPS ↓ Reverse geocoding from latitude / longitude ↓ Determine the prefecture you are in ↓ Determining the probability of precipitation in the prefecture you are in from the weather API ↓ Show if you need an umbrella or not

Such an app had already been released, but it wasn't the one I wanted, so I also tried to create a portfolio.

Operating environment

Target version
iOS 14.0
macOS Catalina 10.15.7
Xcode 12.0
Swift 5.3

Weather API used OpenWeatherMap

Install pod

This time I have installed "Alaomofire" and "SwiftyJSON".

Podfile


pod "Alamofire"
pod "SwiftyJSON"

Reference site

Create Main.storyboad

Main.storyboad スクリーンショット 2020-10-20 21.01.42.png

This time I created HomeVC and UmbrellaVC. Move to Home VC after launching the app → Tap the "Location Information" button → Move to Umbrella VC ① For label, call the prefecture of your current location obtained by reverse geocoding. ② For label, call the probability of precipitation at your current location from the weather API. ③ For label, judge whether an umbrella is necessary or not from the probability of precipitation

Implement

First, create a model of the data to be acquired by the weather API.

CityModel

CityModel.swift


import Foundation

struct cityModel:Decodable{
    
    var list: [List]

    struct List:Decodable {
        var pop:Double
    }
    
}

I think that the structure described here depends on the API data to be acquired. In the weather API "OpenWeather" used this time, the above acquisition was obtained.

JSON data acquired by OpenWeather

The numerical value of the item "pop" in this is the numerical value to be acquired this time (Double type) スクリーンショット 2020-10-20 21.14.22.png

Reference site


UmbrellaVC

UmbrellaVC.swift



import UIKit
import CoreLocation

class UmbrellaVC: UIViewController {
    
    @IBOutlet weak var label1: UILabel!
    var locationText:String = "Osaka"
    
    @IBOutlet weak var label2: UILabel!
    var popText:Int = 0
    
    @IBOutlet weak var label3: UILabel!
    var umbrellaJudgement:String = "I need an umbrella"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = locationText
        label2.text = "\(popText)" + "%"
        label3.text = umbrellaJudgement
        
        self.label3.layer.borderWidth = 2.0
        self.label3.layer.borderColor = UIColor.black.cgColor
        self.label3.layer.cornerRadius = 20

        //Change the color of the letters that appear according to the probability of precipitation so that you can see at a glance
        if popText >= 30{
            label3.textColor = .orange
            
        }else if popText >= 70{
            label3.textColor = .red
        }
    }
}

Assign variables to each label.

Assign to the variable set in UmbrellaVC.

HomeVC

HomeVC.swift


import UIKit
import CoreLocation
import Alamofire
import SwiftyJSON

class HomeVC: UIViewController,CLLocationManagerDelegate {
    
    //Put the initial value in each variable
    var latitudeNow: Double = 39.0000
    var longitudeNow: Double = 140.0000
    var locationManager: CLLocationManager!
    var administrativeArea:String = ""
    var locationNow: String = ""
    private var citymodel: cityModel?
    var doubleOfMaximumPop:Double = 100.0
    var maxPop:Int = 30
    var Judge:String = ""

 override func viewDidLoad() {
        super.viewDidLoad()

        //Image view layout settings
        umbrellaImage.image = UIImage(named:"umbrellaImage")
        umbrellaImage.layer.cornerRadius = 10
                
        //Call locationManager at viewDidload (update location information)
        locationManagerDidChangeAuthorization(CLLocationManager())
        
        //Calling the weather acquisition function
        getWeatherData()

    }

    //Get location information when you press a button
    @IBAction func buttonTapped(_ sender: Any) {
        
        //Press the button to stop updating locationManager
        stopLocationManager()
        
   }

    //Press the button to switch to segue and list the prefecture in Label 1 in Umbrella VC.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if(segue.identifier == "toUmbrellaVC") {
            let  locationNow: UmbrellaVC = (segue.destination as? UmbrellaVC)!
            locationNow.locationText = administrativeArea
            
            let popNow:UmbrellaVC = (segue.destination as? UmbrellaVC)!
            popNow.popText = Int(maxPop)
            
            let umbJud:UmbrellaVC = (segue.destination as? UmbrellaVC)!
            umbJud.umbrellaJudgement = Judge
            
        }
    }

    //Location manager @ iOS 14 Update location information
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        locationManager = CLLocationManager()
        
        let status = manager.authorizationStatus
        
        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            locationManager.delegate = self
            locationManager.startUpdatingLocation()
            
        case .notDetermined, .denied, .restricted:
            showAlert()
            
        default:print("Untreated")
        }
    }

    //Stop updating locationManager information
        func stopLocationManager(){
            locationManager.stopUpdatingLocation()
        }

    //Function to display alerts
    func showAlert(){
        let alertTitle = "Location information acquisition is not permitted."
        let alertMessage = ""Privacy" of the setting app>Please change from "Location services"."
        let alert: UIAlertController = UIAlertController(
            title: alertTitle, message: alertMessage, preferredStyle: UIAlertController.Style.alert
        )
        //OK button
        let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
        
        //Added Action to UIAlertController
        alert.addAction(defaultAction)
        present(alert, animated: true, completion: nil)
        
    }

    //Function to store location information when location information is updated
    //Location manager will not start unless location information is updated * Important
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        
        let location = locations.first
        let latitude = location!.coordinate.latitude
        let longitude = location!.coordinate.longitude
        //Store location information
        self.latitudeNow = Double(latitude)
        self.longitudeNow = Double(longitude)
        
        //After acquiring the location information, reverse geocode it to determine the prefecture.
        let locationA = CLLocation(latitude: latitudeNow, longitude: longitudeNow)
        
        let geocoder: CLGeocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(locationA) { [self](placemarks, error) in
            if let placemark = placemarks?.first {
                self.administrativeArea = placemark.administrativeArea!

            } else {
                self.administrativeArea = "bb"
            }
        }
    }

    //Get the maximum precipitation probability up to 18 hours later using the weather forecast API
    //In OpenWeatherMap, there is a charge for acquiring the probability of precipitation for one day, and it is free to acquire the probability of precipitation every three hours, so use that(I assumed that I would see it before going to work in the morning, so I judged that there was no problem)
    private func getWeatherData()  {

        let id = "Enter API ID"
        let baseUrl = "http://api.openweathermap.org/data/2.5/forecast?lat=" + "\(latitudeNow)" + "&lon=" + "\(longitudeNow)" + "&exclude=daily&lang=ja&cnt=6&.pop&appid=" + "\(id)"
        
        AF.request(baseUrl, method: .get).responseJSON { [self] response in
            guard let data = response.data else {
                return
            }
            do {
                let citymodel = try JSONDecoder().decode(cityModel.self, from: data)
                
                //List API data
                let popNumber = citymodel.list.map{ $0.pop }
            
                //Get max data in list
                var doubleOfMaximumPop = popNumber.max()
                
                //Convert to percentage display of max data
                let maxPop = doubleOfMaximumPop! * 100
                
                //Determine if there is data
                if doubleOfMaximumPop == nil{
                    print(Error.self)
                }else {
                    //If you have the data
                    if doubleOfMaximumPop != nil{
                        //get max data
                        doubleOfMaximumPop = self.doubleOfMaximumPop
                    }else {
                        //If the numbers are the same, pick one of them
                        doubleOfMaximumPop = popNumber[0]
                    }
                }

                //Assign the numerical value obtained by the getweather function to maxPop to the variable
                self.maxPop = Int(maxPop)
                
                //Judge whether an umbrella is necessary by maxPop, and substitute the judged sentence into Judge.
                if self.maxPop <= 30 {
                    self.Judge = "⛅️ No umbrella required ⛅️"
                }else if self.maxPop >= 70 {
                    self.Judge = "☔️ I need an umbrella ☔️"
                }else {
                    self.Judge = "☂️ Peace of mind if you have a folding umbrella ☂️"
                }
                
            }catch let error {
                print("Error:\(error)")
            }
        }
    }
}

It's not readable because you don't really know what the variable name stands for ... Variable name is difficult ...

Difficulties

-Since I did not understand the flow of updating location information with LocationManager → reverse geocoding to acquire data, problems such as data being acquired but not being assigned to variables occurred.

List of reference sites

Core Location changes in iOS 14 Assign variables inside the function to variables outside the function [Notes about Codale] (https://qiita.com/s_emoto/items/deda5abcb0adc2217e86)

Input
---
** Reading **
・ Introduction to iPhone application development "super" that will never be frustrated ・ IPhone application development intensive course that can be mastered in just 2 days ~~ (There is no way you can master it in 2 days by any means) ~~
**Udemy**
・ IOS12: Learn to Code & Build Real iOS 12 Apps in Swift 4.2 It was quite difficult in English, but it was easy to understand because he taught me the concept etc. with image diagrams. iOS is updated from time to time, so I thought I had to keep up with new knowledge.
** Output **
--- **Qiita**

Many people have posted the source code on Qiita, so I was able to create it so far, so I created it with the hope that it would be useful for people in similar circumstances.

Recommended Posts