如何使用Alamofire路由器组织API调用?[swift/ Alamofire5]

wqnecbli  于 2023-09-29  发布在  Swift
关注(0)|答案(3)|浏览(141)

我正在尝试将我的AF请求转换为路由器结构,以使项目更清晰。我得到一个错误:
协议类型“Any”的值不能符合“Encodable”;只有struct/enum/class类型可以符合协议。
请帮我修改一下代码。谢谢您的支持!
我的URL将有一个用户名的占位符,密码将在body中发送。答案是Bool (success), username and bearer token
下面是我的AF请求

  1. let username = usernameTextField.text
  2. let password = passwordTextField.text
  3. let loginParams = ["password":"\(password)"]
  4. AF.request("https://example.com/users/\(username)/login",
  5. method: .post,
  6. parameters: loginParams,
  7. encoder: JSONParameterEncoder.default,
  8. headers: nil, interceptor: nil).response { response in
  9. switch response.result {
  10. case .success:
  11. if let data = response.data {
  12. do {
  13. let userLogin = try JSONDecoder().decode(UsersLogin.self, from: data)
  14. if userLogin.success == true {
  15. defaults.set(username, forKey: "username")
  16. defaults.set(password, forKey: "password")
  17. defaults.set(userLogin.token, forKey: "token")
  18. print("Successfully get token.")
  19. } else {
  20. //show alert
  21. print("Failed to get token with incorrect login info.")
  22. }
  23. } catch {
  24. print("Error: \(error)")
  25. }
  26. }
  27. case .failure(let error):
  28. //show alert
  29. print("Failed to get token.")
  30. print(error.errorDescription as Any)
  31. }
  32. }

到目前为止,我有什么可以转换为AF Router结构:

  1. import Foundation
  2. import Alamofire
  3. enum Router: URLRequestConvertible {
  4. case login(username: String, password: String)
  5. var method: HTTPMethod {
  6. switch self {
  7. case .login:
  8. return .post
  9. }
  10. }
  11. var path: String {
  12. switch self {
  13. case .login(let username):
  14. return "/users/\(username)/login"
  15. }
  16. }
  17. var parameters: Parameters? {
  18. switch self {
  19. case .login(let password):
  20. return ["password": password]
  21. }
  22. }
  23. // MARK: - URLRequestConvertible
  24. func asURLRequest() throws -> URLRequest {
  25. let url = try Constants.ProductionServer.baseURL.asURL()
  26. var request = URLRequest(url: url.appendingPathComponent(path))
  27. // HTTP Method
  28. request.httpMethod = method.rawValue
  29. // Common Headers
  30. request.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.acceptType.rawValue)
  31. request.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.contentType.rawValue)
  32. // Parameters
  33. switch self {
  34. case .login(let password):
  35. request = try JSONParameterEncoder().encode(parameters, into: request) //where I got the error
  36. }
  37. return request
  38. }
  39. }
  40. class APIClient {
  41. static func login(password: String, username: String, completion: @escaping (Result<UsersLogin, AFError>) -> Void) {
  42. AF.request(Router.login(username: username, password: password)).responseDecodable { (response: DataResponse<UsersLogin, AFError>) in
  43. completion(response.result)
  44. }
  45. }
  46. }

