使用 RxSwift 链接登录操作
Chaining login operations with RxSwift
我正在创建一个具有特定双向身份验证过程的应用程序:
首先,基于 REST 的凭据登录,return是服务器上的一个 websocket 端点,加上一个用于连接到它的 authtoken。
只有在websocket成功连接到服务器后,我才应该切换到下一个视图。
尝试在 MVVM 下实现,我为服务器网络调用创建了一个 API 结构:
我的 LoginViewController 将用户名和密码绑定到 LoginViewModel,后者在 return 中将登录 Action 绑定到登录按钮:
func onLogin() -> CocoaAction {
return CocoaAction { _ in
self.loginService.login(username: self.usernameText.value, password: self.passwordText.value)
let MainViewModel = MainViewModel(sceneCoordinator: self.sceneCoordinator)
return self.sceneCoordinator.transition(to: Scene.mainView(mainViewModel), type: .modal).asObservable().map { _ in }
}
}
LoginService 应该 return 用于登录的 Completable,以指示登录成功(并将视图移动到主应用程序屏幕)或向用户显示错误。
protocol LoginServiceType {
@discardableResult
func login(username: String, password: String) -> Completable
}
我在执行此功能时遇到问题。它应该首先调用 REST 登录 API,得到响应后,开始连接到 websocket。服务器 API 的实现如下(在推荐的 RxSwift MVVM 示例下):
struct Server: ServerProtocol {
// MARK: - API Errors
enum Errors: Error {
case requestFailed
}
// MARK: - API Endpoint requests
static func login(for username: String, password: String) -> Observable<JSONObject> {
let parameters = [
"username": username,
"password": password
]
return request(address: Server.Address.login, parameters: parameters)
}
// MARK: - generic request to API
static private func request<T: Any>(address: Address, parameters: [String: String] = [:]) -> Observable<T> {
return Observable.create { observer in
let request = Alamofire.request(address.url.absoluteString,
method: .post,
parameters: parameters,
encoding: JSONEncoding.default,
headers: nil)
request.responseJSON { response in
guard response.result.isSuccess == true, let data = response.data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? T, let result = json else {
observer.onError(Errors.requestFailed)
return
}
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create {
request.cancel()
}
}
}
}
所以我想弄清楚如何连接 REST 调用,它的响应需要端点+令牌,创建 Websocket 并订阅它的连接回调,然后应该 return Completable回到 LoginViewModel。
欢迎任何建议。
我想 "flatmap" 就是您要找的。
看看:
var mynum = Variable(0)
let disposeBag = DisposeBag()
func login() -> Observable<(String,String)> {
return Observable.create { observer in
// Place your server access code
if (<some_condition>) { // if error
observer.on(.error(<custome error>))
}
observer.on(.next(("websocket", "authtoken")))
observer.on(.completed)
return Disposables.create()
}
}
func API(webSiteData: (String, String)) -> Observable<Int> {
return Observable.create { observer in
// Place your second server access code
print (webSiteData.0)
print (webSiteData.1)
observer.on(.next(1)) // assiging "1" just as an example, you may ignore
observer.on(.completed)
return Disposables.create()
}
}
func combine() {
self.login().catchError({ (currentError) -> Observable<(String, String)> in
print (currentError.localizedDescription)
return Observable.just(("",""))
})
.flatMap(self.API)
.bind(to: mynum)
.disposed(by: self.disposeBag)
}
我正在创建一个具有特定双向身份验证过程的应用程序: 首先,基于 REST 的凭据登录,return是服务器上的一个 websocket 端点,加上一个用于连接到它的 authtoken。
只有在websocket成功连接到服务器后,我才应该切换到下一个视图。
尝试在 MVVM 下实现,我为服务器网络调用创建了一个 API 结构:
我的 LoginViewController 将用户名和密码绑定到 LoginViewModel,后者在 return 中将登录 Action 绑定到登录按钮:
func onLogin() -> CocoaAction {
return CocoaAction { _ in
self.loginService.login(username: self.usernameText.value, password: self.passwordText.value)
let MainViewModel = MainViewModel(sceneCoordinator: self.sceneCoordinator)
return self.sceneCoordinator.transition(to: Scene.mainView(mainViewModel), type: .modal).asObservable().map { _ in }
}
}
LoginService 应该 return 用于登录的 Completable,以指示登录成功(并将视图移动到主应用程序屏幕)或向用户显示错误。
protocol LoginServiceType {
@discardableResult
func login(username: String, password: String) -> Completable
}
我在执行此功能时遇到问题。它应该首先调用 REST 登录 API,得到响应后,开始连接到 websocket。服务器 API 的实现如下(在推荐的 RxSwift MVVM 示例下):
struct Server: ServerProtocol {
// MARK: - API Errors
enum Errors: Error {
case requestFailed
}
// MARK: - API Endpoint requests
static func login(for username: String, password: String) -> Observable<JSONObject> {
let parameters = [
"username": username,
"password": password
]
return request(address: Server.Address.login, parameters: parameters)
}
// MARK: - generic request to API
static private func request<T: Any>(address: Address, parameters: [String: String] = [:]) -> Observable<T> {
return Observable.create { observer in
let request = Alamofire.request(address.url.absoluteString,
method: .post,
parameters: parameters,
encoding: JSONEncoding.default,
headers: nil)
request.responseJSON { response in
guard response.result.isSuccess == true, let data = response.data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? T, let result = json else {
observer.onError(Errors.requestFailed)
return
}
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create {
request.cancel()
}
}
}
}
所以我想弄清楚如何连接 REST 调用,它的响应需要端点+令牌,创建 Websocket 并订阅它的连接回调,然后应该 return Completable回到 LoginViewModel。
欢迎任何建议。
我想 "flatmap" 就是您要找的。
看看:
var mynum = Variable(0)
let disposeBag = DisposeBag()
func login() -> Observable<(String,String)> {
return Observable.create { observer in
// Place your server access code
if (<some_condition>) { // if error
observer.on(.error(<custome error>))
}
observer.on(.next(("websocket", "authtoken")))
observer.on(.completed)
return Disposables.create()
}
}
func API(webSiteData: (String, String)) -> Observable<Int> {
return Observable.create { observer in
// Place your second server access code
print (webSiteData.0)
print (webSiteData.1)
observer.on(.next(1)) // assiging "1" just as an example, you may ignore
observer.on(.completed)
return Disposables.create()
}
}
func combine() {
self.login().catchError({ (currentError) -> Observable<(String, String)> in
print (currentError.localizedDescription)
return Observable.just(("",""))
})
.flatMap(self.API)
.bind(to: mynum)
.disposed(by: self.disposeBag)
}