SwiftUI доступ к глобальному состоянию из ViewModel - PullRequest
1 голос
/ 08 февраля 2020

Я экспериментирую со SwiftUI и с трудом подбираю подходящую архитектуру для моего приложения.

То, чего я пытаюсь добиться, - это просто. Я хочу, чтобы начальный экран отображал экран регистрации или домашний экран для данного пользователя в зависимости от текущего состояния аутентификации. Я не могу понять, как сделать так, чтобы первоначальный экран забирал изменения с экрана регистрации после аутентификации. Вот некоторый соответствующий код:

struct InitialView: View {
    @EnvironmentObject var viewModel: InitialViewModel

    var body: some View {
        VStack {
            if viewModel.auth.identity != nil {
                NewHomeView()
            } else {
                SignInView()
            }
        }
    }
}

В представлении входа у меня есть обычная форма входа, и когда пользователь нажимает кнопку входа, я хочу отправить запрос на вход в систему и запомнить полученный токен.


class SignInViewModel: ObservableObject {
    private let auth: AuthStore
    private let signInApi: SignInApi
    private var cancellableSet: Set<AnyCancellable>

    // Input
    @Published var name: String = ""
    @Published var password: String = ""
    @Published var showSignUp: Bool = false

    // Output
    @Published var authSuccess: Bool = false

    init(auth: AuthStore, signInApi: SignInApi) {
        self.auth = auth
        self.signInApi = signInApi
        self.cancellableSet = Set<AnyCancellable>()
    }

    func signIn() {
        signInApi.signIn(email: name, password: password).sink(receiveCompletion: { _ in }) { response in
            self.auth.identity = Identity(
                person: Person(id: 1, name: "user", sex: nil, birthday: nil),
                token: response.token
            )
            self.authSuccess = true
        }.store(in: &cancellableSet)
    }
}

ОДНАКО, это не работает. Даже после нажатия кнопки входа начальный вид не обновляется. Обратите внимание, что я передаю один и тот же AuthStore обеим моделям представлений.

  let auth = AuthStore()

        // Create the SwiftUI view that provides the window contents.
        let contentView = InitialView()
            .environmentObject(InitialViewModel(auth: auth))
            .environmentObject(SignInViewModel(auth: auth, signInApi: SignInApi()))

, где AuthStore определяется как


class AuthStore: ObservableObject {
    @Published var identity: Identity? = nil
}

В идеале, я бы хотел иметь возможность 1) иметь каждый view должен быть в паре со своей собственной виртуальной машиной 2) иметь доступ к глобальному состоянию каждой виртуальной машины через @EnvironmentObject. Однако, кажется, что @EnvironmentObject ограничен только представлениями? Если да, то как мне получить доступ к глобальному состоянию аутентификации внутри каждой виртуальной машины, для которой это требуется?

Ответы [ 2 ]

0 голосов
/ 13 февраля 2020

Рискну угадать здесь, поскольку вы не предоставили детали InitialViewModel.

Вы не наблюдали auth в SignInViewModel и не опубликовали sh это для обновления вида тигра.

Я предполагаю, что та же проблема с InitialViewModel.

class SignInViewModel: ObservableObject {
    private let auth: AuthStore
    private let signInApi: SignInApi

На вопрос 2): вы не можете. (насколько я знаю)

На вопрос 1): можно. Но какой ценой? Есть ли лучшие альтернативы?

Я могу уточнить, если нужно.

0 голосов
/ 08 февраля 2020

Как и в приведенном коде, я бы изменил его следующим образом

struct InitialView: View {
    @EnvironmentObject var viewModel: InitialViewModel
    @EnvironmentObject var signIn: SignInViewModel

    var body: some View {
        VStack {
            if signIn.authSuccess {
                NewHomeView()
            } else {
                SignInView()
            }
        }
    }
}

, а также перенаправил издателя в основной поток в

    signInApi.signIn(email: name, password: password)
        .receive(on: RunLoop.main) // << following update should be on main thread
        .sink(receiveCompletion: { _ in }) { response in
        self.auth.identity = Identity(
            person: Person(id: 1, name: "user", sex: nil, birthday: nil),
            token: response.token
        )
        self.authSuccess = true
    }.store(in: &cancellableSet)
...