小弟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])}
}
可能是因為
你的 MediaMeta 只定義 id,url
struct MediaMeta: Decodable ,Identifiable {
var id = UUID ()
var url : String
}
把其他欄位(normalizedUrl, thumbnail, type, tags, createdAt, updatedAt)也定義上去試看看