Updating a SwiftUI view’s state on navigating back
I’ve recently been exploring SwiftUI as a means to redo JiffyCV, a React Native based app I built a couple of years ago.
JiffyCV had a CV editor that contained a tab view that would allow a user to see a preview of the CV in one tab, select the entries to include in the CV in another and select the theme for the CV and some other customisations in another.
In React Native I had created the CV preview using a webview and I could rely on the render lifecycle of React to re-render that view on navigating back to it.
In SwiftUI I implemented the CV editor as a NavigationView with NavigationLinks going off to the appropriate view for the editor to make changes to the CV’s configuration and coming back to see the results.
I also decided to remove the need for a webview in the CV preview as this had caused some inconsistencies in the original JiffyCV as on iOS the webview used Safari and we had a web service that used Chrome to create the PDF.
Instead as I was targeting iOS natively I could render the PDF in the view itself and update it on fly, giving the user a much more consistent representation of the final result.
encountered an issue though, when I pressed Back on the view for updating the data for the CV the CV wouldn’t automatically update.
I believe this was due to the fact that the CV preview data was stored in state and not as a SwiftData model so wasn’t part of the same observation based render cycle.
So I had to find a means to trigger a re-render of the PDF by updating the CV preview data in state on navigating back.
Triggering a state change on coming back to a view
As I couldn’t rely on listening to updates on the SwiftData models I had to instead look for other ways to do it.
I had initially looked to do this by looking at changes in the navigation stack. I added an onChange listener to check the NavigationPath and if the new value was one less than the old value then it would suggest the view had been popped.
I eventually found that onAppear worked better for triggering the render, wrapping the render in a Task so it didn’t block anything.
This worked for both the initial navigation to the view and coming back which sold me on using this approach.