Swift 获取 HTTP JSON 响应并使用 Codable 解析它

Posted

技术标签:

【中文标题】Swift 获取 HTTP JSON 响应并使用 Codable 解析它【英文标题】:Swift Take HTTP JSON response and parse it using Codable 【发布时间】:2018-11-27 13:18:39 【问题描述】:

所以我试图通过使用 Alamofire 向后端服务器发出 http 请求来执行登录。

我有 2 个级别的请求方法,如下所示:

class func requestWith(url : URL, method: HTTPMethod, parameters: Parameters?, headers: HTTPHeaders, completion: @escaping (_ response: DataResponse<Any>) -> Void)

    let sessionManager = NetworkManager.sharedInstance
    sessionManager.request(url, method: method, parameters: parameters, encoding: URLEncoding.default, headers: headers)
        .validate(statusCode: 200..<300)
        .responseJSON  (response) in
            if let data = response.data 
                print("RESPONSE ALAMOFIRE: code = \(String(describing: response.response?.statusCode)), response = \(String(decoding: data, as: UTF8.self)) ")
            
            completion(response)
    

这是 Alamofire 请求的基本方法,我将其用于我想要添加的每个功能。现在遵循为基本身份验证量身定制的实际登录方法。

class func loginAccount(usernameOrEmail: String?, password: String?, onSuccess success: @escaping (_ data : Data?) -> Void, onFailure failure: @escaping (_ error: Error?, _ errorData: Data?) -> Void)

    let parameters: Parameters = [
        "username" : usernameOrEmail!,
        "password" : password!,
        "grant_type" : "password"
    ]

    let authUser = "WebClient"
    let authPass = "Parola123"

    let authData = "\(authUser):\(authPass)".data(using: String.Encoding.utf8)
    guard let base64Auth = authData?.base64EncodedString(options: []) else  return 
    let headers = [
        "Authorization": "Basic \(String(describing: base64Auth))",
    "Content-Type" : "application/x-www-form-urlencoded"
    ]

    let url = Constants.urls.URL_LOGIN
    APIRequest.requestWith(url: url!, method: .post, parameters: parameters, headers: headers)  (result) in
        switch result.result 
        case .success( _ ):

            if let jsonValue = result.result.value 
                let json = JSON(jsonValue)
                do 
                    let jsonData = try json.rawData( options: [])
                    success(jsonData)

                 catch 
                    print(error.localizedDescription)
                
            

            break

        case .failure(let error):
            print("There was an error in logging in: \(error)")
            failure(error, result.data)
            break
        
    

最后,在 LoginViewController 中点击登录按钮时调用的方法:

@IBAction func signInTapped(_ sender: Any) 
    APIRequest.loginAccount(usernameOrEmail: emailField.text, password: passwordField.text,
      onSuccess:   (successData) in
        self.startLoadingAnimation()
        if let response = successData 
            print("Response123 is: \(response)")


            do 

                let f = try JSONDecoder().decode(tokenResponse.self, from: response)
                print("access token is: \(f.scope)")

             catch 
                print(error.localizedDescription)
            

            self.stopLoadingAnimation()

            if let vc = self.storyboard?.instantiateViewController(withIdentifier: "ChatLogNavi") as? UINavigationController 
                self.present(vc,animated: true, completion: nil)
            

         else 
            

        , onFailure:  (error, failureData) in
        var json = JSON()
        do 
            json = try JSON(data: failureData!)
         catch 
        
        let messageBody = json["message"].stringValue
        let alert = UIAlertController(title: error?.localizedDescription, message: messageBody, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        self.present(alert, animated: true)
    )

    print("\n get account \n")

最重要的是,当我将响应从一种方法发送到另一种方法时,我不知道应该如何设置响应的数据类型。最后,当我到达最后一个方法时,它没有返回任何内容,并说:“无法读取数据,因为它的格式不正确。”我应该修改什么?我对这个 JSON 和 http 请求相当陌生。谢谢!

编辑:tokenResponse 声明如下:

 struct tokenResponse: Codable 
    var tokenType: String?
    var expiresIn: String?
    var refreshToken: String?
    var accessToken: String?
    var scope: String?

    enum CodingKeys: String, CodingKey 
        case tokenType = "token_type"
        case expiresIn = "expires_in"
        case refreshToken = "refresh_token"
        case accessToken = "access_token"
        case scope

    

【问题讨论】:

String(decoding: data, as: UTF8.self) ??应该是String(data: data, encoding: .utf8) 打印 print(error) 而不是 print(error.localizedDescription)。并告诉我们出了什么问题。 @Larme typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "expires_in", intValue: nil)], debugDescription: "预期解码字符串但找到了一个数字而是。”,基础错误:无)) 我不知道tokenResponse结构定义/实现,但很明显,它应该以大写开头,问题是因为你有let expires_in: String,但它应该是@987654331 @(或Double)。好吧,请注意,如果不是这种情况,您可能希望将属性命名为 expiresIn(驼峰式),并使用 CodingKey 来匹配蛇形大小写 请注意,在开发类似的东西时,您必须在处理 UI 的类和处理 API 的类之间划清界限。例如,这显然是 OAuth 实现。这意味着您在发送请求时,可能必须使用刷新令牌重新登录。 【参考方案1】:

我想你想从你的服务中接收一个 json!尝试用这个来改变你的第一个函数:

class func requestWith(url : URL, method: HTTPMethod, parameters: Parameters?, headers: HTTPHeaders, completion: @escaping (_ response: NSDictionary) -> Void)

    let sessionManager = NetworkManager.sharedInstance
    sessionManager.request(url, method: method, parameters: parameters, encoding: URLEncoding.default, headers: headers)
        .validate(statusCode: 200..<300)
        .responseJSON  (response) in
            if let result = response.result.value 
                let jsonResponse = result as! NSDictionary
                print("Your JSON is \(jsonResponse)")
            
            completion(jsonResponse)
    

因此,通过这种方式,您可以从您的函数中获得包含 JSON 的字典!之后你可以解析它来创建你的对象

【讨论】:

嗯,我不应该在完成时返回 jsonResponse 而不是响应吗?

以上是关于Swift 获取 HTTP JSON 响应并使用 Codable 解析它的主要内容,如果未能解决你的问题,请参考以下文章

获得 json 响应后,Swift 4 UI 卡住了

Swift 2,如何从多阶段 JSON 响应中获取值

PHP:如何从 JSON 中获取变量(从 Swift iOS 应用程序发送)并以 JSON 响应

Swift Json 计数并创建 TableView

Alamofire Swift 2.0 json 响应

如何保存响应 JSON 数组并在 swift 中使用用户默认值进行检索