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

SwiftUI中JSONDecoder解码始终返回nil问题求助

SwiftUI Mock JSON解码返回nil的问题

我开发了一个SwiftUI应用,为测试需求创建了Mock仓库,从本地JSON文件获取数据而非调用API,但JSONDecoder的解码结果始终为nil。

本地JSON内容(存储于项目根目录):

[    
    {        
        "ts": 1000000,        
        "id": 1,        
        "userName": "honeybee",        
        "userLocation": "Honey bee",        
        "userImageUrl": "user_1",        
        "title": "Here",        
        "postType": "post",        
        "postImageUrl": "food_1",    },    
    {        
        "ts": 1000000,        
        "id": 2,        
        "userName": "honeybee",        
        "userLocation": "Honey bee",        
        "userImageUrl": "user_2",        
        "title": "Here",        
        "postType": "store",        
        "postImageUrl": "food_2"    
    },
]

FeedItemEntity模型代码:

enum PostType: String, Codable {    
    case Post =  "post"    
    case Store = "store"    
    case Restaurant = "restaurant"    
    case Review = "review"    
    case Recipe = "recipe"
}

struct FeedItemEntity: Codable {    
    var ts: Int    
    var userName: String    
    var userLocation: String    
    var userImageUrl: String    
    var title: String    
    var postImageUrl: String    
    var postType: PostType    
    var rating: Int
}

extension FeedItemEntity: Identifiable {    
    var id: UUID {        
        let id = UUID()        
        return id    
    }
}

Mock服务代码:

class MockedFeedService: FeedServiceProtocol {    
    static let shared = MockedFeedService()

    func getFeed() -> Future<[ios_foodloose_app.FeedItemEntity], Error> {        
        return Future<[FeedItemEntity], Error> { promise in            
            guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json"),                  
                  let data = try? Data(contentsOf: url) else {                
                return promise(.failure(NSError(domain: "", code: 401, userInfo: [ NSLocalizedDescriptionKey: "Error"])))            
            }            
            let response = try? JSONDecoder().decode( [FeedItemEntity].self, from: data)            
            promise(.success(response!))        
        }    
    }    
}

解码失败的原因及修正方案
  • JSON语法错误
    第一个JSON对象的"postImageUrl": "food_1",末尾多了逗号,数组最后也存在多余逗号。JSON不允许尾逗号(trailing comma),这会直接导致解析失败。

  • 模型字段不匹配
    FeedItemEntity里的rating是必填Int类型,但JSON中完全没有这个字段。解码时找不到对应值会抛出错误,try?会将错误转为nil。

  • Identifiable扩展冗余
    你手动生成UUID作为id,但JSON本身包含id字段,模型却未声明该属性,既浪费资源也不符合数据对应逻辑。

  • 错误处理缺失
    用try?直接忽略了解码错误,无法获取具体失败原因,导致无法快速定位问题。

修正后的代码示例

修正后的JSON

[    
    {        
        "ts": 1000000,        
        "id": 1,        
        "userName": "honeybee",        
        "userLocation": "Honey bee",        
        "userImageUrl": "user_1",        
        "title": "Here",        
        "postType": "post",        
        "postImageUrl": "food_1"    
    },    
    {        
        "ts": 1000000,        
        "id": 2,        
        "userName": "honeybee",        
        "userLocation": "Honey bee",        
        "userImageUrl": "user_2",        
        "title": "Here",        
        "postType": "store",        
        "postImageUrl": "food_2"    
    }
]

修正后的FeedItemEntity

enum PostType: String, Codable {    
    case post = "post"    
    case store = "store"    
    case restaurant = "restaurant"    
    case review = "review"    
    case recipe = "recipe"
}

struct FeedItemEntity: Codable, Identifiable {    
    let id: Int // 直接使用JSON中的id
    var ts: Int    
    var userName: String    
    var userLocation: String    
    var userImageUrl: String    
    var title: String    
    var postImageUrl: String    
    var postType: PostType    
    var rating: Int? // 改为可选类型,适配JSON无该字段的情况
}

修正后的Mock服务(添加错误处理)

class MockedFeedService: FeedServiceProtocol {    
    static let shared = MockedFeedService()

    func getFeed() -> Future<[FeedItemEntity], Error> {        
        return Future<[FeedItemEntity], Error> { promise in            
            guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json"),                  
                  let data = try? Data(contentsOf: url) else {                
                return promise(.failure(NSError(domain: "MockService", code: 401, userInfo: [NSLocalizedDescriptionKey: "无法加载JSON文件"])))            
            }            
            do {
                let response = try JSONDecoder().decode([FeedItemEntity].self, from: data)
                promise(.success(response))
            } catch {
                print("解码错误:\(error)") // 打印错误便于调试
                promise(.failure(error))
            }
        }    
    }    
}

内容的提问来源于stack exchange,提问作者Seb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:35:27