Hello,
I have an app which is connected to an MQTT Broker. Via the MQTT Broker the app receives a JSON, let's call it a field, containing an array of lat/lng points, id and a name.
Subscription to the specific topic works, there is a class type which inherts from MKPolygon in order to store the points, the id and the name.
I have created the basic mapkit setup ( as it is shown here on hacking with swift in one of the tutorials)
My question is, where do I put the code which handles the creation of the overlay and especially where do I put the code when a new field is received via the mqtt broker?
Here is my basic mqtt class which connects and listens for messages:
class MqttData: ObservableObject, CocoaMQTTDelegate
{
@Published public var fieldPolygon: FieldPolygon = FieldPolygon()
init() {
let clientID = "CocoaMQTT-" + String(ProcessInfo().processIdentifier)
let websocket = CocoaMQTTWebSocket(uri: "/mqtt")
let mqtt = CocoaMQTT(clientID: clientID, host: "xxx", port: 9001, socket: websocket)
mqtt.username = "xxx"
mqtt.password = "xxx"
mqtt.keepAlive = 60
mqtt.delegate = self
mqtt.connect()
}
func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) {
if (ack == .accept) {
debugPrint("MQTT connection accepted!")
mqtt.subscribe("#", qos: CocoaMQTTQoS.qos0)
}
}
func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) {
}
func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) {
}
func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) {
let topic = message.topic
guard let msg = message.string else {
return
}
let data = Data(msg.utf8)
if ("/hmi/field_select" == topic) {
self.fieldSelectHandler(data: data)
}
}
func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) {
}
func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String]) {
}
func mqttDidPing(_ mqtt: CocoaMQTT) {
}
func mqttDidReceivePong(_ mqtt: CocoaMQTT) {
}
func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) {
}
func fieldSelectHandler(data: Data) {
Task {
do {
let fieldData = try JSONDecoder().decode(FieldData.self, from: data)
let field = try await APIManager.shared.loadFieldData(fieldId: fieldData.fieldId)
var points: [CLLocationCoordinate2D] = []
for coordinate in field.coords {
points.append(CLLocationCoordinate2DMake(coordinate.lat, coordinate.lng))
}
self.fieldPolygon = FieldPolygon(coordinates: &points, count: points.count)
self.fieldPolygon.fieldId = field.id
} catch {
debugPrint("fieldSelectHandler Exception: \(error)")
}
}
}
}
Here is the parent view which integrates the map view
struct HmiMapView: View {
@EnvironmentObject var mqttData: MqttData
var body: some View {
MapView(mqttFieldPolygon: mqttData.fieldPolygon)
}
}
MapView here is a basic implementation (UIViewRepresentable / Coordinator / MKMapViewDelegate)
My first try was to create a private member within my MapView struct, store the polygon within the private member and create a overlay and add it to the map. Whenever a further polygon is received via MQTT, I need to check - has the map already a overlay, if true check if the id is the same and if not, delete the old overlay and create a new one.
So where does this functionality typically exists? But the MapView struct should (or could) not have a private member var.
Regards Marcus