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

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")
}

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?

How do i show current location on map?

In my code I am trying to access a users current location and show it on the map. I am currently getting a fatal error Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value The error is inside ViewDidLoad. What is the proper way to do so? I have all 3 of the correct privacy location *Always and when in use, when in use usage description , always usage description *
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
private var locationManager: CLLocationManager!
private var currentLocation: CLLocation?
//let locationManager = CLLocationManager()
let mapView = MKMapView()
//var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled(){
locationManager.requestAlwaysAuthorization()//This where the fatal error appears
locationManager.requestWhenInUseAuthorization()////This where the fatal error appears
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
mapView.delegate = self
mapView.mapType = .standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
let leftMargin:CGFloat = 10
let topMargin:CGFloat = 60
let mapWidth:CGFloat = view.frame.size.width
let mapHeight:CGFloat = view.frame.size.width
mapView.frame = CGRect(x: leftMargin, y: topMargin, width: mapWidth, height: mapHeight)
view.addSubview(mapView)
if let coor = mapView.userLocation.location?.coordinate{
mapView.setCenter(coor, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
defer { currentLocation = locations.last }
if currentLocation == nil {
// Zoom to user location
if let userLocation = locations.last {
let viewRegion = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 2000, longitudinalMeters: 2000)
mapView.setRegion(viewRegion, animated: false)
}
}
}
func checkLocationAuthorization(authorizationStatus: CLAuthorizationStatus? = nil) {
switch (authorizationStatus ?? CLLocationManager.authorizationStatus()) {
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
case .restricted, .denied:
// show alert instructing how to turn on permissions
print("Location Servies: Denied / Restricted")
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
}
}
}
Assigning requestAlwaysAuthorization and requestWhenInUseAuthorization to locationManager before creating an Instance will crash the application.
Update the code like below,
if CLLocationManager.locationServicesEnabled(){
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}

Calling locationManager within a IBAction function

I'm working on a weather application and I want the user to get data about the weather either by inserting a location or by the current GPS location.
For the GPS location, there is a push button in the interface that can be pressed. At the click of the button, I want to call the locationManager function, but it does not work. Do you think it's something linked with the scope?
I simply put the locationManager function into the IBAction function of the button. That's because I do not know how to call locationManager within IBAction.
class ViewController: UIViewController, CLLocationManagerDelegate {
let key = "d79ac3fea08fac5d21deeabef00*****"
var lat = 11.344533
var lon = 104.33322
let managLoc = CLLocationManager()
#IBAction func locationOption(_ sender: Any, forEvent event: UIEvent) {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
lat = location.coordinate.latitude
lon = location.coordinate.longitude
Alamofire.request("https://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(key)").responseJSON {
response in
if let responseStr = response.result.value {
let jsonResponse = JSON(responseStr)
let jsonWeather = jsonResponse["weather"].array![0]
let jsonTemp = jsonResponse["main"]
let iconName = jsonWeather["icon"].stringValue
self.locationLabel.text = jsonResponse["name"].stringValue
self.typeView.image = UIImage(named: iconName)
self.typeLabel.text = jsonWeather["main"].stringValue
self.tempLabel.text = "\(Int(round(jsonTemp["temp"].doubleValue-273.15)))"
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
self.dayLabel.text = dateFormatter.string(from: date)
}
}
self.managLoc.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
Get the locationManager functions out of the action
class ViewController: UIViewController, CLLocationManagerDelegate {
let key = "d79ac3fea08fac5d21deeabef00*****"
var lat = 11.344533
var lon = 104.33322
let managLoc = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
managLoc.delegate = self
}
#IBAction func locationOption(_ sender: Any, forEvent event: UIEvent) {
self.managLoc.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
lat = location.coordinate.latitude
lon = location.coordinate.longitude
Alamofire.request("https://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(key)").responseJSON {
response in
if let responseStr = response.result.value {
let jsonResponse = JSON(responseStr)
let jsonWeather = jsonResponse["weather"].array![0]
let jsonTemp = jsonResponse["main"]
let iconName = jsonWeather["icon"].stringValue
self.locationLabel.text = jsonResponse["name"].stringValue
self.typeView.image = UIImage(named: iconName)
self.typeLabel.text = jsonWeather["main"].stringValue
self.tempLabel.text = "\(Int(round(jsonTemp["temp"].doubleValue-273.15)))"
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
self.dayLabel.text = dateFormatter.string(from: date)
}
}
self.managLoc.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}

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