How I can make transfer location data to another class? - swift

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?

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)

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

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

Completion in Updating Location Manager

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???