我在要求更改的应用程序中有一个场景。

我对RxSwift并不是很熟悉,所以如果这很明显,请原谅我。

我的 View Controller 呈现一个WKWebView,其中包含一个登录表单。用户填写表单后,将发送回一个属性,该属性当前正在WKNavigationDelegate中打印出来。

我想做的就是将该属性传递给oauthService,它作为ViewModel中的依赖项存在。

我可以在模型中创建一个方法

  func passPropToServie(_ prop: String) {
        // do something
    }

并从 View Controller 中调用它,但是我不确定这是正确的还是执行此操作的“rxswift”方式。

再次抱歉,这是我正在使用的应用程序,因此原始代码仍然是我的习惯。

登录协调员
import UIKit
import RxCocoa
import RxSwift

class LoginCoordinator: BaseCoordinator<()> {
    typealias Dependencies = HasOAuthService

    private let window: UIWindow
    private let dependencies: Dependencies

    init(window: UIWindow, dependencies: Dependencies) {
        self.window = window
        self.dependencies = dependencies
    }

    override func start() -> Observable<()> {
        let viewController = LoginViewController()
        let avm: Attachable<LoginViewModel> = .detached(dependencies)
        let viewModel = viewController.attach(wrapper: avm)

        viewModel.loginURL.drive(onNext: { login in
            viewController.handle(login?.url)
        }).disposed(by: viewController.disposeBag)

        window.rootViewController = viewController
        window.makeKeyAndVisible()

        return viewModel.isLoggedIn
            .asObservable()
            .filter { $0 }
            .map { _ in return }
    }
}

LoginViewController
import UIKit
import WebKit
import RxCocoa
import RxSwift

class LoginViewController: UIViewController, ViewModelAttaching {

    let disposeBag = DisposeBag()

    var viewModel: Attachable<LoginViewModel>!
    var bindings: LoginViewModel.Bindings {
        return LoginViewModel.Bindings()
    }

    private var requestURL: URL?

    lazy var webView: WKWebView = {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.dataDetectorTypes = [.all]
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = self
        webView.allowsBackForwardNavigationGestures = false
        return webView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func loadView() {
        view = webView
    }

    func bind(viewModel: LoginViewModel) -> LoginViewModel {

        return viewModel
    }

    func handle(_ url: URL?) {
        guard let url = url else { return }
        requestURL = url
        webView.load(URLRequest(url: url))
    }
}

extension LoginViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if let url = navigationAction.request.url, url.scheme == "homedev", url.valueOf("code") != nil {
            print(url)
        }
        decisionHandler(.allow)
    }
}

LoginViewModel
import RxSwift
import RxCocoa

final class LoginViewModel: ViewModelType {

    typealias Dependency = HasOAuthService

    let isLoggedIn: Driver<Bool>
    let loginURL: Driver<URLComponents?>

    struct Bindings { }

    init(dependency: Dependency, bindings: Bindings) {

        isLoggedIn = dependency.oauthService.currentUser
            .map { user in return user == true }
            .asDriver(onErrorJustReturn: false)

        loginURL = dependency.oauthService.loginURL
            .map { $0 }
            .asDriver(onErrorJustReturn: nil)
    }
}

最佳答案

这完全取决于您想走多远的兔子洞。 Rx旨在替代我们通常将数据插入对象的所有不同方式。因此,如果将其发挥到极致,您将不会有任何回调闭包,@ IBActions或委托(delegate)。并且您不会分配给var,也不会调用带有数据的函数,只是您可以在另一个类中使用数据。例如,有一些包装器将使您可以消除LoginViewController中的委托(delegate)函数。

我猜想Bindings结构是用于 View 模型的输入,如果是这种情况,那么我可能会在绑定(bind)中添加一个Observable,并在 View Controller 中添加一个发布主题。在发布主题上调用onNext将通过绑定(bind)在 View 模型中被拾取。

您可能会从阅读本文中受益:Integrating RxSwift Into Your Brain and Code Base

关于swift - 从带有RxSwift的ViewController传递值到ViewModel,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56580668/

10-13 08:16