как опубликовать данные сетевого запроса с помощью Combine framework и SwiftUI

Я разрабатываю приложение для iOS с использованием swiftUI и Combine framework, а также MVVM. Я хочу обрабатывать запрос API входа в систему в отдельном классе LoginService, который используется в LoginViewModel. Теперь я хочу знать, как мне публиковать и наблюдать за атрибутами между представлением и ViewModel. Я имею в виду, что ViewModel является ObservableObject и наблюдается в представлении, но поскольку я обрабатываю сетевой запрос в классе службы, как LoginService должен уведомлять LoginViewModel и LoginView о том, что данные получены и представление должно быть обновлено?

import Foundation
import Combine

class LoginViewModel: ObservableObject {

    @Published var user = UserModel()

    @Published var LoginStatus: Bool = false

    @Published var LoginMessage: String = ""

    var service = LoginService()

    func Login(With email: String, And password: String) -> Bool {
        service.validateLogin(email: email, password: password)
        return false
    }
}

Это код для LoginViewModel. Как LoginService должен изменять значения LoginStatus, LoginMessage и user при получении данных с сервера для уведомления View? Я говорю это, потому что, насколько мне известно, вы можете наблюдать за ObservableObjects только в представлении (SwiftUI).


person Alireza Moradi    schedule 30.10.2019    source источник


Ответы (2)


Хорошо, я беру ваш пример и делаю следующее:

Я предполагал, что ваша служба возвращает true или false

import Foundation
import Combine

class LoginViewModel: ObservableObject {

    @Published var LoginStatus: Bool = false

    @Published var LoginMessage: String = ""

    var service = LoginService()

    func Login(_ email: String, _ password: String) -> Bool {
        self.LoginStatus  = service.validateLogin(email: email, password: password)
        return self.LoginStatus
    }
}

На ваш взгляд:


import SwiftUI

struct ContentView : View {

    @ObservedObject var model = LoginViewModel()

    var body: some View {
        VStack {
            Button(action: {
                _ = self.model.Login("TestUser", "TestPassword")
            }, label: {
                Text("Login")
            })
            Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
        }
    }
}

Это должно быть что-то вроде этого.

Я удалил UserModel, потому что вы не должны вкладывать модели.

Надеюсь, это поможет.

ИЗМЕНИТЬ 1:

Чтобы проверить что-то автоматически, вы можете использовать onApear() в своем представлении или прослушивать изменения с помощью olnrecieve(), чтобы обновить пользовательский интерфейс или состояние.

import SwiftUI

struct ContentView : View {

    @ObservedObject var model = LoginViewModel()

    var body: some View {
        VStack {
            Button(action: {
                _ = self.model.Login("TestUser", "TestPassword")
            }, label: {
                Text("Login")
            })
            Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
        }.onAppear {
            // call a function that gets something from your server 
            // and modifies your state
            self.model.validate()
        }.onReceive(self.model.$LoginMessage, perform: { message in
               // here you can update your state or your ui
               // according the LoginMessage... this gets called
               // whenever LoginMessage changes in your model
        })
    }
}

person krjw    schedule 30.10.2019
comment
Спасибо за ответ, но мой вопрос все еще остается без ответа. Теперь, когда я получил данные из API в классе обслуживания, как мне получить к ним доступ в представлении? Предположим, мой API возвращает токен и идентификатор, и я хочу показать этот идентификатор в представлении. - person Alireza Moradi; 31.10.2019
comment
Когда вы изменяете переменную @Published в своей модели, она автоматически обновляет все представления, в которых они используются ... Я показал это, используя логическое значение в представлении, которое было изменено в модели. Обычно при работе с сетью вам необходимо обновить @Published переменные в блоке DispatchQueue.main.async{}. - person krjw; 31.10.2019
comment
Для автоматического запуска проверки вы можете использовать onAppear() Я отредактирую ответ - person krjw; 31.10.2019

Вы установили одностороннюю привязку, т.е. модель -> вид

Теперь не имеет значения, как ваша модель обновляет сервис, при каждом изменении модели просматривайте обновления.

У вашего сервиса есть несколько способов обновить модель.

  1. Обычный URLSession, обновите LoginStatus и LoginMessage в обратном вызове как обычно.

  2. Через издателей Combine, например; URLSessionDataTaskPublisher и обновите его при закрытии приемника.

person Jim lai    schedule 01.01.2020