iOS2014. 5. 19. 19:09




코어블루투스 백그라운드 실행모드

- info.plist에 임의값을 추가하여 속성을 부여 가능

 UIBackgroundModes에 bluetooth-central또는  bluetooth-peripheral를 지정하면 됨.

- 2가지 모드지원

  1. central role

      bluetooth와 low energy peripheral로 통신한다.

  2. peripheral role

      데이터를 공유한다.


 Bluetooth-central 백그라운드 모드

 - 앱이 백그라운드 모드이어도 장치를 찾고(discover) 연결(connect)할 수 있으며 데이터를 탐색하고 연동할 수 있다.

 - CBCentralManagerDelegate와 CBPeripheralDelegate 델리게이트메소드로 앱을 깨울 수 있으며 중요한 central role 이벤트를 처리할수 있게 해준다(연결이나 연결해재, 속성값이 변경되었을때, 그리고 central manager의 상태가 변경되었을 때 등).

 - 백그라운드에서 블루투스동작을 할 때와 포그라운드에서 동작할때가 서러 다르다는것을 인지해야 한다. 특히 장치의 검색과정이 그렇다.

  -> CBCentralManagerScanOptionAllowDuplicatesKey 스캔옵션키는 무시된다. 그리고 여러개의 광고장치의 발견은 하나의 이벤트로 모아진다.

  -> 모든 앱들이 백그라운드에서 장치를 스캐닝한다면 스캐닝 인터벌은 늘어날것이고 그로 인해 장치를 찾는데 오래걸릴것이다.


bluetooth-peripheral 백그라운드모드

info.plist에 UIBackgroundModes키에 bluetooth-peripheral를 셋팅하면 되며 시스템이 등록된 이벤트들이 발생하면 앱을 깨워줄것이다.

추가적으로 연결된 central들로부터의 요청을 처리할 수 있도록 하기 위해 bluetooth framework은 백그라운드 동안 앱의 상태를 공시(advertise)한다. 즉 앱이 포그라운드 상태인지 상태를 체크해야 한다.

 - CBAdvertisementDataLocalNameKey 키는 무시된다. 그리고 페리퍼렁의 로칼네임은 공시되지 않는다.

 - 모든 서비스 UUID들(CBAdvertisementDataServiceUUIDsKey 키에 값으로 저장된)는 "overflow"영역에 위치하며 오로지 하나의 iOS장치(명시적으로 검색하고 있는)에 발견될 수 있다.


어떤 백그라운드 실행모드를 사용할것인가?

 백그라운드모드 한가지나 두가지를 모두 동시에도 사용이 가능하지만 백그라운드로 수행하는 작업은 정확하게 처리하고 정리되어야 한다. 왜냐 하면 배터리사용량때문에 문제가 발생할 수 있기 때문이다.

앱은 블루투스관련 이벤트를 가능한 빨리 처리하고 리턴되어야 하며 다시 대기상태가 될 수 있다. 배터리는 그렇게 절약하는 것이다.

모든 블루투스 백그라운드실행모드를 지원하는 앱은 다음의 단계를 따라야 한다.

  • 앱은 세션기반이어야 하며 사용자에게 블루투스 동작의 시작과 정지를 결정할 수 있는 인터페이스를 제공해야 한다.

  • 꺠어나면 앱은 10초안에 관련 테스크를 모두 마쳐야 한다.안그러면 시스템에 막대한 로드(병목)를 줄수 있으며 앱이 죽을수도 있다.
  • 쓸데없이 깨우는 짓을 하지 않아야 한다. 꼭 필요한 경우에만 앱을 깨울 수 있도록 해야 함.

백그라운드에서 아주 오래걸리는 동작의 수행하기


Some apps may need to use the Core Bluetooth framework to perform long-term actions in the background. As an example, imagine you are developing a home security app for an iOS device that communicates with a door lock (equipped with Bluetooth low energy technology). The app and the lock interact to automatically lock the door when the user leaves home and unlock the door when the user returns—all while the app is in the background. When the user leaves home, the iOS device may eventually become out of range of the lock, causing the connection to the lock to be lost. At this point, the app can simply call theconnectPeripheral:options: method of the CBCentralManagerclass, and because connection requests do not time out, the iOS device will reconnect when the user returns home.

