刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据

Posted

技术标签:

【中文标题】刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据【英文标题】:Refresh Data in SwiftUI View pulled from HTTP Request 【发布时间】:2020-07-09 15:54:38 【问题描述】:

我一直在寻找解决此问题的方法,但我发现的方法似乎都无法解决我的具体情况。希望有了这些细节,有人可以帮助我。我是 Swift 的新手,所以请耐心等待。我正在使用来自我们的服务台票证系统的 REST Api 和 HTTP 请求。我正在尝试找到一种在某些情况发生变化时自动刷新数据的方法,并且还有一种手动刷新的方法,即下拉或实际刷新按钮。如果我能在下面的两个场景中弄清楚这一点,我想我可以将它应用到我的其余场景中。

场景 #1 - 我为工单提交了新注释,但详细视图并未更改以反映该新注释。我必须返回并重新打开视图才能看到更改。

获取工单详情

struct TicketDetails: Codable, Identifiable 
    var id: Int
    var type: String
    var location: Location
    var detail: String
    var notes: [Notes]
    var statustype: StatusType


struct Location: Codable 
    let locationName: String


struct Notes: Codable 
    var prettyUpdatedString: String?
    var mobileNoteText: String?


struct StatusType: Codable 
    var id: Int
    var statusTypeName: String


class FetchTick: ObservableObject 
    func getTicket(id: Int, userApi: String, completion: @escaping (TicketDetails) -> ()) 
        guard let url = URL(string: "URL FOR TICKET DATA") else  return 
        URLSession.shared.dataTask(with: url) (data, _, _) in
            let ticket = try! JSONDecoder().decode(TicketDetails.self, from: data!)
            DispatchQueue.main.async 
                completion(ticket)
            
        
        .resume()
    

在工单上创建新注释

class CreateTicketNote: ObservableObject 
    func CreateNoteAction(ticketId: Int, userApi: String, techNote: String) 
        
        let ticketUrl = URL(string:
            "URLFORTICKET")!

        var request = URLRequest(url: ticketUrl)
        request.httpMethod = "POST"
        
        
        let json: [String: Any] = [
            "noteText": techNote,
            "jobticket": [
                "id": ticketId,
                "type": "Ticket"
                ]
        ]
        
        let data = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

        
        URLSession.shared.uploadTask(with: request, from: data)  (responseData, response, error) in
            if let error = error 
                print("Error making POST request: \(error.localizedDescription)")
                return
            
            
            if let responseCode = (response as? HTTPURLResponse)?.statusCode, let responseData = responseData 
                guard responseCode == 200 else 
                    print("Invalid response code: \(responseCode)")
                    return
                
                
                if let responseJSONData = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments) 
                    print("Response JSON data = \(responseJSONData)")
                
            
        .resume()
    

详细视图

struct DetailsView: View 
    @ObservedObject var ticketStatusAction = TicketStatusAction()
    @ObservedObject var createTicketNote = CreateTicketNote()
    @State var ticket: [TicketDetails] = []
    @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 = ""

    var id: Int
    var displayClient: String
    @Binding var userApi: String
    
    var body: some View 
        ScrollView(.vertical)
            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)

                           
                Divider()

                Text("Most Recent Note")
                .fontWeight(.bold)
                .padding()
                
                if !ticket.isEmpty 
                    Text(clearMarkdown(on: self.ticket.first?.notes.first?.prettyUpdatedString ?? ""))
                           .padding()
                    Text(clearMarkdown(on: self.ticket.first?.notes.first?.mobileNoteText ?? ""))
                           .padding()
                    .fixedSize(horizontal: false, vertical: true)
                       
            
            .onAppear 
                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 = ""
                            ,
                        .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)
                            ,
                        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)
                            ,
                        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)
                            ,
                        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)
                            ,
                        secondaryButton: .cancel())
                
                Spacer()
            
        
    

场景 #2 - 我正在查看票证列表,并希望确保没有其他票证已打开。我想拉刷新列表并显示任何新票。

场地门票

struct TicksByLocation: Codable, Identifiable 
    public var id: Int
    public var type: String
    public var displayClient: String
    public var shortDetail: String


