Scan Bluetooth device failed when I lock screen(use ibeacon) - ios

I made a ibeacon project, now I found two problems:
first,when mobile phone lock screen, I'm going to scan for Bluetooth devices (I'm sure in the beacon region), sometimes Scan failed and returned empty array
second, when I lock screen, sometimes didEnterRegion and didExitRegion have stopped, when I am the light of the screen, they went on again
Now I want to scan the device every time when I lock the screen,what should i do?
MonitoringForRegions code:
let region = BRTBeaconRegion.init(proximityUUID: proxiID, identifier: proxiID.UUIDString)
region.notifyOnEntry = true
region.notifyOnExit = true
region.notifyEntryStateOnDisplay = true
BRTBeaconSDK.startMonitoringForRegions(region)
Appdelegate delegate code:
func beaconManager(manager:BRTBeaconManager,didEnterRegion region:BRTBeaconRegion){
if region.notifyOnEntry {
//PublicMethod().sendLocalNotification(BEACON_TIP_IN)
print("\(NSDate())-------enter--------")
}
}
func beaconManager(manager:BRTBeaconManager,didExitRegion region:BRTBeaconRegion){
if region.notifyOnExit {
//PublicMethod().sendLocalNotification(BEACON_TIP_OUT)
print("\(NSDate())-------exit--------")
}
}
func beaconManager(manager:BRTBeaconManager,didDetermineState state:CLRegionState,forRegion region:BRTBeaconRegion){
print("didDetermineState")
}
scan code:
BRTBeaconSDK .startRangingBeaconsInRegions(regionArray) { (beacons, region, error ) in
for beacon in beacons as! [BRTBeacon]{
print("beacons count:\(beacons.count) name :\(beacon.name) macaddress:\(beacon.macAddress) major:\(beacon.major) minor:\(beacon.minor) ")
}
}
updated at 14:25
I found the first problem more accurately described, when the lock screen, the device is close to the phone, can be scanned, but not far away (this distance can be scanned at the front desk)
So I guess if the scanning distance is not accurate when lock screen?

Did you turn on the Background Modes for Bluetooth in the Xcode project's capabilities page?
https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
You need to first understand the mechanism of CoreBluetooth. Especially you need to understand the different between Ranging and Monitoring, and the limitations of them in background mode. Some operations can only be achieved in the foreground mode.
Detecting beacons via iBeacon Monitoring & Ranging vs CoreBluetooth scanForPeripheralsWithServices
didEnterRegion and didExitRegion is not realtime, there is buffer time in between exit and reenter the region. Also, if you are already in the region before you call the monitor function, the didEnterRegion won't be trigged.
Update
1、Not open ,Before trying to open, or have the same problem
You need to turn it on, and do some settings in order to implement background scanning.
2、Thank you very much, but I found the scan failed, not every time, occasionally.That's what I don't understand.
3、yes,My experiments are the first to leave the area, then enter the area, because I have two beacon equipment together, UUID is different, close one, then I went to scan, and then found sometimes does not scan, sometimes it is possible
I cant find the code of the Bluetooth library that you are using. It seems like it is written by Mainland China developer? Can you post the link of that library?
My exp, the CoreBluetooth ranging functions are quite reliable. So I guess the problem is you didnt turn on the background mode. Turn on Acts as a Bluetooth LE accessory and Uses Bluetooth LE accessories in the Capabilities tab.
Also, I suggest you to read / bookmark Radius Networks's blog. Their developer blog is worth reading.
http://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios.html

Related

BLE device scanning in background using CoreBluetooth swift

