SwiftUI MVVM 将变量从 ViewModel 传递到 API 服务
Posted
技术标签:
【中文标题】SwiftUI MVVM 将变量从 ViewModel 传递到 API 服务【英文标题】:SwiftUI MVVM pass variable from ViewModel to API Service 【发布时间】:2021-01-24 09:21:06 【问题描述】:我对 Swift 和 XCode 非常陌生,在学习了几个教程之后,我已经创建了一个带有 API 服务的 MVVM 架构,但是我无法将我的变量从 ViewModel 传递到我的 API 服务。我想传递 var 用户并从 LoginViewModel 传递到 APIEndpoint
登录视图模型
class LoginViewModel: ObservableObject, LoginService
// I want to send this 2 variables to APIEndpoint
@Published var user = ""
@Published var pass = ""
var cancellables = Set<AnyCancellable>()
init(apiSession: APIService = APISession())
self.apiSession = apiSession
func login()
let cancellable = self.login()
.sink(receiveCompletion: result in
switch result
case .failure(let error):
print("Handle error: \(error)")
case .finished:
break
) (detail) in
print(detail)
cancellables.insert(cancellable)
API端点
enum APIEndpoint
case userLogin
case menuList
extension APIEndpoint: RequestBuilder
var urlRequest: URLRequest
switch self
case .userLogin:
guard let url = URL(string: "https://jcouserapidev.oxxo.co.id/home/app")
else preconditionFailure("Invalid URL format")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Constants.API_TOKEN, forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
// I want to pass the variables here, how??
let body: [String: Any] = ["user": $user, "pass": $pass]
let rb = try! JSONSerialization.data(withJSONObject: body)
request.httpBody = rb
return request
case .menuList:
guard let url = URL(string: "https://jcouserapidev.oxxo.co.id/home/app")
else preconditionFailure("Invalid URL format")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Constants.API_TOKEN, forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
return request
请求生成器
protocol RequestBuilder
var urlRequest: URLRequest get
登录服务
protocol LoginService
var apiSession: APIService get
func login() -> AnyPublisher<LoginAPIResponse, APIError>
extension LoginService
func login() -> AnyPublisher<LoginAPIResponse, APIError>
return apiSession.request(with: APIEndpoint.userLogin)
.eraseToAnyPublisher()
API服务
protocol APIService
func request<T: Decodable>(with builder: RequestBuilder, test: String) -> AnyPublisher<T, APIError>
APISession
struct APISession: APIService
func request<T>(with builder: RequestBuilder) -> AnyPublisher<T, APIError> where T: Decodable
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return URLSession.shared
.dataTaskPublisher(for: builder.urlRequest)
.receive(on: DispatchQueue.main)
.mapError _ in .unknown
.flatMap data, response -> AnyPublisher<T, APIError> in
if let response = response as? HTTPURLResponse
if (200...299).contains(response.statusCode)
print(String(data: data, encoding: .utf8) ?? "")
return Just(data)
.decode(type: T.self, decoder: decoder)
.mapError _ in .decodingError
.eraseToAnyPublisher()
else
return Fail(error: APIError.httpError(response.statusCode))
.eraseToAnyPublisher()
return Fail(error: APIError.unknown)
.eraseToAnyPublisher()
.eraseToAnyPublisher()
提前谢谢大家。
【问题讨论】:
【参考方案1】:您应该以这种形式更改您的枚举:
enum APIEndpoint
case userLogin(user: String, password: String)
case menuList
更改您的 requestBuilder:
extension APIEndpoint: RequestBuilder
var urlRequest: URLRequest
switch self
case .userLogin(let user, let password):
guard let url = URL(string: "https://jcouserapidev.oxxo.co.id/home/app")
else preconditionFailure("Invalid URL format")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Constants.API_TOKEN, forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
let body: [String: Any] = ["user": user, "pass": pass]
let rb = try! JSONSerialization.data(withJSONObject: body)
request.httpBody = rb
return request
case .menuList:
guard let url = URL(string: "https://jcouserapidev.oxxo.co.id/home/app")
else preconditionFailure("Invalid URL format")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(Constants.API_TOKEN, forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
return request
在此之后,在所有login
函数中,您应该将user
和password
作为参数传递
【讨论】:
感谢您的回答,但是我仍然无法从viewmodel中获取要传递给APIEndpoint的值,我需要在LoginViewModel和LoginService中做什么?对不起,我对此很陌生,已经尝试调整代码,但它只是不断弹出错误以上是关于SwiftUI MVVM 将变量从 ViewModel 传递到 API 服务的主要内容,如果未能解决你的问题,请参考以下文章