I am using the code below to do the following.
- Create a new item every 5 seconds and append it to the model
- Display a list of items in the listView
- Display a map of the items in the mapView
If I am in the listView, the list gets properly updated every 5 seconds with the new item. No error message. If I am in the mapView, the map also gets updated (a new marker every 5 seconds), but I get error "[SwiftUI] Publishing changes from within view updates is not allowed, this will cause undefined behavior." Since list and map both display the same model data, I wonder why map complains and list does not. The actual model update is on the main actor, so why is it complaining.
Any idea?
//Model
struct TestApp1Model {
struct TestItem: Identifiable {
var id = UUID()
var name: String
var latitude: Double
var longitude: Double
}
var items = [TestItem]()
}
// ViewModel
class TestApp1ViewModel: ObservableObject {
@Published private var model = TestApp1Model()
private var timer:Timer?
init() {
timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
Task { @MainActor in
self.addItem()
}
}
}
var items:[TestApp1Model.TestItem] {
model.items
}
@MainActor func addItem () {
let name = "Item " + model.items.count.description
let latitude = Double.random(in: 45...55)
let longitude = Double.random(in: 5...11)
model.items.append(TestApp1Model.TestItem(name: name, latitude: latitude, longitude: longitude))
}
}
// View
struct TestApp1View: View {
@StateObject var testVM = TestApp1ViewModel()
@State var region:MKCoordinateRegion
init() {
self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50, longitude: 8), span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 6))
}
var body: some View {
TabView {
listView
.tabItem {
Image(systemName: "list.bullet")
Text("List")
}
.backgroundStyle(Color.white)
mapView
.tabItem {
Image(systemName: "map")
Text("Map")
}
.backgroundStyle(Color.white)
}
}
var listView: some View {
VStack {
List (testVM.items) { item in
HStack {
Text(item.name)
Text(item.latitude.description)
Text(item.longitude.description)
}
}
}
}
var mapView: some View {
Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true,annotationItems: testVM.items) {item in
MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: item.latitude, longitude: item.longitude)) {
Image(systemName: "plus")
.foregroundColor(.red)
}
}
.ignoresSafeArea()
}
}