CBPeripheral, Get Local Peripheral Identifier - ios

I faced a problem about CoreBluetooth Framework.
As Example, I have two devices and each device performs both advertising and scanning. So, I use CBCentralManager and CBPeripheralManager on each.
So, to simplify problem, let's assume that Device1 is broadcasting and Device2 scanning.
When CBCentralManager on Device2 discover the Device1, callback
centralManager:didDiscoverPeripheral:advertisementData:RSSI:
is called and I can get Device1 identifier by calling [peripheral.identifier UUIDString].
How then I can get my local device (Device2) Identifier?
I mean in case that both devices are advertising, Device1 will discover Device2 too and get it's peripheral identifier, but
How can I get exact this identifier (some kind of self-address) on Device2?
P.S. I know that this Identifier is not unique for each device and can change over the time, it's not a problem.

Unfortunately you can't do this. The identifier returned by [peripheral.identifier UUIDString] is auto generated by iOS on the receiving device. It is seeded by the transmitter Mac address (which does not change) and the time the device starts being seen by iOS (which does change).
Because of the second factor is not predictable and the fact that the id generation algorithm is not published, you cannot predict what this id will be on the transmitter side.
Bottom line, if you want a predictable unique id, you cannot use this field

Related

Bluetooth LE iOS unable to scan in background

