Hello,
I have finished Day 72 Challanges and my code runs. However when running simulator I get "Publishing changes from within view updates is not allowed, this will cause undefined behavior." error. I think it is similar to error that Paul explains in "Locking our UI behind Face ID" topic. I have tried to use Task { @MainActor in however could not manage to get rid of those purple errors.
Is there anyway to eliminate them or should I just ignore them?
You can find my Editview and EditViewModel code below. Other parts are same as Paul shows.
import SwiftUI
struct EditView: View {
@Environment(\.dismiss) var dismiss
@StateObject private var editViewModel : EditViewModel
var onSave: (Location) -> Void
var body: some View {
NavigationView {
Form {
Section {
TextField("Place name", text: $editViewModel.name)
TextField("Description", text: $editViewModel.description)
}
Section("Nearby…") {
switch editViewModel.loadingState {
case .loaded:
ForEach(editViewModel.pages, id: \.pageid) { page in
Text(page.title)
.font(.headline)
+ Text(": ") +
Text(page.description)
.italic()
}
case .loading:
Text("Loading…")
case .failed:
Text("Please try again later.")
}
}
}
.navigationTitle("Place details")
.toolbar {
Button("Save") {
let newLocation = editViewModel.savePlaceInfo()
onSave(newLocation)
dismiss()
}
.task {
await editViewModel.fetchNearbyPlaces()
}
}
}
}
init(location: Location, onSave: @escaping (Location) -> Void) {
_editViewModel = StateObject(wrappedValue: EditViewModel(location: location))
self.onSave = onSave
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView(location: Location.example) { newLocation in }
}
}
import Foundation
extension EditView {
@MainActor
class EditViewModel : ObservableObject {
@Published var location : Location
@Published var name: String
@Published var description: String
@Published var loadingState = LoadingState.loading
@Published var pages = [Page]()
enum LoadingState {
case loading, loaded, failed
}
init(location: Location) {
self.location = location
_name = Published(initialValue: location.name)
_description = Published(initialValue: location.description)
}
func savePlaceInfo() -> Location {
var newLocation = location
newLocation.id = UUID()
newLocation.name = name
newLocation.description = description
return newLocation
}
func fetchNearbyPlaces() async {
let urlString = "https://en.wikipedia.org/w/api.php?ggscoord=\(location.coordinate.latitude)%7C\(location.coordinate.longitude)&action=query&prop=coordinates%7Cpageimages%7Cpageterms&colimit=50&piprop=thumbnail&pithumbsize=500&pilimit=50&wbptterms=description&generator=geosearch&ggsradius=10000&ggslimit=50&format=json"
guard let url = URL(string: urlString) else {
print("Bad URL: \(urlString)")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
// we got some data back!
let items = try JSONDecoder().decode(Result.self, from: data)
// success – convert the array values to our pages array
pages = items.query.pages.values.sorted()
loadingState = .loaded
} catch {
// if we're still here it means the request failed somehow
loadingState = .failed
}
}
}
}
Thanks,