在 SwiftUI 列表中显示 JSON 字典
Posted
技术标签:
【中文标题】在 SwiftUI 列表中显示 JSON 字典【英文标题】:Show JSON Dictionary in SwiftUI List 【发布时间】:2020-07-28 15:34:52 【问题描述】:我正在尝试在我的 swift 应用程序中迭代对象字典并将它们显示在列表中。我不断收到“[Note]”类型的错误值没有成员“prettyUpdatedString”。我哪里错了?
更新:感谢社区成员的输入,我已经能够编译代码,但数据仍然没有显示在列表中。没有抛出任何错误,但该区域只是空白。请参阅屏幕截图。
数据:
[
id: 2867,
type: "TechNote",
isSolution: false,
prettyUpdatedString: "1 day ago <strong>Cody Brown</strong> said",
mobileNoteText: "Attempted contact, left voicemail no anwser.",
isTechNote: true,
isHidden: false,
workTime: "0"
,
id: 2863,
type: "TechNote",
isSolution: true,
prettyUpdatedString: "1 day ago <strong>Cody Brown</strong> said",
mobileNoteText: "Computer is repaired and ready to be picked up. Please come to the High School Wednesdays (1pm-2pm). If you have a loaner computer please bring it along with the the student.",
isTechNote: true,
isHidden: false,
workTime: "0"
,
id: 2818,
type: "TechNote",
isSolution: false,
prettyUpdatedString: "2 weeks ago <strong>Cody Brown</strong> said",
mobileNoteText: "Sent to AGI for repair.",
isTechNote: true,
isHidden: false,
workTime: "0"
,
id: 2814,
type: "TechNote",
isSolution: false,
prettyUpdatedString: "2 weeks ago <strong>Cody Brown</strong> said",
mobileNoteText: "Brought to Office",
isTechNote: true,
isHidden: false,
workTime: "0"
,
id: 2790,
type: "TechNote",
isSolution: false,
prettyUpdatedString: "2 weeks ago <strong>Seth Duncan</strong> said",
mobileNoteText: "Left VM",
isTechNote: true,
isHidden: true,
workTime: "0"
,
id: 2717,
type: "TechNote",
isSolution: false,
prettyUpdatedString: "2 weeks ago <strong>Seth Duncan</strong> said",
mobileNoteText: "<br/> We will be at the OHS Front Office from 1-2PM on Wednesdays to assist with computer issues",
isTechNote: true,
isHidden: false,
workTime: "0"
]
获取数据
import SwiftUI
struct Note: Decodable, Identifiable
var id: Int
var prettyUpdatedString: String
var mobileNoteText: String
class FetchTicketNotes: ObservableObject
func getTicketNotes(id: Int, userApi: String, completion: @escaping ([Note]) -> ())
guard let url = URL(string: "URL HERE") else return
URLSession.shared.dataTask(with: url) (data, _, _) in
let ticketNotes = try! JSONDecoder().decode([Note].self, from: data!)
DispatchQueue.main.async
completion(ticketNotes)
.resume()
在视图中
import SwiftUI
struct DetailsView: View
@ObservedObject var ticketStatusAction = TicketStatusAction()
@ObservedObject var createTicketNote = CreateTicketNote()
@State var ticket: [TicketDetails] = []
@State var ticketNotes: [Note] = []
@State private var showingNoteAlert = false
@State private var showingOpenAlert = false
@State private var showingPendingAlert = false
@State private var showingDepotAlert = false
@State private var showingCloseAlert = false
@State private var note: String = ""
@ObservedObject private var keyboard = KeyboardResponder()
var id: Int
var displayClient: String
@Binding var userApi: String
var body: some View
ScrollView(.vertical, showsIndicators: false)
VStack(alignment: .leading)
if !ticket.isEmpty
Text(self.ticket.first?.location.locationName ?? "")
.fontWeight(.bold)
.padding()
Text("\(displayClient) - \(id)")
.fontWeight(.bold)
.font(.system(size:20))
.padding()
Divider()
Text("Status")
.fontWeight(.bold)
.padding()
if !ticket.isEmpty
Text(self.ticket.first?.statustype.statusTypeName ?? "")
.padding()
Text("Details")
.fontWeight(.bold)
.padding()
if !ticket.isEmpty
Text(clearMarkdown(on: self.ticket.first?.detail ?? ""))
.padding()
.fixedSize(horizontal: false, vertical: true)
Text("Room Number")
.fontWeight(.bold)
.padding()
Text(self.ticket.first?.ticketCustomFields.first(where: $0.definitionId == 14)?.restValue ?? "NOT PROVIDED")
.padding()
Text("Computer Number")
.fontWeight(.bold)
.padding()
Text(self.ticket.first?.ticketCustomFields.first(where: $0.definitionId == 11)?.restValue ?? "NOT PROVIDED")
.padding()
Text("Phone Number")
.fontWeight(.bold)
.padding()
Text(self.ticket.first?.ticketCustomFields.first(where: $0.definitionId == 15)?.restValue ?? "NOT PROVIDED")
.padding()
Divider()
Text("Notes")
.fontWeight(.bold)
.padding()
List(ticketNotes) ticketNote in
VStack(alignment: .leading, spacing: 10)
Text(ticketNote.prettyUpdatedString)
.padding()
Text(ticketNote.mobileNoteText)
.padding()
.fixedSize(horizontal: false, vertical: true)
.onAppear
FetchTicketNotes().getTicketNotes(id: self.id, userApi: self.userApi) ticketNotes in
self.ticketNotes = ticketNotes
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
Divider()
Section(header: Text("Create New Note")
.fontWeight(.bold)
.padding()
.padding(10)
.frame(maxWidth: .infinity))
TextField("Enter your note", text: $note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 350)
.padding(15)
Button(action:
self.showingNoteAlert = true
)
Text("Submit Note")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.orange)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()
).actionSheet(isPresented:self.$showingNoteAlert)
ActionSheet(
title: Text("Are you sure you want to add this note to \(displayClient)'s ticket?"),
message: Text("\(self.note)"),
buttons: [
.default(Text("Submit"))
self.createTicketNote.CreateNoteAction(ticketId: self.id, userApi: self.userApi, techNote: self.note);
self.note = "";
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
.cancel()
self.note = ""
])
Divider()
Section(header: Text("Change Ticket Status")
.fontWeight(.bold)
.padding()
.padding(10)
.frame(maxWidth: .infinity))
Button(action:
self.showingOpenAlert = true
)
Text("Open")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.green)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingOpenAlert)
Alert(
title: Text("Are you sure you want change \(displayClient)'s ticket to Open?"),
primaryButton: .default(Text("Open"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 1);
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingPendingAlert = true
)
Text("Pending")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.yellow)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingPendingAlert)
Alert(
title: Text("Are you sure you want to set \(displayClient)'s ticket to Pending?"),
primaryButton: .default(Text("Pending"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 2);
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingDepotAlert = true
)
Text("Depot")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingDepotAlert)
Alert(
title: Text("Are you sure you want to depot \(displayClient)'s ticket?"),
primaryButton: .default(Text("Depot"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 6);
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingCloseAlert = true
)
Text("Close")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingCloseAlert)
Alert(
title: Text("Are you sure you want to close \(displayClient)'s ticket?"),
primaryButton: .destructive(Text("Close"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 3);
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
secondaryButton: .cancel())
Spacer()
.padding()
.padding(.bottom, keyboard.currentHeight)
.edgesIgnoringSafeArea(.bottom)
.animation(.easeOut(duration: 0.16))
【问题讨论】:
解码部分无法工作。根对象是一个数组(注意封闭的[]
),既没有键note
也没有id
。
好的,如果我无法解码,我将如何显示数据?
删除结构 TicketNotes
并解码 [Note].self
并删除 Text(clearMarkdown(on: note.prettyUpdatedString))
中的一个 note
和另一个出现。而且你必须调整一些类型。
【参考方案1】:
这里有很多错误。首先将getTicketNotes
方法参数的解码完成块更新为[TicketNotes]
。
class FetchTicketNotes: ObservableObject
func getTicketNotes(id: Int, userApi: String, completion: @escaping ([Note]) -> ())
guard let url = URL(string: "LINK HERE") else return
URLSession.shared.dataTask(with: url) (data, _, _) in
let ticketNotes = try! JSONDecoder().decode([Note].self, from: data!)
DispatchQueue.main.async
completion(ticketNotes)
.resume()
然后修改.onAppear
,
.onAppear
//...
FetchTicketNotes().getTicketNotes(id: self.id, userApi: self.userApi) ticketNotes in
self.ticketNotes = ticketNotes
最后,
@State var ticketNotes: [Note] = []
//...
Text(clearMarkdown(on: note.prettyUpdatedString))
【讨论】:
检查更新。您需要解码[Note]
并在DetailsView
中更新ticketNotes
。
所以,好消息是,这不会引发任何错误,我可以在调试时看到数据,但坏消息是数据没有显示在应用程序中。我会更新代码和截图。【参考方案2】:
这可能不是最好的解决方案,但它适用于我的情况。我确实在 [TicketDetails] JSON 中找到了我正在寻找的数据,因此它使这变得不那么复杂。在我的 .onAppear 函数中,我捕获了相关数据。
声明变量
@State var ticketNotes: [Notes] = []
在出现时更改
.onAppear
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
self.ticketNotes = ticketDetails.notes
我用 ForEach 遍历了数组中的每一项
VStack(alignment: .leading, spacing: 10)
ForEach(ticketNotes, id: \.self) ticketNote in
Text(clearMarkdown(on: "\(ticketNote.prettyUpdatedString) '\(ticketNote.mobileNoteText)'"))
.padding()
【讨论】:
以上是关于在 SwiftUI 列表中显示 JSON 字典的主要内容,如果未能解决你的问题,请参考以下文章
来自 JSON/Dictionary 的 SwiftUI 列表