1

I am a noob (and my code/model currently will show it so please go easy on me!).

I’m taking a first whack at creating a recipes style (cenotes) app.

So that I can update the data I have a remote json file, so users won't have to reinstall to get the updated data.

The app successfully retrieves and parses the data (that’s a win!), but only when first installed.

It is not subsequently refreshing the data when it is changed online. I have tried using .onAppear and also .refreshable unsuccessfully. I need to delete the App and re-install for it to go and fetch the updated data.

Below are some the redacted versions of my files: Can anybody help? I think that I just have a @StateObject/@EnvironmentObject or something similar incorrect, but can’t spot the error.

Locations.swift

import Foundation

class Locations: ObservableObject {
    @Published var locations = [Location]()
        
    init() {
        load()
    }
    
    func load() {
        let url = URL(string: "https://www.i-love-tulum.com/cenotes/locations.json")!
               URLSession.shared.dataTask(with: url) {(data,response,error) in
                   do {
                       if let d = data {
                           let data = try JSONDecoder().decode([Location].self, from: d)
                           DispatchQueue.main.async {
                               self.locations = data
                           }
                       } else {
                           print("No Data")
                       }
                   } catch {
                       print ("Error")
                   }
               }.resume()
    } }

Location.swift - VM

import Foundation

struct Location: Codable, Identifiable {
    var id: String
    let heroPicture: String
    }

App.swift - The App opens to this tab view

import SwiftUI

@main struct App: App {
    @StateObject var locations = Locations()
    @State private var selection = 1
        
    var body: some Scene {
        WindowGroup {
            TabView (selection:$selection) {
                NavigationView {
                    ListView()
                }
                .tabItem {
                    Image(systemName: "drop.circle")
                    Text("Cenotes")
                } .tag(1)
            }
            .environmentObject(locations)
        }
    } }

ListView.swift - The first selected tab is this view - these locations are not updating after the initial install:

import SwiftUI import MapKit

struct ListView: View {
    @EnvironmentObject var locations: Locations
          
    var body: some View {
        VStack {         
            GeometryReader { geo in
                ScrollView {
                    LazyVStack (alignment: .leading) {
                        ForEach (locations: locations.locations) { location in
                               NavigationLink (
                                    destination: ContentView(location: location),
                                    label: { 
                    Image(location.heroPicture)
                    }
            .navigationTitle("Cenotes")
            .navigationBarTitleDisplayMode(.inline)
    } }
  • This looks all fine to me. The user can't edit the entries right? They are only displayed? Does the App show anything on 2nd launch? – if so, it can only get it from the JSON, as you don't save data otherwise. Put a breakpoint into the load() and see what happens. – ChrisR Feb 10 '22 at 22:29
  • @ChrisR Thanks for the quick response. The user can't edit the entries. Whatever is loading the 1st time, is what is shown on subsequent launches. (Yes I've closed down the app fully, swiped up and even restarted phone). I don't need to reload data every time the app comes from background to front (just on App startup each time ideally), but that would be okay too (as long as the older data would remain and it didn't fail completely if the internet drops in the meantime - my plan will be to implement a fallback on local json data if no internet is connected anyway). – user18175514 Feb 10 '22 at 22:35
  • @ChrisR Even with the data/internet off, on subsequent App loads, I have whatever was loaded from json the first time, so it is definitely being stored on my phone. I do store some bookmarked/favourites within the app (code wasn't included), but I can't see how those UserDefaults would hold onto the json data too). – user18175514 Feb 10 '22 at 22:43
  • yes, I tried it too. I have no idea why, there seems to be some URLSession cache ... I'm afraid I'm not the right one for connection details ... but did the delete and reload do anything? – ChrisR Feb 10 '22 at 23:16
  • aha: https://stackoverflow.com/search?q=URLRequest+Cache+policy – ChrisR Feb 10 '22 at 23:19
  • You're a star. Thanks ChrisR. – user18175514 Feb 11 '22 at 01:06

2 Answers2

2

Try this to reload every time and skip all caches:

UPDATE, now working :)

func load() {
    let url = URL(string: "https://www.i-love-tulum.com/cenotes/locations.json")!
    
    let config = URLSessionConfiguration.default
    config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
    
    let session = URLSession.init(configuration: config)
    session.dataTask(with: url) {(data,response,error) in
        do {
            if let d = data {
                let data = try JSONDecoder().decode([Location].self, from: d)
                DispatchQueue.main.async {
                    self.locations = data
                }
            } else {
                print("No Data")
            }
        } catch {
            print ("Error")
        }
    }.resume()
ChrisR
  • 9,523
  • 1
  • 8
  • 26
0

All is well set up and works fine. This here adds two Buttons to the list, to delete all entries and reload again. Maybe that helps.

struct ListView: View {
    @EnvironmentObject var locations: Locations
    
    var body: some View {
        List {
            ForEach (locations.locations) { location in
                NavigationLink {
                    Text("Content View here ...")
                } label: {
                    VStack(alignment: .leading) {
                        Text(location.name).font(.title)
                        Text(location.aka).foregroundColor(.secondary)
                    }
                }
                .navigationTitle("Cenotes")
                .navigationBarTitleDisplayMode(.inline)
            }
        }
        
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button("Delete all") {
                    locations.locations = []
                }
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Reload") {
                    locations.load()  
                }
            }
        }
    }
}

enter image description here

ChrisR
  • 9,523
  • 1
  • 8
  • 26