1

I have a strange error when attempting to advertise some service data.

func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
    if let beacon = beacons.first, username = NSUserDefaults.standardUserDefaults().objectForKey("username") {
        var major = beacon.major.integerValue
        var minor = beacon.minor.integerValue

        let advertismentData:[String:AnyObject] = [
            CBAdvertisementDataServiceUUIDsKey:[AppDelegate.MajorUUID, AppDelegate.MinorUUID, AppDelegate.IdentifierUUID],
            CBAdvertisementDataServiceDataKey:[AppDelegate.MajorUUID: NSData(bytes: &major, length: sizeof(Int)), AppDelegate.MinorUUID: NSData(bytes: &minor, length: sizeof(Int)), AppDelegate.IdentifierUUID: username.dataUsingEncoding(NSUTF8StringEncoding)]
        ]
        peripheralManger?.startAdvertising(advertismentData)
        manager.stopRangingBeaconsInRegion(region)
    }
}

When passed to start advertising the following error occurs:

2015-10-09 11:17:05.563 BeConvo[280:21134] -[CBUUID UTF8String]: unrecognized selector sent to instance 0x13dd5b420 2015-10-09 11:17:05.564 BeConvo[280:21134] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CBUUID UTF8String]: unrecognized selector sent to instance 0x13dd5b420' * First throw call stack: (0x185364f5c 0x199f5bf80 0x18536bc6c 0x185368c14 0x18526cdcc 0x184fe56b4 0x185361f00 0x184fe4634 0x184fe58a8 0x184fe56d8 0x185361f00 0x184fe4634 0x184fe44dc 0x184fe4704 0x184fe47e8 0x184fe6104 0x184fe7190 0x1000eb9a4 0x1000ebae0 0x185af5f04 0x185af0b14 0x185aeaef4 0x18531c48c 0x18531bdc4 0x18531a20c 0x185248dc0 0x19039c088 0x18a922f44 0x1000ec328 0x19a7868b8) libc++abi.dylib: terminating with uncaught exception of type NSException

If I remove the ServiceDataKey or remove the contents of the dictionary then the advertisement succeeds.

func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
    if let beacon = beacons.first, username = NSUserDefaults.standardUserDefaults().objectForKey("username") {
        var major = beacon.major.integerValue
        var minor = beacon.minor.integerValue

        let advertismentData:[String:AnyObject] = [
            CBAdvertisementDataServiceUUIDsKey:[AppDelegate.MajorUUID, AppDelegate.MinorUUID, AppDelegate.IdentifierUUID],
            CBAdvertisementDataServiceDataKey:[]
        ]
        peripheralManger?.startAdvertising(advertismentData)
        manager.stopRangingBeaconsInRegion(region)
    }
}

I have tried each one of the key/value pairs to see if it a specific one but any of them in any combination causes the error to occur.

Anyone got any ideas?

Thanks for your help.

Jay Bhalani
  • 4,142
  • 8
  • 37
  • 50
Gerard Wilkinson
  • 1,512
  • 14
  • 33

2 Answers2

2

Unfortunately, the CBAdvertisementDataServiceDataKey is read only on iOS. While you can use it to read service data when discovering peripherals, you can not use it to transmit service data using a CBPeriperalManager. See here for more info: https://stackoverflow.com/a/31856456/1461050

If you wish to transmit an iBeacon packet, you can use region.peripheralDataWithMeasuredPower(nil) as @heypiotr suggests in his answer.

For the sake of understanding, note that iBeacon advertisements are manufacturer Bluetooth advertisements, not service advertisements. So even if the technique shown did work, there would be no reason to populate CBAdvertisementDataServiceUUIDsKey as shown in the example. Sending a service advertisement would not trigger iBeacon receivers.

Community
  • 1
  • 1
davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Thanks for your answer David. Is there anyway to advertise a specific value from an iOS device? The reason I cannot just use a beacon is that I want to send a specific piece of information, in this case a username and the major and minor value of the beacon I am responding to (incase there are other beacons in the region that may see the response). – Gerard Wilkinson Oct 09 '15 at 15:47
  • 3
    Yep, that is a common use case. Unfortunately, iOS limits you quite a bit when using a device to transmit a bluetooth advertisement. You can advertise a service UUID (but only without attached data.) Since a long service UUID is 16 bytes, you can use some of these bytes as the matching prefix and the rest to encode the user id and other information. A receiving device would have to scan for all service uuids (only possible in the foreground), look for the matching prefix, then parse out the data from the other bytes. – davidgyoung Oct 09 '15 at 16:51
  • 1
    Another alternative to my last comment is to do this data transfer using a web service. The transmitter would post the data to a server, then the receiver would read this data from the server. It would use network but not bluetooth. – davidgyoung Oct 09 '15 at 16:53
1

If you want to advertise as an iBeacon, it's easier and less error prone to utilize the CLBeaconRegion's peripheralDataWithMeasuredPower method, which will automatically generate appropriate advertisement data for you. Example code snippet below:

let region = CLBeaconRegion(proximityUUID: beacon.proximityUUID,
                            major: beacon.major, minor: beacon.minor,
                            identifier: "iOS beacon")
let advertisementData = region.peripheralDataWithMeasuredPower(nil)
peripheralManger?.startAdvertising(advertisementData)
heypiotr
  • 2,139
  • 2
  • 15
  • 22
  • I think you're mis understanding what I am trying to do. I need to advertise some information back to a beacon. When it enters the region of a beacon the client phone starts advertising this information and the beacon (in this case an iPad) scans for peripherals with the services advertised here and needs to read the service data I am trying to advertise. – Gerard Wilkinson Oct 09 '15 at 11:45
  • 1
    Ah, sorry, you didn't exactly explain what you're trying to do, so I made some assumptions. Keeping in mind David's note that you can't advertise service data via the peripheral manager, maybe you could take a look at the Multipeer Connectivity Framework instead, to exchange data between the iPhone and the iPad. Mattt Thompson has a great guide, as he always does: http://nshipster.com/multipeer-connectivity/ – heypiotr Oct 09 '15 at 13:05
  • I'll take a look thanks. I seems ridiculous to me that you can advertise custom services but not custom service data. I guess they don't want advertisements to be used like this. – Gerard Wilkinson Oct 09 '15 at 15:49