未在SwiftUI视图中呈现来自ObservedObject的数据

未在SwiftUI视图中呈现来自ObservedObject的数据,swiftui,combine,observableobject,observedobject,Swiftui,Combine,Observableobject,Observedobject,我使用combine连接到一个RESTAPI,该API将一些数据拉入一个ObservieObject。整个设置实际上只是MVVM。然后在视图中观察ObservieObject。现在,我遇到了一个无法解决的问题。似乎发生了两次这样的情况。第一次使用ObservedObject中的值进行更新时。然后它立即重新绘制,但现在提取的数据突然消失了。我已经确认提取数据的代码不会再次执行。此外,“视图层次查看器”似乎建议在第二次渲染运行时以某种方式删除应该渲染视图中数据的文本视图 这就是模型的外观(与身份验证

我使用combine连接到一个RESTAPI,该API将一些数据拉入一个ObservieObject。整个设置实际上只是MVVM。然后在视图中观察ObservieObject。现在,我遇到了一个无法解决的问题。似乎发生了两次这样的情况。第一次使用ObservedObject中的值进行更新时。然后它立即重新绘制,但现在提取的数据突然消失了。我已经确认提取数据的代码不会再次执行。此外,“视图层次查看器”似乎建议在第二次渲染运行时以某种方式删除应该渲染视图中数据的文本视图

这就是模型的外观(与身份验证相关的代码来自Firebase):

print语句的输出是 完成 成功 两者在调试输出中只能找到一次

这是一种观点:

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))首先为false,然后为true,表示视图已重新呈现

对我来说有点奇怪的是,我本来希望视图在提取数据之前至少呈现一次,但我看不到任何发生这种情况的迹象


这两天来我一直在想这个词。非常感谢您的帮助和建议

我不知道为什么视图会重新绘制自身,但是您可以对
项目视图模型使用
@StateObject
而不是
@ObservedObject
。你的数据应该保持这种状态

@StateObject var data = ItemViewModel()

ObservedObject
将在每次丢弃和重画视图时重新创建,其中
StateObject
保留它。

我不知道为什么视图会重画自己,但是您可以对
项目视图模型使用
@StateObject
而不是
@ObservedObject
。你的数据应该保持这种状态

@StateObject var data = ItemViewModel()

ObservedObject
将在每次丢弃和重新绘制视图时重新创建,其中
StateObject
保留它。

我猜在视图层次结构的更高层,您有一些东西导致
ItemViewModel
刷新。因为它看起来像是在使用Firebase,所以我怀疑它是Firebase堆栈中的其他东西(比如用户身份验证状态?)

我取出了您的API请求,并用一个简单的
Future
替换了它,该请求保证只响应以证明您的其余代码工作正常(确实如此)

您可能的解决方案是:

  • 按照另一位回答者的建议,使用
    @StateObject
  • 将API调用移动到视图层次结构中存储在较高位置的
    observateObject
    (例如,
    EnvironmentObject
    ),该对象将传递到
    TreatmentCardView
    ,而不是每次都在那里创建它
  • 为了了解Firebase API调用是否带来了另一个副作用,下面是我的简化版,没有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)
                        }
                    )
            })
        }
    }
    
    class ItemViewModel: ObservableObject {
        
        @Published var items = [String]()
        
        private var publisher: AnyCancellable?
        
        func fetchData() {
    
            let myFuture = Future<String, Error> { promise in
                DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
                    promise(.success("TEST STRING"))
                }
            }
            
            self.publisher = myFuture
                .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("String")
                    }
                )
        }
    }
    
    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()
            }
        }
    }
    
    class ItemViewModel:ObserveObject{
    @已发布的变量项=[String]()
    私有var发布者:是否可以取消?
    func fetchData(){
    让我的未来=未来{承诺
    DispatchQueue.main.asyncAfter(截止日期:.now()+5){
    承诺(.success(“测试字符串”))
    }
    }
    self.publisher=myFuture
    .receive(在:DispatchQueue.main上)
    .水槽(
    receiveCompletion:{在中完成
    交换完成{
    案例。失败(let错误):
    打印(“错误”)
    打印(错误)
    案例。完成:
    打印(“成功”)
    }
    },
    receiveValue:{repo in
    打印(“完成”)
    self.items.append(“字符串”)
    }
    )
    }
    }
    结构处理卡片视图:视图{
    让开始日期:字符串
    让uid:Int
    @ObservedObject var data=ItemViewModel()
    @State private var lastTime:String=“2”
    var body:一些观点{
    ZStack{
    GeometryReader{中的几何体
    VStack{
    正文(“标题”)
    .font(.title)
    .foregroundColor(颜色.白色)
    .padding([.top、.leading、.bottom])
    //
    //打印(“ISARRAYEMPTY”)
    //打印(字符串(data.items.isEmpty))
    文本(字符串(data.items.isEmpty))
    .font(.headline)
    .fontWeight(.light)
    .foregroundColor(颜色.白色)
    .框架(宽度:300,高度:25,对齐:。前导)
    .multilitextalignment(.leading)
    .padding(.top)
    }
    }
    }
    .背景(颜色(“彩色原色”))
    .转弯半径(15)
    奥纳佩尔先生{
    self.data.fetchData()
    }
    }
    }
    
    我的猜测是,在视图层次结构的更高层,您有一些东西导致
    ItemViewModel
    刷新。因为它看起来像是在使用Firebase,所以我怀疑它是Firebase堆栈中的其他东西(比如用户身份验证状态?)

    我取出了您的API请求,并用一个简单的
    Future
    替换了它,该请求保证只响应以证明您的其余代码工作正常(确实如此)

    您可能的解决方案是:

  • 按照另一位回答者的建议,使用
    @StateObject
  • 将API调用移动到视图层次结构中存储在较高位置的
    observateObject
    (例如,
    EnvironmentObject
    ),该对象将传递给
    treatmentcardwiew
    ,而不是creati