使用 RxSwift 从 ViewController 向 ViewModel 传递值
Passing value to ViewModel from ViewController w/ RxSwift
我在一个应用程序中有一个场景,我被要求在其中进行更改。
我不是很熟悉RxSwift
所以如果这很明显请原谅我。
我的视图控制器呈现一个 WKWebView
,其中包含一个登录表单。当用户完成表单时,一个 属性 被发回,我目前正在 WKNavigationDelegate
.
中打印出来
我想做的是将 属性 传递给我的 oauthService
,它作为依赖存在于我的 ViewModel
.
中
我可以在模型中创建一个方法,例如
func passPropToServie(_ prop: String) {
// do something
}
然后从视图控制器中调用它,但我不确定这是否正确,或者 '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 { [=12=] }
.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 { [=14=] }
.asDriver(onErrorJustReturn: nil)
}
}
这完全取决于您想深入兔子洞多远。 Rx 旨在取代我们通常将数据推送到对象的所有不同方式。所以如果你把它发挥到极致,你将没有任何回调闭包、@IBActions 或委托;并且您不会分配给 var
,也不会使用数据调用函数,这样您就可以在另一个 class 中使用数据。例如,有一些包装器可以让您消除 LoginViewController 中的委托函数。
我猜 Bindings
结构用于视图模型的输入,如果是这样的话,那么我可能会在绑定中添加一个 Observable 并在视图控制器中添加一个发布主题。在发布主题上调用 onNext
将通过绑定在视图模型中被拾取。
您可能会从阅读这篇文章中获益:Integrating RxSwift Into Your Brain and Code Base
我在一个应用程序中有一个场景,我被要求在其中进行更改。
我不是很熟悉RxSwift
所以如果这很明显请原谅我。
我的视图控制器呈现一个 WKWebView
,其中包含一个登录表单。当用户完成表单时,一个 属性 被发回,我目前正在 WKNavigationDelegate
.
我想做的是将 属性 传递给我的 oauthService
,它作为依赖存在于我的 ViewModel
.
我可以在模型中创建一个方法,例如
func passPropToServie(_ prop: String) {
// do something
}
然后从视图控制器中调用它,但我不确定这是否正确,或者 '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 { [=12=] }
.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 { [=14=] }
.asDriver(onErrorJustReturn: nil)
}
}
这完全取决于您想深入兔子洞多远。 Rx 旨在取代我们通常将数据推送到对象的所有不同方式。所以如果你把它发挥到极致,你将没有任何回调闭包、@IBActions 或委托;并且您不会分配给 var
,也不会使用数据调用函数,这样您就可以在另一个 class 中使用数据。例如,有一些包装器可以让您消除 LoginViewController 中的委托函数。
我猜 Bindings
结构用于视图模型的输入,如果是这样的话,那么我可能会在绑定中添加一个 Observable 并在视图控制器中添加一个发布主题。在发布主题上调用 onNext
将通过绑定在视图模型中被拾取。
您可能会从阅读这篇文章中获益:Integrating RxSwift Into Your Brain and Code Base