Search code examples
iosswiftswiftuiruntime-error

The preview process appears to have crashed using bundle to decode local json file


I'm using a decode function to get data from a json file in the project. There is no error when compelling but the xcode can't resume the canvas.

I'm using a decode function to get data from a json file in the project. There is no error when compelling but the xcode can't resume the canvas.

my data modle:

struct Movie1:Codable {
    var MOVIE_ID:String
    var NAME:String
    var ALIAS:String?
    var ACTORS:String?
    var COVER:String?
    var DIRECTORS:String?
    var DOUBAN_SCORE:Double?
    var DOUBAN_VOTES:Int?
    var GENRES:String?
    var IMDB_ID:String?
    var LANGUAGES:String?
    var MINS:Double?
    var OFFICIAL_SITE:String?
    var REGIONS:String?
    var RELEASE_DATE:String?
    var SLUG:String?
    var STORYLINE:String?
    var TAGS:String?
    var YEAR:String?
    var ACTOR_IDS:String?
    var DIRECTOR_IDS:String?
    
}


my json data:

 {"MOVIE_ID":"27021694",
"NAME":"耶尔玛",
"ALIAS":"",
"ACTORS":"比莉·派佩/布伦丹·考威尔/约翰·麦克米兰",
"COVER":"",
"DIRECTORS":"西蒙·斯通",
"DOUBAN_SCORE":"8.2",
"DOUBAN_VOTES":"187",
"GENRES":"剧情/戏曲",
"IMDB_ID":"tt6847880",
"LANGUAGES":"英语",
"MINS":"0",
"OFFICIAL_SITE":"http://ntlive.nationaltheatre.org.uk/productions/ntlout22-yerma",
"REGIONS":"英国",
"RELEASE_DATE":"2017/8/31",
"SLUG":"72FZ3FZ3b",
"STORYLINE":"A young woman is driven to the unthinkable by her desperate desire to have a child in Simon Stone’s radical production of Lorca’s achingly powerful masterpiece.",
"TAGS":"NTLive/女性/舞台剧/戏剧/英国/英国国家剧院现场/BilliePiper/NTL",
"YEAR":"2017",
"ACTOR_IDS":"比莉·派佩:1049548|Maureen Beattie:|布伦丹·考威尔:1022987|约翰·麦克米兰:1319678|Charlotte Randle:|Thalissa Teixeira:",
"DIRECTOR_IDS":"西蒙·斯通:1336274"}

my bundle function


import Foundation
extension Bundle{
    func decode<T:Codable>(filename:String) -> T {
        guard let url = Bundle.main.url(forResource: filename, withExtension: nil),
              let data = try? Data(contentsOf: url),
              let getResponse = try? JSONDecoder().decode(T.self, from: data)
              else { fatalError("no data")}
        return getResponse
    }
}

my view calling the function


struct homePage: View {
    
    let datas:[Movie1]=Bundle.main.decode(filename: "movieData1.json")
   
    
    var body: some View {
       
        
        HStack {
            Text("hello")
            Text(datas[1].ACTORS ?? "none")
            
        }
      
    }

}

my structure and json file image


