在SwiftUI列表中显示JSON词典

时间:2020-07-28 15:34:52

标签: json swiftui

我正在尝试在我的swift应用中遍历对象字典并将它们显示在列表中。我不断收到错误消息,类型'[Note]'的值没有成员'prettyUpdatedString'。我在哪里错了?

更新:由于社区成员的输入,我已经能够编译代码,但是数据仍未显示在列表中。没有引发任何错误,但该区域只是空白。参见屏幕截图。

enter image description here

enter image description here

enter image description here

数据:

[
{
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))
    }
}

2 个答案:

答案 0 :(得分:0)

这里有很多错误。首先,将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))

答案 1 :(得分:0)

这可能不是最佳解决方案,但它适合我的情况。我确实找到了[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()
            }
        }