Elaborating the Problem in depth with code
I have a data model (API response) using which I am creating a list. The list items should also display their details in detail view. The problem is details of list items are coming from a different API other than the API used for creating the list. The id of one item is passed to API which in response provides the details of the item.
This is the data model(including items only problem specific):
struct TrackSample : Codable, Identifiable {
let id = UUID()
let success : Bool
let message : String
let trackResponse : [TrackResponse]
enum CodingKeys: String, CodingKey{
case success = "IsSuccess"
case message = "Message"
case trackResponse = "ResponseData"
}}
struct TrackResponse : Codable, Identifiable {
let id = UUID()
let patientID : String
let name : String
let testCount : Int
//..some more//
enum CodingKeys: String, CodingKey {
case patientID = "PatientId"
case name = "Name"
case status = "Status"
case testCount = "NoOfTests"
}}
ViewModel to fetch API response ( TrackResource() is a different class which implements the networking call):
class TrackViewModel : ObservableObject
{
@Published var trackReport = [TrackResponse]()
@Published var navigate:Bool = false
//other var
private let trackResource = TrackResource()
func getTrack()
{
//code to get data based on which button is clicked in Options
screen.
There are five options to choose:
if else statement follows//
centerID = "100"
let trackRequest = TrackRequest(CenterId:centerID,
SearchText:searchText, StartDate:startDate, EndDate:endDate)
trackResource.track(trackRequest: trackRequest)
{
response in
if(response?.success==true)
{
DispatchQueue.main.async {
self.navigate = true
self.trackReport = response?.trackResponse ?? []
}
}
else
{
DispatchQueue.main.async {
self.errorMessage = response?.message ?? "No Data"
// self.isPresentingAlert
}
}}}}
The view YdaySample which represents the list :
struct YdaySample: View {
@ObservedObject var tracking : TrackViewModel
var body: some View {
NavigationView{
List{
ForEach(tracking.trackReport)
{ truck in
NavigationLink(destination: TrackDetail(track:truck))
{
YdayRow(truck: truck)
}
}
if(tracking.trackReport.isEmpty){
Text("No Record Found !")
//formatting
}}}}}
struct YdaySample_Previews: PreviewProvider {
static var previews: some View {
YdaySample(tracking: TrackViewModel())
}}
The YdayRow() :
struct YdayRow: View {
var truck : TrackResponse
var body: some View {
VStack{
HStack{
Text(truck.name)
//formatting//
}
HStack{
Text("P.Id:")
//formatting//
Text(truck.patientID)
//formatting//
Spacer()
Text("Total Test:")
//formatting//
Text("\(truck.testCount)")
//formatting//
}}}}
struct YdayRow_Previews: PreviewProvider {
static var previews: some View {
YdayRow(truck: TrackResponse(patientID: "1", name: "test",
status: "null", crmNo: "null",
recordDate: "somedate", uniqueID: "null", testCount: 4))
}
}
TrackDetail() updated:
struct TrackDetail: View {
var track: TrackResponse
@State var patientDetail: DetailResponse
var body: some View {
VStack{
HStack{
Text(track.name)
//formatting
}.frame(maxWidth: .infinity, alignment: .center)
HStack{
Text("P.Id:")
//formatting
Text(track.patientId)
//formatting
}
List{ForEach(patientDetail.detailResponse)
{detail in
HStack
{ Text("Barcode: ")
Text(detail.barcode)
}
}
}.onAppear{
Task{
do{
try await getDetail()
}catch{
Alert(title: Text("Error"),message: Text("Not Found"),
dismissButton: .cancel())
}}}}}
func getDetail() async throws{
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "xx.xxx.xx.xx"
urlComponents.path = "/api/reports/getalltests"
urlComponents.queryItems = [URLQueryItem(name: "centerId", value:
("\(668)")),URLQueryItem(name: "patientId", value: "\
(track.patientId)")]
let url = urlComponents.url
var request = URLRequest(url: url!)
print(request)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-
Type")
request.setValue("Basic xcvgjhalddjdj",forHTTPHeaderField:
"Authorization")
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data,
response, error) in
// Check if Error took place
if let error = error {
print("Error took place \(error)")
return
}
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse {
print("Response HTTP Status code: \(response.statusCode)")
}
if let data = data
{
let dataString = String(data:data,encoding: .utf8)
print(dataString!)
do{
let json = try JSONDecoder().decode(DetailResponse.self,
from: data)
print(json)
DispatchQueue.main.async {
self.patientDetail = json
}
}catch{
print("error \(error)")
}
}
};task.resume()
}}
struct TrackDetail_Previews: PreviewProvider {
static var previews: some View {
TrackDetail(track: TrackResponse(patientId: "4193716", name: "Dummy
Report HCV RNA", status: "null", crmNo: "null", recordDate: "2022-
04-15", uniqueId: "null", testCount: 10), patientDetail:
DetailResponse(success: false, message: "mess", detailResponse:
[]))
}}
print(request) is printing right URL as desired also the patientID is correct in url (http://xxx..x.x/api/reports/getalltests/centerId=668&patientId=(tapped id))
But it is throwing error in decoding saying "Authrization has been denied for this request" error keynotfound(codingkeys(stringvalue: "ResponseData", intvalue:nil), Swift.decodingerror.context(codingpath: [],debugdescription: "No value associated with key CodingKeys(stringvalue: "ResponseData", intvalue:nil)("ResponseData", underlyingError:nil)
struct DetailResponse : Codable{
let success : Bool ?
let message : String
let detailResponse : [PatResponse]
enum CodingKeys: String, CodingKey{
case success = "IsSuccess"
case message = "Message"
case patResponse = "ResponseData"
}}
struct PatResponse : Codable, Identifiable {
var barcode: String
var:id String {barcode}
let testname : String?
let packageName : String?
let status: String?
let sampleRecievedTime: String?
let recordDate: String
enum CodingKeys: String, CodingKey {
case packageName = "PackageName"
case testname = "TestName"
case status = "Status"
case sampleRecievedTime = "SampleRecievedTime"
case recordDate = "RecordDate"
case barcode = "Barcode"
}}
///////**************/////////////////// The detail view is showing name and ID as they are coming from TrackResponse but status and barcode are not as they are from different API.
When the user click/tap an item in list, the patientID and centerId is sent as query param in GET request which in response will provide the details associated with the given patientId (center ID is constant). How to implement this?
How about something like this?
struct TrackDetail: View{
var track: TrackResponse
@State var details: TrackDetails?
var body: some View{
HStack{
//Detail implementation
}.onAppear{
Task{
do{
try await doStuff()
}catch{
//Alert
}
}
}
}
func doStuff() async throws{
// pull Details from Api
}
}