0

我对 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()
    }
}

谢谢大家。

4

1 回答 1

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函数中,您应该传递userpassword作为参数

于 2021-01-24T10:10:57.997 回答