Swift Parsing декодирует 2 разных JSON с 1 URL API - PullRequest
1 голос
/ 19 июня 2019

Привет, я новичок в swift, и я все еще учусь, поэтому я пытаюсь создать контроллер входа в систему и проанализировать данные json, если он исправляет их, проанализировать данные json с id и прочее, и если вход в систему не удастся, то json покажет своего родасообщение.я уже создаю структуру для всех требуемых данных значения, но я получил эту ошибку, в которой говорится, что это nil.

, так что это json, если вход выполнен успешно:

[{"id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1"}]

и это jsonесли вход не выполнен:

[{"message": "Ошибка входа в систему ..", "status": "0"}]

, поэтому в основном он имееттот же URL, я думаю?но я не знаю, я вроде застрял здесь, и мне нужна помощь

struct login : Codable {
    let id : Int
    let name : String
    let email : String
    let status : String
    let message : String

    init(dictionary : [String : Any]) {
        id = (dictionary ["id"] as? Int)!
        name = (dictionary ["name"] as? String)!
        email = (dictionary ["email"] as? String)!
        status = (dictionary ["status"] as? String)!
        message = (dictionary ["message"] as? String)!
    }

    enum CodingKeys : String, CodingKey {
        case id = "id"
        case name = "name"
        case email = "email"
        case status = "status"
        case message = "message"
    }
}

func Login() {

    let Email = EmailField.text!
    let Pass = PasswordField.text!


    print(api)

    guard let JsonUrl = URL(string: api) else {return}
    URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
        guard let data = data else {return}

        do{
            let parsing = try JSONDecoder().decode([login].self, from: data)
            print(parsing)
            self.Loginnn = parsing
            let stats = self.Loginnn.map { $0.status}

            if stats.contains("1"){
                print("Login Success")
                DispatchQueue.main.async {
                    self.appDelegate.loginSeque()
                }
            }else if stats.contains("0") {
                let action = UIAlertAction(title: "Got It", style:             .default, handler: nil)
                let alert = UIAlertController(title: "Wrong Email /   Password", message: "Please Try Again ", preferredStyle: .alert)
                alert.addAction(action)
                self.present(alert, animated: true, completion: nil)

                // so basicly i wanna run this alert action by search status if its contains "0"
            }
        }
    }catch{
        print(error)
    }
}.resume()
}

, поэтому, когда я пытаюсь проверить, не прошел ли мой вход в систему, я не показываю сообщение в моем json в моем журнале, вместо этого он показывает эту ошибку

"keyNotFound (CodingKeys (stringValue:" id ", intValue: nil), Swift.DecodingError.Context (codingPath: [_JSONKey (stringValue:" Index 0 ", intValue: 0)], debugDescription: "Нет значения, связанного с ключом CodingKeys (stringValue: \" id \ ", intValue: nil) (\" id \ ").", UnderError: nil)) "

я просто хочу выскочитькакое-то сообщение или предупреждение, если вход не выполнен из-за неправильного пароля или адреса электронной почты ..... так, может, кто-нибудь может мне помочь, как сделать это наилучшим образом?

Ответы [ 4 ]

1 голос
/ 19 июня 2019

Вы можете объявить типы ответов Успех и Отказ, как показано ниже,

struct LoginSuccess: Decodable {
    var id: Int
    var name: String
    var email: String
    var status: String
}

struct LoginFailure: Decodable {
    var status: String
    var message: String
}

, а затем использовать как,

guard let JsonUrl = URL(string: api) else { return }
URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
    guard let data = data else { return }

        if let success = try? JSONDecoder().decode([LoginSuccess].self, from: data).first {
            GlobalVariable.UserId = String(success.id)
            DispatchQueue.main.async {                    
                 self.appDelegate.loginSeque()
            }
        } else if let failure = try? JSONDecoder().decode([LoginFailure].self, from: data).first {
            let action = UIAlertAction(title: "Got It", style:             .default, handler: nil)
            let alert = UIAlertController(title: "Wrong Email /   Password", message: failure.message, preferredStyle: .alert)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        }
}.resume()
0 голосов
/ 20 июня 2019

