Swift - Get iBeacon - external class - swift

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()

Related

Value of type 'CLBeacon' has no member 'deviceIdentifier'

I trying to make app like Find My to get AirTag informations, but when I try I get error
Value of type 'CLBeacon' has no member 'deviceIdentifier'
How to fix that?
`
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestAlwaysAuthorization()
}
#IBAction func tap(_ sender: Any) {
locationManager.startRangingBeacons(satisfying: CLBeaconIdentityConstraint())
}
func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
for beacon in beacons {
let deviceIdentifier = beacon.deviceIdentifier
let serialNumber = deviceIdentifier.uuidString
print("AirTag serial number: \(serialNumber)")
print("AirTag proximity: \(beacon.proximity)")
print("AirTag RSSI: \(beacon.rssi)")
print("AirTag accuracy: \(beacon.accuracy)")
}
}
}
`
The program will compile to iPhone and it will show AirTag info

Checking if location services are enabled. [Swift]

I am using the following observable object to track the users location:
import Foundation
import MapKit
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var location: CLLocation? = nil
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationServicesEnabled() -> Bool {
return self.locationManager.locationServicesEnabled()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.location = location
}
}
However, when I try to compile I get an error next to the return self.locationManager.locationServicesEnabled() statement. The compiler says: Static member 'locationServicesEnabled' cannot be used on instance of type 'CLLocationManager', Replace 'self.locationManager' with 'CLLocationManager'. I do not understand this error because self.locationManager is an instance of CLLocationManager. Please help.
Static means that locationServicesEnabled is a member of the class, not of the object. Use the classname CLLocationManager as suggested.
Seems like the API has changed, earlier it was an instance method, now it is a class method:
https://developer.apple.com/documentation/corelocation/cllocationmanager/1620566-locationservicesenabled
static func configureLocationManager(manager: CLLocationManager, delegate: CLLocationManagerDelegate?) {
manager.allowsBackgroundLocationUpdates = true
manager.delegate = delegate
manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
switch manager.authorizationStatus {
case .authorizedAlways, .authorizedWhenInUse:
if CLLocationManager.locationServicesEnabled() {
manager.requestLocation()
}
default:
manager.requestWhenInUseAuthorization()
}
}
Here a piece of code that configured location manager and checks whether the app was allowed by user to use location services.
Here is more code that could be useful. This is a file that I created to implement the delegate methods.
https://github.com/aibo-cora/Safe-Bavaria/blob/main/Safe%20Bavaria/View%20Model%20-%20Utility/Find.User.Location.swift

CoreLocation enabled in simulator

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.

Why is this Singleton Location Manager class returning a nil Location?

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

Swift2 didRangeBeacons never called

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 ?