class FetchTicksByLocation: ObservableObject 
    
    func getTicksByLocation(ticketLocation: String, userApi: String, completion: @escaping (([TicksByLocation]) -> ()))
    
    guard let url = URL(string: "URLFORTICKETS") else 
        return
    
        URLSession.shared.dataTask(with: url) (data, response, error) in
            do 
                if let tickData = data 
                    let decodedData = try JSONDecoder().decode([TicksByLocation].self, from: tickData)
                    DispatchQueue.main.async 
                        completion(decodedData)
                    
                 else 
                    print("No data")
                
             catch 
                print("Error")
            
        .resume()
    

按位置显示门票

struct ShowLocationView: View 
    @Binding var ticketLocation: String
    @Binding var userApi: String
    @Binding var tickets: [TicksByLocation]
    @State var openTickets: [TicksByStatusAndLocation] = []
    @State var green = Color.green
    @State var yellow = Color.yellow
    @State var blue = Color.blue
    @State var purple = Color.purple



    var body: some View 
        NavigationView
            List(tickets)  tick in
                VStack(alignment: .leading, spacing: 10) 
                    Text("\(tick.id)")
                        .font(.system(size: 11))
                        .cornerRadius(5)
                    
                    Text(tick.displayClient)

                    Text(tick.shortDetail)
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                    NavigationLink(destination: DetailsView(
                        id: tick.id,
                        displayClient: tick.displayClient,
                        userApi: self.$userApi
                    )) 
                       Text("See Details")
                        .foregroundColor(Color.blue)

                        
                    Divider()
                    
            
            .navigationBarTitle(
                Text("\(ticketLocation) - All (\(tickets.count))"),
                displayMode: .inline
            )
            .navigationBarItems(
                trailing:
                Text("Filter")
                .contextMenu 
                 NavigationLink(
                    destination: ShowLocationAndStatusView(
                        ticketLocation: $ticketLocation,
                        userApi: $userApi,
                        status: "Open",
                        color: green
                        )
                    ) 
                 Text("Open")
                   
                    
                NavigationLink(
                   destination: ShowLocationAndStatusView(
                       ticketLocation: $ticketLocation,
                       userApi: $userApi,
                       status: "Pending",
                       color: yellow
                       )
                   ) 
                Text("Pending")
                  
                    
                NavigationLink(
                   destination: ShowLocationAndStatusView(
                       ticketLocation: $ticketLocation,
                       userApi: $userApi,
                       status: "Depot",
                       color: blue
                       )
                   ) 
                Text("Depot")
                  
                    
                NavigationLink(
                   destination: ShowLocationAndStatusView(
                       ticketLocation: $ticketLocation,
                       userApi: $userApi,
                       status: "Agi",
                       color: purple
                       )
                   ) 
                Text("Agi")
                  
                    
                
            )
        
    

【问题讨论】:

【参考方案1】:

解决方案 #1

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 = ""
                            ])
                   
               

解决方案 #2

使用https://github.com/phuhuynh2411/SwiftUI-PullToRefresh

import SwiftUI
import UIKit

struct ShowSelectedStatus: View 
    @State private var isShowing = false
    @Binding var userApi: String
    @Binding var tickets: [TicksByStatus]
    var ticketStatus: String
    var color: Color

    var body: some View 
        NavigationView
            List(tickets)  tick in
                VStack(alignment: .leading, spacing: 10) 
                    
                    Text(tick.displayClient)
                    .padding(10)
                    .background(self.color)
                    .cornerRadius(5)

                    Text(tick.shortDetail)
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                    NavigationLink(destination: DetailsView(
                        id: tick.id,
                        displayClient: tick.displayClient,
                        userApi: self.$userApi
                    )) 
                       Text("See Details")
                        .foregroundColor(Color.blue)

                        
                    Divider()
                    
            
            .pullToRefresh(isShowing: $isShowing) 
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) 
                    self.isShowing = false
                
                FetchTicksByStatus().getTicksByStatus(
                    ticketStatus: self.ticketStatus,
                    userApi: self.userApi
                 ) 
                    (ticks) in self.tickets = ticks
                    
            
            .navigationBarTitle(
                Text("\(ticketStatus) (\(tickets.count))"),
                displayMode: .inline
            )
        
    

【讨论】:

以上是关于刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式从 SwiftUI 列表中删除行并刷新列表视图?

如何使用 SwiftUI 从文本中提取 Hashtags?

SwiftUI 4.0(iOS 16+)使用新的 Gauge 视图极简实现仪表盘外观

SwiftUI 4.0(iOS 16+)使用新的 Gauge 视图极简实现仪表盘外观

如何从结构视图数组中提取状态? SwiftUI

H.264从SPS中提取视频宽高