ChatHandler

class ChatHandler : NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate

The Bluetooth Manager handles all searching for, creating connection to and sending/receiving messages to/from other Bluetooth devices.

This class handles almost all the logic in the app and is passed around to the different views such that they all have access to the same Bluetooth objects as well as conversations. When the app is launched all the information is stored in memory and written to the persistent storage as needed.

Note

It conforms to a variety of delegates which is used for callback functions from the Apple APIs.

Note

In code the ChatBrain has been divided into files for seperation and isolation of features.
  • Context for CoreData storage

    Declaration

    Swift

    var context: NSManagedObjectContext
  • A simple counter to show amount of relayed messages this session. It is reset when the app is force-closed or the device is restarted.

    Declaration

    Swift

    @Published
    var routedCounter: Int { get set }
  • A UUID which is updated when a ACK message is retrieved. This forces a refresh of the ChatView and the message status is updated.

    Declaration

    Swift

    @Published
    var refreshID: UUID { get set }
  • Holds an array of messages to be delivered at a later point. Used for the queue functionality.

    Declaration

    Swift

    @Published
    var messageQueue: [queuedMessage] { get set }
  • Holds a reference to all devices discovered. If no reference is held then the Bluetooth connection may be dropped.

    Declaration

    Swift

    @Published
    var discoveredDevices: [Device] { get set }
  • Holds the connected characteristics. This is only used for the chat functionality for now.

    Declaration

    Swift

    var connectedCharateristics: [CBCharacteristic]
  • The centralManager acts as our Bluetooth server and receives messages sent by clients to the server.

    Declaration

    Swift

    var centralManager: CBCentralManager!
  • The peripheralManager acts as our Bluetooth clients and establishes connections to other BT servers. It also sends messages.

    Declaration

    Swift

    var peripheralManager: CBPeripheralManager!
  • The characteristic which defines our chat functionality for the Bluetooth API.

    Declaration

    Swift

    var characteristic: CBMutableCharacteristic?
  • A list which holds message IDs which we have seen before. This prevents looping them in the network for ages.

    Declaration

    Swift

    var seenMessages: [Int32]
  • A dictionary which stores how many messages we have received from a connected peripheral. It is cleaned from time to time as well.

    Declaration

    Swift

    var peripheralMessages: [String : [Date]]
  • A dictionary which holds the ids of messages relayed and the corresponding sender of said messages. This is used for DSR.

    Declaration

    Swift

    var senderOfMessageID: [Int32 : String]
  • Seen CBCentrals / This is used for DSR algorithm.

    Declaration

    Swift

    var seenCBCentral: [CBCentral]
  • The initialiser for the ChatBrain. Sets up the centralManager and the peripheralManager.

    Declaration

    Swift

    init(context: NSManagedObjectContext)

    Parameters

    context

    The context for persistent storage to CoreData

  • Remove a device from discoveredDevices and drop connection to it.

    Declaration

    Swift

    func cleanUpPeripheral(_ peripheral: CBPeripheral)

    Parameters

    peripheral

    The peripheral to remove and drop connection to.

  • Undocumented

    Declaration

    Swift

    public func handleScan(result: String)