Setup, uniqueness of the situation, and success scanning in foreground
I am trying to pick up bluetooth readings from a CBPeripheral device. What is interesting about this particular device is that it sends out UUIDs that are embedding information in them directly. (This seems strange, but I don't know enough about BLE to know whether or not that's typical. There is no actual connection occurring in the end - only advertisements are being sent out.)
In the foreground, I am able to identify which readings are mine because [peripheral name] remains constant. So I'm able to pick up the readings that are relevant by doing the following check inside of didDiscoverPeripheral:
if ([[peripheral name] isEqualToString:#"UNIQUE_IDENTIFIER"]) {
NSLog(#"*** Got a reading ***");
}
This is working nicely in the foreground - I am running
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
in a loop (details there seem irrelevant) and the code is able to print out all of the readings that I expect it to print.
Trouble scanning in background and attempts to solve
My app's Info.plist contains the following entry:
I have also check the (what I think is) the appropriate field in the Capabilities tab in XCode:
Neither of these things have turned out to be useful. didDiscoverPeripheral is called perfectly up until the point where I close my application, at which point it stops immediately.
One other thing that I read on the internet was that background processing still won't happen when scanForPeripheralsWithServices is called with nil, however if a non-empty array of services is passed instead, then it should succeed. I'm not sure if this is correct. The problem is, I have only been able to find examples of calling scanForPeripheralsWithServices where the services are identified by their UUIDs, for example
NSArray *services = #[[CBUUID UUIDWithString: #"2456f1b9-26e2-8f83-e744-f34f01e9d701"]];
[self.centralManager scanForPeripheralsWithServices:services options:nil];
but not their name property.
The question
So I guess my question, after all of this, would be: Would providing an array of name filters (instead of UUIDs) help the application to run in the background, and if so, how would I write that in code? If not, what am I missing such that my app still only works in the foreground?
EDIT: I had previously used the terminology "identifier" when I think I meant to say "name", so I went back and changed those. From the documentation, here is the name value that I would like to scan for in the background https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name?language=objc
In order to scan for new devices in the background you must specify the UUIDs of the service(s) that you are interested in. The documentation is quite clear on this.
The identifier for a device is a value locally determined by iOS, not the device itself and will vary for different iOS devices connecting to the same peripheral. If you know the identifier for a device that you have previously discovered then you can use the identifier to connect to the device but you can’t scan for an identifier.
It is possible for a peripheral to include data in its service advertisement and perhaps this is what your peripheral is doing. If so, then you will not be able to get readings in the background since duplicate device discovery events are not delivered when your app is in the background.
You can discover and connect to a new device in the background as long as you know the service that it is advertising but any subsequent data transfers will require the device to issue a notify on a changed characteristic. It cannot use the manufacturer data portion of the advertising frame.

how to retrive the BTLE peripherals when user manually quit app from background?

Explanation :
I am connecting to BTLE peripheral using core bluetooth framework, saving the peripheral identifier in a database.At the same time, I am saving the peripherals in an array to handle reconnection with peripherals.But, when user manually quit the app, my array becomes empty. due to which I won't be able to reconnect those peripherals again.
Now, I have peripherals identifiers in my database. I have read about it and found that there is a delegate method
- (NSArray<CBPeripheral *> *)retrievePeripheralsWithIdentifiers:(NSArray<NSUUID *> *)identifiers NS_AVAILABLE(NA, 7_0)
To get the peripherals but it returns nothing.How I can get the peripherals again?
https://developer.apple.com/library/content/documentation//NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/BestPracticesForInteractingWithARemotePeripheralDevice/BestPracticesForInteractingWithA
I also check above document. In that they mentioned about the methods to reconnect to the peripherals
Retrieving a List of Known Peripherals
The first time you discover a peripheral, the system generates an identifier (a UUID, represented by an NSUUID object) to identify the peripheral. You can then store this identifier (using, for instance, the resources of the NSUserDefaults class), and later use it to try to reconnect to the peripheral using the retrievePeripheralsWithIdentifiers: method of the CBCentralManager class. The following describes one way to use this method to reconnect to a peripheral you’ve previously connected to.
When your app launches, call the retrievePeripheralsWithIdentifiers: method, passing in an array containing the identifiers of the peripherals you’ve previously discovered and connected to (and whose identifiers you have saved), like this:
knownPeripherals =
[myCentralManager retrievePeripheralsWithIdentifiers:savedIdentifiers];
The central manager tries to match the identifiers you provided to the identifiers of previously discovered peripherals and returns the results as an array of CBPeripheral objects. If no matches are found, the array is empty and you should try one of the other two reconnection options. If the array is not empty, let the user select (in the UI) which peripheral to try to reconnect to.
When the user selects a peripheral, try to connect to it by calling the connectPeripheral:options: method of the CBCentralManager class. If the peripheral device is still available to be connected to, the central manager calls the centralManager:didConnectPeripheral: method of its delegate object and the peripheral device is successfully reconnected.
Note: A peripheral device may not be available to be connected to for a few reasons. For instance, the device may not be in the vicinity of the central. In addition, some Bluetooth low energy devices use a random device address that changes periodically. Therefore, even if the device is nearby, the address of the device may have changed since the last time it was discovered by the system, in which case the CBPeripheral object you are trying to connect to doesn’t correspond to the actual peripheral device. If you cannot reconnect to the peripheral because its address has changed, you must rediscover it using the scanForPeripheralsWithServices:options: method.
On application startup I would just discover all the peripherals and then compare discovered peripherals with the peripheral identifiers stored in the database.
After the comparison of the discovered peripherals and peripherals, the connections for desired peripherals can be made.

Unique identifier for peripheral BLE device

So I have peripheral BLE device and I need some identifier for it to later share with another iPhone. Example I connect with iPhone 'A' to peripheral. iPhone 'A' saves peripheral's identifier to database and later I easily can take iPhone 'B' and connect to the peripheral found by this identifier.
Now there is UUID what changes for every iPhone-peripheral connection, but MAC
Address is not available. What could you suggest?
If you do not manufacture the peripheral yourself then you can not uniquely identify peripherals across different iOS devices. This is exactly how Apple wants it to be since it means that developers can't track user's locations or movement patterns based on device addresses. It would be a privacy concern.
If you manufacture the peripheral yourself then you can do pretty much whatever you want. For example: Use a custom Service uuid, add uuid to manufacturer data in advertisement, set unique advertisement names, etc etc.. But if the core purpose of all this is to track your user then I would guess that Apple might deny your submission.
I have a similar problem and many thread like this say you cannot obtain MAC address by CoreBluetooth. I want to connect to a BLE peripheral (I mean open and connectable peripheral) and store some information on a database so that a different iPhone can read the same database and use the informations when it connects to the same kind of BLE peripheral. For now I rely on the device name, obtained like this:
-(void)addDiscoveredDevice:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSString * name = [peripheral name];
}
because NSString * uuid = [[peripheral identifier] UUIDString]; gives me different results on different iOS devices.
Therefore, let's say my first problem is to know how many Apple Watch I have close to my iPhone, the second problem is to know if a given Apple Watch (e.g. John's Apple Watch) is close to my iPhone. Unfortunately this method doesn't work because users (e.g. John) or companies can change the name of their peripherals.
Yes I can rely on a combination of name + service uuid + characteristic uuid but for the same reasons, if the name changes it is useless.
Does anybody found a solution?

