Вам не хватает структуры для этой внешней части 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
, который соответствует вашим конкретным потребностям.