swiftswiftuiservervapor

Postman GET Returns Successful but SwiftUI App GET Returns Decode Error


I am running a vapor server. My app network requests are all working perfectly except for getOrders. When running the request on Postman it returns successfully. Can anyone see what Postman does to get a response that I am failing to do?

This is the error I'm currently getting:

typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "dateOrdered", intValue: nil)], debugDescription: "Expected to decode Double but found a string instead.", underlyingError: nil))

Here's my network request code:

func getOrders() async throws -> [Order] {
        
        let session = URLSession.shared
        var request = URLRequest(url: URL(string: "\(API.url)/orders/")!)
        
        request.httpMethod = "GET"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("Bearer \(token!.value)", forHTTPHeaderField: "Authorization")
            
            // Make the request
        let (data, response) = try await session.data(for: request)
        print(data)
        
        print(response)
            // Ensure we had a good response (status 200)
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                throw GetOrdersError.unableToGetOrders
            }
        print(httpResponse.statusCode)
            
        let decoder = JSONDecoder()
        let orders = try decoder.decode([Order].self, from: data)
        
        print(orders)
        return orders
    }

Here's the Order model in my app:

struct Order: Identifiable, Codable {
    
    var id: UUID?
    var residentID: UUID
    var house: String
    var dateOrdered: Date
    var allPurposeCleaner: Bool
    var airFreshener: Bool
    var laundryPods: Bool
    var dishPods: Bool
    var dishSoap: Bool
    var windex: Bool
    var cloroxWipes: Bool
    var paperTowels: Bool
    var toiletPaper: Bool
    var toiletBowlCleaner: Bool
    var thrift: Bool
    var swifferPads: Bool
    var trashBags: Bool
    var isOrderCompleted: Bool
    
    static let houses = ["Wolf Pack", "Crow House", "Raptor House", "Lemur House", "Orca Pod", "Gorilla House", "Bison House", "Lion's Den"]
    
    
    init(id: UUID?, residentID: UUID, house: String, dateOrdered: Date, allPurposeCleaner: Bool, airFreshener: Bool, laundryPods: Bool, dishPods: Bool, dishSoap: Bool, windex: Bool, cloroxWipes: Bool, paperTowels: Bool, toiletPaper: Bool, toiletBowlCleaner: Bool, thrift: Bool, swifferPads: Bool, trashBags: Bool, isOrderCompleted: Bool) {
        
        self.id = id
        self.residentID = residentID
        self.house = house
        self.dateOrdered = dateOrdered
        self.allPurposeCleaner = allPurposeCleaner
        self.airFreshener = airFreshener
        self.laundryPods = laundryPods
        self.dishPods = dishPods
        self.dishSoap = dishSoap
        self.windex = windex
        self.cloroxWipes = cloroxWipes
        self.paperTowels = paperTowels
        self.toiletPaper = toiletPaper
        self.toiletBowlCleaner = toiletBowlCleaner
        self.thrift = thrift
        self.swifferPads = swifferPads
        self.trashBags = trashBags
        self.isOrderCompleted = isOrderCompleted
    }
}

Here's the Order model on my server:

import Foundation
import Vapor
import Fluent

final class Order: Model, Content {
    
    init() {
    }
    
    static var schema: String = "orders"
    
    init(id: UUID, residentID: UUID, house: String, dateOrdered: Date, allPurposeCleaner: Bool, airFreshener: Bool, laundryPods: Bool, dishPods: Bool, dishSoap: Bool, windex: Bool, cloroxWipes: Bool, paperTowels: Bool, toiletPaper: Bool, toiletBowlCleaner: Bool, thrift: Bool, swifferPads: Bool, trashBags: Bool, isOrderCompleted: Bool) {
            
        self.id = id
        self.residentID = residentID
        self.house = house
        self.dateOrdered = dateOrdered
        self.allPurposeCleaner = allPurposeCleaner
        self.airFreshener = airFreshener
        self.laundryPods = laundryPods
        self.dishPods = dishPods
        self.dishSoap = dishSoap
        self.windex = windex
        self.cloroxWipes = cloroxWipes
        self.paperTowels = paperTowels
        self.toiletPaper = toiletPaper
        self.toiletBowlCleaner = toiletBowlCleaner
        self.thrift = thrift
        self.swifferPads = swifferPads
        self.trashBags = trashBags
        self.isOrderCompleted = isOrderCompleted
    }
    
    @ID(key: .id)
    var id: UUID?
    
    @Field(key: "residentID")
    var residentID: UUID
    
    @Field(key: "house")
    var house: String
    
    @Field(key: "dateOrdered")
    var dateOrdered: Date
    
    @Field(key: "allPurposeCleaner")
    var allPurposeCleaner: Bool
    
    @Field(key: "airFreshener")
    var airFreshener: Bool
    
    @Field(key: "laundryPods")
    var laundryPods: Bool
    
    @Field(key: "dishPods")
    var dishPods: Bool
    
    @Field(key: "dishSoap")
    var dishSoap: Bool
    
    @Field(key: "windex")
    var windex: Bool
    
    @Field(key: "cloroxWipes")
    var cloroxWipes: Bool
    
    @Field(key: "paperTowels")
    var paperTowels: Bool
    
    @Field(key: "toiletPaper")
    var toiletPaper: Bool
    
    @Field(key: "toiletBowlCleaner")
    var toiletBowlCleaner: Bool
    
    @Field(key: "thrift")
    var thrift: Bool
    
    @Field(key: "swifferPads")
    var swifferPads: Bool
    
    @Field(key: "trashBags")
    var trashBags: Bool
    
    @Field(key: "isOrderCompleted")
    var isOrderCompleted: Bool
    
}

Here's Postman working fine:

enter image description here


Solution

  • try this approach using a specific date decoder, adjust if your dates are not iso8601:

      let decoder = JSONDecoder()
      decoder.dateDecodingStrategy = .iso8601  // <-- here
      let orders = try decoder.decode([Order].self, from: data)
    

    To use your own date format, use

    decoder.dateDecodingStrategy = .formatted(myDateFormat)