1

On a button press, I my app is trying to contact an api to receive data. This data is then stored in a published variable inside an observable object. For some reason, the view doesn't populate with the data until the button that opens that view is press more than once. I am looking for the view to update with the information received from the api call on the first button press. The code I am referencing is provided below:

DataFetcher.swift:

class DataFetcher: ObservableObject{
    @Published var dataHasLoaded: Bool = false
    @Published var attendeesLoaded: Bool = false
    @Published var useresUventsLoaded: Bool = false
    @Published var profilesLoaded: Bool = false
    @Published var eventsUpdated: Bool = false
    @Published var events: [eventdata] = []
    @Published var createdEvents: [eventdata] = []
    @Published var profile: profiledata?
    @Published var atendees: [atendeedata] = []
    @Published var IAmAtending: [atendeedata] = []
    @Published var eventNames: [eventdata] = []
    @Published var profileList: [profiledata] = []
    @Published var token: String = UserDefaults.standard.string(forKey: "Token") ?? ""
    private var id: Int = 0

func fetchProfile(id: Int){
        
       // events.removeAll()
        profileUrl.append("/\(id)")
        self.id = id
        let url = URL(string: profileUrl)!
        var request = URLRequest(url: url)
        
        if let range = profileUrl.range(of: "/\(id)") {
           profileUrl.removeSubrange(range)
        }
        
        request.httpMethod = "GET"
        print(self.token)
        request.addValue("Token \(self.token)", forHTTPHeaderField: "Authorization")
        let task = URLSession.shared.dataTask(with: request, completionHandler: parseFetchProfileObject)
                task.resume()
            }
            
            
            func parseFetchProfileObject(data: Data?, urlResponse: URLResponse?, error: Error?){
                guard error == nil else {
                    print("\(error!)")
                    return
                }
                
                guard let content = data else{
                    print("No data")
                    return
                }
                
                if let decodedResponse = try? JSONDecoder().decode(profiledata?.self, from: content) {
                    DispatchQueue.main.async {
                        self.profile = decodedResponse
                        self.profileList.append(self.profile!)
                }
            }
            
        }
    
    
    
    func fetchAtendees(id: Int){
        
       // events.removeAll()
        atendeeUrl.append("/\(id)")
        print(atendeeUrl)
       
        let url = URL(string: atendeeUrl)!
        var request = URLRequest(url: url)
       
        if let range = atendeeUrl.range(of:"/\(id)") {
           atendeeUrl.removeSubrange(range)
        }
        
         request.httpMethod = "GET"
        print(self.token)
        request.addValue("Token \(self.token)", forHTTPHeaderField: "Authorization")
        let task = URLSession.shared.dataTask(with: request, completionHandler: parseFetchAttendeesObject)
                task.resume()
            }

EventsUserCreatedView.swift

import Foundation
import SwiftUI
import Mapbox

struct EventsUserCreatedView: View {
    
    
    @Binding var token: String
    @State private var pressedEvent: Bool = false
    @State private var selectedEvent: Int = 0
    @State private var atendees: [atendeedata] = []
    @State private var profileList: [profiledata] = []
    @State private var showDeleteEventView: Bool = false
    var data: DataFetcher
    var mapStyle: URL
    
    
    var body: some View {
        ZStack{
            //NavigationView {
            
            
            
            if self.mapStyle == MGLStyle.darkStyleURL {
                List{
                    ForEach(self.data.createdEvents){ row in
                        HStack {
                            Button("\((row.poi)!)") {
                                print("Display event information")
                                
                                self.selectedEvent = row.id
                                
                                self.pressedEvent = true
                                
                            }
                            
                            Spacer()
                            Button("Delete") {
                                
                                self.showDeleteEventView = true
                                print("Deletes the event in this row")
                            }.buttonStyle(BorderlessButtonStyle())
                                .padding(4)
                                .background(Color.red)
                                .cornerRadius(5)
                            
                        }.foregroundColor(Color.white)
                    }
                    
                }.background(Color.init(red: 0.05, green: 0.05, blue: 0.05))
                
                //if you hit more buttons than there is room for, it'll be scrollable. make some kind of for loop that iterates over events a user is going to and displays it
                
                //  }.navigationBarTitle("My Events")
                //   .navigationViewStyle(StackNavigationViewStyle())
                
                if pressedEvent{
                    Group{
                        if self.data.profilesLoaded == true{
                            //NavigationView {
                            List{
                                ForEach(self.data.profileList){ row in
                                    HStack {
                                        Text("\(row.name)")
                                            .foregroundColor(Color.purple)
                                        Spacer()
                                    }
                                }
                            }.background(Color.init(red: 0.05, green: 0.05, blue: 0.05))
                            
                            //if you hit more buttons than there is room for, it'll be scrollable. make some kind of for loop that iterates over events a user is going to and displays it
                            
                            //}
                        } else{
                            Spacer()
                            Text("Loading Attendees")
                            Spacer()
                        }
                    }.onAppear{
                        //this can't be done on appear as it won't update when a different
                        self.data.profileList = []
                        self.data.atendees = []
                        DispatchQueue.main.async{
                            
                            self.data.fetchAtendees(id: self.selectedEvent)
                            
                            if self.data.profilesLoaded{
                                self.profileList = self.data.profileList
                                self.atendees = self.data.atendees
                            }
                        }
                    }
                    //.navigationBarTitle("My Attendees")
                    //.navigationViewStyle(StackNavigationViewStyle())
                }

NOTE: datafetcher (the observableobject) is passed to eventsusercreated view by the contentview

any help on how to update my view properly is much appreciated

pawello2222
  • 46,897
  • 22
  • 145
  • 209
TJ D'Alessandro
  • 109
  • 1
  • 19

1 Answers1

5

You've to declare the data as an @ObservedObject.

struct EventsUserCreatedView: View {
    //...
    @ObservedObject var data = DataFetcher()
    //...
}

If you're passing DataFetcher instance as environment object declare it as @EnvironmentObject.

struct EventsUserCreatedView: View {
    //...
    @EnvironmentObject var data: DataFetcher
    //...
}
Frankenstein
  • 15,732
  • 4
  • 22
  • 47