Scan for BLE devices and present them in an UITableView
Like @paulw11 said, I had to creat a delegate protocol:
protocol BluetoothDelegate: class {
func ReloadView()
}
This 'ReloadView' method is declared in my ScanTableView class:
func ReloadView() {
scanTableView.reloadData()
}
Now, I had to do some additional:
- Add the BluetoothDelegate to ScanTableView class
- Declare a variable 'weak var delegate: BluetoothDelegate?' in BluetoothManager class
- call the delegate method with 'delegate?.ReloadView()' at the wished point in BluetoothManager class
- Activate the delegate in ScanTableView by using 'bluetoothManager?.delegate = self'
That's it!
How to show found device list on tableview using Bluetooth LE iOS
For now it shown found list on uitableview but I can not select device to connect. Can you show me? thanks
You should implement one more central manager delegate's method:
centralManager:didConnectPeripheral:error
Inside it you should pass self to peripheral's delegate
peripheral.delegate = self;
At this moment you didn't discovered peripheral service nor characteristics. You should explicitly tell that you want to retrieve all services that peripheral has :
[peripheral discoverServices:nil];
After that CoreBluetooth invokes CBPeripheral delegate method :
peripheral:didDiscoverServices:
At that moment you have all services on peripheral that can be accessed via services property:
peripheral.services
Next step - discover services characteristics:
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil
forService:service];
}
Again you need to implement CBPeripheral method from protocol:
peripheral:didDiscoverCharacteristicsForService:error:
That can be accessed via
service.characteristics
Now you can read/write/setNotify on them.
But for reading/writing/notifying you should implement corresponding CBPeripheralManager protocol methods:
peripheralManager:didReceiveReadRequest:
peripheralManager:didReceiveWriteRequests:
peripheralManager:central:didSubscribeToCharacteristic:
All of that described in documentation:
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html
IOS Bluetooth LE Scanning Devices not adding to the uitableview
There is a lot of code here, but generally it looks OK. Two things do stand out however. Your numberOfSectionsInTableView
is returning 0 - so there will be no content in your table. You should have -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
I suggest that you just store the CBPeripheral in your data array.
Also, once you discover a peripheral and add it to your array you should inform the table view that the data source has been updated (This code assumes you have property tableView
that holds a reference to your tableview ) -
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);
if (![_foundPeripherals containsObject:peripheral]) {
[_foundPeripherals addObject:myDevice ] ;
[self.tableView reloadData]; // Tell the tableview that the data source has changed
}
}
Then your cellForRowAtIndexPath
becomes -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" forIndexPath:indexPath];
if (cell == nil) {
cell = [ [UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"reuseIdentifier"] ;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
}
CBPeripheral *peripheral=[_foundPeripherals objectAtIndex:indexPath.row];
cell.textLabel.text = peripheral.name;
// Configure the cell...
return cell;
}
To connect to the peripheral when the table row is selected -
- (void)tableView: (UITableView *)tableView
didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:@"connectSegue" sender:[_foundPeripherals objectAtIndex:indexPath.row]]; // This is a segue to your BTSentCommandViewController
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
BTSentCommandViewController* sliderVC=( BTSentCommandViewController *)segue.destinationViewController;
sliderVC.centralManager=_centralManager;
sliderVC.periperhal=_sender;
}
In your BTSentCommandViewController
you can connect to the peripheral. You should also set the BTSentCommandViewController
instance as the peripherals delegate and implement all of the CBPeripheralDelegate
methods there
Bluetooth Scan throwing ArgumentOutOfRangeException in Xamarin.iOS
So it turns out that the BLE scan, which runs continuously, was producing a race condition where an item was being removed at the same time that the list was being updated with removed devices.
Invoking on main thread and adding a lock to the property seems to keep that exception from throwing:
private object _deviceListLock = new object();
public ObservableRangeCollection<DeviceListItemViewModel> Devices { get; set; } = new ObservableRangeCollection<DeviceListItemViewModel>();
private void OnDeviceAdded(BleDevice device)
{
InvokeOnMainThread(() =>
{
lock (_deviceListLock)
{
Devices.Add(new DeviceListItemViewModel(device));
}
});
}
private void OnDeviceUpdated(BleDevice device)
{
InvokeOnMainThread(() =>
{
lock (_deviceListLock)
{
Devices.ReplaceRange(Devices.OrderByDescending(x => x.Rssi).ToList());
}
});
}
private void OnDeviceExpired(BleDevice device)
{
InvokeOnMainThread(() =>
{
lock (_deviceListLock)
{
var vm = Devices.FirstOrDefault(d => d.Id == device.Id);
Devices.Remove(vm);
}
});
}
Why this is necessary on iOS, but not on Android, I'm not entirely sure.
Related Topics
Swift Pattern Match on Array<Any>
Create Objects/Instances in Variables Swift
Cllocationmanager and Tvos - Requestwheninuseauthorization() Not Prompting
Swift: Draw a Semi-Sphere in Mkmapview
Sharing Screenshot of Swiftui View Causes Crash
Cache That Can Purge Unused Objects by Demand
Connecting Avaudiosourcenode to Avaudiosinknode Does Not Work
How to Get User Nearby My Location in Geofire,Firebase
How to Programmatically Scroll iOS Wkwebview, Swift 4
Extending Dictionary with Key and Value Constraints
How to Convert Curl Request to Swift Using Alamofire
Query Value Between Two Other Values in Firebase
Exc_Badinstruction When Computing a Hash Value
Uicollectionview + Nsfetchedresultscontroller in Swift 3
Change The 2Nd and 3Rd Pickerview Acording to What Row from The 1St Picker Is Selected