I have this problem with my application, guess i'm approaching this problem wrong.
For every ten seconds i wish to save my current location to the phone.
And every one minute i wish to upload the current location to a web server.
How do i do this?
This is my code so far.
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let newLocation = locations.last! as CLLocation
saveLocations.saveLocation(newLocation.coordinate.longitude, latitude: newLocation.coordinate.latitude)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func startButtonClicked(sender: AnyObject) {
if(startButton.titleLabel?.text == "STARTA") {
tenSecTimer = NSTimer.scheduledTimerWithTimeInterval(10, target:self, selector: Selector("tenSecTimer:"), userInfo: nil, repeats: true)
sixtySecTimer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("sixtySecondsTimer:"), userInfo: nil, repeats: true)
locationManager.startUpdatingLocation()
startButton.setTitle("STOPPA", forState: UIControlState.Normal)
startButton.backgroundColor = UIColor.redColor()
} else {
tenSecTimer.invalidate()
sixtySecTimer.invalidate()
locationManager.stopUpdatingLocation()
startButton.setTitle("STARTA", forState: UIControlState.Normal)
startButton.backgroundColor = UIColor.greenColor()
}
}
//One minute upload
func sixtySecondsTimer(timer : NSTimer){
print("60SEK")
}
//10Second timer
func tenSecTimer(timer : NSTimer) {
print("10SEK")
}
In order to get the automatic alert asking the user for permission to use their location, then you will need to add the NSLocationAlwaysUsageDescription and/or NSLocationWhenInUseUsageDescription keys to your Info.plist, of course depending on which location update type you will make use of. It could look like the following:
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need to know your location because it makes it easier to stalk you</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>We need to know your location because it makes it easier to stalk you</string>
===
UPDATE 1
OP provided more details about the problem
===
To make a POST request then you can either make use of NSURLSession or some popular third party networking library. Many people makes use of either AFNetworking (Obj-C) or Alamofire (Swift) to make their lives easier and code prettier. You can install both of these through CocoaPods.
The following example will be based on Alamofire, but the code will be very similar for AFNetworking. NSURLSession is a different case, which sometimes is needed in complex multi-threading apps or whenever you wanna provide better background support.
NOTE: This code is simply a demo and is all contained in the same UIViewController which is bad practice. You will need to refactor it yourself into seperate models for better code. Also, the NSDateFormatter in the NSDate extension is highly inefficient. Also, this test makes use of HTTP, so ATS is turned off.
//
// ViewController.swift
// LocationUpload
//
// Created by Stefan Veis Pennerup on 13/11/15.
// Copyright © 2015 Kumuluzz. All rights reserved.
//
import UIKit
import CoreLocation
import Alamofire
class ViewController: UIViewController, CLLocationManagerDelegate {
// MARK: - Properties
private let locationManager = CLLocationManager()
private var tenSecTimer = NSTimer()
private var sixtySecTimer = NSTimer()
private var savedLocations = [CLLocation]()
// MARK: - Storyboard outlets
#IBOutlet weak var startButton: UIButton!
// MARK: - Lifecycle methods
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
requestLocationAuthorization()
}
// MARK: - Storyboard actions
#IBAction func startButtonClicked(sender: UIButton) {
let buttonText = startButton.titleLabel?.text!
let shouldTurnUpdatesOn = buttonText == "STARTA"
toggleLocationUpdates(shouldTurnUpdatesOn)
}
// MARK: - Authorization
private func requestLocationAuthorization() {
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
}
}
// MARK: - Helper methods
private func toggleLocationUpdates(toggle: Bool) {
if toggle {
tenSecTimer = NSTimer.scheduledTimerWithTimeInterval(10,
target:self,
selector: Selector("tenSecTimer:"),
userInfo: nil, repeats: true)
sixtySecTimer = NSTimer.scheduledTimerWithTimeInterval(60,
target: self,
selector: Selector("sixtySecondsTimer:"),
userInfo: nil, repeats: true)
locationManager.startUpdatingLocation()
startButton.setTitle("STOPPA", forState: UIControlState.Normal)
startButton.backgroundColor = UIColor.redColor()
}
else {
tenSecTimer.invalidate()
sixtySecTimer.invalidate()
locationManager.stopUpdatingLocation()
startButton.setTitle("STARTA", forState: UIControlState.Normal)
startButton.backgroundColor = UIColor.greenColor()
}
}
// MARK: - CLLocationManagerDelegate
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("\(self.dynamicType), locationManager:didUpdateLocations")
savedLocations += locations
}
// MARK: - Callback methods
func sixtySecondsTimer(timer : NSTimer) {
print("\(self.dynamicType), sixtySecondsTimer")
uploadLocationsToBackend(savedLocations)
}
func tenSecTimer(timer : NSTimer) {
print("\(self.dynamicType), tenSecTimer")
}
// MARK: - Parsers
private struct JsonConstants {
static let Locations = "locations"
static let Timestamp = "timestamp"
static let Latitude = "latitude"
static let Longitude = "longitude"
}
private func parseLocationsToDictionary(locations: [CLLocation]) -> [String: AnyObject] {
var locationsDictionaries = [[String: AnyObject]]()
for loc in locations {
let locDict: [String: AnyObject] = [
JsonConstants.Timestamp: loc.timestamp.toISO8601(),
JsonConstants.Latitude: loc.coordinate.latitude,
JsonConstants.Longitude: loc.coordinate.longitude
]
locationsDictionaries += [locDict]
}
return [JsonConstants.Locations: locationsDictionaries]
}
// MARK: - Network
private func uploadLocationsToBackend(locations: [CLLocation]) {
let url = "http://httpbin.org/post"
let params = parseLocationsToDictionary(locations)
Alamofire.request(.POST, url, parameters: params, encoding: .JSON, headers: nil)
.responseJSON { response in
print("\(self.dynamicType) response: \(response)")
}
}
}
extension NSDate {
func toISO8601() -> String {
let iso8106Formatter = NSDateFormatter()
iso8106Formatter.timeZone = NSTimeZone(name: "UTC")
iso8106Formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
iso8106Formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
return iso8106Formatter.stringFromDate(self)
}
}
Related
I have a situation where I have to call an API to fetch some Vehicles Locations objects in an array after getting the user current location. After fetching vehicles, I have to get the address also from Vehicles Locations data, so for 'n' Vehicles, there will be an 'n' API call and then add annotations on Map.
After that, I have to refresh the Vehicles data every 1 min. So, I created a timer but even after getting the API response, annotations are not displaying on map. Kindly look into this issue.
Below is Map View
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet private var mapView: MKMapView!
var currentLocation: CLLocation?
var user: User?
lazy var vehicleViewModel = {
VehicleViewModel()
}()
var locationUpdateTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
configureLocationManager()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopTimer()
}
func configureLocationManager() {
LocationManager.shared().delegate = self
LocationManager.shared().initializeLocationManager()
}
func configureTimer() {
if locationUpdateTimer == nil {
locationUpdateTimer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(runLocationTimer), userInfo: nil, repeats: true)
}
}
#objc func runLocationTimer() {
fetchVehiclesLocation()
}
func resetMap() {
let annotations = mapView.annotations
mapView.removeAnnotations(annotations)
mapView = nil
}
func initializeMapView() {
mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
mapView.delegate = self
}
func configureMapView() {
let mapDetail = vehicleViewModel.getLatitudeLongitudeLatitudeDeltaLongitudeDelta()
if let latitude = mapDetail.0, let longitude = mapDetail.1, let latitudeDelta = mapDetail.2, let longitudeDelta = mapDetail.3 {
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), latitudinalMeters: latitudeDelta, longitudinalMeters: longitudeDelta)
let scaledRegion: MKCoordinateRegion = mapView.regionThatFits(region)
mapView.setRegion(scaledRegion, animated: true)
mapView.setCameraBoundary(
MKMapView.CameraBoundary(coordinateRegion: region),
animated: true)
let zoomRange = MKMapView.CameraZoomRange(maxCenterCoordinateDistance: 100000)
mapView.setCameraZoomRange(zoomRange, animated: true)
mapView.register(
VehicleAnnotationView.self,
forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
}
}
func fetchVehiclesLocation() {
configureTimer()
initViewModel {
DispatchQueue.main.async {
self.resetMap()
self.initializeMapView()
self.configureMapView()
}
if let user = self.user {
self.vehicleViewModel.fetchVehicleAddress(user: user, completion: { status in
if self.vehicleViewModel.vehicleAnnotationItems.count == 0 {
self.alertWithTitleAndMessageWithOK(("Alert" , "error while fetching vehicle locations"))
} else {
DispatchQueue.main.async {
self.mapView.addAnnotations(self.vehicleViewModel.vehicleAnnotationItems)
}
}
})
}
}
}
func initViewModel(completion: #escaping () -> Void) {
if let user = self.user, let userId = user.userId {
vehicleViewModel.getVehiclesLocation(userId: userId) { (vehicleApiResponse, error) in
if vehicleApiResponse != nil {
completion()
} else {
self.alertWithTitleAndMessageWithOK(("Alert" , error?.localizedDescription ?? "error while fetching vehicles"))
}
}
}
}
func stopTimer() {
if locationUpdateTimer != nil {
locationUpdateTimer!.invalidate()
locationUpdateTimer = nil
}
}
deinit {
stopTimer()
}
}
//MARK: - LocationManagerDelegate methods
extension MapViewController: LocationManagerDelegate {
func didFindCurrentLocation(_ location: CLLocation) {
currentLocation = location
if let currentLocation = currentLocation, (currentLocation.horizontalAccuracy >= 0) {
mapView.showsUserLocation = true
fetchVehiclesLocation()
}
}
}
LocationManager Extension class
import CoreLocation
protocol LocationManagerDelegate: AnyObject {
func didFindCurrentLocation(_ location: CLLocation)
func didFailedToFindCurrentLocationWithError(_ error: NSError?)
func alertLocationAccessNeeded()
}
/**
This class acts as a Singleton for getting location manager updates across the application.
*/
class LocationManager: NSObject {
var manager: CLLocationManager!
private static var sharedNetworkManager: LocationManager = {
let networkManager = LocationManager()
return networkManager
}()
private override init() {
super.init()
manager = CLLocationManager()
}
class func shared() -> LocationManager {
return sharedNetworkManager
}
weak var delegate: LocationManagerDelegate?
//Entry point to Location Manager. First the initialization has to be done
func initializeLocationManager() {
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.distanceFilter = kCLDistanceFilterNone
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.allowsBackgroundLocationUpdates = false
startUpdating()
}
//Start updating locations
func startUpdating() {
manager.startUpdatingLocation()
}
//Check for whether location services are disabled.
func locationServicesEnabled() -> Bool {
let isAllowed = CLLocationManager.locationServicesEnabled()
return isAllowed
}
}
//MARK: - CLLocation Manager delegate methods
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
manager.stopUpdatingLocation()
delegate?.didFindCurrentLocation(location)
// manager.delegate = nil
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
delegate?.didFailedToFindCurrentLocationWithError(error as NSError?)
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .notDetermined:
self.manager.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse, .authorizedAlways:
if locationServicesEnabled() {
self.startUpdating()
}
case .restricted, .denied:
delegate?.alertLocationAccessNeeded()
#unknown default:
print("Didn't request permission for location access")
}
}
}
Your code has a number of problems.
Neither your initializeMapView() function nor your resetMap() function make any sense.
You should add an MKMapView to your Storyboard, then connect it to your mapView outlet, and then don't assign a new value to mapView. Don't set it to nil, and don't replace the map view in the outlet with a brand new map view you create (like you're doing in initializeMapView().) Both of those things will prevent your map from displaying.
You also never create a timer except in your fetchVehiclesLocation() function, which doesn't seem right.
You also don't show how you're setting up your location manager and asking for location updates. (You call a function initializeLocationManager(). I don't believe that is an Apple-provided function. I'm guessing you added it in an extension to the location manager, but you don't show that code.)
You need to ask if the user has granted permission to use the location manager, and trigger a request for permission if not.
Once you have permission to use the location manager, you need to ask it to start updating the user's location.
You don't show any of that code.
Maybe you're doing that in code you didn't show? It also looks like you don't have your CLLocationManagerDelegate methods defined correctly. I don't know of any delegate method didFindCurrentLocation(_:). You will likely need to implement one of the delegate methods locationManager(_:didUpdateLocations:) or locationManager(_:didUpdateTo:from:).
I suggest searching for a tutorial on using the location manager to display the user's location on a map. It's a little involved, and requires some study to set up.
I am building a location-based app that lists nearby coffee houses. App keeps crashing on first build on device because location keeps returning as nil.
This is because the Privacy - Location prompt isn't happening soon enough, even though though the request is earlier in the code. After I close the app after it crashes, that's when I'm prompted to allow my location.
I have three onboarding screens, and when I get to this tableviewcontroller, that's when it crashes.
If I go into Settings > Privacy > Location and manually enable location services, the app works great.
Here's my code (I removed a ton of unnecessary stuff):
import UIKit
import MapKit
import CoreLocation
class ShopTableViewController: UITableViewController, CLLocationManagerDelegate {
#IBAction func filterBack(_ sender: Any) {
getLocale()
shops.sort() { $0.distance < $1.distance }
shops.removeAll()
loadShops()
sortList()
}
//MARK: Properties
var shops = [CoffeeShop]()
var filteredShops = [CoffeeShop]()
var objects: [CoffeeShop] = []
var locationManager = CLLocationManager()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestWhenInUseAuthorization()
}
}
var currentLocation = CLLocation!.self
var userLatitude:CLLocationDegrees! = 0
var userLongitude:CLLocationDegrees! = 0
var locValue:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 1.0, longitude: 1.0)
var refresher: UIRefreshControl! = UIRefreshControl()
func getLocale() {
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.startMonitoringSignificantLocationChanges()
userLatitude = self.locationManager.location?.coordinate.latitude
userLongitude = self.locationManager.location?.coordinate.longitude
print("\(userLatitude), \(userLongitude)")
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
/// self.locationManager.requestWhenInUseAuthorization()
checkLocationAuthorizationStatus()
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
if CLLocationManager.locationServicesEnabled()
{
getLocale()
}
let locValue = self.locationManager.location?.coordinate
noHeight()
loadShops()
sortList()
print("\(locValue?.latitude), \(locValue?.longitude)")
refresher = UIRefreshControl()
refresher.addTarget(self, action: #selector(ShopTableViewController.handleRefresh), for: UIControlEvents.valueChanged)
if #available(iOS 10, *) {
shopTable.refreshControl = refresher
} else {
shopTable.addSubview(refresher)
}
}
}
What am I doing wrong?
requestWhenInUseAuthorization() is an asynchronous method, so your method that wraps it checkLocationAuthorizationStatus() is also async.
However, in your viewDidLoad, you call
checkLocationAuthorizationStatus()
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
This is triggering the locationManager to start before it is authorized. Take a look here at this (somewhat old) link http://nshipster.com/core-location-in-ios-8/
Example
Be sure to conform to CLLocationManagerDelegate
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else if CLLocationManager.authorizationStatus() == . authorizedWhenInUse {
startTrackingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
startTrackingLocation()
// ...
}
}
func startTrackingLocation() {
locationManager.startUpdatingLocation()
getLocale()
//not clear which of these methods require location
let locValue = self.locationManager.location?.coordinate
noHeight()
loadShops()
sortList()
print("\(locValue?.latitude), \(locValue?.longitude)")
}
You need to wait for the authorization response before using location services.
What you are doing now is requesting the authorization and the immediately starting location services. You need to be sure, the app is authorized before getting location.
I've been trying to implement a LocationView Controller which constantly updates the Users Location to the Server. As i debugged the Application and while in use i noticed, that the SourceCode I provided drained my Phones Battery a lot faster than usual. Do I have to cope with this, or is there any way, without reducing the LocationRequirements, to improve the PowerUsage in the following Code?
class LocationViewController: UIViewController {
// MARK: - Outlets
#IBOutlet var mapView: MKMapView!
fileprivate var locations = [MKPointAnnotation]()
var updatesEnabled: Bool = false;
var defaultCentre: NotificationCenter = NotificationCenter.default
//- NSUserDefaults - LocationServicesControl_KEY to be set to TRUE when user has enabled location services.
let UDefaults: UserDefaults = UserDefaults.standard;
let LocationServicesControl_KEY: String = "LocationServices"
public var lastPosition: Date?;
public lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.distanceFilter = 20;
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}()
public var routeLine: MKPolyline = MKPolyline() //your line
public var routeLineView: MKPolylineView = MKPolylineView(); //overlay view
// MARK: - Actions
#available(iOS 9.0, *)
#IBAction func enabledChanged(_ sender: UISwitch) {
if sender.isOn {
self.UDefaults.set(true, forKey: self.LocationServicesControl_KEY)
locationManager.startUpdatingLocation()
locationManager.allowsBackgroundLocationUpdates = true
self.updatesEnabled = true;
} else {
self.UDefaults.set(false, forKey: self.LocationServicesControl_KEY)
locationManager.stopUpdatingLocation()
self.updatesEnabled = false;
self.timer.invalidate()
locationManager.allowsBackgroundLocationUpdates = false
}
}
#IBAction func accuracyChanged(_ sender: UISegmentedControl) {
let accuracyValues = [
kCLLocationAccuracyBestForNavigation,
kCLLocationAccuracyBest,
kCLLocationAccuracyNearestTenMeters,
kCLLocationAccuracyHundredMeters,
kCLLocationAccuracyKilometer,
kCLLocationAccuracyThreeKilometers]
locationManager.desiredAccuracy = accuracyValues[sender.selectedSegmentIndex];
}
// MARK: - Override UIViewController
override func viewDidLoad() {
mapView.delegate = self;
}
}
// MARK: - MKMapViewDelegate
extension LocationViewController: MKMapViewDelegate {
func addRoute() {
mapView.remove(self.routeLine);
var pointsToUse: [CLLocationCoordinate2D] = [];
for i in locations {
pointsToUse += [i.coordinate];
}
self.routeLine = MKPolyline(coordinates: &pointsToUse, count: pointsToUse.count)
mapView.add(self.routeLine)
}
}
// MARK: - CLLocationManagerDelegate
extension LocationViewController: CLLocationManagerDelegate {
func isUpdateValid (newDate: Date) -> Bool{
var interval = TimeInterval()
if(lastPosition==nil){lastPosition=newDate}
interval = newDate.timeIntervalSince(lastPosition!)
if ((interval==0)||(interval>=self.UpdatesInterval)){
return true
} else {
return false
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let mostRecentLocation = locations.last else {
return
}
if(isUpdateValid(newDate: mostRecentLocation.timestamp)){
let position = MKPointAnnotation();
position.coordinate=mostRecentLocation.coordinate;
self.locations.append(position);
self.addRoute();
self.locationAPI.postLocation(location: mostRecentLocation)
lastPosition=mostRecentLocation.timestamp
}
}
}
You need to limit the time when you have the GPS "lit" somehow. If you've got it set to give high accuracy GPS readings constantly, it's going to keep the GPS powered up and you're going to use a lot of power. There's nothing to be done about that.
Possibilities:
You could wake up the GPS once every 10 minutes, run it until you get a good reading, then turn it back off.
You get a good reading when your app first starts, then subscribe to significant location changes, and when you get an update, start location updates, get a new fix, and then turn off location updates again.
I'm not sure how you'd do the first option since Apple doesn't let your app run indefinitely in the background. You'd need your app to be awake and running in the foreground the whole time, which also drains the battery a lot faster than if it is allowed to go to sleep.
I'm trying to set a variable/constant in Swift using a value from a function which finds the user's current location whilst the app is open. When I uncomment the print function it successfully prints the coordinates but I can't access the value outside of the function.
I currently have this as a part of my code which is in the MainVC.swift file as part of the MainVC class:
import UIKit
import MapKit
import CoreLocation
class MainVC: UIViewController, CLLocationManagerDelegate {
//Map
#IBOutlet weak var map: MKMapView!
let manager = CLLocationManager()
let coordinates = locationManager()
//I've also tried using let coordinates = currentCoords
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) -> String {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
lat = String(myLocation.latitude)
long = String(myLocation.longitude)
let currentCoords:String = "\(lat);\(long)"
//print(currentCoords)
self.map.showsUserLocation = true
return currentCoords
}
}
However I'm faced with this error:
"Cannot use instance member 'locationManager' within property initializer; property initializers run before 'self' is available".
When I change the code and use
let coordinates = currentCoords
I get another error showing:
"Use of unresolved identifier 'currentCoords'"
I have also tried using a lazy var and lazy let.
You should just be able to access locationManager.location?.coordinate as in my buttonClick method :
import UIKit
import MapKit
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
let locationManager = CLLocationManager()
#IBAction func buttonClick(_ sender: Any) {
let location = locationManager.location?.coordinate
let lat:Double = (location?.latitude)!
let lon:Double = (location?.longitude)!
print(lat,lon)
}
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
locationManager.delegate = self
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear")
// status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
// authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
showAlert("Location services were previously denied. Please enable location services for this app in Settings.")
}
// we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("didUpdateLocations")
print(locations.last?.coordinate)
}
func showAlert(_ title: String) {
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
This is how you define your public variable
let coordinates:CLLocation = nil
Then in your didUpdateLocations assign the coordinates variable with last element of locations array. Then you'll have access to it everywhere.
Keep in mind didUpdateLocations may be called even up to a few seconds later so your public variable coordinates will be populated after may be up to a few seconds or so.
func coordinatesBecomeAvailable() {
// Here you can print your coordinates or start using them for the first time
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let firstTime:Bool = (coordinates == nil)
coordinates = locations[0]
if (firstTime) // This is first time this function is called
coordinatesBecomeAvailable()
......
< Rest of your code here >
......
< This function does have any return value! no String nothing >
}
You just need to check whether the value of coordinates is nil or not.
Or you can check inside the didUpdateLocations whether this is the first time this delegate method is called.
This is not the most efficient and completed way of initializing and using location manager and I assume you just posted the code related to your problem.
I'm making an app for one of my courses. It is supposed to track the distance traveled and update a label to show how far they've gone. When I open the app it asks for permission to track location. The mapView works, it follows the location but the label is never updated to show the distance traveled. I've added my code below, any help is greatly appreciated!
//
// ViewController.swift
// location_tracker
//
// Created by Dale McCaughan on 2016-10-19.
// Copyright © 2016 Dale McCaughan. All rights reserved.
//
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var theMap: MKMapView!
#IBOutlet weak var l: UILabel!
let locationManager = CLLocationManager()
var startLocation: CLLocation!
var monitoredRegions: Dictionary<String, NSDate> = [:]
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
self.navigationController?.isToolbarHidden = false;
//Status bar style and visibility
UIApplication.shared.statusBarStyle = .lightContent
//Change status bar color
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
//if statusBar.respondsToSelector("setBackgroundColor:") {
statusBar.backgroundColor = UIColor.white
//}
UIToolbar.appearance().backgroundColor = UIColor.white
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the Location Manager
locationManager.delegate = self;
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//Setup the Map View
theMap.delegate = self
theMap.showsUserLocation = true
theMap.userTrackingMode = .follow
// setup test data
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
// authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
showAlert("Location services were previously denied. Please enable location services for this app in Settings.")
}
// we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.startUpdatingLocation()
}
}
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.red
circleRenderer.lineWidth = 1.0
return circleRenderer
}
#IBAction func resetDistance(_ sender: AnyObject) {
startLocation = nil
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
showAlert("enter \(region.identifier)")
monitoredRegions[region.identifier] = Date() as NSDate?
l.text = "in location manager1"
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
showAlert("exit \(region.identifier)")
monitoredRegions.removeValue(forKey: region.identifier)
l.text = "in location manager2"
}
var lastLocation: CLLocation!
var traveledDistance:Double = 0
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {
if let firstLocation = locations.first as? CLLocation
{
theMap.setCenter(firstLocation.coordinate, animated: true)
let region = MKCoordinateRegionMakeWithDistance(firstLocation.coordinate, 1000, 1000)
theMap.setRegion(region, animated: true)
if let oldLocation = lastLocation {
let delta: CLLocationDistance = firstLocation.distance(from: lastLocation)
traveledDistance += delta
}
lastLocation = firstLocation
}
l.text = String(format: "%.3f", traveledDistance/1000) + " kilometers"
}
// MARK: - Helpers
func showAlert(_ title: String) {
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try this:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let firstLocation = locations.first
The delegate signature that you were using wasn't quite what the delegate was looking for. With the signature corrected, you don't need the as CLLocation cast anymore.
I verified that it works with that change. For future reference, you could set a breakpoint at didUpdateLocations and you would see right away that it wasn't getting called and then work backwards from there.
Also make sure that Info.plist contains all the necessary location-related Privacy strings (I usually just put in all three to cover all the bases).