TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Day 72: Error (Publishing changes from within...)

Forums > 100 Days of SwiftUI

Hi, just ended day 72 and everything is working but I keep getting this purple error. Everytime i move the map it starts to create more and more errors. Since this was a follow tutorial, went on Github to check Paul's code and it is exactly like mine, so I don't know what it can be wrong.

Searched for similar problems and noticed another user complaining about the same. Is it a bug related to newer version of XCode?

2      

@orocha is seeing purple!

i move the map it starts to create more and more errors.

Think about what the compiler is telling you!

Publishing changes from within view updates is not allowed....[snip]

While you are telling SwiftUI to change a view based on some parameters you have provided, you then change those parameters as SwiftUI is updating the view! Which value should it use? The one you gave it when you started the update? Or the one you calculated on the fly whilst it was updating?

You see, as soon as your change a value in a view struct, the struct becomes invalid and must redraw itself. So consider the situation where the view has grabbed your new parameter and starts redrawing the view. It's doing great and gets 92% of the way through redrawing and updating the view.

BUT THEN, you change a parameter! It was ever-so-close to finishing drawing the updated view, but you changed a parameter value, thus invalidating the yet-undisplayed view! So SwiftUI starts redrawing (again!) with an even newer value.

Thinking of another example, it may be similar to changing a tyre on your Citroen whilst you are driving down the autopista!

Provide some code snips so we can see the view that's being updated, if you please.

Please note! WWDC introduced new Swiftier version of MapKit. Comments here to fix your Day 72 code may be out-of-date based on latest updates!

See -> What's new in MapKit 2023

Please share!

These improvements may eliminate the problems associated with wrapping a UIKit interface into SwiftUI. It would be great for you to return here and provide your updates including your impressions of the new MapKit updates!

2      

Thanks for the help @Obelix and @stonesexy3.

Reviewed my code again and checked with Paul's code to see if there was any difference that I might have overlooked. Didn't find anything.

Regarding the version part, Paul's code was made in iOS 15 and mine's using iOS 16.2.

Since what was causing the error was me moving the map, checked my Map code to see if i was changing anything in ContentView instead of in my View Controller but only the "selectedPlace" is changing something in ContentView as far as i can tell...

Here's my View Controller code:

import Foundation
import MapKit
import LocalAuthentication

extension ContentView {
    @MainActor class ViewModel: ObservableObject {

        @Published var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50, longitude: 0), span: MKCoordinateSpan(latitudeDelta: 25, longitudeDelta: 25))
        @Published private(set) var locations: [Location]
        @Published var selectedPlace: Location?

        @Published var isUnlocked = false

        let savePath = FileManager.documentsDirectory.appendingPathExtension("SavedPlaces")

        init() {
            do {
                let data = try Data(contentsOf: savePath)
                locations = try JSONDecoder().decode([Location].self, from: data)
            } catch {
                locations = []
            }
        }

        func save() {
            do {
                let data = try JSONEncoder().encode(locations)
                try data.write(to: savePath, options: [.atomic, .completeFileProtection])
            } catch {
                print("Unable to save data.")
            }
        }

        func addLocation() {
            let newLocation = Location(id: UUID(), name: "New location", description: "", latitude: mapRegion.center.latitude, longitude: mapRegion.center.longitude)
            locations.append(newLocation)
            save()
        }

        func update(location: Location) {

            guard let selectedPlace = selectedPlace else { return }

            if let index = locations.firstIndex(of: selectedPlace) {
                locations[index] = location
                save()
            }

        }

        func authenticate() {
            let context = LAContext()
            var error: NSError?

            if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
                let reason = "Please authenticate yourself to unlock your places."

                context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
                    if success {
                        Task { @MainActor in
                            self.isUnlocked = true
                        }
                    } else {
                        //error
                    }
                }
            } else {
                //no biometrics
            }
        }
    }
}

And my ContentView code:

//
//  ContentView.swift
//  BucketList2
//
//  Created by Renato Martins on 11/06/2023.
//

import MapKit
import SwiftUI

struct ContentView: View {

    @StateObject private var viewModel = ViewModel()

    var body: some View {
        if viewModel.isUnlocked {
            ZStack {
                Map(coordinateRegion: $viewModel.mapRegion, annotationItems: viewModel.locations) { location in
                    MapAnnotation (coordinate: location.coordinate) {
                        VStack {
                            Image(systemName: "star.circle")
                                .resizable()
                                .foregroundColor(.red)
                                .frame(width: 44, height: 44)
                                .background(.white)
                                .clipShape(Circle())
                            Text(location.name)
                                .fixedSize()
                        }
                        .onTapGesture {
                            viewModel.selectedPlace = location
                        }
                    }
                }
                .ignoresSafeArea()
                Circle()
                    .fill(.blue.opacity(0.3))
                    .frame(width: 32, height: 32)

                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        Button {
                            viewModel.addLocation()
                        } label: {
                            Image(systemName: "plus")
                        }
                        .padding()
                        .background(.black.opacity(0.75))
                        .foregroundColor(.white)
                        .font(.title)
                        .clipShape(Circle())
                        .padding(.trailing)
                    }
                }
            }
            .sheet(item: $viewModel.selectedPlace) { place in
                EditView(location: place) { newLocation in
                    viewModel.update(location: newLocation)
                }
            }
        } else {
            Button("Unlock Places") {
                viewModel.authenticate()
            }
            .padding()
            .background(.blue)
            .foregroundColor(.white)
            .clipShape(Capsule())
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.