I have an app demanding to scan BLE devices around in background mode when app is not active in foreground.
I have implemented such functionality using CoreBluetooth framework. This is code, I am using to scan device. First I have all device in DB, fetch and creating array.
for item in self.allItems ?? [] {
let uuid = UUID(uuidString: item.identifier)!
let id = CBUUID(nsuuid: uuid)
self.allServiceIds.append(id)
}
And when start scanning, passing same array in method.
self.centralManager?.scanForPeripherals(withServices: self.allServiceIds, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true])
Also I have tried to pass service ids in array as I read lots of articles suggesting in background mode it is required.
Also inside Capabilities, I have checked required options. But still it is not scanning in when app is in background.
Any ideas are welcome.
A few tips:
Get your app working in the foreground first. Only once this is working should you try to get it working in the background.
Make sure that bluetooth is turned on in phone settings.
Make sure you have obtained Bluetooth permission from the user. An iPhone will send a dialog to prompt you for this permission the first time an app runs (it is triggered by CBCentralManager usage.) If you deny this permission, you won't see the dialog again and you must go to Settings -> Your App -> Permissions -> Bluetooth to enable it manually.
Take care that you have set the CBCentralManagerDelegate properly and that you are getting callbacks. In particular, log the callback to centralManagerDidUpdateState(_ central: CBCentralManager) and make sure that you see central.state transition to .poweredOn. Only once you reach the poweredOn state should you start scanning. See: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate
Start off testing in the foreground without filtering self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true]) (note the withServices: nil). Only once you get callbacks detecting peripherals should you start filtering for specific service UUIDs. This will help you eliminate all other problems before filtering on the service.
If you can get all of the above working, there are tricks you can do to get duplicate detections even in the background.
If you cannot get the above working, please described the results of the above, and show more of your code including where you instantiate the CBCentralManager, where you set the delegate, showing the delegate callback methods, and describing which delegate callbacks get called and when.

CoreLocationManager never pauses?

Trying to get my app to use less power, it's tracking location always in the background but I'd like for it automatically pause so I can turn on region watching and use that to resume precise location monitoring once the user moves around a bit.
I've had the app on for half an hour now and the location service is not pausing. I think this has been the case since Apple changed location stuff in iOS 13? I'm not really sure. All the documentation I can find online seems extremely outdated.
Any advice is greatly appreciated, relevant code follows:
init() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
locationManager.activityType = .fitness
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.showsBackgroundLocationIndicator = true
locationManager.startUpdatingLocation()
}
func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
delegate?.paused(tracker: self)
print("MT | LOCATION SERVICES PAUSED!") <---- NEVER GETTING CALLED (been running for 40 minutes now, no location updates, still going though?)
// if not already, start region monitoring
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Collect data
for location in locations {
print("MT | New Location: \(location)")
}
}
I wouldn't rely on connecting to Xcode. Xcode is generally greedy. It doesn't want you to stop your debugging. So it never puts the app in a suspended state.
The callback of locationManagerDidPauseLocationUpdates is somewhat identical to your app being suspended which Xcode prevents.
The way I would test this is to use os.log. Then log the outputs to your Mac's console app. See here.
Just make sure the app is not launched from you running from Xcode. I'd rather disconnect your device from Xcode, kill the app. Disconnect your device from Xcode. Then just tap on the app icon again. That way Xcode cannot intervene.
The issue is caused by your desired accuracy.
You are setting the accuracy to the best, which will make your application run continuously, so it will never pause in any iOS versions.
Furthermore, in a practical test whether it is pausing or not, change the accuracy to kCLLocationAccuracyHundredMeters. It will definitely pause. You can change the filter accuracy as you need for the next update.
Do you think maybe your activityType is preventing the location services to pause?
activityType
The location manager uses the information in this property as a cue to determine when location updates may be automatically paused. Pausing updates gives the system the opportunity to save power in situations where the user's location is not likely to be changing.
.fitness
This activity might cause location updates to be paused only when the user does not move a significant distance over a period of time.
I don't know what your particular use case is but, have you considered using the following function for background location tracking?
startMonitoringSignificantLocationChanges()
You may also try a mix of both, turning on significant location changes for day to day use and allowsBackgroundLocation when doing something like tracking a run, etc.
I think it is quite clear from the docs that this the pause functionality does not work like that. First of all, there is no guarantee that the GPS hardware will pause at all, it depends on the type of activity as well as other activities in the system. It is up to iOS to decide when and if to pause location updates, the property is just a hint.
More important, your use case would not work since if it gets paused, the user would have to interact with your app manually to resume updates.
Apple mentions your case specifically and recommends reducing the accuracy of location updates so it is only using cell tower triangulation instead of GPS (see docs above).
So, instead of using pausesLocationUpdatesAutomatically = true, you could do desiredAccuracy = kCLLocationAccuracyThreeKilometers.
To quote Apple,
[...] consider disabling this property and changing location accuracy to kCLLocationAccuracyThreeKilometers when your app moves to the background. Doing so allows you to continue receiving location updates in a power-friendly manner
Doesn't this cover your use case?