Now imagine that the user is away from home for a few days. If the app is terminated by the system while the user is away, the app will not be able to reconnect to the lock when the user returns home, and the user may not be able to unlock the door. For apps like these, it is critical to be able to continue using Core Bluetooth to perform long-term actions, such as monitoring active and pending connections.

State Preservation and Restoration

Because state preservation and restoration is built in to Core Bluetooth, your app can opt in to this feature to ask the system to preserve the state of your app’s central and peripheral managers and to continue performing certain Bluetooth-related tasks on their behalf, even when your app is no longer running. When one of these tasks completes, the system relaunches your app into the background and gives your app the opportunity to restore its state and to handle the event appropriately. In the case of the home security app described above, the system would monitor the connection request, and re-relaunch the app to handle thecentralManager:didConnectPeripheral: delegate callback when the user returned home and the connection request completed.

Core Bluetooth supports state preservation and restoration for apps that implement the central role, peripheral role, or both. When your app implements the central role and adds support for state preservation and restoration, the system saves the state of your central manager object when the system is about to terminate your app to free up memory (if your app has multiple central managers, you can choose which ones you want the system to keep track of). In particular, for a given CBCentralManager object, the system keeps track of:

  • The services the central manager was scanning for (and any scan options specified when the scan started)

  • The peripherals the central manager was trying to connect to or had already connected to

  • The characteristics the central manager was subscribed to

Apps that implement the peripheral role can likewise take advantage of state preservation and restoration. For CBPeripheralManagerobjects, the system keeps track of:

  • The data the peripheral manager was advertising

  • The services and characteristics the peripheral manager published to the device’s database

  • The centrals that were subscribed to your characteristics’ values

When your app is relaunched into the background by the system (because a peripheral your app was scanning for is discovered, for instance), you can reinstantiate your app’s central and peripheral managers and restore their state. The following section describes in detail how to take advantage of state preservation and restoration in your app.

Adding Support for State Preservation and Restoration

State preservation and restoration in Core Bluetooth is an opt-in feature and requires help from your app to work. You can add support for this feature in your app by following this process:

  1. (Required) Opt in to state preservation and restoration when you allocate and initialize a central or peripheral manager object. This step is described in “Opt In to State Preservation and Restoration.”

  2. (Required) Reinstantiate any central or peripheral manager objects after your app is relaunched by the system. This step is described in “Reinstantiate Your Central and Peripheral Managers.”

  3. (Required) Implement the appropriate restoration delegate method. This step is described in “Implement the Appropriate Restoration Delegate Method.”

  4. (Optional) Update your central and peripheral managers’ initialization process. This step is described in “Update Your Initialization Process.”

Opt In to State Preservation and Restoration

To opt in to the state preservation and restoration feature, simply provide a unique restoration identifier when you allocate and initialize a central or peripheral manager. A restoration identifier is a string that identifies the central or peripheral manager to Core Bluetooth and to your app. The value of the string is significant only to your code, but the presence of this string tells Core Bluetooth that it needs to preserve the state of the tagged object. Core Bluetooth preserves the state of only those objects that have a restoration identifier.

For example, to opt in to state preservation and restoration in an app that uses only one instance of a CBCentralManager object to implement the central role, specify theCBCentralManagerOptionRestoreIdentifierKey initialization option and provide a restoration identifier for the central manager when you allocate and initialize it.

    myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil
         options:@{ CBCentralManagerOptionRestoreIdentifierKey:
         @"myCentralManagerIdentifier" }];

Although the above example does not demonstrate this, you opt in to state preservation and restoration in an app that uses peripheral manager objects in an analogous way: Specify theCBPeripheralManagerOptionRestoreIdentifierKeyinitialization option, and provide a restoration identifier when you allocate and initialize each peripheral manager object.

Reinstantiate Your Central and Peripheral Managers

When your app is relaunched into the background by the system, the first thing you need to do is reinstantiate the appropriate central and peripheral managers with the same restoration identifiers as they had when they were first created. If your app uses only one central or peripheral manager, and that manager exists for the lifetime of your app, there is nothing more you need to do for this step.

