Connecting bluetooth between iPad and iPhone - ios

I'm currently trying to make 2 iOS devices connect with each other so I can send gps locations between the two. Right now I'm using corebluetooth to achieve this.
What I did so far is I created 2 separate projects, one being the receiver one being the sender. I followed some instructions from apple itself to broadcast the iPad. However it doesn't seem to be found by the project that is trying to search for Bluetooth devices. I ran both apps on separate physical devices with bluetooth turned on. Below the code that I've used (keep in mind this is some quick code I wrote to try and see if I can actually find the device.) This is also the first time I work with bluetooth so it's a learning curve for sure.
FYI: I'm planning on using the GATT protocol. I'm also wondering if it can be done without a pairing handshake in the way that I get a popup to accept the connection. I would like the receiver to automatically receive and broadcasted signal and start obtaining the data that is being sent without additional steps needed. Or would I need something like Multipeer Connectivity Framework for this?
Receiver
import UIKit
import CoreBluetooth
import CoreLocation
class ViewController: UIViewController {
private let uuidBeacon = "A26962FC-F9DD-4251-B339-A25B3E808213"
var peripheral: CBPeripheralManager!
var peripheralData: NSMutableDictionary!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
peripheral = CBPeripheralManager(delegate: self, queue: nil)
}
override func viewDidAppear(_ animated: Bool) {
guard let region = createBeaconRegion() else { return }
advertiseDevice(region: region)
}
private func createBeaconRegion() -> CLBeaconRegion? {
let proximityUUID = UUID(uuidString: uuidBeacon)
let major : CLBeaconMajorValue = 100
let minor : CLBeaconMinorValue = 1
let beaconID = "com.example.myDeviceRegion"
return CLBeaconRegion(proximityUUID: proximityUUID!,
major: major, minor: minor, identifier: beaconID)
}
func advertiseDevice(region : CLBeaconRegion) {
peripheralData = region.peripheralData(withMeasuredPower: nil)
}
}
extension ViewController: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .poweredOn:
peripheral.startAdvertising(((peripheralData as NSDictionary) as! [String : Any]))
case .poweredOff:
break
// peripheral.stopAdvertising()
default:
break
}
}
}
Broadcaster
import UIKit
import CoreBluetooth
import CoreLocation
class ViewController: UIViewController {
var centralManager: CBCentralManager!
var sensorTag: CBPeripheral?
var keepScanning: Bool = false
private var timerScanInterval: TimeInterval = 5
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
}
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
keepScanning = true
_ = Timer(timeInterval: timerScanInterval, target: self, selector: #selector(pauseScan), userInfo: nil, repeats: false)
centralManager.scanForPeripherals(withServices: nil, options: nil)
break
case .poweredOff:
break
case .unsupported:
break
case .unauthorized:
break
case .resetting:
break
case .unknown:
break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
peripheral.discoverServices(nil)
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
print(peripheralName)
}
}
#objc private func pauseScan() {
centralManager.stopScan()
_ = Timer(timeInterval: 2, target: self, selector: #selector(resumeScan), userInfo: nil, repeats: false)
}
#objc private func resumeScan() {
if keepScanning {
centralManager.scanForPeripherals(withServices: nil, options: nil)
_ = Timer(timeInterval: timerScanInterval, target: self, selector: #selector(pauseScan), userInfo: nil, repeats: false)
}
}
}
extension ViewController: CBPeripheralDelegate {
}

Related

iOS - BLE peripherals aren't scanned on background mode

I enabled to “Uses Bluetooth LE accessories” and “Acts as a Bluetooth LE accessory” in “Background Modes” settings, and these are added in Info.plist.
 and also I allowed my app to access “Bluetooth sharing”.

And my app does scan BLE peripherals well on foreground mode,
 but when it enter background mode, it doesn’t scan anything no more. If it enter foreground mode again, it does scan well again.
