Please excuse how much of a newbie I am. I started on swift at the beginning of last week...
I am trying to make an app that uses BLE and or MQTT to talk to a raspberry pi through a broker. the BLE side is ok but the MQTT (for when out of BLE range) I'm having trouble with. This is in a swift file of a couple of classes:
import Foundation
import CocoaMQTT
class MQTTManager{
static let shared = MQTTManager()
private var mqttClient: CocoaMQTT
init() {
let clientID = "swift-Trial-13579"
let host = "IP.Goes.Here"
let port = UInt16(1883)
self.mqttClient = CocoaMQTT(clientID: clientID, host: host, port: port)
self.mqttClient.username = "User"
self.mqttClient.password = "Pass"
self.mqttClient.keepAlive = 60
self.mqttClient.connect()
sendMessage(topic: "app/init", message: "init called")
print("MQTT Init Called")
}
func sendMessage(topic:String, message:String){
self.mqttClient.publish(topic, withString: message)
print("publish MQTT called with message: \(message) and a topic of: \(topic)")
}
}
class useProperties: ObservableObject{
@Published var useMQTT = false
@Published var recallMQTTScene = false
@Published var MQTTScene = 0
init(){
}
}
I then have 3 views, ContentView
import SwiftUI
import CocoaMQTT
struct ContentView: View {
@State public var Connection:Bool = false
@State public var SceneMessqe: String = ""
let MQTTHandle = MQTTManager()
// the main view actually is here
var body: some View{
// SceneButton(function: { self.MQTTPub})
VStack{
HStack {
Text("MultiControl POC")
.font(.title)
//.padding()
}
Spacer()
mainSwitch()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(BLEManager())
}
}
mainSwitch
import SwiftUI
import CocoaMQTT
struct mainSwitch: View {
@State public var Connection:Bool = false
@State private var selection: String? = nil
@StateObject var bleManager = BLEManager() // gets from environment.
public var properties = useProperties()
@State private var selectDeviceShown = false
@State var isPresenting = false
public var MQTTHandle = MQTTManager()
var body: some View {
VStack (spacing: 0){
NavigationView{
HStack {
Spacer()
NavigationLink(destination: sheetView(), isActive: $isPresenting) { EmptyView() }// added for nav but not working
Menu("Menu") {
Button("BLE Setup", action: {
self.isPresenting = true // added to trigger nav not workinh
print("Setup button pressed")
//selectDeviceShown = true
})
Button("Reconnect", action: {
bleManager.myCentral.connect(bleManager.wrappedControllers[bleManager.currentSceneSwitchControllerUUID]!.wrappedPerh)
})
Button(action: {
MQTTManager.init()
Connection.toggle()
properties.useMQTT = true
print("connect/disconnect pressed useMQTT = \(properties.useMQTT)")
}, label: {
Text(Connection ? "MQTT Disconnect":"MQTT Connect")
})
Button("Cancel", action: {
print("oops")
})
}
.foregroundColor(Connection ? .green : .red)
.padding(38)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 2)
.foregroundColor(Connection ? .green : .red)
)
Spacer()
Spacer()
}
.fixedSize(horizontal: false, vertical: true)
.frame(maxHeight: 10)
.padding()
}
HStack{
Spacer()
VStack{
Spacer()
SceneButton(sceneName: "Scene 1", sceneNumber: 1)
Spacer()
SceneButton(sceneName: "Scene 3" , sceneNumber: 3)
Spacer()
SceneButton(sceneName: "Scene 5", sceneNumber: 5)
Spacer()
}
Spacer()
VStack{
Spacer()
SceneButton(sceneName: "Scene 2", sceneNumber: 2)
Spacer()
SceneButton(sceneName: "Scene 4", sceneNumber: 4)
Spacer()
SceneButton(sceneName: "Off", sceneNumber: 6)
Spacer()
}
Spacer()
}
Spacer()
}
.environmentObject(useProperties())
.environmentObject(BLEManager())
}
}
struct mainSwitch_Previews: PreviewProvider {
static var previews: some View {
Group {
mainSwitch()
.environmentObject(BLEManager())
.environmentObject(useProperties())
}
}
}
and finally sceneButton
import SwiftUI
import CocoaMQTT
struct SceneButton: View {
var sceneName: String
var sceneNumber: Int
let properties = useProperties()
@State private var isDisabled: Bool = true
@State private var isDuringGesture: Bool = false
@StateObject private var bleManager = BLEManager()
let btnClr:Color = Color.orange
let btnClrOutr:Color = Color.red
let btnPressedClr:Color = Color.gray
let MQTTHandle = MQTTManager()
var body: some View {
if (properties.useMQTT){
Button(sceneName) {
bleManager.writeToCharacteristicButtonPress(peripheral: bleManager.currentSceneSwitchControllerUUID, sceneToGoToo: (sceneNumber).description, setButtonPressed: true) // Note the number is the same number as the button.
bleManager.writeToCharacteristicButtonPress(peripheral: bleManager.currentSceneSwitchControllerUUID, sceneToGoToo: (sceneNumber).description, setButtonPressed: false)
print("BLE Button" + sceneName)
print("\(properties.useMQTT)")
}
.foregroundColor(.white)
.frame(minWidth: 100)
.padding()
.background(Color(red: 1, green: 0.1, blue: 0.1))
.cornerRadius(10)
.padding(10)
.font(.title2)
.font(.system(size: 20))
}else{
Button(sceneName) {
print("\(properties.useMQTT)")
MQTTHandle.sendMessage(topic: "apptest/scene", message: "\(sceneNumber)")
}
.frame(minWidth: 100)
.padding()
.background(Color(red: 0.40, green: 0.60, blue: 0))
.cornerRadius(10)
.padding(10)
}
}
}
struct SceneButton_Previews: PreviewProvider {
static var previews: some View {
SceneButton(sceneName: "Scene X", sceneNumber: 42) //, publishSceneMQTT: sceneHandle
.environmentObject(BLEManager())
.environmentObject(useProperties())
}
}
Currently when the app loads it calls the MQTT init many times, and then doesn't connect reliably, occasionally, maybe 1 in 10 times it connects to send a single message then I can't send more. Ideally it would only connect when I press the connect button in the menu in mainSwitch. However each button (SceneButton) should publish something slightly different.
Firstly, is it an issue that it keeps calling init at start?
Secondly, is there something visible that I am doing wrong to mean its not reliably connecting?
thirdly, (least important) in sceneButton the button should change whether using BLE or MQTT, this variable, useMQTT is set in the mainSwitch file. but doesn't change in sceneButton, what have I done wrong?
You use everywhere (!) different instances of MQTTManager
, because create it via init
, instead you should use everywhere MQTTManager.shared
, like
struct ContentView: View {
@State public var Connection:Bool = false
@State public var SceneMessqe: String = ""
// let MQTTHandle = MQTTManager() // << not this !!
let MQTTHandle = MQTTManager.shared // << this one !!
so review all your code and fix as above.