Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
502 views
in Technique[技术] by (71.8m points)

combine - Data from ObservedObject not rendered in SwiftUI view

I use combine to connect to a REST API which pulls some data into an ObservableObject. The whole setup is really just MVVM. The ObservableObject is then being Observed in a view. Now, I'm stuck with a bug I can't seem to get resolved. What seems to happen is that the View is drawn twice. The first time it updates with the values from the ObservedObject. Then it immediately re-draws, but now the pulled data is suddenly gone. I have confirmed that the code that pulls the data is not executed a second time. Also, the View Hierarchy Viewer seems to suggest that the Text Views which are supposed to be render the data in the view are somehow deleted on the second render run.

This is what the model looks like (the authentication related code is from Firebase):

class ItemViewModel: ObservableObject {
    
    @Published var items = [ScheduleItem]()
    
    private var publisher: AnyCancellable?
    
    func fetchData() {
        
        let currentUser = Auth.auth().currentUser
        currentUser?.getIDTokenForcingRefresh(false, completion: { idToken, error in
        
            let url = URL(string: "abc")!
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("Bearer "+String(idToken!), forHTTPHeaderField: "Authorization")
     
            self.publisher = URLSession.shared
                .dataTaskPublisher(for: url)
                .map(.data)
                .decode(
                    type: [ScheduleItem].self,
                    decoder: JSONDecoder()
                )
                .receive(on: DispatchQueue.main)
                .sink(
                    receiveCompletion: { completion in
                        switch completion {
                        case .failure(let error):
                            print("SINKERROR")
                            print(error)
                        case .finished:
                            print("SINKSUCCESS")
                        }
                    },
                    receiveValue: { repo in
                        print("DONE")
                        self.items.append(contentsOf: repo)
                    }
                )
        })
    }
}

The output from the print statements is DONE SINKSUCCESS Both can only be found once in the debug output.

This is the view:

struct TreatmentCardView: View {
    
    let startDate: String
    let uid: Int
    
    @ObservedObject var data = ItemViewModel()
    
    @State private var lastTime: String = "2"
    
    var body: some View {
        ZStack {
            GeometryReader { geometry in
                VStack{
                    
                    Text("Headline")
                        .font(.title)
                        .foregroundColor(Color.white)
                        .padding([.top, .leading, .bottom])
                    
                    Print("ISARRAYEMPTY")
                    Print(String(data.items.isEmpty))
                    
                    Text(String(data.items.isEmpty))
                                .font(.headline)
                                .fontWeight(.light)
                                .foregroundColor(Color.white)
                                .frame(width: 300, height: 25, alignment: .leading)
                                .multilineTextAlignment(.leading)
                                .padding(.top)
                      
                     
                }
            }
        }
        .background(Color("ColorPrimary"))
        .cornerRadius(15)
        .onAppear{
            self.data.fetchData()
        }
    }
}

Print(String(data.items.isEmpty)) is first false, then true, indicating the view was re-rendered.

What is a bit weird to me is that I would have expected the view to render at least once before the data is pulled, but I don't see any indication of this happening.

I've been trying to make this word for two days now. Any help and advise is greatly appreciated!

question from:https://stackoverflow.com/questions/65925048/data-from-observedobject-not-rendered-in-swiftui-view

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I don't know why the view redraws itself, but you may use @StateObject instead of @ObservedObject for your ItemViewModel. Your pulled data should stay that way.

@StateObject var data = ItemViewModel()

The ObservedObject will be recreated every time a view is discarded and redrawn, where StateObject keeps it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...