У вас уже есть ответ (или три) для этого, но я хочу показать вам, как это сделать без использования JSONSerialization или умозрительного декодирования.

Итак, у нас есть LoginSuccess иLoginFailure типов, которые вы хотите декодировать:

struct LoginSuccess: Decodable {
    var id: Int
    var name: String
    var email: String
}

struct LoginFailure: Decodable {
    var message: String
}

И мы хотим различать их на основе status, который находится в том же контейнере, что и поля этих типов.Итак, мы создаем enum:

enum LoginResult: Decodable {
    case success(LoginSuccess)
    case failure(LoginFailure)

    enum Keys: CodingKey {
        case status
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        if try container.decode(String.self, forKey: .status) == "1" {
            self = .success(try LoginSuccess(from: decoder))
        } else {
            self = .failure(try LoginFailure(from: decoder))
        }
    }
}

Обратите внимание, что enum 'init делает не вызов decoder.decode(LoginSuccess.self).Он передает декодер, который был передан инициализатору LoginSuccess.То же самое с LoginFailure.Это означает, что эти инициализаторы будут извлекать значения из того же контейнера, что и поле status.

Test:

let successData = #"[ { "id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: successData))

// Output:
[__lldb_expr_1.LoginResult.success(__lldb_expr_1.LoginSuccess(id: 891, name: "User", email: "qdpim@immobisp.com"))]

let failureData = #"[ { "message": "Login Failed..", "status": "0" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: failureData))

// Output:
[__lldb_expr_1.LoginResult.failure(__lldb_expr_1.LoginFailure(message: "Login Failed.."))]

Обратите внимание, что поскольку данные вашего примера обернуты в [...], я декодировалмассивы LoginResult.

0 голосов
/ 19 июня 2019

В этой ситуации я бы использовал JSONSerialization для декодирования данных в [[String: Any]] и просмотра содержимого, чтобы определить, какое это сообщение.

В моем коде я предположил, что элемент «status» сообщает нам, был ли это успешный вход или нет, но можно, например, искать наличие «id» или количество элементов в словаре, чтобы определить тип ответа

do {
    let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
    if let response = result.first, let status = response["status"] as? String  {
        if status == "1" {
            if let id = response["id"] as? Int {
                let ids = String(id)
                //...
            }
        } else {
            if let message = response["message"] as? String {
                print(message)
            }
        }
    }
} catch {
    print(error)
}

Ниже мое решение используется в коде из вашего вопроса. Обратите внимание, что я упростил структуру Login, поскольку она используется только при успешном входе

struct Login  {
    let id : Int
    let name : String
    let email : String
}

do {
    let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
    if let response = result.first, let status = response["status"] as? String  {
        if status == "1" {
            //handle success
            let login = Login(id: response["id"] as? Int ?? 0,
                              name: response["name"] as? String ?? "",
                              email: response["email"] as? String ?? "")
            self.Loginnn = login
            DispatchQueue.main.async {
                self.appDelegate.loginSeque()
            }
        } else {
            let action = UIAlertAction(title: "Got It", style: .default, handler: nil)
            let alert = UIAlertController(title: "Wrong Email /   Password", message: "Please Try Again ", preferredStyle: .alert)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        }
    }
} catch {
    print(error)
}
0 голосов
/ 19 июня 2019

Успешный ответ содержит только ключи («id», «name», «email», «status»)

[{"id": 891, "name": "User", "email": "qdpim@immobisp.com", "status": "1"}]

и ответ об ошибке содержит только ключи («сообщение», «статус»)

[{"message": "Ошибка входа ..", "status": "0"}]

Если вы хотите использовать одну и ту же структуру для обоих ответов JSON, вы должны сделать свойства необязательными

struct login : Codable {
    var id: Int?
    var name: String?
    var email: String?
    var status: String?
    var message: String?
}

Кроме того, поскольку ваши ключи совпадают с вашими свойствами, вам не нужно enum CodingKeys или init в этом отношении, если вы используете JSONDecoder().decode

...