Beacons Not Detected When App ls In Foreground - ios

I have an iOS application that detects BLE beacons. The CLLocationManagerDelegate methods have been implemented in AppDelegate. Now when the application is in foreground, the didRangeBeacons gives an empty beacons array in the logs.
I already have tried explicitly starting ranging in the view controllers, but still does not work.
class AppDelegate: UIResponder, UIApplicationDelegate {
...
lazy var locationManager: CLLocationManager = {
let locationManger = CLLocationManager()
locationManger.delegate = self
return locationManger
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.startMonitoring(for: beaconRegion)
locationManager.startMonitoring(for: beaconRegion2)
}
}
...
}
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if beacons.count > 0 {
print("From Region \(region.identifier)")
} else {
print("empty")
}
}
Always prints empty when in foreground.
It should give me the nearest beacons in the region.
The weird thing is when the app is put into background, it immediately detects and shows all the beacons.

Related

Can not called locationManager(_::didUpdateLocations:)

I tried get the coordinates when user launched app.
I setted up the locationManager code in a independent file:
UserLocation.swift:
import Foundation
import CoreLocation
class UserLocation: NSObject, CLLocationManagerDelegate {
var userCurrentLocation: CLLocationCoordinate2D?
let locationManager = CLLocationManager()
func locationSetup() {
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse {
print("authorization error")
return
}
locationManager.distanceFilter = 300
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
print("startUpdatingLocation")
if CLLocationManager.locationServicesEnabled() {
print("locationServicesEnabled")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager getting start")
if let location = locations.last {
self.userCurrentLocation = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
print("print location in locationManager: \(String(describing: userCurrentLocation?.latitude))")
locationManager.stopUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?) {
print(error?.localizedDescription as Any)
return
}
}
then, I call the func in the AppDelegate.swift's application(_::didFinishLaunchingWithOptions:) like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let getUserLocation = UserLocation()
getUserLocation.locationSetup()
}
but , the only calls locationSetup() function successfully , never called the related function locationManager(_::didUpdateLocations:). the print("locationManager getting start") I putted the first line in the locationManager(_::didUpdateLocations:), never printed out
BTW, in the info.plist , I already set Privacy - Location When In Use Usage Description .
can anyone help me out ?
Your getUserLocation is a local variable. It goes out of existence 1 millisecond after you create it. So it never has time to do anything (e.g. location updating). Promote it to an instance variable so that it lives longer:
var getUserLocation : UserLocation?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.getUserLocation = UserLocation()
self.getUserLocation?.locationSetup()
return true
}
(Also please use better variable names. An object reference should not begin with a verb.)
Please cross-check simulator location: Simulator -> Debug -> Locations that shouldn't be None in your case...
Hope this will help you... Thanks :)

iOS - CLLocationManager didUpdateLocations not being called in background

