Created
July 6, 2020 21:03
-
-
Save BrunoScheltzke/4e8b6393fbda190896521aa58be2b1a4 to your computer and use it in GitHub Desktop.
Wrapper class to manage address, location and cep
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AddressViewModel.swift | |
// | |
// Created by Bruno Scheltzke on 05/06/20. | |
// Copyright © 2020 Bruno Scheltzke. All rights reserved. | |
// | |
import Foundation | |
import CoreLocation | |
protocol AddressViewModelDelegate { | |
func didGetAddress(_ address: Address) | |
func failGettingAddress() | |
} | |
class AddressViewModel { | |
var delegate: AddressViewModelDelegate? | |
var address: Address? | |
let locationManager: LocationManager | |
init(address: Address? = nil, locationManager: LocationManager = LocationManager()) { | |
self.locationManager = locationManager | |
locationManager.delegate = self | |
self.address = address | |
} | |
func getAddress() { | |
if let address = address { | |
delegate?.didGetAddress(address) | |
} else { | |
locationManager.getUserAddress() | |
} | |
} | |
func getAddressForCep(cep: String) { | |
guard let urlPath = URL(string: "https://viacep.com.br/ws/\(cep)/json") else { | |
delegate?.failGettingAddress() | |
return | |
} | |
let session = URLSession.shared | |
var request = URLRequest(url: urlPath) | |
request.timeoutInterval = 60 | |
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData | |
session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in | |
DispatchQueue.main.async { | |
if let data = data { | |
do { | |
let address = try JSONDecoder().decode(AddressCep.self, from: data).toAddress() | |
self.address = address | |
self.delegate?.didGetAddress(address) | |
} catch { | |
self.delegate?.failGettingAddress() | |
} | |
} else { | |
self.delegate?.failGettingAddress() | |
} | |
} | |
}).resume() | |
} | |
} | |
extension AddressViewModel: LocationDelegate { | |
func didGetAddress(_ address: Address) { | |
self.address = address | |
delegate?.didGetAddress(address) | |
} | |
func failGettingAddress() { | |
delegate?.failGettingAddress() | |
} | |
} | |
protocol LocationDelegate { | |
func didGetAddress(_ address: Address) | |
func failGettingAddress() | |
} | |
class LocationManager: NSObject { | |
var delegate: LocationDelegate? | |
// - API | |
public var exposedLocation: CLLocation? { | |
return self.locationManager.location | |
} | |
private let locationManager = CLLocationManager() | |
override init() { | |
super.init() | |
locationManager.delegate = self | |
} | |
func getUserAddress() { | |
self.locationManager.requestWhenInUseAuthorization() | |
} | |
func getAddressForCep(cep: String) { | |
let geocoder = CLGeocoder() | |
geocoder.geocodeAddressString(cep) { placemarks, error -> Void in | |
guard let placemark = placemarks?.first else { | |
self.delegate?.failGettingAddress() | |
return | |
} | |
var latitude: String? = nil | |
var longitude: String? = nil | |
if let latitudeC = placemark.location?.coordinate.latitude, | |
let longitudeC = placemark.location?.coordinate.longitude { | |
latitude = "\(latitudeC)" | |
longitude = "\(longitudeC)" | |
} | |
let address = self.addressFrom(placemark: placemark, latitude: latitude, longitude: longitude) | |
self.delegate?.didGetAddress(address) | |
} | |
} | |
private func addressFrom(placemark: CLPlacemark, latitude: String? = nil, longitude: String? = nil) -> Address { | |
let cep = placemark.postalCode ?? "" | |
let uf = placemark.administrativeArea ?? "" | |
let city = placemark.locality ?? "" | |
let bairro = placemark.subAdministrativeArea ?? "" | |
let street = placemark.thoroughfare ?? "" | |
var address = Address(cep: cep, uf: uf, city: city, bairro: bairro, street: street) | |
address.latitude = latitude | |
address.longitude = longitude | |
return address | |
} | |
} | |
// MARK: - Core Location Delegate | |
extension LocationManager: CLLocationManagerDelegate { | |
func locationManager(_ manager: CLLocationManager, | |
didChangeAuthorization status: CLAuthorizationStatus) { | |
switch status { | |
case .notDetermined : return | |
case .authorizedWhenInUse : print("authorizedWhenInUse") // location authorized | |
case .authorizedAlways : print("authorizedAlways") // location authorized | |
case .restricted : print("restricted") // TODO: handle | |
case .denied : print("denied") // TODO: handle | |
@unknown default: | |
break | |
} | |
if CLLocationManager.locationServicesEnabled() { | |
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest | |
locationManager.startUpdatingLocation() | |
} else { | |
delegate?.failGettingAddress() | |
} | |
} | |
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { | |
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate, | |
let location = manager.location else { | |
delegate?.failGettingAddress() | |
return | |
} | |
manager.stopUpdatingLocation() | |
getPlace(for: location) { placemark in | |
guard let placemark = placemark else { | |
self.delegate?.failGettingAddress() | |
return | |
} | |
let address = self.addressFrom(placemark: placemark, latitude: "\(locValue.latitude)", longitude: "\(locValue.longitude)") | |
self.delegate?.didGetAddress(address) | |
} | |
} | |
} | |
// MARK: - Get Placemark | |
extension LocationManager { | |
func getPlace(for location: CLLocation, | |
completion: @escaping (CLPlacemark?) -> Void) { | |
let geocoder = CLGeocoder() | |
geocoder.reverseGeocodeLocation(location) { placemarks, error in | |
guard error == nil else { | |
print("*** Error in \(#function): \(error!.localizedDescription)") | |
completion(nil) | |
return | |
} | |
guard let placemark = placemarks?[0] else { | |
print("*** Error in \(#function): placemark is nil") | |
completion(nil) | |
return | |
} | |
completion(placemark) | |
} | |
} | |
} | |
struct AddressCep: Codable { | |
let cep: String | |
let address: String | |
let additionalInfo: String | |
let bairro: String | |
let city: String | |
let uf: String | |
private enum CodingKeys : String, CodingKey { | |
case cep | |
case uf | |
case city = "localidade" | |
case bairro | |
case address = "logradouro" | |
case additionalInfo = "complemento" | |
} | |
func toAddress() -> Address { | |
var addressComplete = Address(cep: cep, uf: uf, city: city, bairro: bairro, street: address) | |
addressComplete.additionalInfo = additionalInfo | |
return addressComplete | |
} | |
} | |
struct Address: Codable { | |
let cep: String | |
let uf: String | |
let city: String | |
let bairro: String | |
let street: String | |
var streetNumber: String? = nil | |
var latitude: String? = nil | |
var longitude: String? = nil | |
var additionalInfo: String? = nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment