I'm trying to implement a dedicated class to scan for a specific iBeacon (in foreground and in background) but the function didRangeBeacons is never called.
I also set the parameter NSLocationAlwaysUsageDescription in my info.plist.
here is the class I developed:
class iBeacon: NSObject, UIApplicationDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var beaconRegion:CLBeaconRegion!
override init(){
super.init()
print("init...")
let uuidString = "B737D0E7-AF53-9B83-E5D2-922140A91234"
let beaconIdentifier = "nbuit-06B908"
let beaconUUID:NSUUID = NSUUID(UUIDString: uuidString)!
beaconRegion = CLBeaconRegion(proximityUUID: beaconUUID,
identifier: beaconIdentifier)
beaconRegion.notifyOnEntry = true
locationManager = CLLocationManager()
locationManager.delegate = self
if(locationManager!.respondsToSelector("requestAlwaysAuthorization")) {
locationManager!.requestAlwaysAuthorization()
}
locationManager!.pausesLocationUpdatesAutomatically = false
locationManager!.startMonitoringForRegion(beaconRegion)
locationManager!.startRangingBeaconsInRegion(beaconRegion)
locationManager!.startUpdatingLocation()
}
func sendLocalNotificationWithMessage(message: String!) {
let notification:UILocalNotification = UILocalNotification()
notification.alertBody = message
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!){
locationManager.requestStateForRegion(region)
}
func locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) {
if state == CLRegionState.Inside {
locationManager.startRangingBeaconsInRegion(beaconRegion)
}
else {
locationManager.stopRangingBeaconsInRegion(beaconRegion)
}
}
func locationManager(manager: CLLocationManager,
didRangeBeacons beacons: [CLBeacon],
inRegion region: CLBeaconRegion) {
print("didRangeBeacons");
var message:String = ""
if(beacons.count > 0) {
let nearestBeacon:CLBeacon = beacons[0] as! CLBeacon
switch nearestBeacon.proximity {
case CLProximity.Far:
message = "You are far away from the beacon"
case CLProximity.Near:
message = "You are near the beacon"
case CLProximity.Immediate:
message = "You are in the immediate proximity of the beacon"
case CLProximity.Unknown:
return
}
} else {
message = "No beacons are nearby"
}
print("%#", message)
sendLocalNotificationWithMessage(message)
}
func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
print("Beacon in range")
}
func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
print("No beacons in range")
}
func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) {
print("Failed monitoring region: \(error.description)")
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
print("Location manager failed: \(error.description)")
}
}
I'm not able to find the reason why my iBeacon is not detected. I also checked for the UUID and name in a BLE scanner app and it seems to be correct.
If you have any idea to help me that would be great.
EDIT:
I Finally found the reason why the LocationManager was never started, I was instantiating my class like this:
let qualityOfServiceClass = DISPATCH_QUEUE_PRIORITY_DEFAULT
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
print("Running beacon detection in the background queue")
beaconSharedInstance
})
I was expecting to get this instance running in background, but by simply calling my instance like this
beaconSharedInstance
it starts and scans for regions correctly. but as soon as my app is inactive, I have the following log:
Ending background task
and my app stops scanning. I set the following parameters
locationManager.allowsBackgroundLocationUpdates = true
and also set Background Mode capability of my project.
any idea why my instance is stopped when the app is in background ?
Related
I Liked the concept of Protocol Oriented Programming and I am trying to start writing protocols by Protocol Extensions in swift.
I am trying to bring device location using Protocol Extensions.
However I am facing problems on creating that as the following:
import Foundation
import CoreLocation
protocol Locator:CLLocationManagerDelegate {
var locationManager:CLLocationManager!
{ get set }
var locationHandler: ((CLLocation)->())?
{ get set }
func getLocation(completionHandler:#escaping (CLLocation)->())
}
extension Locator {
private var _locationManager:CLLocationManager {
get {return self.locationManager} set {self.locationManager = newValue}
}
private var _locationHandler:((CLLocation)->())? {
get {return self.locationHandler} set {self.locationHandler = newValue}
}
func getLocation(completionHandler:#escaping (CLLocation)->()) {
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
let status = CLLocationManager.authorizationStatus()
switch status {
case .notDetermined:
self.locationManager.requestAlwaysAuthorization()
return
case .denied, .restricted:
return
case .authorizedAlways, .authorizedWhenInUse:
self.locationManager.startUpdatingLocation()
#unknown default:
break
}
self.locationHandler = completionHandler
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse || status == .authorizedAlways {
self.locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Hey this is a location")
if let currentLocation = locations.last {
self._locationHandler?(currentLocation)
}
}
}
However the completion handler is not working properly.
what is my problem and How can I proceed creating this in Protocols concept.
didUpdateLocations is not firing
- CLLocationManagerDelegate was implemented correctly with the viewcontroller as a delegate
import Foundation
import UIKit
import CoreLocation
protocol LocationServiceDelegate {
func tracingLocation(currentLocation: CLLocation)
func tracingLocationDidFailWithError(error: NSError)
}
class LocationService: NSObject, CLLocationManagerDelegate {
static var sharedInstance = LocationService()
var locationManager: CLLocationManager?
var currentLocation: CLLocation?
var delegate: LocationServiceDelegate?
var paymentVC: PaymentViewController?
override init() {
super.init()
self.locationManager = CLLocationManager()
guard let locationManager = self.locationManager else {
return
}
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
locationManager.distanceFilter = 200
}
func startUpdatingLocation() {
print("Starting Location Updates")
self.locationManager!.startUpdatingLocation()
}
func stopUpdatingLocation() {
print("Stop Location Updates")
self.locationManager?.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.currentLocation = location
updateLocation(currentLocation: location)
}
private func locationManager(manager: CLLocationManager, didFailWithError error: Error) {
updateLocationDidFailWithError(error: error as NSError)
}
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.tracingLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.tracingLocationDidFailWithError(error: error)
}
}
this is the extension of the viewcontroller where I implement to custom protocole for corelocation tracking
I call startUpdatingLocations() in viewDidLoad
extension PaymentViewController: LocationServiceDelegate,CLLocationManagerDelegate {
func tracingLocation(currentLocation: CLLocation) {
locationService.currentLocation = currentLocation
}
func tracingLocationDidFailWithError(error: NSError) {
print("Error message: \(error.localizedDescription)")
}
func startUpdatingLocations() {
locationService.locationManager?.delegate = self
locationService.delegate = self
locationService.startUpdatingLocation()
}
func stopUpdatingLocations() {
LocationService.sharedInstance.stopUpdatingLocation()
}
}
the corelocation tracking is not firing in the simulator. However this is enabled.
enter image description here
unfortunately, I have no way to test with a device right now
The only way I know of to simulate core location tracking on a simulator is by choosing one of the location options available in debug tab of the simulator.
I am trying to use the below LocationSingleton Class from this blog in my project. I like the simplicity of its usage.
You start updating location by simply calling:
LocationSingleton.sharedInstance.startUpdatingLocation()
Get the last location by simply calling:
LocationSingleton.sharedInstance.lastLocation
My intention is to start location services, get the last location so that I can then fetch users from Firebase using the location returned.
The issue is that if I call lastLocation straight after startUpdatingLocation it returns nil.
After some debugging I've found the reason is because location services are slow to start on the device and therefore when lastLocation is called the devices hasn't acquired the location yet. I would like to execute the next command as soon as the lastLocation has been recorded. How can I achieve that?
I would like to understand how the Protocol is being used?
import UIKit
import CoreLocation
protocol LocationServiceDelegate {
func locationDidUpdateToLocation(currentLocation: CLLocation)
func locationUpdateDidFailWithError(error: NSError)
}
class LocationSingleton: NSObject,CLLocationManagerDelegate {
var locationManager: CLLocationManager?
var lastLocation: CLLocation?
var delegate: LocationServiceDelegate?
static let sharedInstance:LocationSingleton = {
let instance = LocationSingleton()
return instance
}()
override init() {
super.init()
self.locationManager = CLLocationManager()
guard let locationManagers=self.locationManager else {
return
}
if CLLocationManager.authorizationStatus() == .notDetermined {
//locationManagers.requestAlwaysAuthorization()
locationManagers.requestWhenInUseAuthorization()
}
if #available(iOS 9.0, *) {
// locationManagers.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
locationManagers.desiredAccuracy = kCLLocationAccuracyBest
locationManagers.pausesLocationUpdatesAutomatically = false
locationManagers.distanceFilter = 0.1
locationManagers.delegate = self
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.lastLocation = location
updateLocation(currentLocation: location)
}
#nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager?.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse:
locationManager?.startUpdatingLocation()
break
case .authorizedAlways:
locationManager?.startUpdatingLocation()
break
case .restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
default:
break
}
}
// Private function
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.locationDidUpdateToLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.locationUpdateDidFailWithError(error: error)
}
func startUpdatingLocation() {
print("Starting Location Updates")
self.locationManager?.startUpdatingLocation()
// self.locationManager?.startMonitoringSignificantLocationChanges()
}
func stopUpdatingLocation() {
print("Stop Location Updates")
self.locationManager?.stopUpdatingLocation()
}
func startMonitoringSignificantLocationChanges() {
self.locationManager?.startMonitoringSignificantLocationChanges()
}
}
The location manager works asynchronously and provides delegate methods to get the result.
In your class adopt LocationServiceDelegate, implement the delegate methods and set the delegate for example in viewDidLoad
func locationDidUpdateToLocation(currentLocation: CLLocation)
{
print(LocationSingleton.sharedInstance.lastLocation)
}
func locationUpdateDidFailWithError(error: NSError)
{
print(error)
}
func viewDidLoad()
super viewDidLoad()
let locationSingleton = LocationSingleton.sharedInstance
locationSingleton.delegate = self
locationSingleton.startUpdatingLocation()
}
When a location is detected, one of the delegate methods is called
I'm trying to get geolocation data in an arbitrary class. I'm very new to Swift, so I have no idea why this isn't working?
Any pointers?
import Foundation
import UIKit
import CoreLocation
class GeolocationPlugin:NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var lat: Double = 0
var long: Double = 0
func getLocation() {
print("Getting location")
// For use in foreground
self.locationManager = CLLocationManager()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
// locationManager.startMonitoringSignificantLocationChanges()
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError) {
print("Error while updating location " + error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
self.locationManager.requestLocation()
print("gets here")
}
}
I currently see Getting location and then an error:
2017-03-26 15:42:32.634 IonicRunner[42304:5668243] *** Assertion failure in -[CLLocationManager requestLocation], /BuildRoot/Library/Caches/com.apple.xbs/Sources/CoreLocationFramework_Sim/CoreLocation-2100.0.34/Framework/CoreLocation/CLLocationManager.m:865
2017-03-26 15:42:32.638 IonicRunner[42304:5668243] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Delegate must respond to locationManager:didUpdateLocations:'
The solution wound up being to move the methods out of getLocation(), to properly activate a location in the simulator, and to move where this class was initiated from, so it wasn't immediately released as soon as getLocation() completes.
import Foundation
import UIKit
import CoreLocation
class GeolocationPlugin:NSObject, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var lat: Double = 0
var long: Double = 0
var cb: ((Double, Double) -> Void)? = nil
func getLocation(callback: #escaping (Double, Double) -> Void) {
print("Getting location")
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestLocation()
self.cb = callback
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: NSError) {
print("Error while updating location " + error.localizedDescription)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
//print("locations = \(locValue.latitude) \(locValue.longitude)")
if( self.cb != nil) {
self.cb!(locValue.latitude, locValue.longitude)
}
}
}
I have built a class that should receive ibeacons.
As far as I know, I'm doing everything right. But it doesn't work.
My App doesn't ask for location permissions and the location manager doesn't start updating.
I have also set the attributes in the plist.
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
It only works when I initialize everything right in the AppDelegate.
As in the tutorial here.
http://ibeaconmodules.us/blogs/news/14702963-tutorial-swift-based-ibeacon-app-development-with-corelocation-on-apple-ios-7-8
But I want to outsource this Service to a class.
I don't know what I'm doing wrong. I'm new with Swift, in Objective-C it has always worked.
Can anyone help me?
Here The Code-
ViewController
import UIKit
class ViewController: UIViewController,BeaconServiceProtocolDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//Set BackgroundImage and hide Navigationbar
self.view.backgroundColor = UIColor(patternImage: UIImage(named:"bg"));
self.navigationController?.navigationBar.hidden = true
////////////////////////create new Beacon Service which is looking for beacon which are given with the parameters
var beaconAdvertiseService = BeaconService(parauuidString: "F0018B9B-7509-4C31-A905-1A27D39C003C",parabeaconIdentifier:"iBeaconTalentTeamAudi");
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//Notification if our service found a beacon
func sendLocalNotificationWithMessage(message: String!) {
let notification:UILocalNotification = UILocalNotification()
notification.alertBody = message
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
//Delegate from BeaconService
func foundBeacon() {
println("Found Beacon")
}
}
and of course the class - BeaconAdvertiseService
import Foundation
import CoreBluetooth
import CoreLocation
//BeaconService Delegate
protocol BeaconServiceProtocolDelegate
{
func foundBeacon()
}
import Foundation
import CoreBluetooth
import CoreLocation
//BeaconService Delegate
protocol BeaconServiceProtocolDelegate
{
func foundBeacon()
}
class BeaconService: NSObject, CLLocationManagerDelegate{
let beaconServiceDelegate:BeaconServiceProtocolDelegate?
let uuidString:String//UUID Beacon as String
let beaconIdentifier:String//Identifier Beacon
let beaconUUID:NSUUID//UUID Beacon
let beaconRegion:CLBeaconRegion//Beacon Region
let locationManager:CLLocationManager ////location manager
////////////////////////Init class (constructor) // Params UUID as String & and Identifier from beacon
init(parauuidString:String,parabeaconIdentifier:String) {
self.uuidString = parauuidString
self.beaconIdentifier = parabeaconIdentifier
self.beaconUUID = NSUUID(UUIDString: self.uuidString)
self.beaconRegion = CLBeaconRegion(proximityUUID:self.beaconUUID,identifier: self.beaconIdentifier)
self.locationManager = CLLocationManager();
super.init()
self.initLocationMangager()
}
////////////////////////Init location manager
private func initLocationMangager()
{
println(self.locationManager)
if(self.locationManager.respondsToSelector("requestAlwaysAuthorization")) {
locationManager.requestAlwaysAuthorization()
}
self.locationManager.delegate = self
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.startMonitoringForRegion(beaconRegion)
self.locationManager.startRangingBeaconsInRegion(beaconRegion)
self.locationManager.startUpdatingLocation()
}
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
////////////////////////LocationManager Delegates
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
////////////////////////LocationManager didStartMonitoringForRegion
func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!) {
manager.startRangingBeaconsInRegion(region as CLBeaconRegion)
println("Did Start")
}
////////////////////////LocationManager didEnterRegion
func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
manager.startRangingBeaconsInRegion(region as CLBeaconRegion)
}
////////////////////////LocationManager didRangeBeacons
func locationManager(manager: CLLocationManager!, didRangeBeacons beacons: [AnyObject]!, inRegion region: CLBeaconRegion!) {
var message:String = ""
if(beacons.count > 0) {
let nearestBeacon:CLBeacon = beacons[0] as CLBeacon
switch nearestBeacon.proximity {
case CLProximity.Far:
message = "Far"
case CLProximity.Near:
message = "Near"
case CLProximity.Immediate:
message = "Immediate"
case CLProximity.Unknown:
return
}
} else {
message = "No Beacons"
}
//Call BeaconServiceDelegate
beaconServiceDelegate?.foundBeacon()
}
////////////////////////LocationManager monitoringDidFailForRegion
func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) {
println(error)
}
////////////////////////LocationManager didExitRegion
func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
manager.stopRangingBeaconsInRegion(region as CLBeaconRegion)
}
}
It looks like you are assigning your advertise service to a local variable at the end of your viewDidLoad. This instance will be destroyed at the end of this method (one line later ;))
Please make sure to assign your advertiser to an instance variable or property and assign self (your viewcontroller) as a delegate to your advertiser.
This should do the trick.
I see at least one thing very simple. It looks like the initLocationMangager() method never gets called. You should probably add a call to this method from the init(parauuidString:String,parabeaconIdentifier:String) method.
Sorry I forgot that, i had taken out.
But it also does not go with the calling
self.initLocationMangager()