Is it possible to detect the user's moving activity on the background?

I want to develop an app that detecting the user's moving way (walking, cycling, driving etc...) and send a specific UILocalNotification for each activity type.
My question is: is it possible to detect it on the background (when the app is completely closed) without draining the device's battery? What will be the best way to do it?
Thank you!
There is coprocessor m7(+) in iPhones upper 5s.
It gives you possibility to get device motion.
Just
import CoreMotion
in your file.
Create a CMMotionActivityManager object:
let motionActivityManager = CMMotionActivityManager()
Check if it`s available on your device:
motionActivityManager.isActivityAvailable()
Use this method:
motionActivityManager.startActivityUpdates(to: OperationQueue.main) { (activity) in
if (activity?.automotive)! {
print("User using car")
}
if (activity?.cycling)! {
print("User is cycling")
}
if (activity?.running)! {
print("User is running")
}
if (activity?.walking)! {
print("User is walking")
}
if (activity?.stationary)! {
print("User is standing")
}
if (activity?.unknown)! {
print("Unknown activity")
}
}
It would return you types of user activity.
Regarding the user activity which can be handled in background tasks are the below once which does not mention about (walking, cycling,driving etc...)
Implementing Long-Running Background Tasks
For tasks that require more execution time to implement, you must request specific permissions to run them in the background without their being suspended. In iOS, only specific app types are allowed to run in the background:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as
a navigation app Apps that support Voice over Internet Protocol
(VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Yes it´s possible to do that!
If your iOS app must keep monitoring location even while it’s in the
background, use the standard location service and specify the location
value of the UIBackgroundModes key to continue running in the
background and receiving location updates. (In this situation, you
should also make sure the location manager’s
pausesLocationUpdatesAutomatically property is set to YES to help
conserve power.) Examples of apps that might need this type of
location updating are fitness or turn-by-turn navigation apps.
Read more here.

It's that possible to detect a background beacon source generated by an iOS device

I try to use an iOS device as a beacon(let's say my iPhone5), which should be detected by another iOS devices, let's say (my iPad).
When both the devices are in foreground, my iPad is able to detect the beacon signal generated by my iPhone correctly. From my didRangeBeacons delegate method, I did
func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
if beacons.count > 0{
print(beacons)
}
}
I logged the beacon info
[CLBeacon (uuid:<__NSConcreteUUID 0x124e483b0> AD065817-291B-4422-BD59-CBBFBDB81F17, major:9, minor:6, proximity:2 +/- 0.81m, rssi:-56)]
When my iPhone(the beacon) in a background mode(the screen is not active), I am not able to detect any beacon signal from my iPad, are there any solution for this scenario? I have done a lot of research online, but I couldn't find something that is illuminating.
Monitoring for beacons works when an app is in the background because iOS is performing that service for the app. However ranging only works when the app is in the foreground. If short term background ranging is not enough then you need to enable the Background Modes option in the Capability tab of your project settings and use UIBackgroundModes set to location in your Info.plist.
Yes, you can range a beacon in the background. Normally iOS only allows you to do this for 10 seconds after you go to the background, however, you can use this code to extend your background awake time to up to 3 minutes:
func extendBackgroundRunningTime() {
if backgroundTask != UIBackgroundTaskInvalid {
// if we are in here, that means the background task is already running.
// don't restart it.
return
}
NSLog("Attempting to extend background running time")
var self_terminate: Boolean = true
self.backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithName("DummyTask", expirationHandler: {() -> Void in
NSLog("Background task expired by iOS")
if self_terminate != nil {
UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
}
})
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {() -> Void in
NSLog("Background task started")
while true {
NSLog("background time remaining: %8.2f", UIApplication.sharedApplication().backgroundTimeRemaining)
NSThread.sleepForTimeInterval(1)
}
})
}
You actually don't need background modes for this. You need to set NSLocationAlwaysUsageDescription in info.plist. Your phone will range any of the CLBeaconRegions you set it to look for. I like to use this app to test with iBeacons because it provides simulation and detection, although there are many beacon simulator apps out there, or you can buy an actual ibeacon.
I would try to use ranging in the background as sparingly as possible, because this process can be battery intensive if done in excess. Try to use monitoring in situations where you don't need to know things like Proximity and RSSI that are provided through ranging in order to conserve battery life.
Monitoring Beacons works well in foreground as well as in background.
Monitoring
Monitoring a region enables your app to know when a device enters or exits the range of beacons defined by the region. Imagine a museum with an audio guide application and beacons installed by the two entrances. The app is monitoring for a region encompassing both beacons (think: ‘all entrance beacons’ region) and is notified whenever the user enters the museum. Then it launches a notification, reminding users about the audio guide feature.
Ranging
While Monitoring enables you to detect movement in-and-out of range of the beacons, Ranging is more granular. It returns a list of beacons in range, together with an estimated proximity to each of them.Coming back to our museum example: imagine an "all beacons near the exhibits" region. The audio guide app can scan for all beacons in this region and then check which beacon is the closest. Since each beacon is associated with a particular exhibit, the app will play a description of an artwork relevant to the user’s context.
Ranging works only when the app is running, which most of the time means that the user is actively using the app. This also means that it only needs permission to "Access Your Location While You Use the App".
If you're sure you need persistent background ranging for beacons, you'll need to activate the Background Modes capability for your application—specifically, the Location Updates mode.
Note that for startRangingBeaconsInRegion to work in the background you'll also need to start Standard Location Updates via CLLocationManager's startUpdatingLocation (meaning you need both a CLLocationManager and an ESTBeaconManager in your app).
Note: In iOS 9, you also need to set the allowsBackgroundLocationUpdates property of your CLLocationManager to true.
(from community.estimote.com )
https://community.estimote.com/hc/en-us/articles/203914068-Is-it-possible-to-use-beacon-ranging-in-the-background-

iBeacon broadcast starts, immediately stops

I'm trying to broadcast from an iOS 8.1+ device using Swift. When I run the app, it does successfully broadcast ... but only for a second. I know this because, from another device with a 'scanner' app, I see the beacon appear; the print statement for "power on" also appears as expected.
I have other print statements in peripheralManagerDidUpdateState, but they're never called, so I have no clue why broadcast stops so quickly.
I'm NOT trying to do anything fancy (monitor for regions, determine proximity, broadcast in the background, etc.) -- this is just a normal, run-of-the-mill iBeacon transmit from the foreground.
import CoreBluetooth
class ViewController: UIViewController, CBPeripheralManagerDelegate {
var beaconRegion = CLBeaconRegion()
var beaconData = NSDictionary()
var beaconManager = CBPeripheralManager()
...
Later:
self.beaconRegion = CLBeaconRegion(proximityUUID: bleUuid,
major: bleMajor,
minor: bleMinor,
identifier: "com.please.work")
Later, to initiate broadcast:
self.beaconData = self.beaconRegion.peripheralDataWithMeasuredPower(nil)
self.beaconManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
The delegate:
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) {
if(peripheral.state == CBPeripheralManagerState.PoweredOn) {
println("powered on")
self.beaconManager.startAdvertising(self.beaconData)
} else if(peripheral.state == CBPeripheralManagerState.PoweredOff) {
println("powered off")
self.beaconManager.stopAdvertising()
}
else {
println("something else changed")
}
}
UPDATE
This might be due to a problem with my device (iPhone 6); here are my observations:
Rebooting the device clears the issue.
I'm seeing phenomenon in normal apps. First, I start emitting with this: iBeacon Emitter app. Then (on another device), I register the UUID and monitor with this iBeacon Scanner app. The device appears, but after ~minute, it disappears. Thereafter, if I toggle the emitting device, I see the rapid on/off behavior I'm troubleshooting.
More concerning, the behavior occurs across apps. If I reboot (and clear the issue, see #1), and then cause the issue (see #2) ... the problem then appears via other emitter/scanner apps.
I diagnosed this problem as some sort of low-level Bluetooth conflict with the "Knock to Unlock" app.
"Knock to Unlock" uses BLE for 2-way comms with a computer. The moment I uninstalled the app, the problem resolved itself. I hope this saves someone in the same case a little aggravation.
A few thoughts:
Are you sure your CBPeripheralManager instance is not going out of scope or being overwritten and garbage collected, that your ViewController remains in the foreground and the screen is not locked?
Try using another transmitter app like the free Locate app to verify it is not a device specific issue.
If the Locate app works on the same device, then there must be something else in your app that is interfering with the broadcast at a later time. Perhaps you can share code on Github or elsewhere so others can test.

Resources