ewizard
ewizard

Reputation: 2862

CLLocationManager not monitoring regions

I am perplexed...I had this working today and now I don't know what is going on. It isn't my iBeacon because I can locate it with the Locate iOS app. I am creating my own location manager, not sure if you would call it a subclass exactly because I am subclassing NSObject. Here it is:

//
//  LocationManager.swift
//  onebeacon
//
//  Created by Eamon White on 2/24/18.
//  Copyright © 2018 EamonWhite. All rights reserved.
//

import Foundation
import CoreLocation

protocol LocationManagerDelegate: class {
    func locationManagerDidUpdateLocation(_ locationManager: LocationManager, location: CLLocation)
    func locationManagerDidUpdateHeading(_ locationManager: LocationManager, heading: CLHeading, accuracy: CLLocationDirection)
    func locationManagerDidEnterRegion(_ locationManager: LocationManager, didEnterRegion region: CLRegion)
    func locationManagerDidExitRegion(_ locationManager: LocationManager, didExitRegion region: CLRegion)
    func locationManagerDidDetermineState(_ locationManager: LocationManager, didDetermineState state: CLRegionState, region: CLRegion)
    func locationManagerDidRangeBeacons(_ locationManager: LocationManager, beacons: [CLBeacon], region: CLBeaconRegion)
}


class LocationManager: NSObject, CLLocationManagerDelegate {
    private var locationManager: CLLocationManager!
    weak var delegate: LocationManagerDelegate?
    var beaconsToRange: [CLBeaconRegion]
    var currentLocation: CLLocation!

    override init() {
        self.beaconsToRange = []
        super.init()

        self.locationManager = CLLocationManager()
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        self.locationManager.distanceFilter = kCLDistanceFilterNone
        self.locationManager.headingFilter = kCLHeadingFilterNone
        self.locationManager.pausesLocationUpdatesAutomatically = false
        self.locationManager.delegate = self

        self.enableLocationServices()
    }

    func enableLocationServices() {
        self.checkStatus(status: CLLocationManager.authorizationStatus())
    }

    func checkStatus(status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            // Request when-in-use authorization initially
            locationManager.requestAlwaysAuthorization()
            break

        case .restricted, .denied:
            // Disable location features
            print("send an alert that the app will not function")
            break

        case .authorizedWhenInUse:
            locationManager.requestAlwaysAuthorization()
            // Enable basic location features
            break

        case .authorizedAlways:
            locationManager.startUpdatingLocation()
            locationManager.startUpdatingHeading()
            self.monitorBeacons()
            // Enable any of your app's location features
            break
        }
    }

    func monitorBeacons() {
        print("monitorBeacons()")
        if CLLocationManager.isMonitoringAvailable(for:
            CLBeaconRegion.self) {
            print("monitorBeacons().monitoringIsAvailable")
            // Match all beacons with the specified UUID
            let proximityUUID = UUID(uuidString:
                "12345678-B644-4520-8F0C-720EAF059935")

            let beaconRegion = CLBeaconRegion(
                proximityUUID: proximityUUID!,
                major: 0x0001,
                minor: 0x0002,
                identifier: "iBeacon")

            beaconRegion.notifyEntryStateOnDisplay = true;

            self.locationManager?.startMonitoring(for: beaconRegion)
            print("\(String(describing: self.locationManager?.monitoredRegions)) + monitoredRegions")
        }
    }

    //MARK: - CLLocationManagerDelegate

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        for location in locations {
            self.delegate?.locationManagerDidUpdateLocation(self, location: location)
        }

        self.currentLocation = manager.location
    }

    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
        self.delegate?.locationManagerDidUpdateHeading(self, heading: newHeading, accuracy: newHeading.headingAccuracy)
    }

    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        if region is CLBeaconRegion {
            // Start ranging only if the feature is available.
            if CLLocationManager.isRangingAvailable() {
                locationManager?.startRangingBeacons(in: region as! CLBeaconRegion)

                // Store the beacon so that ranging can be stopped on demand.
                beaconsToRange.append(region as! CLBeaconRegion)
            }
        }
        self.delegate?.locationManagerDidEnterRegion(self, didEnterRegion: region)
    }

    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        self.delegate?.locationManagerDidExitRegion(self, didExitRegion: region)
    }

    func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        if region is CLBeaconRegion {
            print("determined state of beacon")
            // Start ranging only if the feature is available.
            if CLLocationManager.isRangingAvailable() {
                print("determined state of beacon and started ranging")
                locationManager?.startRangingBeacons(in: region as! CLBeaconRegion)
                // Store the beacon so that ranging can be stopped on demand.
                beaconsToRange.append(region as! CLBeaconRegion)
            }
        }

        self.delegate?.locationManagerDidDetermineState(self, didDetermineState: state, region: region)
    }

    func locationManager(_ manager: CLLocationManager,
                         didRangeBeacons beacons: [CLBeacon],
                         in region: CLBeaconRegion) {
        self.delegate?.locationManagerDidRangeBeacons(self, beacons: beacons, region: region)
    }

    func locationManagerShouldDisplayHeadingCalibration(_ manager: CLLocationManager) -> Bool {
        return true
    }

    func locationManager(_ manager: CLLocationManager,
                         didChangeAuthorization status: CLAuthorizationStatus) {
        self.checkStatus(status: status)
    }
}

In my ViewController I use it in a very straight forward fashion like this (at the top: delegate methods...somewhat inconsequential for the problem, at the bottom: instantiation):

import UIKit
import SceneKit
import ARKit
import CoreLocation

class ViewController: UIViewController, ARSCNViewDelegate, LocationManagerDelegate {

