Core Location in Swift - swift

This is sample code. NSLocationAlwaysUsageDescription key in Info.plist installed. All work fine - program receives the coordinates.... displays them.....
And it continues to update them permanently! In the case of the iOS simulator - it is not critical, but in the case of a real application it is very quickly drain the battery. How to make an application launched Core Location, received location, and got shut down Core Location?
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet var locLabel: UILabel
#IBOutlet var latLabel: UILabel
#IBOutlet var lonLabel: UILabel
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func locButton(sender: AnyObject) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager:CLLocationManager, didUpdateLocations locations:AnyObject[]) {
println("locations = \(locationManager)")
var latValue = locationManager.location.coordinate.latitude
var lonValue = locationManager.location.coordinate.longitude
latLabel.text = String(latValue)
lonLabel.text = String(lonValue)
locLabel.text = "success"
}
}

If you are not much concern about high level of accuracy then you should consider startMonitoringSignificantLocationChanges instead of startUpdatingLocation. It will really make a big difference in battery draining.
This method is more appropriate for the majority of applications that just need an initial user location fix and need updates only when the user moves a significant distance. This interface delivers new events only when it detects changes to the device’s associated cell towers, resulting in less frequent updates and significantly lower power usage.
For more detail you can take a look over CLLocationManager Guide

Related

iPhone app not recording "visits" using CLLocationManager delegate

I'm relatively new to Swift and self-taught, and I'm currently having issues with recording Visits using a swift app.
Relevant parts of AppDelegate:
import CoreLocation
class AppDelegate: NSObject, UIApplicationDelegate {
let locationManager = LocationManager.shared
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
locationManager.startMonitoringLocationVisits()
print("AppDelegate just ran")
return true
}
}
Relevant parts of LocationManager:
import CoreLocation
class LocationManager : NSObject, ObservableObject, CLLocationManagerDelegate {
static let shared = LocationManager()
//New location manager
var locationManager = CLLocationManager()
//Seconds between location acquisitions in background
var locationInterval = 120
override init() {
super.init()
locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.activityType = .fitness
self.locationManager.allowsBackgroundLocationUpdates = true
}
}
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
print("received a visit")
//Code that sends a notification every time a visit is recorded
print("attempting to save visit")
self.saveVisit(visit: visit) //Code that saves details from the visit to CoreLocation
}
public func startMonitoringLocationVisits() {
locationManager.startMonitoringVisits()
}
The app compiles and runs fine on my device (iPhone 13 pro running iOS 16) but never records any visits. I've tried an example app I found online and that works correctly on my device, so it must be an issue with my code, but I can't figure out what. I also have a background refresh task recording location coordinates every few minutes in the same app that works correctly, and I'm hesitant to start radically changing location manager code to debug the Visits feature since I'm worried I would break the background task feature. Any advice or direction is appreciated!

When/ How does swift call user created functions

Im currently learning how to code using swift. I created an app that simple displays the users location using map view.
In this code block, when does swift call the function locationManager()? I seem to have never called this function, yet it is running all the logic within it
import UIKit
import MapKit
class ViewController: UIViewController,
CLLocationManagerDelegate{
#IBOutlet weak var map: MKMapView!
#IBOutlet weak var AreaLbl: UILabel!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
let userLocation = locations.last
let viewRegion = MKCoordinateRegion(center: (userLocation?.coordinate)!, latitudinalMeters: 600, longitudinalMeters: 600)
self.map.showsUserLocation = true
self.map.setRegion(viewRegion, animated: true)
}
If there are any resources you recommend for learning swift please let me know!
First, a point of order. In Swift, the name of a function includes parameters. Your function isn't named locationManager(). It's named locationManager(_:didUpdateLocations:) (One unnamed parameter and one parameter named "didUpdateLocations".)
Now to your question.
iOS and Mac OS make pretty common use of the delegate design pattern. In that pattern, one object sets itself up to be another object's "delegate". That basically means that the delegate object agrees to wait by the phone for incoming phonecalls.
When you are an object's delegate, you agree to respond to a set of messages that might be sent to you (or functions that might be called.) You "conform to a protocol" that defines the messages you need to respond to. Some of those messages might be required and some might be optional.
It's not Swift calling your function, it's the OS. Specifically the location manager.
Your viewDidLoad() function includes the line locationManager.delegate = self.
That makes you the location manager's delegate. That means that you conform to the CLLocationManagerDelegate protocol. It tells the location manager that you are ready to accept messages (function calls) from the location manager that are defined by that protocol.
When you do that, and then you tell the location manager to start updating your location with the line locationManager.startUpdatingLocation(), the location manager will start calling your locationManager(_:didUpdateLocations:) function at some future time when it detects a change to the device's location.
That is what is causing your locationManager(_:didUpdateLocations:) function to be called.