CentralManager callback functions. Handles the server side of Bluetooth.

  • Callback function which gets the Bluetooth state of this device.

    If Bluetooth is turned on and functions correctly we will start scanning for peripherals.

    Note

    Cases such as .poweredOff are not handled right now. In the future they should be.

    Declaration

    Swift

    func centralManagerDidUpdateState(_ central: CBCentralManager)

    Parameters

    central

    The Central Manager which has its state updated. Given by Apple APIs.

  • Callback function which is activated if a peripheral is discovered.

    This means that if this function is called a new device is nearby and ready to broadcast messages for us.

    Note

    Converts the peripheral to a Device() and stores it in memory. Otherwise the connection would be dropped.

    Declaration

    Swift

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)

    Parameters

    central

    The central manager which discovers the peripheral.

    peripheral

    The peripheral which is discovered.

    advertisementData

    Holds information such as the name of the peripheral. See more in Apples docs.

    RSSI

    The signal strength to the peripheral device.

  • Callback function if the central manager connected successfully to the peripheral.

    Afterwards we discover what services it has to offer, and checks that it supports the dIM identifier. Otherwise it could be all other Bluetooth devices.

    Declaration

    Swift

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)

    Parameters

    central

    The central manager which connects to the peripheral.

    peripheral

    The peripheral which we connected successfully to.

  • Callback function if we fail to connect to some peripheral.

    Note

    No error handling has been implemented yet.

    Declaration

    Swift

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)

    Parameters

    central

    The central manager which fails to connect.

    peripheral

    The peripheral which we fail to connect to.

    error

    The error description of the failed connection.

  • Callback function called when we lose connection to a peripheral.

    This function cleans up the peripheral and removes it from memory.

    Note

    Should remove the device from the connectedDevices array as well (in the future).

    Declaration

    Swift

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)

    Parameters

    central

    The central which loses its connection to a peripheral.

    peripheral

    The peripheral device which we lose connection to.

    error

    The error description of the lost connection. (out-of-range for example)

  • Callback function whenever a peripheral updates its RSSI.

    Note

    RSSI is the signal strength.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?)

    Parameters

    peripheral

    The peripheral which updates its RSSI.

    RSSI

    The new RSSI value for said peripheral.

    error

    The error description if there are any. Printed to console.

  • Callback function if we discover the dIM UUID on a peripheral device.

    Afterwards we look for the specific characteristic which defines our chat functionality.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)

    Parameters

    peripheral

    The peripheral in which we discover a service.

    error

    The error description if there are any. Printed to the console.

  • Callback function if we discover the characteristic which defines the chat functionality.

    Note

    This means that we are fully connected and ready to receive messages.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)

    Parameters

    peripheral

    The now fully connected peripheral.

    service

    The service which we have discovered, only chat functionality is provided.

    error

    The error description if there are any. Printed to the console.

  • Callback function which does error handling if we receive the wrong notifications.

    Notifications are new messages.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?)

    Parameters

    peripheral

    The peripheral which send the notification.

    characteristic

    The characteristic which it sends notification for (chat functionality).

    error

    Error description if any. Printed to the console.

  • Callback function which is called whenever we receive a new message.

    Checks that we are not receiving too many messages from this particular device and that we are not getting DOS attacked.

    The message is then decoded from JSON and passed to our receivedMessage.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)

    Parameters

    peripheral

    The peripheral which we receive a new message from.

    characteristic

    The chateristic which we receive a new message from.

    error

    Error description if there are any. Also printed to the console.

  • Callback function if a peripheral modifies its services. This is not allowed and we therefore drop our connection to it.

    Declaration

    Swift

    func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService])

    Parameters

    peripheral

    The peripheral modifying its services.

    invalidatedServices

    The service which it invalidates.

PeripheralManager callback functions. Handles the client side of Bluetooth.

  • Start advertisting that we are here to nearby Bluetooth central managers. This is a callback function and called automatically.

    We also set our devices name here.

    Note

    The devices name is not avaiable when the app is backgrounded due to API restrictions.

    Declaration

    Swift

    func startAdvertising(peripheralManager: CBPeripheralManager)

    Parameters

    peripheralManager

    The peripheral manager to start advertising for.

  • Callback function which is called when the peripheral manager updates its state.

    This could be due to Bluetooth being turned off by the user. In the future we may notify the user that the wont be able to receive messages when they turned off Bluetooth.

    When the peripheral updates its state to poweredOn we add the Bluetooth functionality to it as a characteristic.

    Declaration

    Swift

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)

    Parameters

    peripheral

    The peripheral which updates its state.

  • Called when a new central subscribes to our peripheral manager. This means that we have someone to send messages to other than ourselves.

    Declaration

    Swift

    func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic)

    Parameters

    peripheral

    The peripheral which has a new subscription.

    central

    The central which subscribes to the peripheral.

    characteristic

    The characteristic which it subscribes to.

  • Callback function activated when a central unsubscribes from us.

    Nothing is done at the minute but error handling should be done in the future.

    Declaration

    Swift

    func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic)

    Parameters

    peripheral

    The peripheral which is unsubscribed from.

    central

    The central which unsubscribed from us.

    characteristic

    The characteristic (functionality) which the central unsubscribes from.

DSR (Dynamic Source Routing) functionality

  • Add a message to the dicitionary when it is received.

    This is to ensure that we have knowledge of where a message came from to send back ACK messages the shortest route possible.

    Declaration

    Swift

    func addMessageToDSRTable(messageID: Int32, bluetoothID: String)

    Parameters

    messageID

    The id of the message to keep track of.

    bluetoothID

    The id of the sender of the message to save.

  • Check if we have seen a message before. If we have not we will flood the network.

    Declaration

    Swift

    func checkMessageSeenBefore(messageID: Int32) -> Bool

    Parameters

    messageID

    The id of the message to check.

    Return Value

    A boolean to confirm if we have seen the message before.

  • Get the sender of a message given a message id.

    Declaration

    Swift

    func getSenderOfMessage(messageID: Int32) -> String

    Parameters

    messageID

    The message id to check for.

    Return Value

    The Bluetooth UUID as a string.

