1

I am working on a project to display the list of all Bluetooth LE devises with services with specific UUIDs. In method didDiscoverPeripheral, I save the discovered peripherals that are advertising. I use the option dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO] when scanning for peripherals. I have an NSTimer to wake up every 30 seconds to update the list of discovered peripherals and see if all peripherals are still advertising. I use retrievePeripheralsWithIdentifiers method that I pass in an array of NSUUID of discovered and saved peripherals. This method is supposed to return an array of CBPeripherals that are still advertising. But it returns the original array of all the peripherals that I pass in as an argument, and it never sorts out the peripherals that are no longer advertising. Am I using this method incorrectly?

savedPeripherals is an NSDictionary where device ID is the Key, and CBPeripheral is the Value.

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:30
                                target:self
                                selector:@selector(updateActivePeripherals:)
                                userInfo:nil
                                repeats:YES];

    -   (void) updateActivePeripherals:(NSTimer *)timer
    {
        NSMutableArray *peripherals = [[NSMutableArray alloc]init];
        if (self.savedPeripherals.count > 0)
        {
            for(id key in self.savedPeripherals)
            {
                CBPeripheral *item = [self.savedPeripherals objectForKey:key];
                NSString *identifier=[NSString stringWithFormat:@"%@", [[item identifier] UUIDString]];
                NSUUID *id=[[NSUUID alloc]initWithUUIDString:identifier];

                if (id)
                    [peripherals addObject:id];
            }

        }

        if (peripherals.count > 0)
        {
            NSArray *list =[_centralManager retrievePeripheralsWithIdentifiers:peripherals];
        }
    }
}
Jade
  • 3,156
  • 1
  • 34
  • 44
Alina
  • 61
  • 5

1 Answers1

3

The documentation doesn't state that retrievePeripheralsWithIdentifiers will return peripherals that are still visible/advertising. Rather, it says it returns:

A list of peripherals that the central manager is able to match to the provided identifiers.

The central manager will return known peripherals even if the peripheral is not currently visible. This is to allow apps to connect automatically to a particular peripheral once it becomes visible. The workflow goes like this:

  1. Application launches and retrieves desired peripheral identifier (such as from NSUserDefaults)
  2. Application requests CBPeripheral via retrievePeripheralsWithIdentifiers
  3. Application issues a connect to that peripheral
  4. If the peripheral is currently visible, then the connection happens immediately and the delegate method is called
  5. If the peripheral is not currently visible then the connection will happpen when/if the peripheral becomes visible and the delegate method will be called.

In order to achieve the functionality you require, you will need to use CBCentralManagerScanOptionAllowDuplicatesKey:YES and keep an age against each peripheral. For example, you could use the following approach:

  1. Set up a dictionary, keyed by the peripheral identifier
  2. When a peripheral is seen, store an NSNumber in the dictionary with value 30, also store the peripheral in your table view's source array if necessary
  3. Set up an NSTimer to fire every second.
  4. Each time the timer fires, go through the dictionary and decrement the value stored against each key
  5. For each value that has decremented to 0, remove it from the tableview array and update the tableview
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • That what I was thinking of doing, but I was hoping for a bit less resource intense approach. Setting the option dictionaryWithObjectsAndKeys to YES drains i-Phone battery quickly. Thank you for such prompt response! – Alina Apr 03 '16 at 02:13
  • Well, that option only takes effect when your application is in the foreground and the Bluetooth radio won't drain the battery that quickly compared to the display. This is your only option if you want to display visible peripherals. It is the same process that iOS would need to perform internally anyway if it were to provide you a list of visible peripherals, except it would need to do this all the time in order to be able to respond to an app's request; which may never come, so it would be less efficient than your app doing it as required – Paulw11 Apr 03 '16 at 02:27