UILabel will not update to show fetched location coordinates

So I'm just trying to do a simple test in Xcode, where the app will fetch the user's current location and display the coordinates on screen. This can then be updated by pressing a 'Fetch Location' button.
The app doesn't seem to be fetching any coordinates (the UILabel only ever displays default text).
It's just a single-page app. And yes, the #IBOutlet and #IBAction are correctly linked.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var labelLocation: UILabel!
var locationManager = CLLocationManager()
var myPosition = CLLocationCoordinate2D()
#IBAction func fetchLocation(_ sender: Any) {
locationManager.startUpdatingLocation()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
print("Got Location \(newLocation.coordinate.latitude), \(newLocation.coordinate.longitude)")
myPosition = newLocation.coordinate
locationManager.stopUpdatingLocation()
labelLocation.text = "Got Location \(newLocation.coordinate.latitude), \(newLocation.coordinate.longitude)"
}
}
Step 1:
Enable the Privacy - Location When In Use Usage Description in your info.plist file
Go to your info.plist file
Click on + on Information property list and type Privacy - Location When In Use Usage Description
In the value type why you want to request the users location (this will be shown to the user)
Step 2:
Get the location
Use this code:
guard let latitude = manager.location?.coordinate.latitude, let longitude = manager.location?.coordinate.longitude else { return }
locationManager.stopUpdatingLocation()
labelLocation.text = "Got Location \(latitude), \(longitude)"
Ok, I think that you can resolve it adding the "Privacy - Location When In Use Usage Description" key into Info.plist, this is an important step in Location topic.
Regards!

CLLocationManager and tvOS - RequestWhenInUseAuthorization() not prompting

I seem to be having a little trouble getting tvOS to prompt the user for location data authorization. I literally copied and pasted working code from iOS and it seems to not be prompting the user. I am using the code listed below as well as the NSLocationWhenInUseUsageDescription key with a string value. The only difference in the api that I see is on iOS it uses startupdatinglocation() and on tvOS it uses requestLocation() (similar to watchOS) I've stepped through the problem and it is infact hitting requestWhenInUseAuthorization() but simply doesn't prompt the user.
Any idea what is going on?
import UIKit
import CoreLocation
class ViewController: UIViewController {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if CLLocationManager.locationServicesEnabled(){
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.NotDetermined{
locationManager.requestWhenInUseAuthorization()
}
else if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedWhenInUse{
locationManager.requestLocation()
}
}
}
It turns out that a CFBundleDisplayName key and $(PRODUCT_NAME)value is needed in order for the prompt to display.

How do I save annotations?

I have my code so it puts an annotation on the map each time the user clicks a button, but when the user closes out of the app, the annotation disappears. How do I make it so the annotations stays on the map even when the user closes the app? Below is my code:
import UIKit
import CoreLocation
import MapKit
class UpdateCar: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var lblLocation: UILabel!
var locationManager = CLLocationManager()
var myPosition = CLLocationCoordinate2D()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
println("Updating Car Location \(newLocation.coordinate.latitude) , \(newLocation.coordinate.longitude) ")
myPosition = newLocation.coordinate
locationManager.stopUpdatingLocation()
lblLocation.text = "\(newLocation.coordinate.latitude) , \(newLocation.coordinate.longitude)"
}
#IBAction func findUserLocationAndDropPin(sender: UIButton) {
var userLocationCoordinates = CLLocationCoordinate2DMake(locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude)
var pinForUserLocation = MKPointAnnotation()
pinForUserLocation.coordinate = userLocationCoordinates
mapView.addAnnotation(pinForUserLocation)
mapView.showAnnotations([pinForUserLocation], animated: true)
}
}
You have to save it in a persistent store.
Few options:
CoreData, the native way of saving data, recommended, not too easy
NSUserDefaults, usually thought for small stuff, also native, not recommended, very easy though
Another API for managing a persistent store like Realm (similar to CoreData, a little easier but not native)
//when I need to save for example, the last date on which the user login my app will use the setObject function, this will save a value ("10/05/2015") in the "lastlogin" key
var lastLogin = "10/05/2015"
NSUserDefaults.standarUserDefaults().setObject(lastLogin, forkey: "lastLogin")
//And when I need to retrieve the stored value in the "lastlogin" key which I use is "objectForKey" function
NSUserDefaults.standarUserDefaults().objectForKey("lastLogin")
see following link:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html#//apple_ref/occ/instm/NSUserDefaults/setObject:forKey: