I try to find a way to scan for BLE devices and present them in an UITableView. The scan, connect, read and write functionality for BLE devices is clear and works! So my questions are focused on the interaction between the 'ScanTableView' and 'BletoothManager' class.
These are my two classes:
// ScanTableView.swift
import UIKit
class ScanTableView: UITableViewController {
@IBOutlet var scanTableView: UITableView!
var bluetoothManager = BluetoothManager?()
var tableViewScanTime = 5
var timer1: NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl!.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let _ = bluetoothManager?.peripheralArray.count {
return bluetoothManager!.peripheralArray.count
}
else {
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = scanTableView.dequeueReusableCellWithIdentifier("scanCell",forIndexPath: indexPath)
cell.textLabel!.text = bluetoothManager!.peripheralArray[indexPath.row].name
cell.detailTextLabel!.text = bluetoothManager!.peripheralArray[indexPath.row].RSSI
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
bluetoothManager!.selectedPeripheral = bluetoothManager!.peripheralArray[indexPath.row]
bluetoothManager!.connectPeripheral(bluetoothManager!.selectedPeripheral!)
}
func refresh() {
scanTableView.userInteractionEnabled = false
timer1 = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "scanTableViewRefresh", userInfo: nil, repeats: true)
bluetoothManager = BluetoothManager()
}
func scanTableViewRefresh() {
scanTableView.reloadData()
tableViewScanTime--
if tableViewScanTime <= 0 {
timer1.invalidate()
bluetoothManager!.CBmanager.stopScan()
print("StopScan")
tableViewScanTime = 5
bluetoothManager!.peripheralArray.sortInPlace({$0.RSSI < $1.RSSI})
self.refreshControl!.endRefreshing()
self.scanTableView.userInteractionEnabled = true
}
}
}
// BluetoothManager.swift
import UIKit
import CoreBluetooth
class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
struct BluetoothPeripheral {
let name: String
let UUID: String
let RSSI: String
let peripheral: CBPeripheral
init(name: String, UUID: String, RSSI: NSNumber, peripheral: CBPeripheral) {
self.name = "\(name)"
self.UUID = "\(UUID)"
self.RSSI = "\(RSSI)"
self.peripheral = peripheral
}
}
let DEVICE_NAME:String! = "TEST"
//Creat an instance of ScanTableView Class
var scanTableView: ScanTableView()
var peripheralArray: [BluetoothPeripheral] = []
var selectedPeripheral: BluetoothPeripheral?
var characteristicArray: [CBCharacteristic] = []
var CBmanager: CBCentralManager = CBCentralManager()
var measurementValue: [[AnyObject?]] = [[]]
//Basic functions
override init() {
super.init()
CBmanager = CBCentralManager(delegate: self, queue: nil)
}
func connectPeripheral(selectedPeripheral: BluetoothPeripheral) {
CBmanager.connectPeripheral(selectedPeripheral.peripheral, options: nil)
}
func disconnectPeripheral(selectedPeripheral: BluetoothPeripheral) {
for characteristic in characteristicArray {
selectedPeripheral.peripheral.setNotifyValue(false, forCharacteristic: characteristic as CBCharacteristic)
}
CBmanager.cancelPeripheralConnection(selectedPeripheral.peripheral)
}
func ScanForPeripherals() {
CBmanager.scanForPeripheralsWithServices(nil, options: nil)
print("Scanning")
}
func centralManagerDidUpdateState(central: CBCentralManager) {
switch(central.state) {
case .PoweredOn:
CBmanager.scanForPeripheralsWithServices(nil, options: nil)
print("scan")
case .PoweredOff, .Resetting, .Unauthorized, .Unsupported, .Unknown:
peripheralArray.removeAll()
//This invokes an exception
//scanTableView.scanTableView.reloadData()
print("NO BLE!")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
let UUID = "\(peripheral.identifier)".substringFromIndex("\(peripheral.identifier)".startIndex.advancedBy(31))
if let peripheralName = peripheral.name {
if peripheralName.containsString(DEVICE_NAME) {
peripheralArray.append(BluetoothPeripheral(name: peripheral.name!, UUID: UUID, RSSI: RSSI, peripheral: peripheral))
print(peripheralArray)
}
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("Connected")
measurementValue.removeAll()
peripheral.delegate = self
selectedPeripheral!.peripheral.discoverServices(nil)
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Fail")
}
func centralManager(central: CBCentralManager, willRestoreState dict: [String : AnyObject]) {
print("Restore")
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Disconnected")
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
for characteristic in service.characteristics as [CBCharacteristic]!{
if characteristic.properties.contains(CBCharacteristicProperties.Notify) {
peripheral.discoverDescriptorsForCharacteristic(characteristic)
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
}
func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
if characteristic.isNotifying {
characteristicArray.append(characteristic as CBCharacteristic)
peripheral.readValueForCharacteristic(characteristic)
}
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
//Store new characteristic values
}
}
Now my questions:
The shown code works but I'm not able to interact between the two classes. For example I would like to reload my opened ScanTableView from my BluetoothManager class. That's not possible... every time when I try this, I get an exception that I would unwrap an optional. Why? Are there any differences between 'normal' classes and the classes shown in the GUI (UITableView, UIView...)? I documented the exception line...
It would be really nice, if anyone could explain me what to do in such situations :).
I'm happy for any suggestions or improvements!