IOS Application which can run in background and can connect to Device
While iBeacon is very fast at detecting in the background and works best, it is not absolutely necessary. You can detect a BLE service on iOS in the background and connect to it when it is discovered.
To do this you must:
- In your Info.plist declare:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
- In your AppDelegate in
didFinishLaunching
you must trigger a BLE scan start
centralManager?.scanForPeripherals(withServices: [myServiceUUID], options: nil)
If you do the above, after your app is terminated, iOS will auto-launch your app and you will get a callback to didDiscoverPeripheral
when a new peripheral is discovered.
While this really does work, it is very difficult to test correctly because of the way iOS keeps track of duplicates. If a device has been detected already, and you kill your app you will not get a callback about its discovery -- iOS keeps track that it was already detected. It will only give you a launch and callback for a new device.
Here is how I would test it:
- Modify your app to send a local notification when it detects the BLE device. VERIFY THIS WORKS.
- Turn off your BLE device
- Launch your app
- Reboot your phone.
- Turn on your BLE device
- Wait for the notification above. Be patient! When you reboot your phone, the BLE stack may not be fully started for a few minutes.
The phone reboot step above is critical to clear out the cache of what devices the iPhone has already seen and are therefore ineligible for a callback.
Finally, you can accelerate this process with a BLE device that also advertises iBeacon as background detections are much faster and less temperamental. But as you note, you cannot use iBeacon to connect to a peripheral using CoreBluetooth. So you must advertise BOTH from your BLE device. This is a common approach I use all the time. Use iBeacon to accelerate the wake-up of your app and CoreBluetooth service advertisement detections to establish a connection.
Reconnect Bluetooth 4.0 connection when user force quits the app
There is no need to re-scan for the device; You should save its identier
property the first time you discover it. You can then use retrievePeripherals(withIdentifiers:)
to try and get the reference to the CBPeripheral
. If you succeed, simply call connect
.
Your code shows an attempt to do something like this, but you have used (what I presume is) your service UUID, and you aren't doing anything with the result from retrievePeripherals(withIdentifiers:)
(You also won't reach this code because of the guard
statement).
You should use something like:
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
// Check if the user's INSIDE the region, not outside...
guard state == CLRegionState.inside else { print("Error"); return }
let content = UNMutableNotificationContent()
content.title = "Device found!"
content.body = "You're close to your BLE Device, reestablish connection!"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "1389402823904", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (err:Error?) in
}
if self.mainPeripheral == nil {
let defaults = UserDefaults.standard
if let uuidString = defaults.string(forKey:"peripheralID"),
let uuid = UUID(uuidString: uuidString),
let peripheral = centralManager.retrievePeripherals(withIdentifiers: [uuid]).first {
self.mainPeripheral = peripheral
}
}
if let mainPeripheral = mainPeripheral
centralManager.connect(mainPeripheral, options: nil)
} else {
//TODO: Scan for peripheral. Note that you can't use `services:nil` in the background
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
if peripheral.name == "DOPEY" {
let defaults=UserDefaults.standard
defaults.set(peripheral.identifier.uuidString,forKey:"peripheralID")
peripheral.delegate = self
mainPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
centralManager.stopScan()
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.discoverServices([HM_10_ServiceCharacteristic])
}
Launch app in background when BLE device is in range
In case of user forcibly kills the app by double clicking on home button and swipe out, it will not be relaunched until the user launches it again.
Coming to your case when Device is rebooted - Yes, you can configure State Preservation and Restoration to relaunch the app when user has not killed it forcibly before rebooting.
( Note: If the device requires a passcode to unlock, apps will not be relaunched until the device is unlocked for the first time after a restart )
Also, it is important to keep in mind that the app will be relaunched and restored if and only if it is pending on a specific Bluetooth event or action (like scanning, connecting, or a subscribed notification characteristic), and this event has occurred.
For more info:
Conditions Under Which Bluetooth State Restoration Will Relaunch An App
Related Topics
iOS - Spritekit - How to Calculate the Distance Between Two Nodes
How to Detect Fullscreen Mode Using Avplayerviewcontroller in Swift
Private VS. Fileprivate on Declaring Global Variables/Consts in Swift3
iOS How to Use Uiapplication Launchapplicationwithidentifier Which Is in Private APIs
Drawing Rounded Rect in Core Graphics
Recording from Remoteio: Resulting .Caf Is Pitch Shifted Slower + Distorted
How to Play Multiple Audio Files Simultaneously
iOS - Another Thread Needs to Send Reloaddata to the Mainthread
Dequeuereusablecellwithidentifier:Forindexpath: VS Dequeuereusablecellwithidentifier:
Displaying Youtube Video Ads with Youtube Iframe
Duplicate Interface Definition for Class 'Gtmhttpuploadfetcher'
How to Change the Height of Uitextfield in Uialertcontroller in Swift
Is This a Bug with Mkmapkitdelegate Mapview:Didupdateuserlocation
Memory Leak with "Libbacktracerecording.Dylib" in React Native iOS Application
Urlsessiondelegate Function Not Being Called
Avassetresourceloaderdelegate Methods Not Working on Device