I want my iOS app to update it's location when closed and make post requests but location manager didUpdateLocations doesn't get called from the background despite it working fine in normal app use. Below is my SendLocation class. Calling determineCurrentLocation() get it going.
My problem is that if I call determineCurrentLocation() from another class while the app is running - it works perfectly. But when I call it from the App Delegate didUpdateLocations never gets called.
class SendLocation: NSObject, CLLocationManagerDelegate{
var locationManager:CLLocationManager = CLLocationManager()
struct LocationStruct{
var latitude:CLLocationDegrees?, longitude:CLLocationDegrees?
}
var locationStruct = LocationStruct()
var userLocation: CLLocation? = nil
func sendLocationPost(){
// This function makes the POST
}
func determineCurrentLocation(){
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates=true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled(){
DispatchQueue.main.async {
print("test1") // This prints fine from background
self.locationManager.startUpdatingLocation()
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
print("test2") // This never gets printed from backgrounf
locationManager.stopUpdatingLocation()
userLocation = locations[0] as CLLocation
locationStruct.latitude=userLocation?.coordinate.latitude
locationStruct.longitude=userLocation?.coordinate.longitude
sendLocationPost()
}
Below are the two functions I edited in my App Delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
let sendLocation = SendLocation()
sendLocation.determineCurrentLocation()
}
My Info.plist has been changed to be correct, as far as I know.
Here is a picture of my Background Modes
And my privacy settings
Just in case I'm missing something there.
If anyone has any ideas what I've done wrong then I'd be very appreciative of some help.

How to keep Core Location and Core Bluetooth running when app is not running (killed/ terminated)?

I got stuck when trying to keep my function running if I terminate the app.
Is it possible to keep core location (geofencing / geolocating) and core bluetooth running when app is not running ? If possible how to solve my issue? I already check the background modes, and implement core location method.
this is my code :
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var viewController = ViewController()
var window: UIWindow?
var locationManager = CLLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.pausesLocationUpdatesAutomatically = false
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
}
beaconRegion.notifyEntryStateOnDisplay = true
beaconRegion.notifyOnEntry = true
beaconRegion.notifyOnExit = true
locationManager.startMonitoring(for: beaconRegion)
locationManager.stopRangingBeacons(in: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
locationManager.startUpdatingLocation()
return true
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if (region.identifier == beaconRegionIdentifier) {
manager.requestState(for: region)
goBackground()
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("you exited region")
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if (region.identifier == beaconRegionIdentifier) {
manager.stopUpdatingLocation()
switch state {
case .inside:
manager.startRangingBeacons(in: region as! CLBeaconRegion)
manager.startUpdatingLocation()
case .outside:
let delay = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: delay) {
manager.requestState(for: region)
}
case .unknown:
let delay = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: delay) {
manager.requestState(for: region)
}
}
}
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
let state = UIApplication.shared.applicationState
switch(state){
case.active,
.inactive,
.background:
let mainView = UserDefaults.standard.bool(forKey: "toMainView")
var isWakeUpRunning = UserDefaults.standard.bool(forKey: "isWakeUpRunning")
if isWakeUpRunning {
if mainView {
for aBeacon in beacons{
if aBeacon.rssi > WAKE_UP_THRESHOLD && aBeacon.rssi != 0{
UserDefaults.standard.set(false, forKey: "isWakeUpRunning")
self.viewController.startFunction()
manager.stopRangingBeacons(in: region)
}else if aBeacon.rssi != 0 && aBeacon.rssi < WAKE_UP_THRESHOLD {
manager.stopRangingBeacons(in: region)
manager.requestState(for: region)
}
}
}
}else{
manager.stopRangingBeacons(in: region)
manager.requestState(for: region)
}
}
}
func goBackground() {
let app = UIApplication.shared
var bgTask: UIBackgroundTaskIdentifier = 0
bgTask = app.beginBackgroundTask(expirationHandler: {
NSLog("Expired: %lu", bgTask)
app.endBackgroundTask(bgTask)
})
NSLog("Background: %lu", bgTask)
}
}
From my code, it can run in both in foreground and background. but when i swipe up the app from task switcher, it cannot work. and i cannot see the log too from xcode. Any ideas to help me ?
Once you swipe up the app from the task switcher your app will be killed, but CoreLocation will still be working for you at the OS level, looking for beacons that match your region:
locationManager.startMonitoring(for: beaconRegion)
Once there is a change on that region (either a didEnter or a didExit), then your app will be re-launched into the background. At this time the following methods will be called in order:
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)
locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion)
At that time your app will continue running in the background. Because your app starts a background task, this will continue for 180 seconds before being suspended.
However, your app will not run between the time it is swiped off the screen and when the next region transition happens. This is the best you can do.

How to disable location service programmatically in iOS?

I have the following:
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
...
let locationManager = CLLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.distanceFilter = 3000.0
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("my location: \(locations.first?.coordinate)")
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
}
}
But even when the app is turned off i see the location service icon in status bar:
I wonder if there is a way to disable it programmatically. Thanks in advance!
In your plist file, you must make sure the location row is Privacy - LocationWhenInUseUsageDescription not Privacy - LocationAlwaysUsageDescription

iOS background mode location update - manager doesn't update in background mode

I just followed http://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial location update part.
But manager doesn't print location info in background mode.
Then manager print logs to Xcode's console when app enter foreground.
Is this code right?
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var manager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
return true
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
if UIApplication.sharedApplication().applicationState != .Active {
NSLog("App is backgrounded. New location is %#", newLocation)
}
}
.....
}
I got answer for myself.
If your app running on iOS 9.0 or above and want to run your location service app on background,
you have to allowsBackgroundLocationUpdates variable set to true.
so here is my code.
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var manager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
if #available(iOS 9.0, *){
manager.allowsBackgroundLocationUpdates = true
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
if UIApplication.sharedApplication().applicationState != .Active {
NSLog("App is backgrounded. New location is %#", newLocation)
}
}
.....
}

Resources