Координаты подставляются в константу Coordinates в ViewController.
Coordinates является структурой в APIWeatherManager.
ViewController
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet weak var locationLabel: UILabel!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var pressureLabel: UILabel!
@IBOutlet weak var humidityLabel: UILabel!
@IBOutlet weak var temperatureLabel: UILabel!
@IBOutlet weak var appearentTemperatureLabel: UILabel!
@IBOutlet weak var refreshButton: UIButton!
@IBOutlet weak var activiryIndicator: UIActivityIndicatorView!
let locationManager = CLLocationManager()
@IBAction func refreshButtonTapped(_ sender: UIButton) {
toggleActivityIndicator(on: true)
fetchCurrentWeatherData()
}
func toggleActivityIndicator(on: Bool) {
refreshButton.isHidden = on
if on {
activiryIndicator.startAnimating()
} else {
activiryIndicator.stopAnimating()
}
}
lazy var weatherManager = APIWeatherManager(apiKey: "2a6d8e376a69c1ae07d4a52dd0c2dfdc")
let coordinates = Coordinates(latitude: 54.741704, longitude: 55.984471)
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
fetchCurrentWeatherData()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation = locations.last! as CLLocation
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(userLocation, completionHandler: { (placemarks, error) -> Void in
if let city = placemarks?[0].locality {
self.locationLabel.text = city
}
})
print("my locayion latitude: \(userLocation.coordinate.latitude), longitude: \(userLocation.coordinate.longitude)")
}
func fetchCurrentWeatherData(){
weatherManager.fetchCurrentWeatherWith(coordinates: coordinates) { (result) in
self.toggleActivityIndicator(on: false)
switch result {
case .Success(let currentWeather):
self.updateUIWith(currentWeather: currentWeather)
case .Failure(let error as NSError):
let alertController = UIAlertController(title: "Unable to get data ", message: "\(error.localizedDescription)", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
default: break
}
}
}
func updateUIWith(currentWeather: CurrentWeather) {
self.imageView.image = currentWeather.icon
self.pressureLabel.text = currentWeather.pressureString
self.temperatureLabel.text = currentWeather.temperatureString
self.appearentTemperatureLabel.text = currentWeather.appearentTemperatureString
self.humidityLabel.text = currentWeather.humidityString
}
}
APIManager
import Foundation
typealias JSONTask = URLSessionDataTask
typealias JSONCompletionHandler = ([String: AnyObject]?, HTTPURLResponse?, Error?) -> Void
protocol JSONDecodable {
init?(JSON: [String: AnyObject])
}
protocol FinalURLPoint {
var baseURL: URL { get }
var path: String { get }
var request: URLRequest { get }
}
enum APIResult<T> {
case Success(T)
case Failure(Error)
}
protocol APIManager {
var sessionConfiguration: URLSessionConfiguration { get }
var session: URLSession { get }
func JSONTaskWith(request: URLRequest, completionHandler: @escaping JSONCompletionHandler) -> JSONTask
func fetch<T: JSONDecodable>(request: URLRequest, parse: @escaping ([String: AnyObject]) -> T?, completionHandler: @escaping (APIResult<T>) -> Void)
}
extension APIManager {
func JSONTaskWith(request: URLRequest, completionHandler: @escaping JSONCompletionHandler) -> JSONTask {
let dataTask = session.dataTask(with: request) { (data, response, error) in
guard let HTTPResponse = response as? HTTPURLResponse else {
let userInfo = [
NSLocalizedDescriptionKey: NSLocalizedString("Missing HTTP Response", comment: "")
]
let error = NSError(domain: SWINetworkingErrorDomain, code: 100, userInfo: userInfo)
completionHandler(nil, nil, error)
return
}
if data == nil {
if let error = error {
completionHandler(nil, HTTPResponse, error)
}
} else {
switch HTTPResponse.statusCode {
case 200:
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: AnyObject]
completionHandler(json, HTTPResponse, nil)
} catch let error as NSError {
completionHandler(nil, HTTPResponse, error)
}
default:
print("We have got response status \(HTTPResponse.statusCode)")
}
}
}
return dataTask
}
func fetch<T>(request: URLRequest, parse: @escaping ([String: AnyObject]) -> T?, completionHandler: @escaping (APIResult<T>) -> Void) {
let dataTask = JSONTaskWith(request: request) { (json, response, error) in
DispatchQueue.main.async(execute: {
guard let json = json else {
if let error = error {
completionHandler(.Failure(error))
}
return
}
if let value = parse(json) {
completionHandler(.Success(value))
} else {
let error = NSError(domain: SWINetworkingErrorDomain, code: 200, userInfo: nil)
completionHandler(.Failure(error))
}
})
}
dataTask.resume()
}
}
APIWeatherManager
import Foundation
struct Coordinates {
let latitude: Double
let longitude: Double
}
enum ForecastType: FinalURLPoint {
case Current(apiKey: String, coordinates: Coordinates)
var baseURL: URL {
return URL(string: "https://api.forecast.io")!
}
var path: String {
switch self {
case .Current(let apiKey, let coordinates):
return "/forecast/\(apiKey)/\(coordinates.latitude),\(coordinates.longitude)"
}
}
var request: URLRequest {
let url = URL(string: path, relativeTo: baseURL)
return URLRequest(url: url!)
}
}
final class APIWeatherManager: APIManager {
var sessionConfiguration: URLSessionConfiguration
lazy var session: URLSession = {
return URLSession(configuration: self.sessionConfiguration)
} ()
let apiKey: String
init(sessionConfiguration: URLSessionConfiguration, apiKey: String) {
self.sessionConfiguration = sessionConfiguration
self.apiKey = apiKey
}
convenience init(apiKey: String) {
self.init(sessionConfiguration: URLSessionConfiguration.default, apiKey: apiKey)
}
func fetchCurrentWeatherWith(coordinates: Coordinates, completionHandler: @escaping (APIResult<CurrentWeather>) -> Void) {
let request = ForecastType.Current(apiKey: self.apiKey, coordinates: coordinates).request
fetch(request: request, parse: { (json) -> CurrentWeather? in
if let dictionary = json["currently"] as? [String: AnyObject] {
return CurrentWeather(JSON: dictionary)
} else {
return nil
}
}, completionHandler: completionHandler)
}
}
CurrentWeather
import Foundation
import UIKit
struct CurrentWeather {
let temperature: Double
let apparentTemperature: Double
let humidity: Double
let pressure: Double
let icon: UIImage
}
extension CurrentWeather: JSONDecodable {
init?(JSON: [String : AnyObject]) {
guard let temperature = JSON["temperature"] as? Double,
let apparentTemperature = JSON["apparentTemperature"] as? Double,
let humidity = JSON["humidity"] as? Double,
let pressure = JSON["pressure"] as? Double,
let iconString = JSON["icon"] as? String else {
return nil
}
let icon = WeatherIconManager(rawValue: iconString).image
self.temperature = temperature
self.apparentTemperature = apparentTemperature
self.humidity = humidity
self.pressure = pressure
self.icon = icon
}
}
extension CurrentWeather {
var pressureString: String {
return "\(Int(pressure * 0.750062)) mm"
}
var humidityString: String {
return "\(Int(humidity * 100)) %"
}
var temperatureString: String {
return "\(Int(5 / 9 * (temperature - 32)))˚C"
}
var appearentTemperatureString: String {
return "Feels like: \(Int(5 / 9 * (apparentTemperature - 32)))˚C"
}
}