performseguewithidentifier with iBeacons - ios

im trying to build a demo app using Estimote Beacons. i want the app to open e specific viewcontroller when the user is near a beacon. im using performseguewithidentifier but when the app starts is opens only the first viewcontroller representing the first beacon which is in the range and it doesnt open the other ones when i go near the other beacons. it somehow just stops ranging for other beacons.
below is the code im using to range for beacons:
func beaconManager(manager: AnyObject, didRangeBeacons beacons: [CLBeacon],
inRegion region: CLBeaconRegion) {
let knownBeacons = beacons.filter{ $0.proximity != CLProximity.Unknown}
if (knownBeacons.count > 0) {
let closestBeacon = knownBeacons [0] as CLBeacon
if(closestBeacon.minor.integerValue==50557){
performSegueWithIdentifier("VC1", sender: nil)
}
else if(closestBeacon.minor.integerValue==37890){
performSegueWithIdentifier("VC2", sender: nil)
}
else if(closestBeacon.minor.integerValue==18976){
performSegueWithIdentifier("VC3", sender: nil)
}
else {
self.view.backgroundColor = UIColor.brownColor()
}

I haven't used Estimote's custom library, but I assume it's similar to the location manager's.
In the Core Location Manager, if your app is in the background you get an entered region notification when you first enter a new beacon region, and then you only get ranging information for a few seconds.
If you've set up your region with a unique UUID and Major ID but no minor ID, then all beacons with that UUID and Major ID are considered part of the same region and you won't reliably get ranging notifications as the beacons with different minor IDs become the closest beacon.
If you want to handle multiple beacons in range at the same time and distinguish between them you'll need to create separate beacon regions for each beacon's UUID, Major ID and minor ID.
I don't know if that's the issue you're facing, but it could be.

Related

If two iBeacons are close together, can one signal override another on the way to a device (iPhone)

I have a simple question that I can't find the answer to. I am testing an app with some iBeacons, and I observe that one of my iBeacons never begins to range...the region is monitored, its just not being detected. When I look at console output the other beacons get detected, and then a few seconds later, another beacon gets detected, but it has the same UUID/major/minor/identifier values as one of the other locations already detected.
So all but one of the beacons get recognized and their states get determined. Then it appears that one of the beacons that has already been determined, gets determined again (same info). These beacons are somewhat close together (about a meter apart in x, y but on different floors (2nd floor and 3rd floor), and I'm wondering if one beacon signal could override another...which would be why I am seeing the info for one of the beacons twice. Otherwise I'm not sure why I can't detect this one beacon.
Here is some of my code, I've combed over it and I can't find anything wrong with human error stuff like entering correct major/minor/UUID values.
This function gets called to start monitoring the beacons:
func monitorBeacons() {
print("monitorBeacons()")
if CLLocationManager.isMonitoringAvailable(for:
CLBeaconRegion.self) {
print("monitorBeacons().monitoringIsAvailable")
// Match all beacons with the specified UUID
let proximityUUIDA = UUID(uuidString:
"12345678-B644-4520-8F0C-720EAF059935")
let proximityUUIDB = UUID(uuidString:
"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
let beaconRegionA = CLBeaconRegion(
proximityUUID: proximityUUIDB!,
major: 0x0001,
minor: 0x0004,
identifier: "locationA 49398")
let beaconRegionB = CLBeaconRegion(
proximityUUID: proximityUUIDB!,
major: 0x0001,
minor: 0x0002,
identifier: "locationB 49267")
let beaconRegionC = CLBeaconRegion(
proximityUUID: proximityUUIDB!,
major: 0x0001,
minor: 0x0005,
identifier: "locationC 49141")
let beaconRegionD = CLBeaconRegion(
proximityUUID: proximityUUIDA!,
major: 0x0001,
minor: 0x0002,
identifier: "locationD DSDTECH")
let beaconRegionE = CLBeaconRegion(
proximityUUID: proximityUUIDB!,
major: 0x0001,
minor: 0x0001,
identifier: "locationE 33803")
self.locationManager?.startMonitoring(for: beaconRegionA)
self.locationManager?.startMonitoring(for: beaconRegionB)
self.locationManager?.startMonitoring(for: beaconRegionC)
self.locationManager?.startMonitoring(for: beaconRegionD)
self.locationManager?.startMonitoring(for: beaconRegionE)
print("\(String(describing: self.locationManager?.monitoredRegions)) + monitoredRegions")
}
}
This happens when their state gets determined:
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if region is CLBeaconRegion {
print("determined state of beacon for region - \(region)")
// 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)
}
}
}
console output:
determined state of beacon for region - CLBeaconRegion (identifier:'locationA 49398', uuid:E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major:1, minor:4)
determined state of beacon for region - CLBeaconRegion (identifier:'locationB 49267', uuid:E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major:1, minor:2)
determined state of beacon for region - CLBeaconRegion (identifier:'locationC 49141', uuid:E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major:1, minor:5)
determined state of beacon for region - CLBeaconRegion (identifier:'locationE 33803', uuid:E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major:1, minor:1)
determined state of beacon for region - CLBeaconRegion (identifier:'locationE 33803', uuid:E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major:1, minor:1)
You can see the last two beacons are the same, but it shouldn't be determining the state twice for one beacon, which is why I think the signals are being confused maybe for the one I am missing locationD DSDTECH and locationE 33803.
UPDATE
I changed the major/minor values (which conflicted with another beacons...but not really because the UUID's were different...so it shouldn't have mattered) and now the beacon gets recognized during didDetermineState...but I don't receive any info from it during ranging. I can see in the logs that it went through the locationManager?.startRangingBeacons(in: region as! CLBeaconRegion) function`...so it I should be receiving info (UUID/major/minor/identifier/anything) from it.
Multiple beacons with same UUID, major and minor will behave like as one region.
I think you missing the status. didDetermineState is called when ever state of any region changes (did exit, or did enter) or if you explicitly call requestStateForRegion for that region. Two states of one beacon in your logs, I think one is for status enter and other is for status exit.
In didDetermineState check region state and only start raging if region state is CLRegionStateInside
if state == CLRegionStateInside {
locationManager?.startRangingBeacons(in: region as! CLBeaconRegion)
beaconsToRange.append(region as! CLBeaconRegion)
}
If you are already in the region didDetermineState won't be called unless you exit the region and enter again. To overcome this you should call requestStateForRegion for each region like this.
self.locationManager?.startMonitoring(for: beaconRegionA)
self.locationManager?.requestState(for: beaconRegionA)
So after figuring out the didDetermineState, I took my iBeacon and plugged it in closer to me, and it started receiving info - so it is just a crappy beacon :)

How to detect iBeacons without know UUID,Any Possible to get core bluetooth or Some other way

Is it possible to detect iBeacons without knowing their UUID?
Is there any way to use Core Bluetooth or some other method?
You must know at least the UUID you are looking for in order to create a CLBeaconRegion. There is no way on iOS to scan for "all beacons".
iBeacons are specifically obscured from Core Bluetooth discovery.
While you cannot detect beacons on iOS without specifying the ProximityUUID up front, you can set up ranging to look for a large number of ProximityUUIDs -- I have successfully ranged for 100 of them at the same time (although 1000 crashes my app.)
By using an online beacon database, you can find a list of UUIDs known to be close to you. This won't detect every beacon for sure, but it will allow you to detect many that are around you without having to build the UUIDs into your app.
Here's an example using the NingoSDK to get the 100 nearest ProximityUUIDs to your location and register them for ranging.
let locationManager = CLLocationManager()
// Get up to 100 beacon ProximityUUIDs within 1km from our current location, so we can use these for beacon ranging
let queryClient = QueryBeaconClient(authToken: Settings().getSetting(key: Settings.ningoReadonlyApiTokenKey)!)
queryClient.queryFirstIdentifiers(latitude: latitude, longitude: longitude, radiusMeters: 1000, limit: 100) { (proximityUUIDStrings, errorCode, errorDetail) in
if let proximityUUIDStrings = proximityUUIDStrings {
NSLog("There are now \(proximityUUIDStrings.count) nearby beacon uuids")
if proximityUUIDStrings.count > 0 {
for region in locationManager.rangedRegions {
locationManager.stopRangingBeacons(in: region as! CLBeaconRegion)
}
for uuidString in proximityUUIDStrings {
let region = CLBeaconRegion(proximityUUID: UUID(uuidString: uuidString)!, identifier: uuidString)
locationManager.startRangingBeacons(in: region)
}
BeaconTracker.shared.updateTransientUuids(uuids: proximityUUIDStrings)
}
}
}

Receiving Location even when app is not running in Swift

Still very new to Swift. I have come from an Android background where there is BroadcastReceiver that can deliver location info to a service even though the app isn't running.
So I was looking for something similar in iOS/Swift and it appears that before this wasn't possible but it may be now. I am developing for iOS 10 but would be great if it was backwards compatible.
I found
startMonitoringSignificantLocationChanges
which I can execute to start delivering location updates, although this raises a few questions. Once I call this and my app is NOT running, are the updates still being sent ? And how would the app wake up to respond ?
Also restarting the phone and when it return, does this mean I still need call startMonitoringSignificantLocationChanges again meaning that I would have to wait for the user to execute my app. Or does it remember the setting after reboot ?
Still a little confused how to get around this, here's a brief explanation of what I am trying to do.
I would like to update the location of the phone even though the app is not running, this would be sent to a rest service every so often.
This way on the backend services I could determine if somebody is within X meters of somebody also and send them a push notification.
It may or may not be a good solution but if I were you I would have used both startMonitoringSignificantLocationChanges and regionMonitoring.
Here is the sample I made which worked well with iOS 13.
Lets take regionMonitoring first. We have certainly no problems when the app is in foreground state and we can use the CLLocationManager's didUpdate delegate to get the location and send it to the server.
Keep latest current location in AppDelegate's property, lets say:
var lastLocation:CLLocation?
//And a location manager
var locationManager = CLLocationManager()
We have two UIApplicationDelegates
func applicationDidEnterBackground(_ application: UIApplication) {
//Create a region
}
func applicationWillTerminate(_ application: UIApplication) {
//Create a region
}
So whenever the user kills the app or makes the app go to background, we can certainly create a region around the latest current location fetched. Here is an example to create a region.
func createRegion(location:CLLocation?) {
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
let coordinate = CLLocationCoordinate2DMake((location?.coordinate.latitude)!, (location?.coordinate.longitude)!)
let regionRadius = 50.0
let region = CLCircularRegion(center: CLLocationCoordinate2D(
latitude: coordinate.latitude,
longitude: coordinate.longitude),
radius: regionRadius,
identifier: "aabb")
region.notifyOnExit = true
region.notifyOnEntry = true
//Send your fetched location to server
//Stop your location manager for updating location and start regionMonitoring
self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoring(for: region)
}
else {
print("System can't track regions")
}
}
Make use of RegionDelegates
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered Region")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Exited Region")
locationManager?.stopMonitoring(for: region)
//Start location manager and fetch current location
locationManager?.startUpdatingLocation()
}
Grab the location from didUpdate method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if UIApplication.shared.applicationState == .active {
} else {
//App is in BG/ Killed or suspended state
//send location to server
// create a New Region with current fetched location
let location = locations.last
lastLocation = location
//Make region and again the same cycle continues.
self.createRegion(location: lastLocation)
}
}
Here I have made a 50m region radius circle. I have tested this and it is called generally after crossing 100m from your center point.
Now the second approach can me using significantLocationChanges
On making the app go background or terminated, we can just stop location manager for further updating locations and can call the startMonitoringSignificantLocationChanges
self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoringSignificantLocationChanges()
When the app is killed, the location is grabbed from didFinishLaunching method's launchOptions?[UIApplicationLaunchOptionsKey.location]
if launchOptions?[UIApplicationLaunchOptionsKey.location] != nil {
//You have a location when app is in killed/ not running state
}
Make sure to keep BackgroundModes On for Location Updates
Also make sure to ask for locationManager?.requestAlwaysAuthorization() by using the key
<key>NSLocationAlwaysUsageDescription</key>
<string>Allow location</string>
in your Info.plist
There can be a third solution by taking 2 LocationManagers simultaneously.
For region
Significant Location Changes
As using significantLocationChanges
Apps can expect a notification as soon as the device moves 500 meters
or more from its previous notification. It should not expect
notifications more frequently than once every five minutes. If the
device is able to retrieve data from the network, the location manager
is much more likely to deliver notifications in a timely manner.
as per the give Apple Doc
So it totally depends on your requirements as the location fetching depends on many factors like the number of apps opened, battery power, signal strength etc when the app is not running.
Also keep in mind to always setup a region with good accuracy.
I know that this will not solve your problem completely but you will get an idea to move forward as per your requirements.

