Get location updates in background - only for Country change iOS - ios

What is the best way in core location to get background location updates only when there is change in country?

You can use the reverseGeocodeLocation of the CLGeocoder to get the current country for your location.
func locationManager(_ manager: CLLocationManager, didUpdateLocations objects: [CLLocation]) {
let location = objects.last!
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location!) { (places, error) in
if(error == nil){
if(places != nil){
let place: CLPlacemark = places![0]
let country = place.country
// do something if its changed
}
} else {
//handle error
}
But the issue will be you need to be monitoring location for this to happen. You can use startMonitoringSignificantLocationChanges as one option or you could set desired accuracy to something big like kCLLocationAccuracyThreeKilometers both of which will reduce the amount of power used by location updates.

What you are looking for is called geofencing, there are great articles about it. Any way you'll need to implement functions like
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) (CLLocationManagerDelegate)
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) (CLLocationManagerDelegate)
open func requestAlwaysAuthorization() (CLLocationManager)
func startMonitoring(for region: CLRegion) (CLLocationManager)

Related

Catch every moment when user change his location

I am using Google Map SDK. I want to catch every moment when user changes his location, maybe from CLLocationManagerDelegate. Can anyone say which method can execute previous job?
I tried to use func locationManager( _ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) but it doesnot work well. When I run application, after 3-4 seconds this method call then If I change my location it doesnot do nothing.
Did you call startUpdatingLocation()?
Suppose that the below code is your CLLocationManager.
lazy var locationMgt: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
manager.distanceFilter = 1 // 1 meter
manager.activityType = .automotiveNavigation
manager.allowsBackgroundLocationUpdates = true
#if os(iOS)
manager.pausesLocationUpdatesAutomatically = false
#endif
manager.delegate = self
return manager
}()
Then ask for permission and start updating the location:
locationMgt.requestWhenInUseAuthorization()
// you can call startUpdatingLocation() inside this function or call it right after requestWhenInUseAuthorization()
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationMgt.startUpdatingLocation()
}
Then your delegate implementation:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let last = locations.last else { return }
// here user's last location is available.
}

How to call "didUpdateLocation" method from viewDidLoad?

I am making a weather app which gets temperature info by sending current coordinates. So I am using CLLocationManager. I haven't make any HTTP request yet. I would like to see "Location Unavailable" on my screen but it is not working.
I am following a tutorial and wrote exactly same code but in tutorial it is working. In debug session, it doesn't jump to didFailwithError or didUpdateLocations methods.I added all necessary things in my
info.plist and my phone's location services are open. According to my research locationManager.startUpdatingLocation() should call automatically
these methods but when I run my app nothing appears on UI.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("you got the location")
}
//Write the didFailWithError method here:
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
cityLabel.text = "Location Unavaiable"
}
And
let locationManager = CLLocationManager ()
override func viewDidLoad() {
super.viewDidLoad()
//TODO:Set up the location manager here.
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
You are updating location too soon, in fact before you get the permission from user.
After you ask for permission
locationManager.requestWhenInUseAuthorization()
Delegate method will call
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
Now its time to start updates. Guarding the status is a good idea since you don't want to start updating unless user permits it.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else { return }
manager.startUpdatingLocation()
}
Now you'll get location updates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
}

didEnterRegion & didDetermineState not being called when the app is not running while implementing Geofencing without iBeacon

