Skip to content

Instantly share code, notes, and snippets.

@BrunoScheltzke
Created July 6, 2020 21:03
Show Gist options
  • Save BrunoScheltzke/4e8b6393fbda190896521aa58be2b1a4 to your computer and use it in GitHub Desktop.
Save BrunoScheltzke/4e8b6393fbda190896521aa58be2b1a4 to your computer and use it in GitHub Desktop.
Wrapper class to manage address, location and cep
//
// 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