Search code examples
jsonswiftuienvironmentobjectonappearrefreshable

SwiftUI - Get data in remote JSON data to refresh on App startup


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)
    } }

Solution

  • 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()