LoginViewController类(我替换了AF.request代码)

  1. APIClient.login(password: password, username: username) { result in
  2. switch result {
  3. case .success(let user):
  4. print(user)
  5. case .failure(let error):
  6. print(error.localizedDescription)
  7. }

可编码UsersLogin模型

  1. struct UsersLogin: Codable {
  2. let success: Bool
  3. let username: String
  4. let token: String?
  5. enum CodingKeys: String, CodingKey {
  6. case success = "success"
  7. case username = "username"
  8. case token = "token"
  9. }
  10. }
lztngnrs

lztngnrs1#

我花了一段时间,但终于修好了。我也整理了一下代码。

  1. enum Router: URLRequestConvertible {
  2. case login([String: String], String)
  3. var baseURL: URL {
  4. return URL(string: "https://example.com")!
  5. }
  6. var method: HTTPMethod {
  7. switch self {
  8. case .login:
  9. return .post
  10. }
  11. }
  12. var path: String {
  13. switch self {
  14. case .login(_, let username):
  15. return "/users/\(username)/login"
  16. }
  17. }
  18. func asURLRequest() throws -> URLRequest {
  19. print(path)
  20. let urlString = baseURL.appendingPathComponent(path).absoluteString.removingPercentEncoding!
  21. let url = URL(string: urlString)
  22. var request = URLRequest(url: url!)
  23. request.method = method
  24. switch self {
  25. case let .login(parameters, _):
  26. request = try JSONParameterEncoder().encode(parameters, into: request)
  27. }
  28. return request
  29. }
  30. }

使用

  1. let username = usernameTextField.text
  2. AF.request(Router.login(["password": password], username)).responseDecodable(of: UsersLogin.self) { (response) in
  3. if let userLogin = response.value {
  4. switch userLogin.success {
  5. case true:
  6. print("Successfully get token.")
  7. case false:
  8. print("Failed to get token with incorrect login info.")
  9. }
  10. } else {
  11. print("Failed to get token.")
  12. }
  13. }
展开查看全部
hmtdttj4

hmtdttj42#

我用这种方法解决了一个类似的问题。我创建了一个协议Routable

  1. enum EncodeMode {
  2. case encoding(parameterEncoding: ParameterEncoding, parameters: Parameters?)
  3. case encoder(parameterEncoder: ParameterEncoder, parameter: Encodable)
  4. }
  5. protocol Routeable: URLRequestConvertible {
  6. var baseURL: URL { get }
  7. var path: String { get }
  8. var method: HTTPMethod { get }
  9. var encodeMode: EncodeMode { get }
  10. }
  11. extension Routeable {
  12. // MARK: - URLRequestConvertible
  13. func asURLRequest() throws -> URLRequest {
  14. let url = baseURL.appendingPathComponent(path)
  15. var urlRequest: URLRequest
  16. switch encodeMode {
  17. case .encoding(let parameterEncoding, let parameters):
  18. urlRequest = try parameterEncoding.encode(URLRequest(url: url), with: parameters)
  19. case .encoder(let parameterEncoder, let parameter):
  20. urlRequest = URLRequest(url: url)
  21. urlRequest = try parameterEncoder.encode(AnyEncodable(parameter), into: urlRequest)
  22. }
  23. urlRequest.method = method
  24. return urlRequest
  25. }
  26. }

我的路由器看起来像这个

  1. enum WifiInterfacesRouter: Routeable {
  2. case listActive(installationId: Int16?)
  3. case insert(interface: WifiInterface)
  4. var encodeMode: EncodeMode {
  5. switch self {
  6. case .listActive(let installationId):
  7. guard let installationId = installationId else {
  8. return .encoding(parameterEncoding: URLEncoding.default, parameters: nil)
  9. }
  10. return .encoding(parameterEncoding: URLEncoding.default, parameters: ["idInstallation": installationId])
  11. case .insert(let interface):
  12. return .encoder(parameterEncoder: JSONParameterEncoder.default, parameter: interface)
  13. }
  14. }
  15. var baseURL: URL {
  16. return URL(string: "www.example.com/wifiInterfaces")!
  17. }
  18. var method: HTTPMethod {
  19. switch self {
  20. case .listActive: return .get
  21. case .insert: return .post
  22. }
  23. }
  24. var path: String {
  25. switch self {
  26. case .listActive: return "listActive"
  27. case .insert: return "manage"
  28. }
  29. }
  30. }

解决生成错误
协议“可编码”作为类型不能符合协议本身
我使用了有用的AnyCodable库。Codable的类型擦除实现。

展开查看全部
ebdffaop

ebdffaop3#

你不能将Parameters字典与Encodable类型一起使用,因为[String: Encodable]的字典不是Encodable,就像错误所说的那样。我建议将asURLRequest过程的这一步移到一个单独的函数中,例如:

  1. func encodeParameters(into request: inout URLRequest) {
  2. switch self {
  3. case let .login(parameters):
  4. request = try JSONParameterEncoder().encode(parameters, into: request)
  5. }
  6. }

不幸的是,对于有许多路由的路由器来说,这并不能很好地扩展,所以我通常将路由分解成小的枚举,并将参数移动到单独的类型中,这些类型与路由器结合起来产生URLRequest

相关问题