iOS swift Codable не работает с Alamofire для вложенных данных JSON? - PullRequest
0 голосов
/ 09 ноября 2019

Я новичок в концепции Alamofire и Codable. В iOS кто-то может сказать мне, как использовать это для доступа к моим данным json.

Это мой ответ json.

{"subscriptions": [
        {
            "batch_user_id": 23,
            "batch_name": "demo batch",
            "course_name": "IELTS",
            "start_date": "Nov 01 2019",
            "end_date": "Nov 30 2019",
            "no_of_days": 21,
            "total_no_of_days": 30,
            "extended_date": "Nov 30 2019",
            "extended": false,
            "course_extensions": [
                {
                    "id": 31,
                    "amount": "3500.0",
                    "course_id": 1,
                    "is_active": true,
                    "number_of_days": 5
                },

этокодируемый код:

 struct course_extensions: Codable {
        let id: String
        let amount: String
        let course_id: String

        private enum CodingKeys: String, CodingKey {
            case id = "id"
            case amount = "amount"
            case course_id = "course_id"
        }
    }

    struct subscriptions: Codable {
        let batch_user_id: String
        let batch_name: String
        let course_extensions: course_extensions

        private enum CodingKeys: String, CodingKey {
            case batch_user_id
            case batch_name
            case course_extensions = "course_extensions"
        }
    }
    struct User: Codable {
        let status: String
        let message: String
        let subscriptions: subscriptions
    }

Это мой сервисный вызов с alamofire:

// MARK: - Service call

func fetchUserData() {
    AF.request(SMAConstants.my_subscriptions, method: .get, parameters: nil, headers: nil)
        .responseJSON { (response) in
            switch response.result {
            case .success(let value):
                let swiftyJsonVar = JSON(value)
                print(swiftyJsonVar)
            case .failure(let error):
                print(error)

            }
    }
}

Может кто-нибудь помочь мне получить доступ к данным вложенного массива с помощью codable. заранее спасибо.

1 Ответ

2 голосов
/ 09 ноября 2019

Вам не хватает структуры для этой внешней части JSON:

struct ResponseObject: Codable {
    let subscriptions: [Subscription]
}

И вы можете использовать обычные свойства camelCase:

struct Subscription: Codable {
    let batchUserId: Int
    let batchName: String
    let courseExtensions: [CourseExtension]
}

struct CourseExtension: Codable {
    let id: Int
    let amount: String
    let courseId: Int
    let isActive: Bool
}

Несколько замечаний:

  • Имена типов struct должны начинаться с заглавных букв в соответствии с соглашением.
  • Эти CodingKeys в этом случае не нужны.
  • Будьте осторожны с вашими типами. Число из них Int и Bool. Используйте только типы String, если значение указано в кавычках.
  • Очевидно, что для краткости я исключил некоторые свойства из вышеупомянутых типов struct, но добавил все недостающие свойства, но оставлял их в стороне. с соглашением camelCase.

В любом случае, вы можете затем сказать декодеру преобразовать ключи JSON snake_case в имена свойств camelCase, используя:

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase

    let object = try decoder.decode(ResponseObject.self, from: data)
    print(object.subscriptions)
} catch {
    print(error)
}

Так, например, если используется Alamofire5:

let decoder: JSONDecoder = {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    return decoder
}()

func fetchUserData() {
    AF.request(SMAConstants.mySubscriptions))
        .responseDecodable(of: ResponseObject.self, decoder: decoder) { response in
            guard let value = response.value else {
                print(response.error ?? "Unknown error")
                return
            }

            print(value.subscriptions)
    }
}

Получено:

[Subscription(batchUserId: 23, batchName: "demo batch", courseExtensions: [CourseExtension(id: 31, amount: "3500.0", courseId: 1, isActive: true)])]

Кстати, я замечаю, что ваши даты имеют формат MMM d yyyy. Хотели бы вы преобразовать их в Date объекты? Если это так, вы можете использовать декодер, который определяет форматер даты следующим образом:

let decoder: JSONDecoder = {
    let decoder = JSONDecoder()

    decoder.keyDecodingStrategy = .convertFromSnakeCase

    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.dateFormat = "MMM d yyyy"
    decoder.dateDecodingStrategy = .formatted(formatter)

    return decoder
}()

Тогда вы можете определить startDate и endDate как Date объекты. Затем, когда вы представляете эти даты в пользовательском интерфейсе, вы можете использовать DateFormatter, чтобы показать хорошее локализованное отображение даты, а не просто фиксированный, уродливый MMM d yyyy формат.

Чтобы представить дату впользовательский интерфейс, то вы бы сделали что-то вроде:

let dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    return formatter
}()

А затем:

label.text = dateFormatter.string(from: date)

И говорящий на английском языке в США увидит:

15 апреля 2019

Испанский, говорящий на американском языке, увидит:

абр. 15, 2019

Испанский говорящий в Испании увидит:

15 abr 2019

Итог, пользователь увидит даты вформат, который они ожидают, а не жестко закодированный в каком-то определенном американском английском формате. Кроме того, у вас есть выбор: использовать формат .long там, где позволяет пространство (например, «15 апреля 2019 года») или .short, когда вам не хватает места (например, «04/15/19»). Просто выберите dateStyle, который соответствует вашим конкретным потребностям.

...