Completion in Updating Location Manager - swift

I'm having an issue with the Update Location Manager.
So this is what i want to do. When the user goes into the view, it asks for location permision, when the user refuses i have set up a code that i will not post here cause it works. But if he accepts i run a function that i get the latitude and longitude of his. And then i want to run a function in which i do a query to an API. The following code is what I made
var tolat: Double = 0.0
var tolon: Double = 0.0
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
getData() { result in
switch result {
case .Success: self.eventsTable.reloadData()
case .Failure(let error): break
}
}
}
func getData(completion: Resulty<Void> -> Void) {
print("the lat \(tolat) and the lon \(tolon)")
completion(.Success())
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
tolat = locValue.latitude
tolon = locValue.longitude
locationManager.stopUpdatingLocation()
}
and after the end of the view
enum Resulty<T> {
case Success(T)
case Failure(NSError)
}
So it prints 0.0 the latitude and longitude. How can i make the func getData run when the Location Latitude and Longitude actually has data???

Related

Swift Threading Issue with CLLocationManager

I'm fairly new to Swift. I'm working on an app that tracks a user's location and also uses MultipeerConnectivity that allows users to chat with one another. It looks like the CLLocationManager is hogging the main thread, so the MultipeerConnectivity chat isn't working properly. I've tried using DispatchQueue.main.async around code blocks in my chat class (example code below), but it's still not working. Is there I way I can thread this so I get location updates and users can chat? Any advice or resources would be appreciated. Thanks!
func join() {
DispatchQueue.main.async {
self.peers.removeAll()
self.messages.removeAll()
self.session = MCSession(peer: self.myPeerId, securityIdentity: nil,
encryptionPreference: .required)
self.session?.delegate = self
guard
let window = UIApplication.shared.windows.first,
let session = self.session
else { return }
let mcBrowserViewController = MCBrowserViewController(serviceType:
ChatConnectionManager.service, session: session)
mcBrowserViewController.delegate = self
window.rootViewController?.present(mcBrowserViewController, animated: true)
}
}
Location Manager code:
import Foundation
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject,
CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
#Published var locationStatus: CLAuthorizationStatus?
#Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
lastLocation = location
print(#function, location)
}
}
I use the location manager in other classes to get the user's last location. For example:
CLLocationCoordinate2D(latitude: locationManager.lastLocation?.coordinate.latitude ?? 0, longitude: locationManager.lastLocation?.coordinate.longitude ?? 0)

How I can make transfer location data to another class?