Beacon Ranging in the Background on iOS

I'm currently trying to find a way to range beacons in the background in iOS by using location monitoring and then triggering the ranging like so:
func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) {
if (state == .Inside) {
locationManager.startRangingBeaconsInRegion((region as? CLBeaconRegion)!)
}
}
I'm then trying to get an API call to be made in the beacon ranging
func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
for beacon in beacons {
let minor = beacon.minor as Int
let major = beacon.major as Int
do {
try APICall.canSeeBeacons(major, minor: minor)
} catch {
print("Error making API call")
}
}
}
However this only works for about ten minutes while the phone is in the background, after ten minutes it no longer works but I'm hoping to make it continuous so that API calls can always be made when a beacon is found. I do also have the correct keys set in the permissions and I'm using requestAlwaysAuthorization() on my locationManager
Take a look on the Apple's iOS Dev Library regarding Background Executions:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
If the type of your app is one of the accepted types for longer background executions, add the key Required background modes to your Info.plist with an array, containing one or more of the enabled types: audio, location, voip, newsstand-content, external-accessory and/or bluetooth-central.
Just note that this will be reviewed by Apple before publishing your app on the App Store.
EDITED:
I didn't use this for a while, but tested now and it seems it's even easier since Xcode 6.
Follow the steps:
1. Add the key Required background modes to your Info.plist
2. Go to Capabilities
3. Select the background mode(s) that fit.
I had the same problem. As said a commentator I've moved location manager delegate methods into the App Delegate. And also I added this:
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
And it works.

can I scan for beacons without specifying "region" is swift?

I have this code to scan beacons
var closetBeacon: NSUUID?
let locationManager = CLLocationManager()
let region = CLBeaconRegion(proximityUUID: NSUUID(UUIDString: "B9407F30-F5F8-466E-AFF9-25556B57FE6D")!, identifier: "my_beacons")
func authorizeBeaconScan() -> Void{
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.AuthorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startRangingBeaconsInRegion(region)
}
I understand region is supposed to filter only beacons I care about.
1) If I have few beacons I care about, how do I pass them all to CLBeaconRegion(..)?
2) can I scan for beacon without specifying region?
You have to have UUID of beacons to scan.
Without UUID you can't scan beacons.
1) You can scan all beacons of same UUID for region by only specifying UUID.
2) You can scan specific beacons of one group having common major value by specifying UUID and major value.
3) You can also scan for specific beacon by using UUID, major and minor value of that beacon.
You have to have at-least one UUID of beacon to create region and start scanning it.

Resources