Message queue functionality.

  • A message stored together with a date for queue.

    See more

    Declaration

    Swift

    struct queuedMessage
  • Add a message to the message queue.

    This function is used to deliver messages over long distances.

    Declaration

    Swift

    func messageQueueAdd(_ message: Message)

    Parameters

    message

    The message to add to the queue of messages.

  • Remove old messages from the message queue such that we do not deliver messages older than a set amount.

    Declaration

    Swift

    func checkMessageQueue()
  • Called when we establish a new connection to a central manager. This sends over all the queued messages but clean up first such that no old messages are sent.

    Declaration

    Swift

    func messageQueueNewConnection(_ central: CBCentral)

    Parameters

    central

    The newly connected central device.

Relay messages functionality.

  • Relay a message which we have received and is not for us.

    This function simply sends all received messages not for us to all connected servers (central managers).

    Declaration

    Swift

    func relayMessage(_ message: Message)

    Parameters

    message

    The message to relay still in its encrypted format.

  • Relay message but to be used for DSR routing. Therefore we require a specific Bluetooth ID to send the send the message to.

    Note

    This function is only used for ACK messages currently.

    Declaration

    Swift

    func relayMessage(_ message: Message, _ bluetoothID: String)

    Parameters

    message

    The message to send.

    bluetoothID

    The BluetoothID to send it to (a UUID as a string).

Receiving messages.

  • Retrieve a message from a sender and handle it accordingly.

    If the message is not for us we relay it to connected Bluetooth centrals. Otherwise, if it is for us, we start decrypting it and adding it to the correct conversation. Then we send an ACK message to confirm that we have received it.

    Declaration

    Swift

    func retrieveMessage(_ messageEncrypted: Message)

    Parameters

    messageEncrypted

    The message that we have received. Then we determine if it is for us.

  • Called if we have received a READ message type. This is to handle that type of messages correctly.

    Also confirms that the message is formatted correctly.

    Declaration

    Swift

    func receivedRead(message: Message, conversation: ConversationEntity) -> Bool

    Parameters

    message

    The READ message that we have received.

    conversation

    The conversation in which the message is to be handled.

    Return Value

    A boolean that confirms that the type of message is a READ type.

  • Handles ACK message types. Also confirms that the message is correctly formatted and updates the conversation.

    Declaration

    Swift

    func receivedAck(message: Message, conversation: ConversationEntity) -> Bool

    Parameters

    message

    The ACK message we have received.

    conversation

    The conversation in which it is handled.

    Return Value

    A boolean confirming that it is or is not an ACK message.

Helper functions

  • A helper function to decrypt a message to a string.

    Declaration

    Swift

    func decryptRetrievedMessageToString(message: Message, conversation: ConversationEntity) -> String?

    Parameters

    message

    The message to decrypt.

    conversation

    The conversation to decrypt the message for.

    Return Value

    The decrypted content of the message or nil if it cannot be decrypted.

Sending messages.

  • Sends a message to a specific user.

    This is done by encrypting the content of the message, generating new id’s and passing it along to the peripheral manager to send out to connected central managers.

    Declaration

    Swift

    func sendMessage(for conversation: ConversationEntity, text message: String, context: NSManagedObjectContext)

    Parameters

    conversation

    The conversation for whom we want to send a message.

    message

    The message that we want to send. It is encrypted in this function.

    context

    The context which we save the message. Used for persistent storage to CoreData.

  • Sends a message of id’s which confirms that we have read the received messages.

    This function is only called if we have enabled the read functionality in the settings menu.

    We send a message with all the id’s of the messages that we have read formatted as READ/id1/id2/id2....

    Declaration

    Swift

    func sendReadMessage(_ conversation: ConversationEntity)

    Parameters

    conversation

    The conversation which we have recently opened.

  • Sends an ACK message to confirm that we have received a message. If we use DSR (Dynamic Source Routing) this messages is sent on the shortest route possible.

    Declaration

    Swift

    func sendAckMessage(_ message: MessageEntity)

    Parameters

    message

    The message which we want to send an ACK message for.