Reputation: 1177
I have a app that has a UILabel that I would like to be updated by another swift class? The class is a service class and has no relation to the view controller with the UILabel but I would still like that service class to be able to update the label.
I think that this answer was what I needed but it does not work for Swift 3 and the text the label changes to is hardcoded which is not what I wanted either. I could not find anything else helpful about this.
BTService.swift:
import Foundation
import CoreBluetooth
let BLEServiceUUID = CBUUID(string: "025A7775-49AA-42BD-BBDB-E2AE77782966")
let PositionCharUUID = CBUUID(string: "A9CD2F86-8661-4EB1-B132-367A3434BC90") //RX let
let WriteCharUUID = CBUUID(string: "F38A2C23-BC54-40FC-BED0-60EDDA139F47") //TX let F38A2C23-BC54-40FC-BED0-60EDDA139F47
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
class BTService: NSObject, CBPeripheralDelegate {
var peripheral: CBPeripheral?
var positionCharacteristic: CBCharacteristic?
var writeCharacteristic: CBCharacteristic?
init(initWithPeripheral peripheral: CBPeripheral) {
super.init()
self.peripheral = peripheral
self.peripheral?.delegate = self
}
deinit{
self.reset()
}
func startDiscoveringServices() {
self.peripheral?.discoverServices([BLEServiceUUID])
}
func reset() {
if peripheral != nil {
peripheral = nil
}
// Deallocating therefore send notification
self.sendBTServiceNotificationWithIsBluetoothConnected(false)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
let uuidsForBTService: [CBUUID] = [PositionCharUUID]
let uuidsForBTService1: [CBUUID] = [WriteCharUUID]
if (peripheral != self.peripheral) { // Wrong Peripheral
return
}
if (error != nil) {
return
}
if ((peripheral.services == nil) || (peripheral.services?.count == 0)) {
// No Services
return
}
for service in peripheral.services! {
if service.uuid == BLEServiceUUID {
peripheral.discoverCharacteristics(uuidsForBTService, for: service
)
peripheral.discoverCharacteristics(uuidsForBTService1, for: service )
} }
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if (peripheral != self.peripheral) { // Wrong Peripheral
return }
if (error != nil) {
return
}
for characteristic in service.characteristics! { if characteristic.uuid == PositionCharUUID {
self.positionCharacteristic = (characteristic )
peripheral.setNotifyValue(true, for: characteristic )
self.sendBTServiceNotificationWithIsBluetoothConnected(true)
}
else if characteristic.uuid == WriteCharUUID {
self.writeCharacteristic = (characteristic )
peripheral.setNotifyValue(true, for: characteristic )
self.sendBTServiceNotificationWithIsBluetoothConnected(true)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
if(it != nil){
print("Val: \(it)") //Stackoverflow: This is what needs to be sent to the ViewController to be updated!!!
}
}
func writePosition(position: UInt8) {
if self.writeCharacteristic == nil {
return
}
var positionValue = position
let data = NSData(bytes: &positionValue, length: MemoryLayout<UInt8>.size)
self.peripheral?.writeValue(data as Data, for: self.writeCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
func readPosition() -> NSString? {
if self.positionCharacteristic == nil {
return nil }
self.peripheral?.readValue(for: self.positionCharacteristic!)
if ((self.positionCharacteristic?.value) != nil) {
print(self.positionCharacteristic!.value!)
return NSString(data: self.positionCharacteristic!.value!, encoding:
String.Encoding.utf8.rawValue) }
return nil
}
func sendBTServiceNotificationWithIsBluetoothConnected(_ isBluetoothConnected: Bool) {
let connectionDetails = ["isConnected": isBluetoothConnected]
NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServiceChangedStatusNotification), object: self, userInfo: connectionDetails)
}
}
The ViewController:
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var imgBluetoothStatus: UIImageView!
@IBOutlet weak var positionSlider: UISlider!
@IBOutlet weak var BottomLabel: UILabel!
var timerTXDelay: Timer?
var allowTX = true
var lastPosition: UInt8 = 255
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Rotate slider to vertical position
// Watch Bluetooth connection
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.connectionChanged(_:)), name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)
// Start the Bluetooth discovery process
_ = btDiscoverySharedInstance
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.stopTimerTXDelay()
}
@IBAction func positionSliderChanged(_ sender: UISlider) {
self.sendPosition(UInt8(sender.value))
}
func connectionChanged(_ notification: Notification) {
// Connection status changed. Indicate on GUI.
let userInfo = (notification as NSNotification).userInfo as! [String: Bool]
DispatchQueue.main.async(execute: {
// Set image based on connection status
if let isConnected: Bool = userInfo["isConnected"] {
if isConnected {
self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Connected")
// Send current slider position
self.sendPosition(UInt8( self.positionSlider.value))
} else {
self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Disconnected")
}
}
});
}
func sendPosition(_ position: UInt8) {
// Valid position range: 0 to 180
if !allowTX {
return
}
// Validate value
if position == lastPosition {
return
}
else if ((position < 0) || (position > 180)) {
return
}
// Send position to BLE Shield (if service exists and is connected)
if let bleService = btDiscoverySharedInstance.bleService {
bleService.writePosition(position: position)
lastPosition = position;
// Start delay timer
allowTX = false
if timerTXDelay == nil {
timerTXDelay = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.timerTXDelayElapsed), userInfo: nil, repeats: false)
}
}
}
func timerTXDelayElapsed() {
self.allowTX = true
self.stopTimerTXDelay()
// Send current slider position
self.sendPosition(UInt8(self.positionSlider.value))
}
func stopTimerTXDelay() {
if self.timerTXDelay == nil {
return
}
timerTXDelay?.invalidate()
self.timerTXDelay = nil
}
}
In the BTService, there is a didUpdateValueFor
function and I would like the it
variable to be sent to the ViewController and have it set as the BottomLabel.
Upvotes: 0
Views: 481
Reputation: 1539
it should be similar to your existing BLEServiceChangedStatusNotification
:
define the name:
let BLEServicePeripheralChangedNotification = "kBLEServicePeripheralChangedNotification"
add the observer in viewDidLoad
:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.peripheralChanged(_:)), name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)
remove the observer in deinit
:
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)
add the function:
func peripheralChanged(_ notification: Notification) {
// peripheral status changed. Indicate on GUI.
let userInfo = (notification as NSNotification).userInfo as! [String: NSString]
if let it: NSString = userInfo["it"] {
//BottomLabel should be bottomLabel in declaration!
BottomLabel.text = it
}
}
add the post to notification center:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
if(it != nil){
print("Val: \(it)")
let itDetails = ["it": it]
NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServicePeripheralChangedNotification), object: self, userInfo: itDetails)
}
}
PS: BottomLabel
should be bottomLabel
in declaration.
Upvotes: 1