If your app uses more than one central or peripheral manager or if it uses a manager that isn’t around for the lifetime of your app, your app needs to know which managers to reinstantiate when it is relaunched by the system. You can access a list of all the restoration identifiers for the manager objects the system was preserving for your app when it was terminated, by using the appropriate launch option keys (UIApplicationLaunchOptionsBluetoothCentralsKey orUIApplicationLaunchOptionsBluetoothPeripheralsKey) when implementing your app delegate’sapplication:didFinishLaunchingWithOptions: method.

For example, when your app is relaunched by system, you can retrieve all the restoration identifiers for the central manager objects the system was preserving for your app, like this:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 
    NSArray *centralManagerIdentifiers =
        launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
    ...

After you have the list of restoration identifiers, simply loop through it and reinstantiate the appropriate central manager objects.

Implement the Appropriate Restoration Delegate Method

After you have reinstantiated the appropriate central and peripheral managers in your app, restore them by synchronizing their state with the state of the Bluetooth system. To bring your app up to speed with what the system has been doing on its behalf (while it was not running), you must implement the appropriate restoration delegate method. For central managers, implement thecentralManager:willRestoreState: delegate method; for peripheral managers, implement theperipheralManager:willRestoreState: delegate method.

In both of the above delegate methods, the last parameter is a dictionary that contains information about the managers that were preserved at the time the app was terminated. For a list of the available dictionary keys, see the Central Manager State Restoration Options constants in CBCentralManagerDelegate Protocol Reference and thePeripheral_Manager_State_Restoration_Options constants inCBPeripheralManagerDelegate Protocol Reference.

To restore the state of a CBCentralManager object, use the keys to the dictionary that is provided in thecentralManager:willRestoreState: delegate method. As an example, if your central manager object had any active or pending connections at the time your app was terminated, the system continued to monitor them on your app’s behalf. As the following shows, you can use theCBCentralManagerRestoredStatePeripheralsKey dictionary key to get of a list of all the peripherals (represented byCBPeripheral objects) the central manager was connected to or was trying to connect to:

- (void)centralManager:(CBCentralManager *)central
      willRestoreState:(NSDictionary *)state {
 
    NSArray *peripherals =
        state[CBCentralManagerRestoredStatePeripheralsKey];
    ...

What you do with the list of restored peripherals in the above example depends on the use case. For instance, if your app keeps a list of the peripherals the central manager discovers, you may want to add the restored peripherals to that list to keep references to them. As described in “Connecting to a Peripheral Device After You’ve Discovered It,” be sure to set a peripheral’s delegate to ensure that it receives the appropriate callbacks.

You can restore the state of a CBPeripheralManager object in a similar way by using the keys to the dictionary that is provided in theperipheralManager:willRestoreState: delegate method.

Update Your Initialization Process

After you have implemented the previous three required steps, you may want to take a look at updating your central and peripheral managers’ initialization process. Although this is an optional step, it can be important in ensuring that things run smoothly in your app. As an example, your app may have been terminated while it was in the middle of exploring the data of a connected peripheral. When your app is restored with this peripheral, it won’t know how far it made it the discovery process at the time it was terminated. You’ll want to make sure you’re starting from where you left off in the discovery process.

For example, when initializing your app in thecentralManagerDidUpdateState: delegate method, you can find out if you successfully discovered a particular service of a restored peripheral (before your app was terminated), like this:

    NSUInteger serviceUUIDIndex =
        [peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,
        NSUInteger index, BOOL *stop) {
            return [obj.UUID isEqual:myServiceUUIDString];
        }];
 
    if (serviceUUIDIndex == NSNotFound) {
        [peripheral discoverServices:@[myServiceUUIDString]];
        ...

As the above example shows, if the system terminated your app before it finished discovering the service, begin the exploring the restored peripheral’s data at that point by calling thediscoverServices:. If your app discovered the service successfully, you can then check to see whether the appropriate characteristics were discovered (and whether you already subscribed to them). By updating your initialization process in this manner, you’ll ensure that you’re calling the right methods at the right time.


Posted by 삼스