Geofence didExitRegion is never called - swift

I'm trying to implement Geofencing into an app I'm working on. currently, I'm unable to pass the
desired output to the console because didExitRegion is never called.
What might be the cause of this and how may I go about solving it?
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var locationManager = CLLocationManager()
func setupManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
//locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
// Set Geofencing region
let geofencingRegion: CLCircularRegion = CLCircularRegion(center: CLLocationCoordinate2DMake(30.510074, 114.330510), radius: 100, identifier: "Wuhan")
geofencingRegion.notifyOnExit = true
geofencingRegion.notifyOnEntry = true
// Start monitoring
locationManager.startMonitoring(for: geofencingRegion)
}
func makeUIView(context: Context) -> MKMapView {
setupManager()
let mapView = MKMapView(frame: UIScreen.main.bounds)
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
class MapAppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
return true
}
}
extension MapAppDelegate{
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Hello World")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Welcome Home")
}
}
The output I'm getting is multiple iterations of
2020-12-11 18:41:29.916937+0800 Geofencing[4225:235542] [VKDefault] Style Z is requested for an invisible rect
I want to create a state to check whether the user has entered the area or left the area to pull other view controllers on my MainView so I'm starting here.

The reason didExitRegion is never called is that you are not setting the CLLocationManager delegate.
In MapView you are creating an instance of CLLocationManager and then calling .startMonitoring on this instance. But you never set the delegate on this instance.
True, you are setting the delegate on the instance you created in your MapAppDelegate, but that is not the instance on which you are calling .startMonitoring.
You should refactor your code in such a way that you only create a single instance of CLLocationManager, and be sure to set the delegate on that instance.

Related

Why my didUpdate userLocation method doesn't get called?

I have a MapKit in my view, and I want to access the userLocation data. However, the actual function
func firstMapView(_ firstMapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation){
print("it never gets called")
}
never ends up being called.
The whole code looks like this:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{
#IBOutlet weak var firstMapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
firstMapView.delegate = self
}
func firstMapView(_ firstMapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation){
print("it never gets called")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
print("Authorized when in use detected")
locationManager.startUpdatingLocation()
firstMapView.showsUserLocation = true
}
}
}
When I run the code I get the debug message: print("Authorized when in use detected")
I saw this question, and tried to copy the things, but it didn't help.
Use this delegate function
func mapView(mapView: MKMapView!, didUpdateUserLocation userLocation: MKUserLocation!) {
print(userLocation)
}
delete this one
func firstMapView(_ firstMapView: MKMapView, didUpdateUserLocation userLocation: MKUserLocation){
print("it never gets called")
}

Wrong initial user location in MapView gps

I want to get the user location, that's what I did:
I set locationManager in my ViewController:
var locationManager = CLLocationManager()
func setUpLocationManager() {
locationManager.delegate = self
locationManager.distanceFilter = 5
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
mapView?.showsUserLocation = true
locationManager.startUpdatingLocation()
}
Here is the function that is called once the location is updated:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if !locations.isEmpty {
print("\(locations[0])")
}
}
}
It prints the initial location of the user position (see image), but I consider it wrong, because it an approximation.
I also tried with:
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
let accuracy = userLocation.location?.horizontalAccuracy
if accuracy == locationManager.desiredAccuracy {
print("\(locations[0])")
}
}
But it never prints the location because locationManager.desiredAccuracy is equal to -2 and accuracy is always > 0.
This what I see on the mapView:
Initially the user pin shows a position near to the real position (but it is not right), subsequently the user pin moves to the real user position.
I want the function didUpdateLocations to be called when the user pin is on the right position.
My question is similar to this, but it was in objective-c.
Any hints? Thanks
ref. MKMapView's user location is wrong on startup or resume
Swift version:
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
let accuracy = userLocation.location?.horizontalAccuracy
if accuracy ... {
}
}

Make custom location annotations in using Mapbox