I am trying to implement Geofencing with out iBeacon. The
didStartMonitoringFor is getting called, but
didEnterRegion & didDetermineState not being called when the app is not running.
I am calling the requestState in didStartMonitoringFor. So the didDetermineState is being called for the first time. But not getting called while location changes. Can some one help me ?
Thanks in advance !
Thank God ! I got the answer by myself.
I have been doing all declaration and delegate method implementation in Home screen classs. I changed all part to AppDelegate class. Also made some changes to properties for location manager as
locationManager.delegate = self
locationManager.activityType = .automotiveNavigation
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = 10.0
locationManager.requestAlwaysAuthorization()
Also implemented both delegate methods
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion){
manager.requestState(for: region)
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == .inside
{
addNotification(region: region)
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
addNotification(region: region)
}
And it worked !

Display "Cannot find iBeacon" message

My question is very simple. I would like to display an error message i.e. "Cannot find iBeacon" if iBeacon monitoring fails, after calling startSearchingForSessions via a button press after being called in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
if self.locationManager.responds(to: #selector(CLLocationManager.requestWhenInUseAuthorization)) {
self.locationManager.requestWhenInUseAuthorization()
}
self.locationManager.delegate = self
self.locationManager.pausesLocationUpdatesAutomatically = false
let uuid = UUID(uuidString: "869A6E2E-AE14-4CF5-8313-8D6976058A7A")
self.beaconRegion = CLBeaconRegion(proximityUUID: uuid!, identifier: "com.dejordan.myapp"
startSearchingForSessions()
}
func startSearchingForSessions() {
// Start looking for the beacons with that UUID and Identifier.
self.locationManager.startMonitoring(for: self.beaconRegion)
self.locationManager.startRangingBeacons(in: self.beaconRegion)
self.locationManager.startUpdatingLocation()
}
And handling the found beacons thusly:
// Required by the Location Manager.
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
self.locationManager.startRangingBeacons(in: self.beaconRegion)
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == CLRegionState.outside {
print("Cannot Find Beacon")
}
}
// Required by the Location Manager.
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
self.locationManager.stopRangingBeacons(in: self.beaconRegion)
}
// This is called if any beacons are found.
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
var result = Array<CLBeacon>()
for beacon in beacons {
result.append(beacon)
}
foundBeacons = result
// If we found any, we need to see
// what class they belong to based on information
// from Parse.
self.identifyFoundBeacons()
// We can stop looking for beacons now.
self.locationManager.stopMonitoring(for: self.beaconRegion)
self.locationManager.stopRangingBeacons(in: self.beaconRegion)
self.locationManager.stopUpdatingLocation()
}
I have implemented the delegate error methods in an attempt to find where this occurs but thus far in navigating the mounds of documentation on iBeacon I have come up fruitless.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location manager failed: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
print("Failed monitoring region: \(error.localizedDescription)")
}
Thank you!
If you simply want to know when beacons are not detected (vs. when there was a low-level failure to look for beacons), then simply use the following delegate method:
public func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == CLRegionState.outside {
print("Cannot find beacon")
}
}
Interestingly enough, didRangeBeacons in the delegate method
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion)
gets called with an empty array of [CLBeacon]s, wherein I can use the beacons array size to determine whether or not any beacons were found.
Not what I expected, but this has solved my problem!

How can I listen for delegate response from another function?

I want to have a function called requestData, which will get the user's current location, and then performing a URL-request. I need the requestData-function to have a callback for when the request is completed, wether or not it succeeded. This is what I have come up with so far:
requestData(_ completion:#escaping ()->()){
self.locationManager.requestLocation()
// Wait for the location to be updated
let location:CLLocation? = //myLocation
self.performRequest(with: location, completion: completion)
}
func performRequest(with location:CLLocation?, completion:#escaping ()->()){
//Does the URL-request, and simply calls completion() when finished.
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {//Success}
else{//Error}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{//Error}
My thoughts are to call requestData, it will request myLocation, and then call performRequest. But CLLocationManager does requestLocation with delegate-callbacks rather than a block. How should I do this?
Everything would've been great if requestLocation would've been like this:
self.locationManager.requestLocation( { (locations, error) in
if locations {}
else {}
})
But it's not..
To clarify, this is code in a widget (TodayExtension), which, as I understand, needs the callback, as I need the widgetPerformUpdate(completionHandler:) to wait for my own completionHandler before triggering its own.
The CLLocation contains several data to check the accuracy and time for the location you get. A solution for you could be to check the accuracy of this data before you make the request to performRequest().
Check out the documentation for the CLLocation and look at my pseudo code below. In de didUpdateLocations you can get an idea of what I think might be a solution for you. I wrote it directly in SO so don't hate on the errors.
But basically use:
var horizontalAccuracy: CLLocationAccuracy { get }
var verticalAccuracy: CLLocationAccuracy { get }
var timestamp: Date { get }
let location:CLLocation? //myLocation
func requestData(){
self.locationManager.requestLocation()
// Wait for the location to be updated
}
func performRequest(with location:CLLocation?, completion:#escaping ()- >()){
//Does the URL-request, and simply calls completion() when finished.
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
//When the location is accurate enough we can call
//the performRequest()
if location.horizontalAccuracy < 15 && location.timestamp > (Date().timestampSince1970 - 60){
//Accurate enough?? Then do the request
self.performRequest(with: location, completion: completion)
}else{
//Not accurate enough...wait to the next location update
}
}
else{//Error}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{//Error}

Resources