import CoreLocation
class LocationManager: NSObject {
static let shared = LocationManager()
private let locationManager = CLLocationManager()
private override init() {
super.init()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if CLLocationManager.locationServicesEnabled(){
locationManager.startUpdatingLocation()
}
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let locations = locations.first else { return }
let latitude = locations.coordinate.latitude
let longitude = locations.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(locations) { placemarks, error in
if (error != nil){
print("Error in reverseGeocode")
}
guard let placemark = placemarks else { return }
if placemark.count > 0 {
guard let placemark = placemark.first else { return }
guard let city = placemark.locality else { return }
guard let country = placemark.country else { return }
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error.localizedDescription)")
}
}
I have got data from location manager, and I would like transfer city, country, latitude, longitude data to another class, how I can make it?

Why is MKLocalSearch for "nearby restaurants" returning locations in different cities?

I'm building a a simple application that allows a user to save their location for later.
The Goal: A user pins their address when at a business, and we save the name of the business
My approach: I'm requesting the location from a locationManager. Then I reverse geocode the CLLocation into a CLPlacemark. Since the placemark isn't recognizing the business name, I start a MKLocalSearch for "restaurants nearby". The response.mapItems are returning locations in completely different cities.
I have specified the region and verified that the placemark is correctly returning the user's address. So, I believe the issue lays within the MKLocalSearch.
Why does it return results for different cities?
Updated: All code From View Controller
class ViewController: UIViewController {
let locationManager = CLLocationManager()
var places = [MKMapItem]()
let naturalLanguageQuery = "closest places to eat"
let queries = ["restaurants", "places to eat", "breakfast", "lunch", "dinner"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading here
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
}
#IBAction func getLocation() {
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func add(placemark: CLPlacemark) {
search(placemark: placemark, index: self.queries.count - 1)
}
func searchCompleted(placemark: CLPlacemark) {
guard let foo = Scraper.shared.sortByDistance(userPlacemark: placemark, items: places) else { return }
for item in Scraper.shared.filterForUnique(items: foo) {
print(item)
if item.placemark.addressString == placemark.addressString {
}
}
}
func search(placemark: CLPlacemark, index: Int) {
let request = MKLocalSearchRequest()
guard let coordinate = placemark.location?.coordinate else { return }
request.region = MKCoordinateRegionMakeWithDistance(coordinate, 1600, 1600)
request.naturalLanguageQuery = queries[index]
MKLocalSearch(request: request).start { (response, error) in
guard error == nil else { return }
guard let response = response else { return }
guard response.mapItems.count > 0 else { return }
for item in response.mapItems {
self.places.append(item)
}
if index != 0 {
self.search(placemark: placemark, index: index - 1)
} else {
self.searchCompleted(placemark: placemark)
}
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let geocoder = CLGeocoder()
if let loc = locations.last {
geocoder.reverseGeocodeLocation(loc) { (placemarks, error) in
if let error = error {
print("error")
} else {
if let placemark = placemarks?.first {
print(placemark.debugDescription)
self.add(placemark: placemark)
}
}
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
Given your code, it appears your logic is correct, but depending on the location, you may not get what you want. Apple's documentation for the MKCoordinateRegion states that "specifying a region does not guarantee that the results will all be inside the region. It is merely a hint to the search engine." MKLocalSearch Documentation

Adding Start Button for Location Services [Swift 3.0 - Xcode]

I am trying to add a start button to start up my navigation and to find my current location, but nothing happens after my button is pressed?
Any help would be greatly appreciated!
Note: Map does load up, but the locationManager function does nothing, its just like it hasn't been pressed.
Heres my code:
import UIKit
import MapKit
import CoreLocation
class ThirdViewController: UIViewController , CLLocationManagerDelegate{
let manager = CLLocationManager()
func START(){
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01,0.01) //shows the size of map screen
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude,location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
self.map.showsUserLocation = true
}
}
#IBAction func STARTNAV(_ sender: UIButton) {
START()
}
Use didUpdateLocations delegate method for getting current coordinates
& add Maps configuration in plist file as
//MARK: locations ...
let locationManager = CLLocationManager()
func start() {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 100.0;
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
//locationManager.requestWhenInUseAuthorization()
let authorizationStatus = CLLocationManager.authorizationStatus()
let selector = #selector(self.locationManager.requestWhenInUseAuthorization)
if self.locationManager.responds(to:selector) {
if authorizationStatus == .authorizedAlways
|| authorizationStatus == .authorizedWhenInUse {
self.locationManager.startUpdatingLocation()
}else{
self.locationManager.requestWhenInUseAuthorization()
}
}else{
self.locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print((locationManager.location?.coordinate.latitude) ?? "No values")
let status = CLAuthorizationStatus.self
print("status-------->\(status)")
let locationValue : CLLocationCoordinate2D = (manager.location?.coordinate)!
let location = CLLocation(latitude: locationValue.latitude, longitude: locationValue.longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if error != nil{
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0]
if let locationName = pm.addressDictionary!["SubLocality"] as? NSString {
print("locationName is \(locationName)")
}
}
else{
print("Problem with the data received from geocoder")
}
})
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("locationManager-failed")
}

How do I append Altitude Integers

I need to display the current Altitude in a current app I am building. However, when the Altitude is determined, the label gets displayed with xxx.xxxxxxxxxxxx
It goes super far on the other side of the decimal point. I only want it to give a at most, 2 numbers on the other side of the decimal point. How is this possible?
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations [0]
self.speedLabel.text = String(location.speed)
self.altitudeLabel.text = String(location.altitude)
CLGeocoder().reverseGeocodeLocation(location) { (placemarks, error) in
if error != nil {
print(error!)
}
That is my current code.
Like I said, I only want to display the Altitude as, "XX.XX" and not XX.XXXXXXXXX
Try this:
extension Double {
func decimal(places: Int) -> Double {
let resultString = NSString(format: "%.\(places)f" as NSString, self) as String
return Double(resultString)!
}
}
self.altitudeLabel.text = location.altitude.decimal(places: 2)
Just try:
self.altitudeLabel.text = String(format: "%.2f", location.altitude)