How to find the specific BLE 4.0 peripheral from two devices with same service UUID

For BLE 4.0, it provides API to discover peripherals with array of service UUID.
I just want to find the specific one. How to achieve this ?
If need assign the identifier to the specific device, how to do it ?
(I think my question need some context of core bluetooth of iOS. )
The process for reconnecting to known peripherals is described in the Core Bluetooth Programming Guide.
Essentially if you know the UUID of the device you want to reconnect to (which is the identifier property of the CBPeripheral object) then you can use the retrievePeripheralsWithIdentifiers: method of the CBCentralManager to obtain the CBPeripheral object and then attempt a connection -
NSUUID *pid=[[NSUUID alloc]initWithUUIDString:#"45D956BB-75E4-6CEB-C06C-D531B00174E4" ];
NSArray *peripherals=[self.centralManager retrievePeripheralsWithIdentifiers:#[pid]];
if ([peripherals count]>0)
{
CBPeripheral *peripheral=[peripherals objectAtIndex:0];
peripheral.delegate=self;
[self.connectingPeripherals addObject:peripheral];
NSLog(#"Found peripheral %# with name %#",peripheral.identifier,peripheral.name);
[self.centralManager connectPeripheral:peripheral options:nil];
}
This may not work, and you may have to scan for your peripheral as the identifier of some peripherals (iOS devices in particular) periodically change their UUID.
The programming guide notes -
Note: A peripheral device may not be available to be connected to for
a few reasons. For instance, the device may not be in the vicinity of
the central. In addition, some Bluetooth low energy devices use a
random device address that changes periodically. Therefore, even if
the device is nearby, the address of the device may have changed since
the last time it was discovered by the system, in which case the
CBPeripheral object you are trying to connect to doesn’t correspond to
the actual peripheral device. If you cannot reconnect to the
peripheral because its address has changed, you must rediscover it
using the scanForPeripheralsWithServices:options: method.
You will also have to scan the first time you encounter a peripheral.
Tell me if I'm wrong. You have 2 devices working as central role who wants to connect to one BLE peripheral.
What it is important is the UUID of the services in the BLE peripheral. From your devices in central role you should search peripherals with a wanted UUID Service. Thats all.
To develop with iOS please follow Core Bluetooth Programming Guide. You have a good tutorial there.
As an example of the Apple documentation:
[myCentralManager scanForPeripheralsWithServices:nil options:nil];
In this line of code you can add an Array of UUID objects (CBUUID) instead of nil for scanForPeripheralsWithServices:

CoreBluetooth: What is the lifetime of unique UUIDs

Following off of this question:
Corebluetooth, How to get a unique UUID?
After connecting to a peripheral, I can retrieve its CFUUIDRef, which is unique, and can use it to reconnect via retrievePeripherals. However, I have found that I can not use it to reconnect after closing the application.
Is the unique UUID generated after a connection only valid for the lifetime of the application or are we supposed to be able to save it for later use? I am trying to have my application remember one particular peripheral, but besides peripheral.UUID, I don't know of any other unique identifiers.
Long story short, YES you can use the UUID to reconnect to the same device even after you close the application (in exactly the way you say).
I assume however, that you are not actually pairing with peripheral. That's a big problem right there. You need to actually establish the pairing request and get the peripheral to show up in the bluetooth table. The UUID will then be solidified with the iOS device and will remain until you flush the Network Settings of the iOS device.
The other possibility is that your BLE device has a firmware problem, wherein after you disconnect, it forces itself into advertising mode or something. This will also mess with your ability to reconnect. Let me know if you have any questions!

Resources