iOS Dev. Lesson 10

iOS Dev. Lesson 10

Agenda

  1. Put all weather data fetching into Weather.swift
  2. Showing cities in Map View
  3. Showing external website with SFSafariController

Weather.swift

struct WeatherService {
    // 3 functions here    
}
struct WeatherService {
    public func fetchCurrentWeather( completion: ((Weather) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(Weather.self, from: data)
                
                completion?(json)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    // 2 more functions here    
}

What is

completion function parameter?

struct WeatherService {
    public func fetchCurrentWeather( completion: ((Weather) -> Void)? = nil) {
        //...
                
		completion?(json)
        
        //...
    }
    
    // 2 more functions here    
}
public func hello( completion: ((String)->Void)?=nil) {
	completion?("Result Here")
}
struct WeatherService {
    public func fetchCurrentWeather( completion: ((Weather) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(Weather.self, from: data)
                
                completion?(json)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    public func fetchForecastWeather( completion: (([ForecastDay]) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(Forecast.self, from: data)
                
                completion?(json.forecasts)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    // 1 more function here    
}
struct WeatherService {
    public func fetchCurrentWeather( completion: ((Weather) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(Weather.self, from: data)
                
                completion?(json)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    public func fetchForecastWeather( completion: (([ForecastDay]) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(Forecast.self, from: data)
                
                completion?(json.forecasts)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    public func fetchCitiesWeather( completion: (([CityWeather]) -> Void)? = nil) {
        guard let url = URL(string: "https://dev.makzan.net/cities_weather.json") else { return }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            
            if error != nil {
                print(error!)
                return
            }
            
            do {
                let json = try JSONDecoder().decode(CitiesWeather.self, from: data)
                
                completion?(json.cities)
                
            } catch let error{
                print("Fetch error.")
                print(error)
            }
        }.resume()
    }
    
    // 1 more function here    
}
struct Weather:Decodable {
    var date: String = ""
    var temperature: String = ""
    var humidity: String = ""
    var windSpeed: String = ""
    var windDescription: String = ""
    var today:String = ""
    var statusCode:String = ""
    var todayDescription = ""
    var tomorrowDescription = ""
}
struct Forecast:Decodable {
    var forecasts: [ForecastDay] = []
}

struct ForecastDay:Decodable {
    var date:String = ""
    var statusCode:String = ""
    var temperatureLow:String = ""
    var temperatureHigh:String = ""
    var description:String = ""
}
struct CitiesWeather:Decodable {
    var cities: [CityWeather] = []
}
struct Coordinate:Decodable {
    var latitude: Double
    var longitude: Double
}
struct CityWeather:Decodable {
    var cityName:String = ""
    var date:String = ""
    var temperatureLow:String = ""
    var temperatureHigh:String = ""
    var description:String = ""
    var coordinate:Coordinate
}
struct StatusCode {

    // https://xml.smg.gov.mo/#Status
    static let statusCodes = [
        "01": "sun.max.fill",
        "a1": "moon.fill",
        "02": "cloud.sun.fill",
        "a2": "cloud.moon.fill",
        "03": "cloud.fill",
        "04": "cloud.fill",
        "12": "cloud.rain.fill",
        "13": "cloud.rain.fill",
        "16": "cloud.heavyrain.fill",
        "17": "cloud.heavyrain.fill",
        "18": "cloud.bolt.rain.fill",
        "25": "cloud.bolt.fill",
        "27": "cloud.rain.fill",
        "28": "cloud.rain.fill",
        "c8": "cloud.moon.rain.fill",
        "29": "cloud.rain.fill",
        "c9": "cloud.moon.rain.fill",
    ]

    static func symbolFor(statusCode:String) -> String {
        return statusCodes[statusCode] ?? "sun.fill"
    }
}

Map View

CitiesMapViewController

 

import MapKit

import UIKit
import MapKit

class CitiesMapViewController: UIViewController {

    @IBOutlet weak var mapView: MKMapView!
    
    var cities: [CityWeather] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        
        let coordinate = CLLocationCoordinate2D(latitude: 32.59037534587093, longitude: 114.95212987586083)
        
        // 5,000KM x 3,000KM
        let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 5000*1000, longitudinalMeters: 3000*1000)
        
        mapView.setRegion(region, animated: false)
        
        // fetch cities weather
        WeatherService().fetchCitiesWeather { (cities) in
            self.cities = cities
            DispatchQueue.main.async {
                self.renderCities()
            }
            
        }
    }

    func renderCities() {
        var annotations: [MKAnnotation] = []
        for city in cities {
            let annotation = MKPointAnnotation()
            annotation.coordinate = CLLocationCoordinate2D(latitude: city.coordinate.latitude, longitude: city.coordinate.longitude)
            annotation.title = city.cityName
            annotation.subtitle = "\(city.temperatureLow)—\(city.temperatureHigh) \(city.description)"
            annotations.append(annotation)
        }
        mapView.addAnnotations(annotations)
    }

}

SFSafariController

 

import SafariServices

@IBAction func tapDetails(_ sender: Any) {
    guard let url = URL(string: "https://www.smg.gov.mo/") else {
      return
    }
    let vc = SFSafariViewController(url: url)
    self.show(vc, sender: false)
}

deck

By makzan