その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

Core Bluetooth のラッパーライブラリ『LGBluetooth』の使い方

Core Bluetooth はそれほど規模の大きいフレームワークではないので、最初は全容を把握するためにライブラリに頼らずそのまま使ってみるのがおすすめなのですが、ペリフェラルのスキャンやコネクト時のタイムアウト処理等、結局毎回書く必要があって面倒だなーと思う部分もあります。


そのあたりいい感じに書かれているOSSがないかなとGitHub検索してみたところ、次の2つが良さそうでした。


どちらも block-based を売りにしています。


ヘッダだけ見ると YmsCoreBluetooth の方がペリフェラルの保存まで考慮されていて高機能そうな気もしたのですが、どっちも試すのは面倒なので、

  • 更新日が新しい
  • スター数が多い
  • タイムアウト処理が実装されている

という理由からまず LGBluetooth の方だけ試してみました。

ペリフェラルをスキャンする

LGCentralManager の `scanForPeripheralsByInterval:services:options:completion:` メソッドでスキャンを開始します。第1引数に スキャンする時間 [秒] を NSUInteger で指定できるようになっています。

[[LGCentralManager sharedInstance] scanForPeripheralsByInterval:5
                                                       services:services
                                                        options:options
                                                     completion:^(NSArray *peripherals) {
                                                         
                                                         // スキャン完了後の処理
                                                     }];


が、この処理、ソースコードを読んでみると、タイムアウトというよりは、指定時間が経過したら completion ブロックを実行するという実装になっていました。つまり、たとえば scanInterval に10秒を指定したらその間にいくつペリフェラルが見つかっても完了ブロックは実行されません。


これじゃあ使い物にならない、ということで、次のプロパティを追加して Pull Request を送りました。

@property (assign, nonatomic) NSUInteger peripheralsCountToStop;


たとえば下記のように1をセットしておくと、scanInterval 経過前でも、ペリフェラルが1つ見つかった時点で stopScan してスキャン完了ブロックが呼ばれます。

[[LGCentralManager sharedInstance] setPeripheralsCountToStop:1];


既に Merge されています

ペリフェラルに接続する

[peripheral connectWithCompletion:^(NSError *error) {

	// 完了処理
}];

サービスを探索する

[peripheral discoverServicesWithCompletion:^(NSArray *services, NSError *error) {
    
    // 完了処理
}];

キャラクタリスティックを探索する

[service discoverCharacteristicsWithCompletion:^(NSArray *characteristics, NSError *error) {

    // 完了処理    
}];

データをwriteする

[self.characteristic writeValue:data
                     completion:^(NSError *error) {
                         
                         // write完了後の処理
                     }];

接続が切れたときに処理を行う

CBCentralManagerDelegate は内包されてるので、たとえば切断時の処理とかはどうするかというと、 `centralManager:didDisconnectPeripheral:error:` のタイミングで `kLGPeripheralDidDisconnect` という名前の通知が発行されるので、それを監視しておきます。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(peripheralDidDisconnect:)
                                             name:kLGPeripheralDidDisconnect
                                           object:nil];
- (void)peripheralDidDisconnect:(NSNotification *)notification {
    
    // 切断時の処理
}

ログを黙らせる

デフォルトのままだと、デバッグビルドでは値がとんでくるたびにログをはくようになっています。


これを黙らせるには、Podfileで次のように記述します。

pod 'LGBluetooth', :git => 'https://github.com/SocialObjects-Software/LGBluetooth.git'

# インストール後に実行される処理を記述
post_install do |installer|

  POD_TARGET_NAME = "Pods-LGBluetooth"

  # 変更したいビルドターゲットを探す
  classy_pods_target = installer.project.targets.find{ |target| target.name == POD_TARGET_NAME }
  unless classy_pods_target
    raise ::Pod::Informative, "Failed to find '" << POD_TARGET_NAME << "' target"
  end

  # ビルド設定を追加
  classy_pods_target.build_configurations.each do |config|
    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'LG_BLE_SILENCE'
  end

end


上記はCocoaPods使用時ですが、ソースを直接プロジェクトに追加して使用する場合は次のようにpchファイル等で定義するか、

#define LG_BLE_SILENCE


ビルド設定の [Preprocessor Macros] に "LG_BLE_SILENCE" を追加すればOKです。


参考記事:

所感

以上のように API はわかりやすいし、ソースも読みやすいし、プルリクにもすぐに対応してくれるので、結構いい感じです。