Как исправить неверный ответ JSON в swift при использовании запроса get в Alamofire? - PullRequest
0 голосов
/ 14 мая 2019

Я создаю приложение, в котором оно извлекает PatientList с API-сервера и отображает TableView.После проверки он возвращает 200 код состояния, но падает до ошибки invalidJSON.Но когда я зарегистрировался в Почтальон , он возвращает 200 код состояния и правильно извлекает записи.Я совершенно сбит с толку, какая часть моих кодов вызывает ошибку, так как я новичок в Swift.Я ищу помощь, чтобы решить проблему.Ниже приведены мои примеры кодов для ваших ссылок.Заранее большое спасибо.

Patient.swift

struct Patient: Codable {
    let hospitalNumber: Int
    let patientName: String
    let totalAmount: Double

enum CodingKeys: String, CodingKey {
    case hospitalNumber = "hospitalNumber"
    case patientName = "patientName"
    case totalAmount = "totalAmount"
   }
}

APIService.swift

struct PatientList {
    typealias getPatientListTaskCompletion = (_ patientListperPayout: [Patient]?, _ error: NetworkError?) -> Void


    static func getPatientList(doctorNumber: Int, periodId: Int, completion: @escaping getPatientListTaskCompletion) {

        guard let patientPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPerPayout)?periodId=\(periodId)&doctorNumber=\(doctorNumber)") else {
            completion(nil, .invalidURL)
            return
        }

        let sessionManager = Alamofire.SessionManager.default
        sessionManager.session.getAllTasks { (tasks) in
            tasks.forEach({ $0.cancel() })
        }

        Alamofire.request(patientPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
            guard HelperMethods.reachability(responseResult: response.result) else {
                completion(nil, .noNetwork)
                return
            }

            guard let statusCode = response.response?.statusCode else {
                completion(nil, .noStatusCode)
                return
            }

            switch(statusCode) {
            case 200:
                guard let jsonData = response.data else{
                    completion(nil, .invalidJSON)
                    print(statusCode)
                    return
                }

                let decoder = JSONDecoder()

                do {
                    let patientListArray = try decoder.decode([Patient].self, from: jsonData)
                    let sortedPatientListArray = patientListArray.sorted(by: { $0.patientName < $1.patientName })
                    completion(sortedPatientListArray, nil)
                }catch{
                    completion(nil, .invalidJSON)
                    print(statusCode)
                }
            case 400:
                completion(nil, .badRequest)
            case 404:
                completion(nil, .noRecordFound)
            default:
                print("UNCAPTURED STATUS CODE FROM getPatientList\nSTATUS CODE: \(statusCode)")
                completion(nil, .uncapturedStatusCode)
                }
            }
        }

Controller.swift

var patientList: [Patient]! {
    didSet {
       performSegue(withIdentifier: patientListIdentifier, sender: self)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    self.latestCreditedAmountTableView.dataSource = self
    self.latestCreditedAmountTableView.delegate = self

    configureTableViewCell()
    showTotalCreditedAmount()
     getDoctorPayoutSummary(doctorNumber: doctorNumber)

}

func getDoctorPayoutSummary(doctorNumber: Int) {
  self.payoutSummary = payoutSummaryDetails
        self.taxRateVatRateLabel.text = "\(self.payoutSummary.taxRate) / \(self.payoutSummary.vatRate)"
        self.getPatientList()
        self.latestCreditedAmountTableView.reloadData()
        return
}

 func getPatientList() {

    APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
        guard let patientListPerPayout = patientListArray, error == nil else {
            if let networkError = error {
                switch networkError {
                case .noRecordFound:
                    let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
                    alertController.addAction(UIAlertAction(title: "OK", style: .default))
                    self.present(alertController, animated: true, completion: nil)
                case .noNetwork:
                    let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)

                    alertController.addAction(UIAlertAction(title: "OK", style: .default))
                    self.present(alertController, animated: true, completion: nil)
                default:
                    let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
                    alertController.addAction(UIAlertAction(title: "OK", style: .default))
                    self.present(alertController, animated: true, completion: nil)
                }
            }
            return
        }

        self.patientList = patientListPerPayout

        return

    }
}

JSON Response

[
   {
   "hospitalNumber": null,
   "patientName": null,
   "totalAmount": 31104
   },
   {
   "hospitalNumber": "",
   "patientName": "LastName, FirstName",
   "totalAmount": 3439.8
   }
]

Ответы [ 2 ]

1 голос
/ 14 мая 2019

Ваш ответ JSON показывает, что некоторые поля могут быть нулевыми - hospitalNumber и patientName как минимум. Также hospitalNumber - это строка в JSON - спасибо @Don за указание. Ваш struct также должен быть в состоянии справиться с этим обнуляемостью, сделав также отображаемые поля обнуляемыми. * Т.е. 1005 *

struct Patient: Codable {
  let hospitalNumber: String?
  let patientName: String?
  let totalAmount: Double

  enum CodingKeys: String, CodingKey {
    case hospitalNumber = "hospitalNumber"
    case patientName = "patientName"
    case totalAmount = "totalAmount"
  }
}

Вам нужно будет сделать то же самое для totalAmount, если это также может быть нулевым. Правильно ли API для возврата null в любых обстоятельствах - это, конечно, другой вопрос - возможно, необходимо решить вопрос о том, насколько полезен нулевой номер или название больницы.

Убедитесь, что вы не распаковываете поля при их использовании.

0 голосов
/ 14 мая 2019

Просто внесите изменения в класс вашей модели.Определите переменную класса модели как optional, которая не является обязательной для API.

struct Patient: Codable {
    var hospitalNumber: String?
    let patientName: String?
    let totalAmount: Double?

    enum CodingKeys: String, CodingKey {
        case hospitalNumber = "hospitalNumber"
        case patientName = "patientName"
        case totalAmount = "totalAmount"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        if let hospitalNumb = try container.decode(Int?.self, forKey: .hospitalNumber) {
            hospitalNumber = String(hospitalNumb)
        } else {
            hospitalNumber = try container.decode(String.self, forKey: .hospitalNumber)
        }
        patientName = try container.decode(String.self, forKey: .patientName)
        totalAmount = try container.decode(Double.self, forKey: .totalAmount)
    }
}

Примечание:

Codable ИЛИ Decodable не работает, еслитип отличается для того же ключа, или вы можете сказать, что этот тип отличается от указанного типа.

...