I am using Mapbox to create my app. When I click a button I would like it to mark my current location and add a marker to it as it currently does. I then would like the abiilty to tap on this marker and have it display the current location information such as the adress of the marked point.
Right now all I have is...
https://imgur.com/a/RSx0G
I would like to note that I am using Xcode 9.1 and Swift 4. Thank you for all your feedback in advance.
Currently the swift file looks like...
import Foundation
import UIKit
import CoreLocation
import Mapbox
import MapKit
import MapboxGeocoder
class SecondViewController: UIViewController, CLLocationManagerDelegate, MGLMapViewDelegate, UITextFieldDelegate {
let geocoder = Geocoder.shared
let dismissesAutomatically: Bool = false
let isAnchoredToAnnotation: Bool = true
weak var delegate: MGLCalloutViewDelegate?
let tipHeight: CGFloat = 10.0
let tipWidth: CGFloat = 20.0
#IBOutlet var mapView: MGLMapView!
let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func markStuff(_ sender: Any) {
}
#IBAction func refLocation(_ sender: Any) {
manager.startUpdatingLocation()
}
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
return nil
}
func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation)
{
print("tap on callout")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
mapView.setCenter(center, zoomLevel: 10, animated: true)
let annotation = MGLPointAnnotation()
annotation.coordinate = location.coordinate
mapView.selectAnnotation(annotation, animated: true)
annotation.title = "Testing"
annotation.subtitle = "\(annotation.coordinate.latitude), \(annotation.coordinate.longitude)"
self.mapView.addAnnotation(annotation)
manager.stopUpdatingLocation()
You can create a subclass of MGLUserLocationAnnotationView, then use that as your view for the MGLUserLocation annotation.
For example, if your custom subclass was called YourLocationAnnotationView(), you could do something like:
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {This custom view is created below.
if annotation is MGLUserLocation && mapView.userLocation != nil {
return YourLocationAnnotationView()
}
return nil
}
}
For a complete implementation, see this example from the official documentation.

Swift: Trying to get coordinates, but don't know how to call locationManager() function

I am trying to get the users latitude and longitude.
import Foundation
import CoreLocation
class locationModule: NSObject, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
func getLocation() {
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var location: CLLocationCoordinate2D = (manager.location?.coordinate)!
print(location.latitude)
print(location.longitude)
}
}
This is code is in a seperate Swift file, So I need getLocation() to be able to call func locationManager, how would I call the locationManager function from the getLocation() function.
Your code is fine, but it's not being called, probably because you didn't add the key to your Info.plist.
For your code, add NSLocationAlwaysUsageDescription to your Info.plist, with a value such as All your base are belong to us, so I use your location whenever I want. as the String value. Then just start your locationManager somewhere like func applicationDidBecomeActive(application: UIApplication) like this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locMod:locationModule?
func applicationDidBecomeActive(application: UIApplication) {
if ( locMod == nil )
{
locMod = locationModule()
}
locMod?.getLocation()
}
The first time, you will see the prompt, then you will see the location being logged.
FYI, here's how I organized the locationModule:
class locationModule: NSObject, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
internal func getLocation() {
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
// MARK : CLLocationManagerDelegate protocol
#objc
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
print("\(location.coordinate.latitude), \(location.coordinate.longitude)")
}
}
}

Swift: location not being updated when returned from background

I have a Swift app that I am trying to update the location when the app is returned from the background, but it doesn't seem to work when returned from the background.
On launch of the app, I'll get the location just fine. After getting the location I call stopUpdatingLocation() so I don't continue to get the location:
locationManager.stopUpdatingLocation()
Then, in my AppDelegate.swift I startUpdatingLocation again:
func applicationWillEnterForeground(application: UIApplication) {
ViewController().locationManager.startUpdatingLocation()
}
This is my code so far:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println("Error while updating location " + error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var userLocation:CLLocation = locations[0] as CLLocation
println("\(userLocation.coordinate.latitude),\(userLocation.coordinate.longitude)")
locationManager.stopUpdatingLocation()
}
}
However, whenever I background the app (click home), and then return to the app the location isn't updated. Any idea what I might be doing wrong here?
In applicationWillEnterForeground, the code is creating a new, local instance of ViewController that is never displayed, does not yet have a locationManager created and so has no effect.
It is not referring to the ViewController instance that already exists and is displayed (and has the locationManager instance that was originally started).
Instead, it should get a reference to the existing instance. Assuming ViewController is the root view controller, you could do:
func applicationWillEnterForeground(application: UIApplication) {
if let rvc = window?.rootViewController as? ViewController {
rvc.locationManager.startUpdatingLocation()
}
}
However, it might be better practice to let the ViewController class itself manage its own behavior. This way, the app delegate doesn't have to find a reference to the view controller's instance and it doesn't directly access the view controller's internal state and ViewController becomes more self-contained.
In addition to the app delegate method applicationWillEnterForeground, it's possible to monitor these events from anywhere using the UIApplicationWillEnterForegroundNotification notification.
In ViewController, you can register and unregister for the notification in (for example) viewWillAppear and viewWillDisappear. When registering, you indicate which method to call for the event and everything is handled inside ViewController (and the code in applicationWillEnterForeground can be removed).
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "willEnterForegound",
name: UIApplicationWillEnterForegroundNotification,
object: nil)
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(
self,
name: UIApplicationWillEnterForegroundNotification,
object: nil)
}
func willEnterForegound() {
locationManager.startUpdatingLocation()
}