SwiftUI项目调用Deezer API解码失败问题求助
问题分析与解决:Deezer API流派数据解码失败
问题描述
在SwiftUI项目中调用Deezer流派分类接口获取数据时,解码并赋值数组的步骤失败,程序进入else分支。相同的程序结构调用Apple iTunes API及其他公开测试API均能正常工作,疑惑是Deezer API本身存在问题,还是请求环节有误。
用户提供的代码
GenreModel代码
import Foundation struct Genre: Codable { let id: Int let name: String let picture: String }
GenreViewModel代码
import Foundation final class GenreViewModel: ObservableObject{ @Published var genres: [Genre] = [] @Published var hasError = false @Published var error: UserError? func fetchGenres(){ hasError = false let genreUrl = "https://api.deezer.com/genre" if let url = URL(string: genreUrl){ URLSession.shared.dataTask(with: url){ [weak self] data,response, error in DispatchQueue.main.async { if let error = error{ self?.hasError = true self?.error = UserError.custom(error: error) } else{ let decoder = JSONDecoder() //decoder.keyDecodingStrategy = .useDefaultKeys if let data = data, let genres = try? decoder.decode([Genre].self, from: data){ self?.genres = genres } else{ self?.hasError = true self?.error = UserError.failedToDecode } } } }.resume() } } } extension GenreViewModel{ enum UserError: LocalizedError{ case custom(error: Error) case failedToDecode var errorDescription: String?{ switch self{ case .failedToDecode: return "Failed to decode" case .custom(let error): return error.localizedDescription } } } }
GenreView代码
import SwiftUI struct GenreView: View{ let genre: Genre var body: some View{ VStack(alignment: .leading){ Text("**Genre**: \(genre.id)") Text("**Name**: \(genre.name)") Text("**Picture**: \(genre.picture)") } } } struct GenreView_Previews: PreviewProvider{ static var previews: some View{ GenreView(genre: .init(id: 0, name: "a", picture: "pic")) } }
ContentView代码
import SwiftUI struct ContentView: View { @StateObject private var gVM = GenreViewModel() var body: some View { NavigationView{ ZStack{ List{ ForEach(gVM.genres, id: \.id){ genre in GenreView(genre: genre).listStyle(.plain) } }.listStyle(.plain).navigationTitle("Users") .alert(isPresented: $gVM.hasError, error: gVM.error){ Button(action: gVM.fetchGenres){ Text("try again") } } }.onAppear(perform: gVM.fetchGenres) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
问题原因
不是Deezer API的问题,也不是请求环节错误,核心问题是你的模型结构和Deezer API返回的JSON结构不匹配。
Deezer的流派接口返回的JSON根对象不是直接的流派数组,而是一个包含data字段的对象,data字段里才是流派数组,大致结构如下:
{ "data": [ {"id": 1, "name": "Rock", "picture": "...", ...}, {"id": 2, "name": "Pop", "picture": "...", ...} ], "total": 25, "next": "..." }
而你当前的代码直接尝试将返回数据解码为[Genre].self(流派数组),结构完全不匹配,所以解码失败进入else分支。
解决方案
1. 新增根响应模型
在GenreModel中新增一个用来包裹响应数据的结构体:
import Foundation struct GenreResponse: Codable { let data: [Genre] } struct Genre: Codable { let id: Int let name: String let picture: String }
2. 修改ViewModel中的解码逻辑
将GenreViewModel里的解码代码改为解码GenreResponse.self,然后取出data数组赋值给genres:
// 替换原有的解码代码 if let data = data, let response = try? decoder.decode(GenreResponse.self, from: data) { self?.genres = response.data }
调试建议
为了更快定位类似的解码问题,建议:
- 替换
try?为do-catch结构,捕获并打印具体的解码错误,比如:
do { let response = try decoder.decode(GenreResponse.self, from: data) self?.genres = response.data } catch { print("解码错误:\(error)") self?.hasError = true self?.error = UserError.failedToDecode }
- 可以将返回的
data转换为字符串打印,查看实际返回的JSON结构,和模型做对比:
if let data = data, let jsonString = String(data: data, encoding: .utf8) { print("返回的JSON:\(jsonString)") }
内容的提问来源于stack exchange,提问作者vesadeceeger
相关产品推荐
相关产品推荐

