Я работаю с TMDB API уже некоторое время. Все идет хорошо, кроме как при попытке добавить локализацию.
Короче говоря, я получаю ошибку ниже, когда декодирую ключ с именем "iso_3166_1", строка объединяется с числом. Мне нужно отфильтровать значение этого ключа как iso_3166_1 = "CN", тогда я смогу получить элемент перевода на китайском языке, чтобы упростить. Я пытаюсь использовать CodingKey таким образом case iso31661 = "iso_3166_1"
. Но это не работает, «iso31661» возвращается с nil, однако оно имеет значение в JSON данных.
Так как я могу установить ключ в модели, когда он находится в этом формате?
Ошибка:
ERROR: keyNotFound(CodingKeys(stringValue: "iso_3166_1", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "translations", intValue: nil), CodingKeys(stringValue: "translations", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"iso_3166_1\", intValue: nil) (\"iso_3166_1\").", underlyingError: nil))
JSON Данные
{
"adult": false,
"backdrop_path": "/xPMbNZwn1nQzc5fsjnhuu3fDndA.jpg",
"belongs_to_collection": null,
"budget": 1200000,
"genres": [],
"homepage": "",
"id": 500,
"imdb_id": "tt0105236",
"original_language": "en",
"original_title": "Reservoir Dogs",
"overview": "A botched robbery indicates a police informant, and the pressure mounts in the aftermath at a warehouse. Crime begets violence as the survivors -- veteran Mr. White, newcomer Mr. Orange, psychopathic parolee Mr. Blonde, bickering weasel Mr. Pink and Nice Guy Eddie -- unravel.",
"popularity": 20.717,
"poster_path": "/g7spS2Y4SZoQoC6Hn7zoqEqdYqR.jpg",
"production_companies": [],
"production_countries": [],
"release_date": "1992-09-02",
"revenue": 2859750,
"runtime": 99,
"spoken_languages": [
{
"iso_639_1": "en",
"name": "English"
}
],
"status": "Released",
"tagline": "Every dog has his day.",
"title": "Reservoir Dogs",
"video": false,
"vote_average": 8.2,
"vote_count": 8946,
"translations": {
"translations": [
{
"iso_3166_1": "BG",
"iso_639_1": "bg",
"name": "български език",
"english_name": "Bulgarian",
"data": {
"homepage": "",
"overview": "Банда престъпници подготвят най-големия обир на скъпоценности. Всичко се развива по плана, но изненадващо нищо неподозиращите бандити са обградени от полицията и става ясно, че един от тях е предател. Сюжетът се разгръща от изследване на мъжката психика до дисекция на престъпното съзнание, докато всички се опитват да разберат какво е провалило перфектно замисления удар.",
"runtime": 105,
"tagline": "",
"title": "Глутница кучета"
}
},
{
"iso_3166_1": "ES",
"iso_639_1": "ca",
"name": "Català",
"english_name": "Catalan",
"data": {
"homepage": "",
"overview": "Uns delinqüents professionals que no es coneixen entre ells i que es mantenen a l'anonimat darrere de noms de colors (senyor Rosa, senyor Blanc, senyor Taronja...) han preparat minuciosament el robatori a una joieria. En el moment de l'atracament apareix inesperadament la policia i es produeix una massacre. Tot fa sospitar que hi ha un traïdor infiltrat. Reunits a porta tancada dins d'un vell magatzem abandonat, els supervivents s'enfrontaran entre ells decidits a descobrir qui els ha conduït a aquesta situació límit.",
"runtime": 0,
"tagline": "",
"title": ""
}
},
...
...
Модель данных
import SwiftUI
struct Movie: Codable {
let id: Int
let overview: String?
let title: String?
let translations: Translation
static var `default`: Movie {
Movie(id: 0, overview: "", title: "", translations: Translation.default)
}
}
struct Translation: Codable {
let translations: [TranslationItem]
static var `default`: Translation {
Translation(translations: [])
}
}
struct TranslationItem: Codable {
let iso31661: String
let name: String
let data: DataDetail
enum CodingKeys: String, CodingKey {
case iso31661 = "iso_3166_1"
case name
case data
}
}
struct DataDetail: Codable {
let overview: String
let title: String
}
WebService
import Foundation
import Combine
enum HTTPError: LocalizedError {
case statusCode
case post
}
struct WebService {
private var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
private var session: URLSession = {
let config = URLSessionConfiguration.default
config.urlCache = URLCache.shared
config.waitsForConnectivity = true
return URLSession(configuration: config, delegate: nil, delegateQueue: nil)
}()
private func createPublisher<T: Codable>(for url: URL) -> AnyPublisher<T, Error> {
print("Publisher URL: \(url)")
return session.dataTaskPublisher(for: url)
.tryMap { output in
guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {
print("Response: \(output.response)")
do {
let ss = try self.decoder.decode(Response.self, from: output.data)
print("ss: \(ss)")
} catch {
print(error)
}
throw HTTPError.statusCode
}
return output.data
}
.decode(type: T.self, decoder: decoder)
.eraseToAnyPublisher()
}
func getMovieDetail(movieId: Int) -> AnyPublisher<Movie, Error> {
createPublisher(for: TMDBClient.Endpoints.movieDetail(movieId).url)
}
}
ViewModel
import SwiftUI
import Combine
class MovieListViewModel: ObservableObject {
private var webService = WebService()
private var cancellableSet: Set<AnyCancellable> = []
@Published var movie = Movie.default
func getMovieDetail(movieId: Int) {
webService.getMovieDetail(movieId: movieId)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { status in
switch status {
case .finished:
break
case .failure(let error):
print("ERROR: \(error)")
break
}
}) { movie in
self.movie = movie
}.store(in: &self.cancellableSet)
}
}
Main ContentView
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var model = MovieListViewModel()
var body: some View {
NavigationView {
Text(String(self.model.movie.translations.translations.filter{$0.iso31661 == "CN"}.first?.data.title ?? ""))
}.onAppear() {
self.model.getMovieDetail(movieId: 500)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}