iT邦幫忙

0

[SWIFTUI] 如何從多層JSON取得資料

orih 2020-09-10 12:43:271617 瀏覽

小弟SWIFTUI新手,想從DCARD API學習從JSON取值,以下mediameta鍵裏url鍵有時會出現不止一項資料(以下例子為4個url連結),如何可以以文字於list view裏逐一顯示?

這個問題我在網上找了很久沒有相關例子,求眾大神解答

https://www.dcard.tw/_api/forums/movie/posts?popular=true

JSON結構如下

[
{
"id": 234379383,
"title": "關於板橋大遠百威秀售票問題",
"excerpt": "先用官方app看了時間跟位置之後去現場買票,排隊後接著就是售票sop,售票員表示G排中間沒有位置,要坐側邊靠走道的位置(23.24),中間要坐到第五排(F排),我就想說10分鐘前還有位子,也太熱門了吧",
"topics": [
"電影",
"售票",
"板橋"
],
"mediaMeta": [
{
"id": "af55f054-d6c4-4e8f-b87b-b54490dfffdd",
"url": "https://i.imgur.com/qNMtpq1l.jpg",
"normalizedUrl": "https://i.imgur.com/qNMtpq1l.jpg",
"thumbnail": "https://i.imgur.com/qNMtpq1l.jpg",
"type": "image/thumbnail",
"tags": [1 item],
"createdAt": "2020-09-08T16:17:42.187Z",
"updatedAt": "2020-09-08T16:17:42.187Z"
},
{
"id": "af55f054-d6c4-4e8f-b87b-b54490dfffdd",
"url": "https://i.imgur.com/qNMtpq1.jpg",
"normalizedUrl": "https://imgur.com/qNMtpq1",
"thumbnail": "https://i.imgur.com/qNMtpq1l.jpg",
"type": "image/imgur",
"tags": [1 item],
"createdAt": "2020-09-08T16:17:42.187Z",
"updatedAt": "2020-09-08T16:17:42.187Z"
},
{
"id": "e7a1a95e-c065-4d1c-b85f-ab1f13622c48",
"url": "https://i.imgur.com/N50Pxs0.jpg",
"normalizedUrl": "https://imgur.com/N50Pxs0",
"thumbnail": "https://i.imgur.com/N50Pxs0l.jpg",
"type": "image/imgur",
"tags": [1 item],
"createdAt": "2020-09-08T16:17:42.187Z",
"updatedAt": "2020-09-08T16:17:42.187Z"
},
{
"id": "e6d40d5b-1591-4bee-8a4d-c137ea0baa10",
"url": "https://i.imgur.com/KKjFuTq.jpg",
"normalizedUrl": "https://imgur.com/KKjFuTq",
"thumbnail": "https://i.imgur.com/KKjFuTql.jpg",
"type": "image/imgur",
"tags": [1 item],
"createdAt": "2020-09-08T16:17:42.187Z",
"updatedAt": "2020-09-08T16:17:42.187Z"
}
],

},

我想取得的欄位

import Foundation

struct Topic: Identifiable {

var id = UUID ()
var title: String!
var excerpt: String!
var topics: [String]
var mediameta: [MediaMeta]?
  
}
struct MediaMeta: Decodable ,Identifiable {
     var id = UUID ()
     var url : String
}
 
extension Topic: Decodable {
enum CodingKeys: String, CodingKey {
    case title
    case excerpt
    case topics
    case mediaMeta

    }

    enum MediaKeys: String, CodingKey {
    case mediaMeta
       case url
    }

     init(from decoder: Decoder) throws {
         let values = try decoder.container(keyedBy: CodingKeys.self)

         title = try values.decode(String.self, forKey: .title)
        excerpt = try values.decode(String.self, forKey: .excerpt)
        
        //topics
        var topicsContainer = try values.nestedUnkeyedContainer(forKey: .topics)
        
        var topicArray = [String]()
        while !topicsContainer.isAtEnd {
            topicArray.append(try topicsContainer.decode(String.self))
        } 
        topics = topicArray
        
        //mediaMeta

        var mediaUnKeyContainer = try values.nestedUnkeyedContainer(forKey: .mediaMeta)
        var mediaArray = [MediaMeta]()
        while !mediaUnKeyContainer.isAtEnd {
         
            mediaArray.append(try mediaUnKeyContainer.decode(MediaMeta.self))
                   }
            mediameta = mediaArray

    }
     
}

每一欄顯示方式

import SwiftUI
import Combine

struct CellView: View {
    
    var topic: Topic

     var body: some View {
           HStack {
  
                       VStack {
                        Spacer()
                        HStack {
                            Text(topic.title)
                                .foregroundColor(.blue)
                                .lineLimit(nil)
                                Spacer()
                        }
                        HStack {
                            Text(topic.excerpt).foregroundColor(.gray)
                            Spacer()
                        }
                        
                        HStack {
                       ForEach(topic.topics.indices, id: \.self) { index in
                                Text(self.topic.topics[index])}
                            .foregroundColor(.blue)
                            .lineLimit(nil)
                            Spacer()
                        }

            }
                        Spacer()
    
                }.frame(height: 130)
            }
        }

從JSON取值

import Foundation

class DataStore:  ObservableObject{
   
   @Published var topics = [Topic]()
  
   init() {
              load()
          }

   func load() {
       let urlStr = URL(string: "https://www.dcard.tw/_api/forums/movie/posts?popular=true")!
       
           URLSession.shared.dataTask(with: urlStr) {(data, response, error) in
               do{
                   if let bigData = data {
                        let decodedData = try JSONDecoder().decode([Topic].self, from: bigData)
       DispatchQueue.main.async {
           print(decodedData)
           self.topics = decodedData
                       }
                   } else {
                       print("No Data")
                   }
               }catch {
                   print("Error")
               }
       }.resume()
    }
}

ContentView

import Foundation
import SwiftUI

struct ContentView : View {

    @ObservedObject var datastore = DataStore()

    var body: some View {

               VStack {
               
                List(datastore.topics){ topic in
                    CellView(topic: topic)    }
                    
                }
            }
        }

struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
        
    }
}