More specifically, centralManagerDidUpdateState(_ central: CBCentralManager) always does work well on any modes. but didDiscover doesn't work on background mode.
I want to BLE scanning does work well on any modes and I think I have done everything I can.
Relevant Code
BLEManager.swift
import UIKit
import CoreBluetooth
final class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralManagerDelegate {
static let shared: BLEManager = .init()
var centralManager: CBCentralManager?
var peripheralManager: CBPeripheralManager?
var scannedPeripherals: [CBPeripheral] = .init()
var confirmedPeripheralStates: [String: CBPeripheralState] = .init()
override init() {
super.init()
self.centralManager = .init(delegate: self, queue: nil)
self.peripheralManager = .init(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
self.startScan()
default:
central.stopScan()
}
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
print("[BLE] state: \(peripheral.state.rawValue)")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
self.scannedPeripherals.appendAsUnique(peripheral)
central.connect(peripheral)
self.confirm(peripheral)
}
func startScan() {
guard let central = self.centralManager else {
return
}
let isBackgroundMode: Bool = (UIApplication.shared.applicationState == .background)
let cbuuids: [CBUUID] = self.scannedPeripherals.map { CBUUID(nsuuid: $0.identifier) }
central.stopScan()
central.scanForPeripherals(withServices: isBackgroundMode ? cbuuids : nil)
}
func confirm(_ peripheral: CBPeripheral) {
let uuid: String = peripheral.identifier.uuidString
// Prevent duplicate logging.
if peripheral.state != self.confirmedPeripheralStates[uuid] {
self.log(peripheral)
}
self.confirmedPeripheralStates[uuid] = peripheral.state
}
func log(_ peripheral: CBPeripheral, completion: (() -> Void)? = nil) {
guard let name = peripheral.name,
let state = self.getPeripheralStateAsString(peripheral.state) else {
return
}
print("[BLE] \(name) was \(state) on \(self.getApplicationStateAsString())")
}
}
extension BLEManager {
func getApplicationStateAsString() -> String {
switch UIApplication.shared.applicationState {
case .active:
return "active"
case .inactive:
return "inactive"
case .background:
return "background"
}
}
func getPeripheralStateAsString(_ state: CBPeripheralState) -> String? {
switch state {
case .disconnected:
return "disconnected"
case .connecting:
return "connecting"
case .connected:
return "connected"
default:
return nil
}
}
}
extension Array where Element: Equatable {
mutating func appendAsUnique(_ element: Element) {
if let index = self.index(where: { $0 == element }) {
self.remove(at: index)
}
self.append(element)
}
}
AppDelegate.swift
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.
BLEManager.shared.startScan()
}
Did I miss anything?

BLE Connection between iPhone and iPad with JustWorks bonding

