You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 13:05:08