I am modifying an app that discovers a device via Bluetooth BLE in order to plot the values into a graph. It all works fine (thanks to the help of one of you in the past week). The app was initially written by my husband a while ago and during the discovery of the device, he was using checking code like :
NSLog(#" Failed to Connect to Peripheral : %# with UUID: %# ", peripheral, peripheral.UUID);
or
NSLog(#" Connected to Peripheral : %# with UUID: %# ", peripheral, peripheral.UUID);
In front of each of these lines (and some more) I get the warning messages that UUID is deprecated: first deprecated in ios 7.0
Since quite a while, I try to find out by what it has been replaced, but on Google, it doesn't look like other people have the same problem and when I go to the Apple Documentation : here
there is no mention of it being deprecated.
I don't understand...
Could somebody please help ? Thanks
EDIT: ADDING SOME INFO
This is what I get on my computer on the console
Update
I have double checked and the UUID property on CBPeer is also deprecated.
From the docs in Xcode I found -
Deprecation Statement
Use the identifier property instead.
And if you use peripheral.identifier.UUIDString you don't get a deprecation warning.
So, just in case anybody reads this:
Here is the general way to find out what to use instead of the deprecated API call.
1) Mark the deprecated call
2) Right click "Jump to definition"
3) Read what it says there.
In the case of UUID which was asked here, the answer would be 2 lines below:
#property (readonly, nonatomic) NSUUID *identifier NS_AVAILABLE(NA, 7_0);
You can access service UUID by CBAdvertisementDataServiceUUIDsKey in advertisementData in
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
Related
I have an app where I am trying to integrate the HealthKit and pull steps related data aggregated by day using the HKStatisticsCollectionQuery. Requirement is to pull steps data specific to only iPhone and Apple Watch devices separately (no de-duplication) which have contributed to the health app.
The HKSource class only exposes the following properties:
name - Cannot be used as the user can change this to anything from just 'XXXX iPhone'
bundleIdentifier - Provides us the UUID for the device (unique per device, so different for every iPhone/Watch), and it looks like com.apple.health.UUID, here's what the Apple documentation says : "For apps, this property holds the app’s bundle identifier. For supported Bluetooth LE devices, this property holds a UUID for the device."
I am able to pull all sources (using a HKSourceQuery) which have the bundleIdentifier prefix of 'com.apple.health', but am unable to deduce which is an Apple iPhone versus which is an Apple iWatch.
Has anybody faced a similar situation before, and is there any other way to identify which source is an iPhone or Apple Watch?
Any help would be great!.Thanks!
Not the best solution but, I have figured out a way to distinguish between the watch and the phone using the following process:
I noticed that all step data coming from the iPhone/Watch have the following bundleIdentifier format:
com.apple.health.DeviceUUID
Note that manually entered data into the Health app has a bundle identifier of com.apple.Health (with a capital 'H').
So, first thing, get the device name for the phone using:
NSString *deviceName = [[UIDevice currentDevice] name];
Next, fetch all the sources for which there is a prefix match of 'com.apple.health' in the bundleIdentifier. This should give you the iPhone and the Apple watch as the valid sources and ignore the manual entries and all other apps.
Next, check if the name of the device is the same in the source, then its your iPhone, the other source should be your Apple Watch.
Here's a sample source query for fetching the sources :
- (void)fetchSources
{
NSString *deviceName = [[UIDevice currentDevice] name];
NSMutableArray *dataSources = [[NSMutableArray alloc] init];
HKQuantityType *stepsCount = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSourceQuery *sourceQuery = [[HKSourceQuery alloc] initWithSampleType:stepsCount
samplePredicate:nil
completionHandler:^(HKSourceQuery *query, NSSet *sources, NSError *error)
{
for (HKSource *source in sources)
{
if ([source.bundleIdentifier hasPrefix:sourceIdentifier])
{
if ([source.name isEqualToString:deviceName])
// Iphone
else
// Apple Watch
[dataSources addObject:source];
}
}
}];
[self.healthStore executeQuery:sourceQuery];
}
You can now create a predicate with each source for your data pull using the NSPredicate class:
NSPredicate *sourcesPredicate = [HKQuery predicateForObjectsFromSource:source];
Note that my first thought was to match the UUID, but when I generate a UUID using the NSUUID class, it does not match with the one present in the bundle identifier in the pulled sources.
Also, you can change the name of the phone to whatever you want, it will automatically update in the Health app as well.
As I said, not the best solution but works for me, and it's the only way I could find to do this. Please let me know if you were able to find a better solution. Thanks.
I am writing an iOS app to communicate with a BLE device. The device can change names between connections (not during the BLE connection), but iOS refuses to change the device name.
For example: I can connect to the device when its name is SadName. I disconnect it, shut down the app, etc. and change the device's name to HappyName. But, when I scan for devices iOS still shows the peripheral name as SadName.
If I debug the app and look at:
(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
the value of peripheral.name is SadName so I don't think that it is something that I am interpreting incorrectly in code. I should mention that when I scan for devices, my code is:
[self.CM scanForPeripheralsWithServices:nil options:0]; // Start scanning
I am guessing that it is simply because the devices UUID is the same so iOS is pulling it from its cached devices list, but I want to override that.
Thoughts? Sorry, I am new to iOS.
Cheers -
MSchmidtbauer
The CoreBluetooth API of iOS SDK does not provide a way to force refresh the peripheral name.
Currently it is not feasible to use peripheral.name in iOS when the device name in the BLEdevice changes.
Apple suggests to scan for a specific device by specifying a list of CBUUID objects (containing one or more service UUIDs) that you pass to scanForPeripheralsWithServices:
NSArray *services = #[[CBUUID UUIDWithString: #"2456e1b9-26e2-8f83-e744-f34f01e9d701"] ]; // change to your service UUID!
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
[self.manager scanForPeripheralsWithServices:services options:dictionary];
This reduces the number of calls of didDiscoverPeripheral. Do not just pass nil to scanForPeripheralsWithServices. It also allows your app to scan for a peripheral when in background state.
If you are looking for a way to broadcast dynamic information that's available before a connection is established, you can use the Advertise or Scan Response Data. The peripheral can be configured to broadcast the entries called Local Name and Manufacturer Specific Data. This data is availabe in the didDiscoverPeripheral:
- (void)centralManager: (CBCentralManager *)central
didDiscoverPeripheral: (CBPeripheral *)peripheral
advertisementData: (NSDictionary *)advertisementData
RSSI: (NSNumber *)RSSI {
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
NSData *manufacturerData = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey];
NSLog(#"Local: name: %#", localName);
NSLog(#"Manufact. Data: %#", [manufacturerData description]);
}
Local Name is an NSString, so write only printable characters on the BLE device in this filed. Manufacturer Data is an NSData, this can contain any byte value, so you can even have binary data here.
Depending on the BLE device you use, the length of Local Name and Manufacturer Specific Data is limited.
On my BLE device,I can send the 128 Bit service UUID and a 8 char Local Name with the Advertise Data. The Manufacturer Specific Data goes into the Scan Response Data and can be 29 bytes long.
Good thing about using the Adv./Scan Response Data is, it can change on this BLE device without a power cycle.
Suggestion:
Use the service UUID to filter when scanning (UUID must be part of advertising data! I omitted it in the above description)
Use the Advertise/Scan Response Data for further filtering
Forget about peripheral.name as long as there is no deterministic refresh available
Your guessing is correct.
It is because of the core-blutetooth cache.
Generally changing name / services / characteristics on BLE devices are "not supported". All these parameters are getting cached.
There are two ways of solving this:
restart bluetooth adapter, so bluetooth cache gets cleared (I'm afraid there is no way to do this programatically, but i might be wrong)
your device BLE implements the GATT Service Changed characteristic: read about this here: core_v4.1.zip
Vol 3, Part G, 2.5.2, and Vol 3, Part G, 7.1.
Alternatively check the advertisement data of your BLE device. It might have a name property which should get refreshed every time the BLE device is advertising data (advertising data doesn't get cachced).
The CBPeripheralDelegate protocol contains a method...
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
... which is made for this purpose.
Edit - just realized that the second part of the accepted answer above has the same solution :-( I should have read more closely. I will leave this answer here anyway, since it includes RoboVM code.
I have found a solution to this problem. Adding the GATT Service Changed characteristic didn't work, nor did reading the device name directly from the Device Name characteristic 2A00 since iOS hides the Generic Access service. However, if the peripheral includes its local name in an advertising packet, it is available from the advertisement data dictionary provided on a scan result using the retrieval key CBAdvertisementDataLocalNameKey. I copy this into my BLE device wrapper and use it instead of the name available from the CBPeripheral. Example code in Java for RoboVM is below. The OBJC or Swift equivalent is straightforward.
#Override
public void didDiscoverPeripheral(CBCentralManager cbCentralManager, CBPeripheral cbPeripheral, CBAdvertisementData cbAdvertisementData, NSNumber rssi) {
NSData manufacturerData = cbAdvertisementData.getManufacturerData();
byte[] data = null;
if(manufacturerData != null)
data = manufacturerData.getBytes();
IosBleDevice bleDevice = new IosBleDevice(cbPeripheral);
String name = cbAdvertisementData.getLocalName();
if(name != null && !name.equals(cbPeripheral.getName())) {
CJLog.logMsg("Set local name to %s (was %s)", name, cbPeripheral.getName());
bleDevice.setName(name);
}
deviceList.put(bleDevice.getAddress(), bleDevice);
if(!iosBlueMaxService.getSubscriber().isDisposed()) {
BleScanResult bleScanResult = new IosBleScanResult(bleDevice,
cbAdvertisementData.isConnectable(),
data);
bleScanResult.setRssi(rssi.intValue());
iosBlueMaxService.getSubscriber().onNext(bleScanResult);
}
}
I just need to get my ios device's bluetooth state.
Is there somebody know the API,I have search for the apple.com but didn't found sth, also I know I can use the CoreBluetooth,but it can just be used when IOS6 later,so what about IOS5?
Use CBCentralManager for that, the property state has these values:
typedef enum {
CBCentralManagerStateUnknown = 0,
CBCentralManagerStateResetting,
CBCentralManagerStateUnsupported,
CBCentralManagerStateUnauthorized,
CBCentralManagerStatePoweredOff,
CBCentralManagerStatePoweredOn,
}
Find all the documentation here:
CBCentralManager Apple Doc
I am in big trouble with my exams.
I have to write an iOS app that uses an external sensor made by Texas Instruments, it's called TI Sensortag.
TI's documentation, in my humble opinion, is really poor and complicated to understand for an entry level programmer.
I tried to ask in the E2E forum but they weren't able to help me, their answer was something like "Um, well, we don't know, go away and ask someone else", ...
I added the CoreBluetooth framework to my project an created a CentralManager. I am able to find my device, connect and get his name and (sometimes) his RSSI.
Now what I'm trying to do is to ask my CBPeripheral object if it has some services for me or something like that. I've found the Complete Attribute Table but I have no idea how to use it...
I know I have to activate some services or something like that but I really don't now ho to do it, I googled a lot but I've not found something helpful...
I'm trying to enable my sensor with this method, but I'm doing something wrong.
-(void) configureSensorTag
{
uint8_t myData = 0x01;
NSData *data = [[NSData alloc] initWithBytes:&myData length:1];
[BLEUtility writeCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA02-0451-4000-B000-000000000000" data:data];
[BLEUtility setNotificationForCharacteristic:myPer sUUID:#"F000AA00-0451-4000-B000-000000000000" cUUID:#"F000AA01-0451-4000-B000-000000000000" enable:YES];
NSLog(#"Configured TI SensorTag IR Termometer Service profile");
}
Moreover I'm trying to retrive Sensortag's services with this method
[peripheral discoverServices:nil];
and his delegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(#"Found service");
if (error) {
NSLog(#"Error: %#", error);
}
}
but it is never called.
Has someone any idea?
Thank you very much!
Unfortunately I can't help you with the details of the iOS, but I can help you with understanding the sensor tag. If you look at that attribute PDF you linked you'll find entries marked "GATT_CLIENT_CHAR_CFG_UUID". It's 16 bits of flags where only the 2 least significant bits are used. It even says in there 'Write "01:00" to enable notifications, "00:00" to disable'. (That's the least significant bit because it's encoded in little-endian format)
So, you're sending a 0x01 to turn on the IR temperature sensor, but you haven't turned on the notifications. Turning it on will then cause the device to stream notifications back to the client. The accelerometer doesn't require turning on, so maybe you should try that first.
I have no idea what that second chunk of code is supposed to be doing... sorry.
Ok ok I got it,
there were any software problem, I mean, not by iOS side.
Sensortag has a wrong firmware and so it did'n work.
I've changed Sensortag and now everything works fine.
Thank you anyway!
I'm using bonjour to find other devices. Each device uses TXTRecordData to share its name:
NSDictionary* dictionary = #{ #"name": #"Goose" };
[service setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:dictionary]];
Each device listens for changes:
- (void) netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data
{
...
}
About 80% of the time it works - didUpdateTXTRecordData is called when a name is changed.
Sometimes the other devices are never notified.
I've checked and setTXTRecordData returns YES when the data is lost.
How can I make sure updates to the TXTRecordData makes it to other devices?
Someone posted a gist demonstrating what is possibly the above bug.
For you Apple people out there, the gist says the relevant rdar is rdar://11018654