I'm currently working on a app that needs to send data from a iPhone to a iPad. I've been able to find the devices and connecting them by using both the CentralManager and PeriphiralManager. However right now it's showing me a message to asking to pair every single time. But when I press the "NO" option it will still bond by using JustWorks (at least it seems because when I go to bluetooth in settings it will just say "iPad" and I can't see information about the device other than that and it disappears from the list completely when I disconnect).
I was wondering how I could make sure the popup asking for pairing doesn't show up at all. I read somewhere to disable encryption on the Peripheral however I'm not sure how to do this when the iPad for instance is the Peripheral. The code I'm using right now looks as follows (this is the first time I'm working with corebluetooth and I'm currently still messing around with it and trying to get a grasp of how it works).
import UIKit
import CoreBluetooth
import CoreLocation
class ViewController: UIViewController {
#IBOutlet weak var receiveLabel: UILabel!
#IBOutlet weak var sendButton: UIButton!
var centralManager: CBCentralManager!
var peripheralManager: CBPeripheralManager!
var peripherals: [CBPeripheral] = []
var keepScanning: Bool = false
private var timerScanInterval: TimeInterval = 5
static let SERVICE_UUID = CBUUID(string: "4DF91029-B356-463E-9F48-BAB077BF3EF5")
static let RX_UUID = CBUUID(string: "3B66D024-2336-4F22-A980-8095F4898C42")
static let RX_PROPERTIES: CBCharacteristicProperties = .write
static let RX_PERMISSIONS: CBAttributePermissions = .writeable
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}
}
extension ViewController {
#IBAction func sendMessage(_ sender: UIButton) {
for per in peripherals {
centralManager.connect(per, options: nil)
}
}
func updateAdvertisingData() {
if peripheralManager.isAdvertising {
peripheralManager.stopAdvertising()
}
peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [ViewController.SERVICE_UUID], CBAdvertisementDataLocalNameKey: "Test"])
}
func initService() {
let serialService = CBMutableService(type: ViewController.SERVICE_UUID, primary: true)
let rx = CBMutableCharacteristic(type: ViewController.RX_UUID, properties: ViewController.RX_PROPERTIES, value: nil, permissions: ViewController.RX_PERMISSIONS)
serialService.characteristics = [rx]
peripheralManager.add(serialService)
}
}
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
centralManager.scanForPeripherals(withServices: [ViewController.SERVICE_UUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
break
default: break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
peripheral.discoverServices(nil)
peripherals.append(peripheral)
}
}
extension ViewController: CBPeripheralDelegate, CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
initService()
updateAdvertisingData()
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
let characteristic = characteristic as CBCharacteristic
let message = "TestMessage".data(using: .utf8)
peripheral.writeValue(message!, for: characteristic, type: CBCharacteristicWriteType.withResponse)
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
for request in requests {
if let value = request.value {
let messageText = String(data: value, encoding: String.Encoding.utf8)
receiveLabel.text = messageText
}
self.peripheralManager.respond(to: request, withResult: .success)
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
}
Even though you only searched for peripherals that are advertising your specific service, when you discover a peripheral, you call
peripheral.discoverServices(nil)
This will discover all services on the discovered peripheral (which is another iOS device), not just your specific service.
Then, for each service you discover all characteristics and for each characteristic you discover you write to it with
let message = "TestMessage".data(using: .utf8)
peripheral.writeValue(message!, for: characteristic, type: CBCharacteristicWriteType.withResponse)
Since you have discovered all characteristics for all services on the device and then attempt to write to each one, if any of those characteristics require encryption you will trigger the pairing dialog. When you cancel the pairing dialog, that particular write will fail, but your app will keep on working (which is why you see the connection in settings).
You should refine your code so that it only discovers the specific service you are interested in and only attempts to write to your characteristic. This will prevent the pairing dialog from being triggered as your characteristic does not require encryption.

How can we instantiate a BLE connected peripheral in every view controller swift class instead of a Singleton?

