3

I am working on an iPhone app and have implemented CBCentralManager. Also updated plist with background modes, initialized centralmanager with an identifier.

Also have added this code in the didFinishLaunchingWithOptions

if var centralManagerIdentifiers: NSArray = launchOptions?    [UIApplicationLaunchOptionsBluetoothCentralsKey] as? NSArray {
    // Awake as Bluetooth Central
    // No further logic here, will be handled by centralManager willRestoreState

    for identifier in  centralManagerIdentifiers {
        if identifier as NSString == "centralManager"{
            var notification = UILocalNotification()
            notification.alertBody = String(centralManagerIdentifiers.count) 
            notification.alertAction = "open" 
            notification.fireDate =  NSDate()
            notification.soundName = UILocalNotificationDefaultSoundName 
            UIApplication.sharedApplication().scheduleLocalNotification(notification)

            }
        }
}

I have created a central manager in different class and that is singleton.

    class var sharedInstance: BLEManager {
    struct Singleton {
        static let instance = BLEManager()
    }

    return Singleton.instance
}


override init() {
    super.init()
    let centralQueue = dispatch_queue_create(“centralManager_queue”, DISPATCH_QUEUE_SERIAL)
    centralManager = CBCentralManager(delegate: self, queue: centralQueue, options: [CBCentralManagerOptionRestoreIdentifierKey : "centralManager"])
}

If i don't use my app for a day or two and then peripheral starts advertising, app wakes and fires this notification but doesn't call any CBCentral delegate method. I have also implemented willRestoreState method but thats also not getting card.

Use case: I need to connect the peripheral and send data once its starts advertising, even though app is not being used. Where should I handle connection process when app gets didFinishLaunchingWithOptions call? do i have to initialize the centralManager in the did finishlaunch method?

Paragon
  • 982
  • 2
  • 10
  • 37

1 Answers1

4

The central manager must be initialized immediately to handle the delegate call. The delegate method gets called once the CBPeripheralManager is created and the delegate is passed to the init method. If the identifier of the manager matches the pending session the state will be restored.

A good point to instantiate the manager is in the didFinishLaunchingWithOptions method.

You can test it easy by causing a crash, or by pressing the stop button of xCode while testing, and waiting for a new action triggered by the BLE (a notification from a notifying characteristic for example). It's much faster than waiting few days that the system kills the app for inactivity.

Giuseppe Lanza
  • 3,519
  • 1
  • 18
  • 40
  • so it works first time, when i turn on the peripheral it calls the willRestoreState , didUpdateState, didConnectPeripherial. When i turn off the peripheral it also called willRestoreState and if i turn back on it doesn't do anything unless i unlock the iphone. – Paragon Aug 18 '15 at 14:53
  • This is a different problem. It looks like you are not using properly the restoration flow. – Giuseppe Lanza Aug 18 '15 at 15:08
  • is it supposed to call willRestoreState when i turn off the peripheral? – Paragon Aug 18 '15 at 15:11
  • i have added everything to appdelegate now and doing this in didFinishLaunchingWithOptions self.centralManager = centralManagerIdentifiers["myCentralManager"] as! CBCentralManager – Paragon Aug 18 '15 at 15:16
  • when you turn off the peripheral you get a didDisconnected call, so iOS will try to awake your app to deliver the message. When you turn on the peripheral again there is no reason for iOS to call again the delegate unless you don't have a discover action running. In that case, it works only if the scan is with the option CBCentralManagerScanOptionAllowDuplicatesKey to NO (or without options). If you expect to connect automatically again to the peripheral, it is not magic. you must implement the reconnect flow by your own. As i said, this is a different question. – Giuseppe Lanza Aug 18 '15 at 15:33
  • in the willRestore delegate you should reschedule all the actions that you were running. so if you had a scan, you should start the scan again, and so on. Read the dictionary provided in the will restore delegate call that gives you all the informations you need to restore your state. Again, it is not magic. iOS will do nothing for you other than providing you this dictionary that says "when your app died, this was the situation. create it again and handle what's new." – Giuseppe Lanza Aug 18 '15 at 15:37
  • thanks for the help, i'll accept you answer. just small confusion i scanForPeripheralsWithServices in the centralManagerDidUpdateState method. do i have to do it somewhere else as well like didDisconnectPeripheral? Currently i am reconnecting the peripheral in diddisconnetPeripherial method – Paragon Aug 18 '15 at 15:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87287/discussion-between-paragon-and-giuseppe-lanza). – Paragon Aug 18 '15 at 15:42
  • @ Giuseppe Lanza when i turn off my peripheral it calls willRestoreState method. Weird thing is Optional([kCBRestoredScanServices: ( "6E400456-B598-F393-E0A9-E50E24DCCA9E" ), kCBRestoredPeripherals: ( "" )]) Dictionary says state is connected – Paragon Aug 18 '15 at 19:39
  • Of course. Because then you will get the delegate call to didDisconnected. Set the peripheral delegate in that point – Giuseppe Lanza Aug 18 '15 at 19:46
  • when i turn off the peripheral, it calls willRestore method but doesn't call didDisconnectPeripheral method. i am setting the delegate. if let peripherals:[CBPeripheral] = dict[CBCentralManagerRestoredStatePeripheralsKey] as! [CBPeripheral]! { if peripherals.count > 0 { for peripheral in peripherals as [CBPeripheral]!{ connectingPeripheral = peripheral connectingPeripheral.delegate = self – Paragon Aug 19 '15 at 15:59