Swift - различный вывод на основе JSON содержимого ответа - PullRequest
0 голосов
/ 02 апреля 2020

Этот вопрос, вероятно, будет звучать как основа c, и что-то, что, вероятно, можно найти с помощью поиска, но, несмотря на мои усилия по поиску в StackOverflow и просто в Google, я не могу найти ни одной актуальной темы или сообщения относительно того, как обрабатывать различные ответы REST API, и, как я выяснил, наличие актуального потока важно для предотвращения проблем в будущем при возникновении ошибок. Итак, чтобы перейти к нему, у меня есть конечная точка API на моем сервере для входа в систему. Он отвечает, как можно было бы предположить, с двумя случаями, указанными учетными данными для входа в систему;

Если информация для входа в систему успешна, он возвращает это JSON Объект:

{
    "user": {
        "id": 1,
        "type": "user",
        "name": "username",
        "api_token": "accesstokenhere"
    },
    "access_token": "accesstokenhere"
}

Если это не удается, он дает этот ответ

{
    "message": "Invalid credentials"
}

Теперь у меня есть экран входа в мое приложение, после нажатия «войти» отправьте информацию на сервер и получите ответ, который не является проблемой и хорошо документирован. На данный момент у меня есть следующий код:

import SwiftUI
import Combine
import Foundation

public struct UserModel: Decodable {
    let id: Int
    let username: String
    let age: Int

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case username = "name"
        case age = "age"
    }
}

public struct UserResponse: Decodable {
    public let user: UserModel
    public let accessToken: String

    enum CodingKeys: String, CodingKey {
        case user = "user"
        case accessToken = "access_token"
    }
}


public class UserFetcher: ObservableObject {
    public let objectWillChange = PassthroughSubject<UserFetcher,Never>()

    @Published var hasFinished: Bool = false {
        didSet {
            objectWillChange.send(self)
        }
    }
    var user: UserResponse?

    @Published var incorrectLogin: Bool = false {
        didSet {
            objectWillChange.send(self)
        }
    }

    init(){
        guard let url = URL(string: "https://mywebsite.com/api/login") else { return }

        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"

        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in

            do {
                if let d = data {
                    let decodedRes = try JSONDecoder().decode(UserResponse.self, from: d)
                    DispatchQueue.main.async {
                        self.user = decodedRes
                        self.hasFinished = true
                        print("Dispatching")
                    }
                } else {
                    print("No Data")
                }
            } catch {
                print("Error")
            }

        }.resume()
    }
}

Я взял этот раздел полностью, за исключением незначительных изменений, чтобы подогнать другой объект из другого файла, который у меня есть для аналогичной задачи, хотя у него нет альтернативы ответы, и поэтому мне не пришлось обрабатывать любые другие типы ответов данных.

Я все еще довольно новичок в Swift, поэтому у меня есть базовое c понимание синтаксиса do-try-catch, но я Не так, как я мог бы поймать различные модели ответов или где разместить их в моем коде, чтобы предотвратить любые ошибки.

В идеале, я хотел бы, чтобы он переключал переменную invalidLogin, которая может наблюдаться, и вызывал всплывающее окно с неверной информацией для входа, как на всех экранах входа, когда вы вводите неверные учетные данные. Если этого не произойдет, он должен просто переключить переменную hasFinished и оставить значение falseLogin равным false, а затем я бы использовал пользовательскую модель для выполнения всех закулисных дел.

Опять же, я все еще довольно Я новичок в Swift, я уверен, что здесь есть проблемы с безопасностью или что-то еще, что я пропускаю, и, пожалуйста, дайте мне знать, если это так.

1 Ответ

0 голосов
/ 02 апреля 2020

Подходящим решением является перечисление со связанными типами.

Добавьте структуру для случая ошибки

public struct ErrorResponse: Decodable {
    let message : String
}

и перечисление

enum Response : Decodable {

    case success(UserResponse)
    case failure(ErrorResponse)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            let userData = try container.decode(UserResponse.self)
            self = .success(userData)
        } catch DecodingError.typeMismatch {
            let errorData = try container.decode(ErrorResponse.self)
            self = .failure(errorData)
        }
    }
}

После декодирования данные switch о результате и обработки дел

do {
    if let d = data {
        let result = try JSONDecoder().decode(Response.self, from: d)
        switch result {
            case .success(let userData):
                DispatchQueue.main.async {
                    self.user = userData
                    self.hasFinished = true
                    print("Dispatching")
                }
            case .success(let errorData):
                print(errorData.message)
            // handle the error
        }

    } else {
        print("No Data")
    }
} catch {
    print(error) // never print a meaningless literal string in a Decoding catch block
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...