Solution

  • Your Movie1 structure is wrong. All your var in Movie1 should be String?. No Double, no Int

    Here is the code that works for me:

    struct Movie1:Codable {
        var MOVIE_ID:String
        var NAME:String
        var ALIAS:String?
        var ACTORS:String?
        var COVER:String?
        var DIRECTORS:String?
        var DOUBAN_SCORE:String?   // <-- here
        var DOUBAN_VOTES:String?  // <-- here
        var GENRES:String?
        var IMDB_ID:String?
        var LANGUAGES:String?
        var MINS:String? // <-- here
        var OFFICIAL_SITE:String?
        var REGIONS:String?
        var RELEASE_DATE:String?
        var SLUG:String?
        var STORYLINE:String?
        var TAGS:String?
        var YEAR:String?
        var ACTOR_IDS:String?
        var DIRECTOR_IDS:String?
    }
    
        struct ContentView: View {
        @State var movieData: [Movie1] = []
        
        var body: some View {
            HStack {
                Text("hello")
                if !movieData.isEmpty {
                    Text(movieData[0].ACTORS ?? "none")
                }
            }
            .onAppear {
                if let datas: [Movie1] = Bundle.main.decode(filename: "movieData1.json") {
                    movieData = datas
                }
            }
        }
    }
    
    // do what @Paulw11 is suggesting, use do/try/catch with [T]?
    extension Bundle{
        func decode<T:Codable>(filename:String) -> [T]? {
            guard let url = Bundle.main.url(forResource: filename, withExtension: nil),
                  let data = try? Data(contentsOf: url),
                  let getResponse = try? JSONDecoder().decode([T].self, from: data)
                  else {
                      return nil
                  }
            return getResponse
        }
    }
    

    data in your "movieData1.json" file (note the start "[" and end "]"):

    [
    {
    "MOVIE_ID":"27021694",
    "NAME":"耶尔玛",
    "ALIAS":"",
    "ACTORS":"比莉·派佩/布伦丹·考威尔/约翰·麦克米兰",
    "COVER":"",
    "DIRECTORS":"西蒙·斯通",
    "DOUBAN_SCORE":"8.2",
    "DOUBAN_VOTES":"187",
    "GENRES":"剧情/戏曲",
    "IMDB_ID":"tt6847880",
    "LANGUAGES":"英语",
    "MINS":"0",
    "OFFICIAL_SITE":"http://ntlive.nationaltheatre.org.uk/productions/ntlout22-yerma",
    "REGIONS":"英国",
    "RELEASE_DATE":"2017/8/31",
    "SLUG":"72FZ3FZ3b",
    "STORYLINE":"A young woman is driven to the unthinkable by her desperate desire to have a child in Simon Stone’s radical production of Lorca’s achingly powerful masterpiece.",
    "TAGS":"NTLive/女性/舞台剧/戏剧/英国/英国国家剧院现场/BilliePiper/NTL",
    "YEAR":"2017",
    "ACTOR_IDS":"比莉·派佩:1049548|Maureen Beattie:|布伦丹·考威尔:1022987|约翰·麦克米兰:1319678|Charlotte Randle:|Thalissa Teixeira:",
    "DIRECTOR_IDS":"西蒙·斯通:1336274"
    }
    ]
    

    EDIT1: adding a new element.

    Adding a new var is no problem at all. In your Movie1 struct:

    struct Movie1:Codable {
        var isFavored:Bool   // <--- 
        var MOVIE_ID:String
        ....
    }
    

    and at the same time you need to modify your data to include this new element, like this:

    [
    {
    "isFavored": true,
    "MOVIE_ID":"27021694",
    ...
    }
    ]
    

    If you do not want your data to have a isFavored element, then give it an initial value in the Movie1 struct, and use CodingKeys to omit this isFavored That will remove the JSON coding and decoding of isFavored:

    struct Movie1:Codable {
        var MOVIE_ID:String
        var NAME:String
        var ALIAS:String?
        var ACTORS:String?
        var COVER:String?
        var DIRECTORS:String?
        var DOUBAN_SCORE:String?   
        var DOUBAN_VOTES:String?  
        var GENRES:String?
        var IMDB_ID:String?
        var LANGUAGES:String?
        var MINS:String? 
        var OFFICIAL_SITE:String?
        var REGIONS:String?
        var RELEASE_DATE:String?
        var SLUG:String?
        var STORYLINE:String?
        var TAGS:String?
        var YEAR:String?
        var ACTOR_IDS:String?
        var DIRECTOR_IDS:String?
        
        var isFavored: Bool = false  // <--- here
        
        // note the missing isFavored
        enum CodingKeys: String, CodingKey {
            case MOVIE_ID, NAME,ALIAS,ACTORS,COVER,DIRECTORS,DOUBAN_SCORE
            case DOUBAN_VOTES,GENRES,IMDB_ID,LANGUAGES,MINS,OFFICIAL_SITE
            case REGIONS,RELEASE_DATE,SLUG,STORYLINE,TAGS,YEAR,ACTOR_IDS,DIRECTOR_IDS
        }
        
    }