Hi I have a Swift app that I'm trying to learn.
Iam using Adafruit Basic Chat as a reference.
https://github.com/adafruit/Basic-Chat/tree/master/Basic%20Chat
How can I pass the connected peripheral to another view controller scene, without a singleton class?
I have gone through a lot of links but can someone tell me the simplest form of code to extend the peripheral scope to all my view controller activities?
I am able to read and write data using the following code but how can I clear the RX buffer after I have received the data as I sometimes find the rx data seems to overwrite the received bytes.
// BLECentralViewController
//
// BLECentralViewController.swift
// Basic Chat
//
// Created by Trevor Beaton on 11/29/16.
// Copyright © 2016 Vanguard Logic LLC. All rights reserved.
//
import Foundation
import UIKit
import CoreBluetooth
var txCharacteristic : CBCharacteristic?
var rxCharacteristic : CBCharacteristic?
var blePeripheral : CBPeripheral?
var characteristicASCIIValue = NSString()
class BLECentralViewController : UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDelegate, UITableViewDataSource{
//Data
var centralManager : CBCentralManager!
var RSSIs = [NSNumber]()
var data = NSMutableData()
var writeData: String = ""
var peripherals: [CBPeripheral] = []
var characteristicValue = [CBUUID: NSData]()
var timer = Timer()
var characteristics = [String : CBCharacteristic]()
//UI
#IBOutlet weak var baseTableView: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
#IBAction func refreshAction(_ sender: AnyObject) {
disconnectFromDevice()
self.peripherals = []
self.RSSIs = []
self.baseTableView.reloadData()
startScan()
}
override func viewDidLoad() {
super.viewDidLoad()
self.baseTableView.delegate = self
self.baseTableView.dataSource = self
self.baseTableView.reloadData()
/*Our key player in this app will be our CBCentralManager. CBCentralManager objects are used to manage discovered or connected remote peripheral devices (represented by CBPeripheral objects), including scanning for, discovering, and connecting to advertising peripherals.
*/
centralManager = CBCentralManager(delegate: self, queue: nil)
let backButton = UIBarButtonItem(title: "Disconnect", style: .plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backButton
}
override func viewDidAppear(_ animated: Bool) {
disconnectFromDevice()
super.viewDidAppear(animated)
refreshScanView()
print("View Cleared")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("Stop Scanning")
centralManager?.stopScan()
}
/*Okay, now that we have our CBCentalManager up and running, it's time to start searching for devices. You can do this by calling the "scanForPeripherals" method.*/
func startScan() {
peripherals = []
print("Now Scanning...")
self.timer.invalidate()
centralManager?.scanForPeripherals(withServices: [BLEService_UUID] , options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
Timer.scheduledTimer(timeInterval: 17, target: self, selector: #selector(self.cancelScan), userInfo: nil, repeats: false)
}
/*We also need to stop scanning at some point so we'll also create a function that calls "stopScan"*/
func cancelScan() {
self.centralManager?.stopScan()
print("Scan Stopped")
print("Number of Peripherals Found: \(peripherals.count)")
}
func refreshScanView() {
baseTableView.reloadData()
}
//-Terminate all Peripheral Connection
/*
Call this when things either go wrong, or you're done with the connection.
This cancels any subscriptions if there are any, or straight disconnects if not.
(didUpdateNotificationStateForCharacteristic will cancel the connection if a subscription is involved)
*/
func disconnectFromDevice () {
if blePeripheral != nil {
// We have a connection to the device but we are not subscribed to the Transfer Characteristic for some reason.
// Therefore, we will just disconnect from the peripheral
centralManager?.cancelPeripheralConnection(blePeripheral!)
}
}
func restoreCentralManager() {
//Restores Central Manager delegate if something went wrong
centralManager?.delegate = self
}
/*
Called when the central manager discovers a peripheral while scanning. Also, once peripheral is connected, cancel scanning.
*/
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,advertisementData: [String : Any], rssi RSSI: NSNumber) {
blePeripheral = peripheral
self.peripherals.append(peripheral)
self.RSSIs.append(RSSI)
peripheral.delegate = self
self.baseTableView.reloadData()
if blePeripheral == nil {
print("Found new pheripheral devices with services")
print("Peripheral name: \(String(describing: peripheral.name))")
print("**********************************")
print ("Advertisement Data : \(advertisementData)")
}
}
//Peripheral Connections: Connecting, Connected, Disconnected
//-Connection
func connectToDevice () {
centralManager?.connect(blePeripheral!, options: nil)
}
/*
Invoked when a connection is successfully created with a peripheral.
This method is invoked when a call to connect(_:options:) is successful. You typically implement this method to set the peripheral’s delegate and to discover its services.
*/
//-Connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("*****************************")
print("Connection complete")
print("Peripheral info: \(String(describing: blePeripheral))")
//Stop Scan- We don't need to scan once we've connected to a peripheral. We got what we came for.
centralManager?.stopScan()
print("Scan Stopped")
//Erase data that we might have
data.length = 0
//Discovery callback
peripheral.delegate = self
//Only look for services that matches transmit uuid
peripheral.discoverServices([BLEService_UUID])
//Once connected, move to new view controller to manager incoming and outgoing data
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let uartViewController = storyboard.instantiateViewController(withIdentifier: "UartModuleViewController") as! UartModuleViewController
uartViewController.peripheral = peripheral
navigationController?.pushViewController(uartViewController, animated: true)
}
/*
Invoked when the central manager fails to create a connection with a peripheral.
*/
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if error != nil {
print("Failed to connect to peripheral")
return
}
}
func disconnectAllConnection() {
centralManager.cancelPeripheralConnection(blePeripheral!)
}
/*
Invoked when you discover the peripheral’s available services.
This method is invoked when your app calls the discoverServices(_:) method. If the services of the peripheral are successfully discovered, you can access them through the peripheral’s services property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
*/
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("*******************************************************")
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let services = peripheral.services else {
return
}
//We need to discover the all characteristic
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
// bleService = service
}
print("Discovered Services: \(services)")
}
/*
Invoked when you discover the characteristics of a specified service.
This method is invoked when your app calls the discoverCharacteristics(_:for:) method. If the characteristics of the specified service are successfully discovered, you can access them through the service's characteristics property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
*/
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("*******************************************************")
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let characteristics = service.characteristics else {
return
}
print("Found \(characteristics.count) characteristics!")
for characteristic in characteristics {
//looks for the right characteristic
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Rx) {
rxCharacteristic = characteristic
//Once found, subscribe to the this particular characteristic...
peripheral.setNotifyValue(true, for: rxCharacteristic!)
// We can return after calling CBPeripheral.setNotifyValue because CBPeripheralDelegate's
// didUpdateNotificationStateForCharacteristic method will be called automatically
peripheral.readValue(for: characteristic)
print("Rx Characteristic: \(characteristic.uuid)")
}
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Tx){
txCharacteristic = characteristic
print("Tx Characteristic: \(characteristic.uuid)")
}
peripheral.discoverDescriptors(for: characteristic)
}
}
// Getting Values From Characteristic
/*After you've found a characteristic of a service that you are interested in, you can read the characteristic's value by calling the peripheral "readValueForCharacteristic" method within the "didDiscoverCharacteristicsFor service" delegate.
*/
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == rxCharacteristic {
if let ASCIIstring = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) {
characteristicASCIIValue = ASCIIstring
print("Value Recieved: \((characteristicASCIIValue as String))")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: nil)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
print("*******************************************************")
if error != nil {
print("\(error.debugDescription)")
return
}
if ((characteristic.descriptors) != nil) {
for x in characteristic.descriptors!{
let descript = x as CBDescriptor!
print("function name: DidDiscoverDescriptorForChar \(String(describing: descript?.description))")
print("Rx Value \(String(describing: rxCharacteristic?.value))")
print("Tx Value \(String(describing: txCharacteristic?.value))")
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("*******************************************************")
if (error != nil) {
print("Error changing notification state:\(String(describing: error?.localizedDescription))")
} else {
print("Characteristic's value subscribed")
}
if (characteristic.isNotifying) {
print ("Subscribed. Notification has begun for: \(characteristic.uuid)")
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Disconnected")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("Error discovering services: error")
return
}
print("Message sent")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
guard error == nil else {
print("Error discovering services: error")
return
}
print("Succeeded!")
}
//Table View Functions
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.peripherals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Connect to device where the peripheral is connected
let cell = tableView.dequeueReusableCell(withIdentifier: "BlueCell") as! PeripheralTableViewCell
let peripheral = self.peripherals[indexPath.row]
let RSSI = self.RSSIs[indexPath.row]
if peripheral.name == nil {
cell.peripheralLabel.text = "nil"
} else {
cell.peripheralLabel.text = peripheral.name
}
cell.rssiLabel.text = "RSSI: \(RSSI)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
blePeripheral = peripherals[indexPath.row]
connectToDevice()
}
/*
Invoked when the central manager’s state is updated.
This is where we kick off the scan if Bluetooth is turned on.
*/
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
// We will just handle it the easy way here: if Bluetooth is on, proceed...start scan!
print("Bluetooth Enabled")
startScan()
} else {
//If Bluetooth is off, display a UI alert message saying "Bluetooth is not enable" and "Make sure that your bluetooth is turned on"
print("Bluetooth Disabled- Make sure your Bluetooth is turned on")
let alertVC = UIAlertController(title: "Bluetooth is not enabled", message: "Make sure that your bluetooth is turned on", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title: "ok", style: UIAlertActionStyle.default, handler: { (action: UIAlertAction) -> Void in
self.dismiss(animated: true, completion: nil)
})
alertVC.addAction(action)
self.present(alertVC, animated: true, completion: nil)
}
}
}
//UART SCENE
//
// UartModuleViewController.swift
// Basic Chat
//
// Created by Trevor Beaton on 12/4/16.
// Copyright © 2016 Vanguard Logic LLC. All rights reserved.
//
import UIKit
import CoreBluetooth
class UartModuleViewController: UIViewController, CBPeripheralManagerDelegate, UITextViewDelegate, UITextFieldDelegate {
//UI
#IBOutlet weak var baseTextView: UITextView!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var inputTextField: UITextField!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var switchUI: UISwitch!
//Data
var peripheralManager: CBPeripheralManager?
var peripheral: CBPeripheral!
private var consoleAsciiText:NSAttributedString? = NSAttributedString(string: "")
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"Back", style:.plain, target:nil, action:nil)
self.baseTextView.delegate = self
self.inputTextField.delegate = self
//Base text view setup
self.baseTextView.layer.borderWidth = 3.0
self.baseTextView.layer.borderColor = UIColor.blue.cgColor
self.baseTextView.layer.cornerRadius = 3.0
self.baseTextView.text = ""
//Input Text Field setup
self.inputTextField.layer.borderWidth = 2.0
self.inputTextField.layer.borderColor = UIColor.blue.cgColor
self.inputTextField.layer.cornerRadius = 3.0
//Create and start the peripheral manager
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
//-Notification for updating the text view with incoming text
updateIncomingData()
}
override func viewDidAppear(_ animated: Bool) {
self.baseTextView.text = ""
}
override func viewDidDisappear(_ animated: Bool) {
// peripheralManager?.stopAdvertising()
// self.peripheralManager = nil
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
func updateIncomingData () {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "Notify"), object: nil , queue: nil){
notification in
let appendString = "\n"
let myFont = UIFont(name: "Helvetica Neue", size: 15.0)
let myAttributes2 = [NSFontAttributeName: myFont!, NSForegroundColorAttributeName: UIColor.red]
let attribString = NSAttributedString(string: "[Incoming]: " + (characteristicASCIIValue as String) + appendString, attributes: myAttributes2)
let newAsciiText = NSMutableAttributedString(attributedString: self.consoleAsciiText!)
self.baseTextView.attributedText = NSAttributedString(string: characteristicASCIIValue as String , attributes: myAttributes2)
newAsciiText.append(attribString)
self.consoleAsciiText = newAsciiText
self.baseTextView.attributedText = self.consoleAsciiText
}
}
#IBAction func clickSendAction(_ sender: AnyObject) {
outgoingData()
}
func outgoingData () {
let appendString = "\n"
let inputText = inputTextField.text
let myFont = UIFont(name: "Helvetica Neue", size: 15.0)
let myAttributes1 = [NSFontAttributeName: myFont!, NSForegroundColorAttributeName: UIColor.blue]
writeValue(data: inputText!)
let attribString = NSAttributedString(string: "[Outgoing]: " + inputText! + appendString, attributes: myAttributes1)
let newAsciiText = NSMutableAttributedString(attributedString: self.consoleAsciiText!)
newAsciiText.append(attribString)
consoleAsciiText = newAsciiText
baseTextView.attributedText = consoleAsciiText
//erase what's in the text field
inputTextField.text = ""
}
// Write functions
func writeValue(data: String){
let valueString = (data as NSString).data(using: String.Encoding.utf8.rawValue)
//change the "data" to valueString
if let blePeripheral = blePeripheral{
if let txCharacteristic = txCharacteristic {
blePeripheral.writeValue(valueString!, for: txCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func writeCharacteristic(val: Int8){
var val = val
let ns = NSData(bytes: &val, length: MemoryLayout<Int8>.size)
blePeripheral!.writeValue(ns as Data, for: txCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
//MARK: UITextViewDelegate methods
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
if textView === baseTextView {
//tapping on consoleview dismisses keyboard
inputTextField.resignFirstResponder()
return false
}
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x:0, y:250), animated: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollView.setContentOffset(CGPoint(x:0, y:0), animated: true)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
return
}
print("Peripheral manager is running")
}
//Check when someone subscribe to our characteristic, start sending the data
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print("Device subscribe to characteristic")
}
//This on/off switch sends a value of 1 and 0 to the Arduino
//This can be used as a switch or any thing you'd like
#IBAction func switchAction(_ sender: Any) {
if switchUI.isOn {
print("On ")
writeCharacteristic(val: 1)
}
else
{
print("Off")
writeCharacteristic(val: 0)
print(writeCharacteristic)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
outgoingData()
return(true)
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
if let error = error {
print("\(error)")
return
}
}
}
The peripheral keeps disconnecting and starts scanning soon as I start a new view controller using performsegue method.
It would be really helpful if someone could shed some light?

Updating code to Swift 3

I am a beginner to programming with swift and programming in general. I am currently working on an app which allows one phone to send a message (peripheral) to another which will receive the message (central). I have taken most of the code from an app built using swift 2 and have tweaked it slightly to remove any errors. As a result, my code is a jumble of swift 2/3 (or so I have been told). I am not sure how to update this code fully to swift 3 which I need someones help to do. I believe it is the IBActions and delegates that are outdated but I am completely clueless on how to amend it.
I have tried running the app and it runs successfully but when I press any of the buttons nothing happens as far as I can tell. I have a basic understanding of what my code does and what each function is there for but I find myself stuck on what to do for it to work. If anyone has experience working with Bluetooth via swift and can see what I am doing wrong or perhaps if I haven't included something important. I am led to believe the issue is that all the code has not been updated to swift 3 but I can't be sure. I have read the Apple documentation for Core bluetooth.
PS: I know this question is very vague as I am unsure on what part of my code I need to fix so it is very difficult to make it specific to a single issue. Sorry!
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBPeripheralManagerDelegate, CBCentralManagerDelegate, CBPeripheralDelegate {
let messageUUID = CBUUID(string: "053D6600-821E-46A7-AC25-43A81D948E8B")
let inputUUID = CBUUID(string: "49A79D26-5323-4374-81EA-29B099AF85C8")
let otherUUID = CBUUID(string: "053D6600-821E-46A7-AC25-43A81D948E87")
var peripheralManager: CBPeripheralManager!
var characteritic: CBMutableCharacteristic!
var getDataPeripheralCharacteristic: CBMutableCharacteristic!
var service: CBMutableService!
var outputData:String = ""
var centralManager: CBCentralManager!
var connectingPeripheral: CBPeripheral?
var centralWriteCharacteristic: CBCharacteristic!
#IBOutlet var inputLabel: UITextField!
#IBOutlet weak var outputLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func centralMode(_ sender: UIButton) {
centralManager = CBCentralManager(delegate: self, queue: nil)
peripheralManager = nil
}
#IBAction func peripheralMode(_ sender: UIButton) {
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
centralManager = nil
}
#IBAction func sendData(_ sender: UIButton) {
if (peripheralManager != nil) {
let passed = peripheralManager.updateValue(inputLabel.text!.data(using: String.Encoding.utf8)!, for: characteritic!, onSubscribedCentrals: nil)
if passed == false {
print("error couldn't send data")
}
}
if (centralManager != nil) {
if (connectingPeripheral != nil) {
connectingPeripheral?.writeValue(inputLabel.text!.data(using: String.Encoding.utf8)!, for: centralWriteCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if (peripheral.state == .poweredOn) {
characteritic = CBMutableCharacteristic(type: messageUUID, properties: CBCharacteristicProperties.notify, value: nil, permissions: CBAttributePermissions.readable)
getDataPeripheralCharacteristic = CBMutableCharacteristic(type: inputUUID, properties:
CBCharacteristicProperties.write, value: nil, permissions: CBAttributePermissions.writeable)
service = CBMutableService(type: otherUUID, primary: true)
service.characteristics = [characteritic!, getDataPeripheralCharacteristic!]
peripheralManager!.add(service)
}
}
func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?) {
peripheral.stopAdvertising()
let identifier = Bundle.main.bundleIdentifier!
let manufacturerData = identifier.data(using: String.Encoding.utf8, allowLossyConversion: false)
let dataToBeAdvertised:[String: Any] = [
CBAdvertisementDataLocalNameKey : "Sample peripheral",
CBAdvertisementDataManufacturerDataKey : manufacturerData as Any,
CBAdvertisementDataServiceUUIDsKey : [messageUUID, inputUUID],
]
peripheralManager.startAdvertising(dataToBeAdvertised)
}
func peripheralManager(peripheral: CBPeripheralManager, didReceiveWriteRequests requests: [CBATTRequest]) {
for request in requests {
if (request.characteristic.uuid.uuidString == inputUUID.uuidString) {
outputData = String(data: request.value!, encoding: String.Encoding.utf8)!
outputLabel.text = outputData
}
}
}
func peripheralManager(peripheral: CBPeripheralManager, didReceiveReadRequest request: CBATTRequest) {
}
func peripheralManager(peripheral: CBPeripheralManager, central: CBCentral, didSubscribeToCharacteristic characteristic: CBCharacteristic) {
outputLabel.text = "Press send message"
print("Now connected")
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
centralManager.scanForPeripherals(withServices: nil, options: nil)
//search for peripheral device
}
}
//when peripheral is discovered, this method is called
private func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
self.centralManager.stopScan();
self.connectingPeripheral = peripheral;
peripheral.delegate = self;
centralManager.connect(peripheral, options: nil)
}
//if connection request from central is succesfull, this method is called
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
peripheral.delegate = self;
peripheral.discoverServices(nil)
}
//if central discovers services, this method is called
private func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
for service: CBService in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
private func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
if (service.uuid.uuidString == otherUUID.uuidString) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.uuid.uuidString == messageUUID.uuidString {
peripheral.setNotifyValue(true, for: characteristic)
} else if characteristic.uuid.uuidString == inputUUID.uuidString {
centralWriteCharacteristic = characteristic
}
}
}
}
private func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
if error != nil {
print("Error reading characteristics");
}
if (characteristic.uuid == messageUUID) {
outputData = String(data: characteristic.value!, encoding: String.Encoding.utf8)!
print(outputData)
outputLabel.text = outputData
}
}
}
If the issue is because of swift 2/3 mismatch. Try upgrading your code to swift3.
Go to Xcode-edit-convert- to current swift syntax.
This will lead to few build errors, identify them and fix them.
Make your sure if you have any pod or cart file , do not forget to upgrade them as well.
Hopefully this should resolve your issue.

