advertising tx power level in iOS - ios

I am currently programming the peripheral side of an app. I want to advertise the tx power level, but all i have found as far as tx documentation is:
CB_EXTERN NSString * const CBAdvertisementDataTxPowerLevelKey; // A NSNumber
I have tried to implement this in the following way:
/** Start advertising
*/
- (IBAction)switchChanged:(id)sender
{
[self.peripheralManager startAdvertising:#{ CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
[self.peripheralManager startAdvertising: CBAdvertisementDataTxPowerLevelKey];
}
#end
I keep getting a warning on my last line of code saying "Incompatible pointer types sending 'NSString*' to parameter of type 'NSDictionary*'. I understand that my TxPowerLevelKey is a NSString, but what is NSDictionary referring to?

Other answers have addressed how your dictionary is defined, however, you were looking for a higher level issue; How to transmit the txPower level from an iOS device.
The answer is that currently you can't. After you fixed your code, it compiled and ran, but CoreBluetooth is simply ignoring that key.
As stated by the documentation:
An optional dictionary containing the data you want to advertise. The
possible keys of an advertisementData dictionary are detailed in
CBCentralManagerDelegate Protocol Reference. That said, only two of
the keys are supported for peripheral manager objects:
CBAdvertisementDataLocalNameKey and
CBAdvertisementDataServiceUUIDsKey.
Hope that helps

In Objective-C, #{} is shorthand for [NSDictionary dictionaryWithObjectsAndKeys:(id), ..., nil]. The warning indicates the -[PeripheralManager startAdvertising] method is expecting an NSDictionary. Try wrapping the key in a dictionary with a Boolean True value (represented as an NSNumber object with #(YES)):
[self.peripheralManager startAdvertising:#{ CBAdvertisementDataTxPowerLevelKey : #(YES)}];

As you don't seem to know what an NSDictionary* object is please see the Apple Documentation for NSDictionary.
But to answer your question the warning
Incompatible pointer types sending 'NSString*' to parameter of type 'NSDictionary*
which is referring to
[self.peripheralManager startAdvertising: CBAdvertisementDataTxPowerLevelKey];
Is because startAdvertising: will be declared something like
- (void)startAdvertising:(NSDictionary *)start;
So it is expecting you to pass in an NSDictionary* object whereas at the moment you are passing in an NSString* object.
You can resolve this in one of two ways. The first way would be to use the short hand way like you have already done here
[self.peripheralManager startAdvertising:#{ CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
Notice that the shorthand version of an NSDictionary* object starts at #{ and ends at } so the to declare an NSDictionary* object this way it would be something like #{ Key : Object } so for you it would be #{ CBAdvertisementDataTxPowerLevelKey : #(YES) }
The second way of declaring this would be to do it as I would think of the normal way like :
[NSDictionary dictionaryWithObjectsAndKeys:#(YES), CBAdvertisementDataTxPowerLevelKey, nil]
if you have any questions please just ask.

Related

CLPeripheralManager.startAdvertising does not accept return value of CLBeaconRegion.peripheralDataWithMeasuredPower

According to the Swift 2.0 documentation for CLBeaconRegion, it should still be possible to pass the output of the peripheralDataWithMeasuredPower: method to the startAdvertising: method of CLPeripheralManager.
Getting Beacon Advertisement Data
- peripheralDataWithMeasuredPower:
Retrieves data that can be used to advertise the current device as a beacon.
Declaration
SWIFT
func peripheralDataWithMeasuredPower(_measuredPower: NSNumber?) -> NSMutableDictionary
OBJECTIVE-C
- (NSMutableDictionary<NSString *,id> * _Nonnull)peripheralDataWithMeasuredPower:(NSNumber * _Nullable)measuredPower
Parameters
measuredPower The received signal strength indicator (RSSI) value (measured in decibels) for the device.
This value represents the measured strength of the beacon from one
meter away and is used during ranging. Specify nil to use the default
value for the device.
Return Value
A dictionary of data that you can
use in conjunction with a CBPeripheralManager to advertise the current
device as a beacon.
Discussion
The returned dictionary encodes the beacon’s identifying
information along with other information needed to advertise the
beacon. You should not need to access the dictionary contents
directly. Pass the dictionary to the startAdvertising: method of a
CBPeripheralManager to begin advertising the beacon.
Availability
Available in iOS 7.0 and later.
However, peripheralDataWithMeasuredPower: returns an NSMutableDictionary whereas the startAdvertising: method of CLPeripheralManager accepts a Swift Dictionary of [String : AnyObject]?, although the documentation contends that it accepts an NSDictionary. The following code that worked in Swift 1.0:
// Set up a beacon region with the UUID, Major and Minor values
let region = CLBeaconRegion(proximityUUID:beaconUUID!, major:withMajor.unsignedShortValue, minor:withMinor.unsignedShortValue, identifier:"com.example.beacon")
// Attempt to set up a peripheral with the measured power
let peripheralData = region.peripheralDataWithMeasuredPower(withPower)
_peripheralManager!.startAdvertising(peripheralData)
In Swift 2.0 the same code fails to compile with a warning:
fails to compile, with a warning:
NSDictionary is not convertible to [String : AnyObject]; did you
mean to use as! to force downcast?
However, forcing a downcast always fails.
Is this a documentation bug, a bug in Swift 2.0, or am I missing something?
The problem seems to be that NSMutableDictionary is not easily convertible to Swift's Dictionary, but NSDictinoary is. So I ended up converting the NSMutableDictionary to a NSDictinoary first:
let pd = NSDictionary(dictionary: region.peripheralDataWithMeasuredPower(nil))
as! [String: AnyObject]
peripheralManager.startAdvertising(pd)
And it works!

App crashing when trying to setValue in NSMuableDictionary

My app is crashing when I try to set value in NSMutableDictionary.
Here is the code below which demostrate the crash, I am not able to find out any crash log also in the console.
NSArray *b =[[a objectAtIndex:1] valueForKey:#"value"];
NSMutableDictionary *b1 =[b objectAtIndex:0];
NSString *str = self.tes;
[b1 setValue:str forKey:#"value"];
Please help me regarding this.
b1 Dictionary log
{
question = "vale";
type = a;
}
setValue:forKey: is part of the NSKeyValueCoding protocol, which among other things, lets you access object properties from the likes of Interface Builder. setValue:forKey: is implemented in classes other than NSDictionary.
setObject:forKey: is NSMutableDictionary's reason to exist. Its signature happens to be quite similar to setValue:forKey:, but is more generic (e.g. any key type).
So in your case just replace all setValue:forKey with setObject:forKey and valueForKey: with objectForKey:
--
Difference between objectForKey and valueForKey?
So you said you are getting EXC_BAD_ACCESS crash. Try turning on Zombies in Xcode’s Scheme editor. See Enable and Debug Zombie objects in iOS using Xcode 5.1.1. That question explains how to use it, but you don’t need to run Instruments as mentioned there.
Then just run the app and Xcode will show you the reason of EXC_BAD_ACCESS. Don’t forget to turn Zombies off after you don’t need it.
The name Zombie comes from the fact that under this mode all deallocated objects will be marked and will be kept as special regions of memory (not live, not dead, zombies). Once your code attempts to use such deallocated object, Xcode will notice and print helpful message.

NSMutableDictionary Literal

CLBeaconRegion *region;
NSMutableDictionary *beacons;
....
beacons[region] = beacons;
Is the code above setting beacons key as region to the value of beacons? I thought the key has to be a string in a NSDictionary/NSMutableDictionary?
When you say:
beacons[region] = beacons;
That's the same as:
[beacons setObject:beacons forKey:region];
The first is just syntactical shortcut for second. Either way, it's probably not what you want, since it doesn't usually make sense to set a dictionary key/value pair that points back to the dictionary itself. What are you actually trying to do?
The key in a dictionary doesn't have to be a string, but it does have to conform to the NSCopying protocol, see the definition:
- (void)setObject:(id)anObject
forKey:(id<NSCopying>)aKey
beacons[region] = beacons; seems to be incorrect as you are adding the beacons dictionary to itself under the key region.
Note that when using key-value coding the key must be a string see Key-Value Coding Fundamentals.

CoreBluetooth - CBMutableCharacteristic or CBMutableService with meaningful UUID

I am developing an open source block based wrapper for Core Bluetooth.
Currently I am developing the Peripheral Manager part.
I have it broadcasting and I can see the service and its characteristic. When looking at other services I can see they have been given meaningful names such as Battery and Current time.
When I try and provide a meaningful name for any service or characteristic I create it returns the error String Characteristic name does not represent a valid UUID.
My code so far is
[_peripheralManager startAdvertising:#{CBAdvertisementDataLocalNameKey : #"Peripheral name",
CBAdvertisementDataServiceUUIDsKey : [CBUUID UUIDWithNSUUID:[NSUUID UUID]]}];
CBMutableCharacteristic *transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:#"Characteristic name"]
properties:CBCharacteristicPropertyRead
value:[#"Test value" dataUsingEncoding:NSUTF8StringEncoding]
permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:#"Service name"]
primary:YES];
[transferService setCharacteristics:#[transferCharacteristic]];
[_peripheralManager addService:transferService];
If I replace any initWithType arguments with [CBUUID UUIDWithNSUUID:[NSUUID UUID]] it works but is not nice to show to the user.
Is there a way to set the UUID of the CBService or CBCharacteristic to a meaningful string?
It is also worth noting that the value of the characteristic is null when viewed, bonus points for seeing why this is.
Thanks
The Bluetooth organisation has accepted some services and their characteristics as standards. These can be referred to by a 4 hex digit assigned number when using the Core Bluetooth library - See the CBUUID documentation
User defined UUIDs are not recognised by the Core Bluetooth library as they are not assigned numbers, so you can only display them with their full UUID.
The value of the characteristic will be null if you haven't issued a read or received a notify.
Use the terminal command uuidgen to create a user defined UUID.
As Paul mentioned, the value will be null until you read the value of the characteristic. It seems like a lot of work but after you discover a peripheral you connect to it (don't forget to retain the peripheral and set the peripheral's delegate property), then discover its services, then discover the characteristics for the services, then you may read the value of the characteristics like so:
[peripheral readValueForCharacteristic:characteristic]; //where peripheral is the connected CBPeripheral
In the CBCentralManagerDelegate method peripheral:didUpdateValueForCharacteristic:error: the value can be read:
NSLog(#"%#",[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding]);
Based on the code you included, it is not necessary to include the advertisement data service UUID key in your options dictionary . If you would like to specify a data service UUID that is specific to your app, start advertisementing like this:
[_peripheralManager startAdvertising:#{CBAdvertisementDataLocalNameKey : #"Peripheral name",
CBAdvertisementDataServiceUUIDsKey : #[[CBUUID UUIDWithString:#"generated-string-using-uuidgen"]]}];
You can then set your CBCentralManager to scan for that specific data service uuid like this:
[_centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:#"generated-string-using-uuidgen"]] options:nil];

How to extract a value from a key/value pair in NSDictionary (iOS)

When debugging in XCode, the debugger is telling me that the NSDictionary object contains 1 key/value pair. When the debug console prints the description of the key/value pair is shows:
Printing description of testdictionary:
{
"Unknown (<1809>)" = <000000ff>;
}
I want to extract both the <1809> and the <000000ff>. I have tried both the valueForKey and objectforKey methods as described elsewhere on this site. But I think I am having difficulty understanding what is the key and what is the value here.
For example, is "Unknown (<1809>)" the key? Or is "<1809>" the key? Or is 1809 the key?
Thanks Tim for the reply.
The NSDictionary comes from the CoreBluetoothFramework the didDiscoverPeripheral: method is called and passes advertising data into an NSDictionary called "advertisementData".
This dictionary contains all sorts of stuff like the advertising channel and device name. However, I am trying to extract just the advertising data from "advertisementData". I used the key provided by corebluetooth "CBAdvertisementDataServiceDataKey" like this:
NSData* information;
information = [advertisementData objectForKey:CBAdvertisementDataServiceDataKey];
I was declaring "information" as an NSDictionary* object before. But changed it to NSData* after some more reading on Apples documentation. The result is the same. The debugger says that it contains a key/value pair as follows:
"Unknown (<1809>)" = <000000ff>;
Thanks again.
Nik
When you do not know the keys that are present in the dictionary, for example, because the key-value pairs come from an external source, you can use enumerateKeysAndObjectsUsingBlock: method to go through all key-value pairs present in the dictionary:
[testdictionary enumerateKeysAndObjectsUsingBlock::^(id key, id object, BOOL *stop) {
NSLog(#"The key is %#", key);
NSLog(#"The value is %#", object);
}];
I've never seen this before so this is nothing more than an educated guess:
The dictionary may have been casted from CFDictionaryRef, in which case both the key and value are const void * (instead of NSObject). The key might have been some Core Foundation type holding a file descriptor (hence 1809). The value could be a pointer (or an integer casted to a "pointer": (void *)32).
You should try and find out where the dictionary originates from, because it's the only thing that's going to give you any valuable information.
Update: the docs state that the value of CBAdvertisementDataServiceDataKey is a dictionary. The keys are CBUUID objects, representing CBService UUIDs and the values are NSData objects. (1)

Resources