    func locationManagerDidUpdateLocation(_ locationManager: LocationManager, location: CLLocation) {

    }

    func locationManagerDidUpdateHeading(_ locationManager: LocationManager, heading: CLHeading, accuracy: CLLocationDirection) {

    }

    func locationManagerDidEnterRegion(_ locationManager: LocationManager, didEnterRegion region: CLRegion) {

    }

    func locationManagerDidExitRegion(_ locationManager: LocationManager, didExitRegion region: CLRegion) {

    }

    func locationManagerDidDetermineState(_ locationManager: LocationManager, didDetermineState state: CLRegionState, region: CLRegion) {

    }

    func locationManagerDidRangeBeacons(_ locationManager: LocationManager, beacons: [CLBeacon], region: CLBeaconRegion) {
        print("\(beacons) + beacons for ranging")
        if beacons.count > 0 {
            let nearestBeacon = beacons.first!
            let major = CLBeaconMajorValue(truncating: nearestBeacon.major)
            let minor = CLBeaconMinorValue(truncating: nearestBeacon.minor)

            print("major: \(major)")
            print("minor: \(minor)")
            print("accuracy: \(nearestBeacon.accuracy)")

            switch nearestBeacon.proximity {
            case .immediate:
                print("--- immediate ---")
            case .near:
                print("--- near ---")
            case .far:
                print("--- far ---")
            case .unknown:
                print("--- proximity unknown ---")
            }
        }
    }


    var sceneView: ARSCNView!
    var locationManager: LocationManager!

    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView = ARSCNView()

        locationManager = LocationManager()
        locationManager.delegate = self

        ...

My console output is:

2018-02-24 20:40:30.927542-0500 onebeacon[1275:423523] [DYMTLInitPlatform] platform initialization successful
2018-02-24 20:40:32.799470-0500 onebeacon[1275:423484] Metal GPU Frame Capture Enabled
2018-02-24 20:40:32.801237-0500 onebeacon[1275:423484] Metal API Validation Enabled
monitorBeacons()
monitorBeacons().monitoringIsAvailable
Optional(Set([CLBeaconRegion (identifier:'iBeacon', uuid:12345678-B644-4520-8F0C-720EAF059935, major:1, minor:2)])) + monitoredRegions

All of the iBeacon details are correct, there is no way they could have changed from earlier today as it has been plugged in this whole time and not accessed. The problem summarized: it appears to add the region to the "regions-to-be-monitored" list, but the monitoring never seems to start because I get no console messages relaying the iBeacon information.

UPDATE

I will leave the answer I provided as it might be part of it (maybe not)...but now it seems to be starting twice:

2018-02-24 21:35:13.341162-0500 onebeacon[1345:444027] [DYMTLInitPlatform] platform initialization successful
2018-02-24 21:35:16.504017-0500 onebeacon[1345:443977] Metal GPU Frame Capture Enabled
2018-02-24 21:35:16.505384-0500 onebeacon[1345:443977] Metal API Validation Enabled
monitorBeacons()
monitorBeacons().monitoringIsAvailable
Optional(Set([CLBeaconRegion (identifier:'iBeacon', uuid:12345678-B644-4520-8F0C-720EAF059935, major:1, minor:2)])) + monitoredRegions
2018-02-24 21:35:16.747004-0500 onebeacon[1345:443977] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2018-02-24 21:35:16.747969-0500 onebeacon[1345:443977] [MC] Reading from public effective user settings.
monitorBeacons()
monitorBeacons().monitoringIsAvailable
Optional(Set([CLBeaconRegion (identifier:'iBeacon', uuid:12345678-B644-4520-8F0C-720EAF059935, major:1, minor:2)])) + monitoredRegions

As you can see the monitorBeacons()... lines happen twice, I checked to make sure that there is no other instantiation of LocationManager and there isn't.

UPDATE

I narrowed it down...the problem seems to be that the didDetermineState function is not firing initially, I have to walk out of range (or close to it) from the beacon...and then walk back and the didDetermineState method fires, does anyone know why it isn't firing when I am starting in the radius of the beacon?

UPDATE

It appears that my phone is recognizing that it is in a region because when I walk out of the region, the didExitRegion method fires, and then the didDetermineState method...odd because the state should already be determined if the didExitRegion method is firing:

2018-02-24 23:01:09.650445-0500 onebeacon[422:33762] [App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction
2018-02-24 23:01:09.651978-0500 onebeacon[422:33762] [App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction
2018-02-24 23:01:15.007844-0500 onebeacon[422:33762] [App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction
2018-02-24 23:01:15.007893-0500 onebeacon[422:33762] [App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction
2018-02-24 23:02:00.002451-0500 onebeacon[422:33762] Status bar could not find cached time string image. Rendering in-process.

...(put in for easier reading)...

did exit region --- CLBeaconRegion (identifier:'iBeacon', uuid:12345678-B644-4520-8F0C-720EAF059935, major:1, minor:2)
determined state of beacon
determined state of beacon and started ranging
[] + beacons for ranging
[] + beacons for ranging
[] + beacons for ranging
[] + beacons for ranging
[] + beacons for ranging
[] + beacons for ranging
[] + beacons for ranging

What is the pre-commit handler warning? Are those relevant?

Upvotes: 2

Views: 918

Answers (1)

ewizard
ewizard

Reputation: 2862

My didDetermineState function stopped working on initial load of the app. If I walk outside the range of the beacon, and back into range - the didDetermineState method fires (along with enter/exit region methods). I purchased another iBeacon...and it works fine...also, curiously, when I use both beacons together, both beacons get detected on initial load by didDetermineState. I am not sure why didDetermineState stopped working for the one beacon...when being used in isolation.

Upvotes: 0

Related Questions