Failed to Advertise and Discover BLE with Swift

I am trying to advertise BLE with the following
var perMan: CBPeripheralManager!
let myCustomServiceUUID: CBUUID = CBUUID(string: "109F17E4-EF68-43FC-957D-502BB0EFCF46")
var myService: CBMutableService!
override func viewDidLoad() {
super.viewDidLoad()
perMan = CBPeripheralManager(delegate: self, queue: nil)
myService = CBMutableService(type: myCustomServiceUUID, primary: true)
perMan.addService(myService)
}
// Broadcast when Bluetooth is on
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) {
if peripheral.state == CBPeripheralManagerState.PoweredOn {
// Start advertising over BLE
self.perMan.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [myService.UUID]])
println("Adertising")
} else if peripheral.state == CBPeripheralManagerState.PoweredOff {
self.perMan.stopAdvertising()
}
}
And want to discover the peripheral with
import UIKit
import CoreBluetooth
class SearchViewController: UIViewController, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
#IBOutlet weak var beaconsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
// Listen to BLE of IPhones
let services: NSArray = ["7521105F-8937-48B7-A875-66E6FE21D714"]
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
// Found IPhone
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println("Hallo:")
println(RSSI)
}
// CBCentralManagerDelegate
func centralManagerDidUpdateState(central: CBCentralManager!) {
}
However the didDiscoverPeripheral method is never called. I am testing on two IPhone5.
Can anyone tell me what I am doing wrong?

Resources