Bluetoothで接続したPeripheralのサービスを検索しても、didDiscoverServicesが呼び出されない。
BLEデバイスを検索するViewから検索して接続すると、
違うTABbarに遷移して、デバイス上のServiceをTableViewで表示する
プログラムを作成しています。
そのなかで、検索→接続→遷移まではうまくいくのですが、
didDiscoverServicesが呼び出されない状態です。
特にエラーも出ない状態で手詰まり状態なので、どこに問題があるのか、
教えて頂けないでしょうか?
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var SearchController: UIViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Tabに設定するViewControllerのインスタンスを生成.
let SearchTab: UIViewController = SearchViewController()
let MainTab: UIViewController = MainViewController()
// タブを要素に持つArrayの.を作成する.
let myTabs = NSArray(objects: SearchTab, MainTab)
// UITabControllerの作成する.
let TabBarController: UITabBarController = UITabBarController()
// ViewControllerを設定する.
TabBarController.setViewControllers(myTabs as? [UIViewController], animated: false)
// RootViewControllerに設定する.
self.window!.rootViewController = TabBarController
// Windowを表示する.
self.window!.makeKeyAndVisible()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
SearchView.swift
import UIKit
import CoreBluetooth
struct Constants {
static let SERVICE_UUID = "5061"
}
class SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CBCentralManagerDelegate {
var ActivityIndicator: UIActivityIndicatorView!
var myTableView: UITableView!
var myUuids: [String] = []
var myNames: [String] = []
var myRssis: [String] = []
var myPeripheral: [CBPeripheral] = []
var myCentralManager: CBCentralManager!
var myTargetPeripheral: CBPeripheral!
let myButton: UIButton = UIButton()
var serviceUUID : CBUUID!
var scanning = false
let dataSets = NSMutableArray()
init() {
super.init(nibName: nil, bundle: nil)
// Viewの背景色をCyanに設定する.
self.view.backgroundColor = UIColor.cyan
// tabBarItemのアイコンをFeaturedに、タグを1と定義する.
self.tabBarItem = UITabBarItem(tabBarSystemItem: .search, tag: 1)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: Bundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "機器を検索してください";
// 圧力のサービスUUIDを取得.
self.serviceUUID = CBUUID(string: Constants.SERVICE_UUID)
// Status Barの高さを取得.
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
// Viewの高さと幅を取得.
let displayWidth = self.view.frame.width
let displayHeight = self.view.frame.height
// TableViewの生成( status barの高さ分ずらして表示 ).
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
myTableView.estimatedRowHeight = 80
//myTableView.rowHeight = UITableViewAutomaticDimension
// Cellの登録.
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
// DataSourceの設定.
myTableView.dataSource = self
// Delegateを設定.
myTableView.delegate = self
// Viewに追加する.
self.view.addSubview(myTableView)
// ボタン生成
myButton.frame = CGRect(x: 0,y: 0,width: 100,height: 100)
myButton.layer.cornerRadius = 50.0
myButton.backgroundColor = UIColor.red;
myButton.layer.masksToBounds = true
myButton.setTitle("検索 開始", for: UIControlState())
myButton.titleLabel?.font = UIFont.systemFont(ofSize: 20)
myButton.setTitleColor(UIColor.white, for: UIControlState())
myButton.layer.position = CGPoint(x: self.view.frame.width/2, y:self.view.frame.height/3*2)
myButton.tag = 1
myButton.addTarget(self, action: #selector(SearchViewController.onClickMyButton(_:)), for: .touchUpInside)
// UIボタンをViewに追加.
self.view.addSubview(myButton);
// ActivityIndicatorを作成&中央に配置
ActivityIndicator = UIActivityIndicatorView()
ActivityIndicator.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
ActivityIndicator.center = self.view.center
// クルクルをストップした時に非表示する
ActivityIndicator.hidesWhenStopped = true
// 色を設定
ActivityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
//Viewに追加
self.view.addSubview(ActivityIndicator)
}
/*
ボタンイベント.
*/
func onClickMyButton(_ sender: UIButton){
// scan中だったら
if scanning{
self.myCentralManager.stopScan()
myButton.setTitle("検索 開始", for: UIControlState())
scanning = false
}
// scan止まってたら
else{
// 配列をリセット.
myNames = []
myUuids = []
myRssis = []
myPeripheral = []
myButton.setTitle("停止", for: UIControlState())
// CoreBluetoothを初期化および始動.
myCentralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
scanning = true
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("state \(central.state)");
switch (central.state) {
case .poweredOff:
print("Bluetoothの電源がOff")
case .poweredOn:
print("Bluetoothの電源はOn")
// BLEデバイスの検出を開始 (サービス指定)
myCentralManager.scanForPeripherals(withServices: [self.serviceUUID], options: nil)
scanning = true
case .resetting:
print("レスティング状態")
case .unauthorized:
print("非認証状態")
case .unknown:
print("不明")
case .unsupported:
print("非対応")
}
}
/*
BLEデバイスが検出された際に呼び出される.
*/
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber) {
print("NAME: \(String(describing: peripheral.name))")
print("advertisementData:\(advertisementData)")
print("RSSI: \(RSSI)")
print("UUID: \(peripheral.identifier.uuidString)")
print(" ")
let kCBAdvDataLocalName = advertisementData["kCBAdvDataLocalName"] as? String
if kCBAdvDataLocalName != nil {
myNames.append(kCBAdvDataLocalName!)
}
else {
myNames.append("no name")
}
myRssis.append("\(RSSI)")
myPeripheral.append(peripheral)
myUuids.append("\(peripheral.identifier.uuidString)")
myTableView.reloadData()
}
/*
Cellの総数を返す.
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myUuids.count
}
/*
Cellに値を設定する.
*/
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier:"MyCell" )
// Cellに値を設定.
cell.textLabel!.sizeToFit()
cell.textLabel!.textColor = UIColor.red
cell.textLabel!.text = "\(myNames[indexPath.row]) / RSSI : \(myRssis[indexPath.row])"
cell.textLabel!.font = UIFont.systemFont(ofSize: 20)
// Cellに値を設定(下).
cell.detailTextLabel!.text = "UUID : \(myUuids[indexPath.row])"
cell.detailTextLabel!.font = UIFont.systemFont(ofSize: 13)
return cell
}
/*
Cellが選択された際に呼び出される.
*/
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
print("Uuid: \(myUuids[indexPath.row])")
print("Name: \(myNames[indexPath.row])")
print("RSSI: \(myRssis[indexPath.row])")
// クルクルスタート
ActivityIndicator.startAnimating()
// ボタン隠す
myButton.isHidden = true
self.myTargetPeripheral = myPeripheral[indexPath.row]
myCentralManager.connect(self.myTargetPeripheral, options: nil)
}
/*
Peripheralに接続
*/
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connect")
// 遷移するViewを定義する.
let mySecondViewController: MainViewController = MainViewController()
print("setPeripheral")
mySecondViewController.setCentralManager(manager: central)
mySecondViewController.setPeripheral(target: peripheral)
mySecondViewController.searchService()
// アニメーションを設定する.
//mySecondViewController.modalTransitionStyle = UIModalTransitionStyle.partialCurl
//print(self.navigationController ?? <#default value#>)
print("Go to Second View Controller")
print("")
// Viewの移動する.
self.navigationController?.pushViewController(mySecondViewController, animated: true)
self.tabBarController?.selectedIndex = 1;
// クルクルスタート
ActivityIndicator.stopAnimating()
// ボタン隠す
myButton.isHidden = false
//scanストップ
self.myCentralManager.stopScan()
myButton.setTitle("検索 開始", for: UIControlState())
scanning = false
}
/*
Peripheralに接続失敗した際
*/
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if let e = error {
print("Error: \(e.localizedDescription)")
return
}
print("not connnect")
}
/*
Peripheralが切断した際
*/
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral:CBPeripheral, error: Error?) {
print("接続が切断されました。")
// アラート作成
let alertController = UIAlertController(title: "接続が切れました", message: "", preferredStyle: .alert)
// アラートのボタン作成
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
// アラートの表示
present(alertController, animated: true, completion: nil)
if error != nil {
print("エラー: \(String(describing: error))")
}
}
}
MainView.swift
import UIKit
import CoreBluetooth
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CBPeripheralDelegate {
var myTableView: UITableView!
var myServiceUuids: [String] = []
var myService: [CBService] = []
var myButtonBefore: UIButton!
var myTargetPeriperal: CBPeripheral!
var myCentralManager: CBCentralManager!
init() {
super.init(nibName: nil, bundle: nil)
// Viewの背景色をGreenに設定する.
self.view.backgroundColor = UIColor.green
// tabBarItemのアイコンをFeaturedに、タグを2と定義する.
self.tabBarItem = UITabBarItem(tabBarSystemItem: .mostViewed, tag: 2)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: Bundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue
let barHeight = UIApplication.shared.statusBarFrame.size.height
let displayWidth = self.view.frame.width
let displayHeight = self.view.frame.height
// TableViewの生成( status barの高さ分ずらして表示 ).
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
// Cellの登録.
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
// DataSourceの設定.
myTableView.dataSource = self
// Delegateを設定.
myTableView.delegate = self
// Viewに追加する.
self.view.addSubview(myTableView)
}
// MARK: - CBPeripheralDelegate
/*
CentralManagerを設定
*/
func setCentralManager(manager: CBCentralManager) {
self.myCentralManager = manager
print(manager)
print("CentralManager: \(manager)")
}
/*
接続先のPeripheralを設定
*/
func setPeripheral(target: CBPeripheral) {
self.myTargetPeriperal = target
print("接続先Peripheral: \(target)")
}
/*
Serviceの検索
*/
func searchService(){
print("searchService")
self.myTargetPeriperal.delegate = self
self.myTargetPeriperal.discoverServices(nil)
}
/*
Serviceが見つかったとき
*/
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("didDiscoverServices")
for service in peripheral.services! {
myServiceUuids.append(service.uuid.uuidString)
myService.append(service)
print("P: \(peripheral.name) - Discovered service S:'\(service.uuid)'")
}
myTableView.reloadData()
}
// MARK: - UITableViewDelegate
/*
Cellが選択された際に呼び出される.
*/
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("ServiceUuid: \(myServiceUuids[indexPath.row])")
}
// MARK: - UITableViewDataSource
/*
Cellの総数を返す.
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myServiceUuids.count
}
/*
Cellに値を設定する.
*/
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier:"MyCell" )
// Cellに値を設定.
cell.textLabel!.sizeToFit()
cell.textLabel!.textColor = UIColor.red
cell.textLabel!.text = "\(myServiceUuids[indexPath.row])"
cell.textLabel!.font = UIFont.systemFont(ofSize: 15)
// Cellに値を設定(下).
cell.detailTextLabel!.text = "Service"
cell.detailTextLabel!.font = UIFont.systemFont(ofSize: 12)
return cell
}
override func didMove(toParentViewController parent: UIViewController?) {
if parent == nil {
self.myCentralManager.cancelPeripheralConnection(self.myTargetPeriperal)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}