從print看到url鍵資料

Dcard.Topic(id: 88DBE7F7-265B-4245-BF9F-6459D030053E, title: Optional("關於板橋大遠百威秀售票問題"), excerpt: Optional("先用官方app看了時間跟位置之後去現場買票,排隊後接著就是售票sop,售票員表示G排中間沒有位置,要坐側邊靠走道的位置(23.24),中間要坐到第五排(F排),我就想說10分鐘前還有位子,也太熱門了吧"), topics: ["電影", "售票", "板橋"], mediameta: Optional([Dcard.MediaMeta(id: AF55F054-D6C4-4E8F-B87B-B54490DFFFDD, url: "https://i.imgur.com/qNMtpq1l.jpg"), Dcard.MediaMeta(id: AF55F054-D6C4-4E8F-B87B-B54490DFFFDD, url: "https://i.imgur.com/qNMtpq1.jpg"), Dcard.MediaMeta(id: E7A1A95E-C065-4D1C-B85F-AB1F13622C48, url: "https://i.imgur.com/N50Pxs0.jpg"), Dcard.MediaMeta(id: E6D40D5B-1591-4BEE-8A4D-C137EA0BAA10, url: "https://i.imgur.com/KKjFuTq.jpg")]))]

我加上以下語法取url值會出錯

 HStack {
        ForEach(topic.mediameta.indices, id: \.self) { index in
        Text(self.topic.mediameta[index])}
       }
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

0
海綿寶寶
iT邦大神 1 級 ‧ 2020-09-10 16:03:32

可能是因為
你的 MediaMeta 只定義 id,url

struct MediaMeta: Decodable ,Identifiable {
     var id = UUID ()
     var url : String
}

把其他欄位(normalizedUrl, thumbnail, type, tags, createdAt, updatedAt)也定義上去試看看

我要發表回答

立即登入回答