【问题标题】:SwiftUI/Combine no updates to @ObservedObject from @PublishedSwiftUI/Combine 没有从 @Published 更新到 @ObservedObject
【发布时间】:2020-04-27 22:33:07
【问题描述】:

我有一个登录屏幕,然后是一个概览屏幕。当用户登录成功时,登录响应会发回一个项目列表,我想在随后的概览屏幕上显示该列表。

我可以看到响应已成功映射,但概览视图未收到对 @ObservedObject 的任何更新。我可能会遗漏一些明显的东西,但我已经浏览了很多文章,但没有设法让任何工作。任何帮助表示赞赏!

登录视图

import SwiftUI

struct LogonView: View {

    @State private var username: String = ""
    @State private var password: String = ""
    @State private var inputError: Bool = false
    @State private var errorMessage: String = ""
    @State private var loading: Bool? = false
    @State private var helpShown: Bool = false
    @State private var successful: Bool = false


    //MARK:- UIView
    var body: some View {

        NavigationView {

            VStack {

                VStack {
                    TextField("Username", text: $username)
                        .padding(.horizontal)
                        .disabled(loading! ? true : false)
                    Rectangle()
                        .frame(height: 2.0)
                        .padding(.horizontal)
                        .foregroundColor(!inputError ? Color("SharesaveLightGrey") : Color("SharesaveError"))
                        .animation(.easeInOut)
                }.padding(.top, 80)


                VStack {
                    SecureField("Password", text: $password)
                        .padding(.horizontal)
                        .disabled(loading! ? true : false)
                    Rectangle()
                        .frame(height: 2.0)
                        .padding(.horizontal)
                        .foregroundColor(!inputError ? Color("SharesaveLightGrey") : Color("SharesaveError"))
                        .animation(.easeInOut)
                }.padding(.top, 40)



                if (inputError) {
                    HStack {
                        Text(errorMessage)
                            .padding(.top)
                            .padding(.horizontal)
                            .foregroundColor(Color("SharesaveError"))
                            .animation(.easeInOut)
                            .lineLimit(nil)
                            .font(.footnote)
                        Spacer()
                    }
                }


                SharesaveButton(action: {self.submit(user: self.username, pass: self.password)},
                       label: "Log on",
                       loading: $loading,
                       style: .primary)
                    .padding(.top, 40)
                .animation(.interactiveSpring())


                NavigationLink(destination: OverviewView(), isActive: $successful) {
                    Text("")
                }

                Spacer()

            }
            .navigationBarTitle("Hello.")
            .navigationBarItems(
                trailing: Button(action: { self.helpShown = true }) {
                     Text("Need help?").foregroundColor(.gray)
                })
                .sheet(isPresented: $helpShown) {
                    SafariView( url: URL(string: "http://google.com")! )
            }

        }
    }


    //MARK:- Functions
    private func submit(user: String, pass: String) {

        loading = true
        inputError = false
        let resultsVM = ResultsViewModel()

        resultsVM.getGrants(user: user, pass: pass,

        successful: { response in
            self.loading = false
            if ((response) != nil) { self.successful = true }
        },

        error: { error in
            self.inputError = true
            self.loading = false
            self.successful = false
            switch error {
            case 401:
                self.errorMessage = "Your credentials were incorrect"
            default:
                self.errorMessage = "Something went wrong, please try again"
            }
        },

        failure: { fail in
            self.inputError = true
            self.loading = false
            self.successful = false
            self.errorMessage = "Check your internet connection"
        })
    }
}

结果视图模型

import Foundation
import Moya
import Combine
import SwiftUI

class ResultsViewModel: ObservableObject {

    @Published var results: Results = Results()

    func getGrants(
        user: String,
        pass: String,
        successful successCallback: @escaping (Results?) -> Void,
        error errorCallback: @escaping (Int) -> Void,
        failure failureCallback: @escaping (MoyaError?) -> Void
        )
    {
        let provider = MoyaProvider<sharesaveAPI>()

        provider.request(.getSharesave(username: user, password: pass)) { response in

            switch response.result {

            case .success(let value):
                do {
                    let data = try JSONDecoder().decode(Results.self, from: value.data)
                    self.results = data
                    successCallback(data)

                } catch {
                    let errorCode = value.statusCode
                    errorCallback(errorCode)
                }

            case .failure(let error):
                failureCallback(error)
            }
        }
    }

}

概览视图

import SwiftUI
import Combine

struct OverviewView: View {

    @ObservedObject var viewModel: ResultsViewModel = ResultsViewModel()

    var body: some View {

        let text = "\(self.viewModel.results.market?.sharePrice ?? 0.00)"

        return List {
            Text(text)
        }           
    }
}

【问题讨论】:

    标签: ios swift swiftui combine observedobject


    【解决方案1】:

    您向ResultsViewModel 的一个实例提交了请求

    private func submit(user: String, pass: String) {
    
        loading = true
        inputError = false
        let resultsVM = ResultsViewModel() // << here 
    

    尝试从ResultsViewModel的另一个实例中读取数据

    struct OverviewView: View {
    
        @ObservedObject var viewModel: ResultsViewModel = ResultsViewModel() // << here
    

    但必须是一个实例,所以修改如下

    1) 在OverviewView

    struct OverviewView: View {
    
        @ObservedObject var viewModel: ResultsViewModel // expects injection 
    

    2) 在LogonView

    struct LogonView: View {
        @ObservedObject var resultsViewModel = ResultsViewModel() // created once
    
    

    并为OverviewView注入相同的实例

    NavigationLink(destination: OverviewView(viewModel: self.resultsViewModel), isActive: $successful) {
        Text("")
    }
    

    并提交

        private func submit(user: String, pass: String) {
    
            loading = true
            inputError = false
            let resultsVM = self.resultsViewModel // use created
    

    【讨论】:

    • 除了将其注入下一个视图之外,还有其他方法吗?目的地可能与 LogonView 不同(例如,在登陆 LogonView 之前可能会经过一两个视图)
    【解决方案2】:

    请在初始化OverviewView 更改后尝试NavigationLink 如下所示

    NavigationLink(destination: OverviewView(viewModel: self.resultsVM),
                                             isActive: $successful) {
           Text("")
    }
    

    OverviewView 中传递results 作为参数,如下所示

    NavigationLink(destination: OverviewView(results: self.resultsVM.results),
                                             isActive: $successful) {
           Text("")
    }
    .....
    struct OverviewView: View {
        let results: Results
        var body: some View {
            let text = "\(self.results.market?.sharePrice ?? 0.00)"
            return List {
               Text(text)
            }           
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 1970-01-01
      • 2020-02-26
      相关资源
      最近更新 更多