You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中@Published变量不更新问题排查(Swift 5、Combine)

问题描述

我正在开发一个使用@Published属性包装器更新后端拉取数据的ViewModel。定义了继承自ObservableObject的ViewResult类来管理数据状态,ViewModel中的guidelines是@Published修饰的ViewResult<[Guideline]>类型。

执行CreateGuideline、DeleteGuideline、ReorderGuidelines等操作时,API调用成功且已修改guidelines内的数据,但SwiftUI的ListView必须退出编辑模式后才会更新,具体流程:

  • 点击右上角进入编辑模式
  • 输入文本并执行VM.CreateGuideline()
  • API调用成功,修改@Published的guidelines变量
  • 视图未更新
  • 点击右上角“Done”退出编辑模式
  • ListView才显示新条目

我怀疑是对ObservableObject和@Published的使用有误,也可能是EditableView与APIResultView的嵌套结构导致问题。尝试过给VM方法添加@escaping闭包、使用inout参数,但都没解决。

ViewResult类定义

class ViewResult<T> : ObservableObject {
    @Published var data: T?
    var state: ViewResultState = .loading
    var errorCode: APIResponseCode? = nil
    
    init(){
        self.state = .loading
        self.data = nil
    }
    
    ///OTHER COVIENIENCE INITIALIZERS NOT SHOWN
    
    
    ///Sets object and state variables if the APIResponse was successful.
    ///If it was unsuccessful, it sets error code and sets the ViewResult State to code.
    func SetData(_ result: APIResult<T>) -> Void {
        switch result {
        
            case .success(let data):
                self.data = data.data
                self.state = .object
                
            case .failure(let error):
                self.errorCode = APIResponseCode.Error(error)
                self.state = .code
        }
    }
}

GuidelinesVM类定义

class GuidelinesVM : ObservableObject {
    
    var community_id: Int
    
    @Published var guidelines: ViewResult<[Guideline]> = ViewResult(defaultValue: [])
    private var dispatcher : ViewModelAPIDispatcher = ViewModelAPIDispatcher()
    
    init(community_id: Int){
        self.community_id = community_id
      
        //Get guidelines before view is initialized. 
        //**NOTE: I AM CHANGING THIS TO EXECUTE IN THE ONAPPEAR CLOSURE
        dispatcher.SendRequest(.getCommunityGuidlines(community_id: community_id)){ result in
            self.guidelines.SetData(result)
        }
    }
    
    func ReorderGuidelines(_ fromOffsets : IndexSet, _ toOffset : Int){
        
        //Conversions from Swift's List system to mine
        let oldOffset = fromOffsets.first! + 1
        let guideline_id = self.guidelines.data![oldOffset - 1].id
        let newOffset = toOffset > oldOffset ? toOffset : toOffset + 1
        
        //Dispatch API Call
        dispatcher.SendRequest(.reOrderGuidelines(community_id: community_id, guideline_id: guideline_id, fromOffset: oldOffset, toOffset: toOffset)) { apiResult in
            if IsSuccessful(apiResult) {
                self.guidelines.data!.move(fromOffsets: fromOffsets, toOffset: toOffset)
            }
        }
    }
    
    func DeleteGuideline(_ offsets: IndexSet){
        let index = offsets.first!
        dispatcher.SendRequest(.deleteGuideline(community_id: community_id, guideline_id: index)){ result in
            if IsSuccessful(result) {
                self.guidelines.data!.remove(at: index)
            }
        }
    }
    
    func CreateGuidline(orderNumber: Int, text: String) {
        dispatcher.SendRequest(.createGuideline(community_id: community_id, orderNumber: orderNumber, text: text)){
            result in
            if(IsSuccessful(result)){
                //Right now just generate random int. I know this is terrible and lazy, but MVP
                self.guidelines.data!.append(Guideline(id: Int.random(in: -1000...0), order_number: self.guidelines.data!.count + 1, text: text))
            }
        }
    }
    
}

CommunityGuidelinesView视图定义

struct CommunityGuidelinesView: View {

    var psCommunityData : psCommunityClass
    @State private var newGuideline : String = ""
    @ObservedObject private var VM : GuidelinesVM
    
    init(community : psCommunityClass){
        self.psCommunityData = community
        self.VM = GuidelinesVM(community_id: community.id)
    }
    
    var body: some View {
        
        EditableView(userRole: psCommunityData.role){ inEditMode in
            
            VStack{
                APIResultView(apiResult: $VM.guidelines){ guidelines in
                    
                    List {
                        // LIST DISPLAY CODE
                        }
                        .onDelete { indexSet in
                            VM.DeleteGuideline(indexSet)
                        }
                        .onMove{ fromOffsets, newOffset in
                            VM.ReorderGuidelines(fromOffsets, newOffset)
                        }
                        //NEED TO PASS A BINDING FOR NESTED VIEW CONTROL
                        if inEditMode.wrappedValue {
                            
                            EditingView(
                                //VIEW IF NOT EDITING
                                },
                                EditingContent: {
                                    //VIEW IF EDITING

                                }, completion: {
                                    //FUNCTION EXECTUED ONCE DONE EDITING
                                    VM.CreateGuidline(orderNumber: guidelines.count + 1, text: newGuideline)
                                }
                            )
                        }
                    }
                }
            }
        }
    }
}

APIResultView视图定义

struct APIResultView<SuccessDisplay : View, T>: View {
    
    @Binding var apiResult : ViewResult<T>
    var content : (T) -> SuccessDisplay
    
    
    var body: some View {
        switch apiResult.state{
        case .object:
            content(apiResult.data!)
        case .loading:
            ProgressView("Loading...")
        case .code:
            if let errorCode = apiResult.errorCode {
                HttpStatusView(statusCode: errorCode)
            } else {
                HttpStatusView(statusCode: apiResult.data as! APIResponseCode)
            }
            
        }
    }
}

内容的提问来源于stack exchange,提问作者RadiantReasons

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 16:57:21