skip to Main Content

I have the following function:

func executeGet( completion: @escaping (Data?, Error?) -> Void) {
    AF.request("https:URL",
               method:.get,
               headers:headers).response{ response in
        debugPrint(response)

     
        if let error = response.error {
            completion(nil, error)
        }
        else if let jsonArray = response.value as? Data{
            completion(jsonArray, nil)
        }
        
    }
}

Which is being called as follows:

executeGet() { (json, error) in
    if let error = error{
        print(error.localizedDescription)
        
    }
    else if let json = json {
        print(type(of:json))
        print(json)
        let welcome = try? JSONDecoder().decode(Welcome.self, from: json)
        print(welcome)

    }
}

But for some reason, my ‘welcome’ value always returns nil. Can anyone suggest what could’ve gone wrong? When I print(json) I’m getting ‘294 Bytes’ for some reason so clearly something went wrong before decoding, right?

EDIT: Upon Udi’s request here’s the Welcome struct

// MARK: - Welcome
struct Welcome: Codable {
    let statusCode: Int
    let messageCode: String
    let result: Result
}

// MARK: - Result
struct Result: Codable {
    let id: String
    let inputParameters: InputParameters
    let robotID: String
    let runByUserID, runByTaskMonitorID: JSONNull?
    let runByAPI: Bool
    let createdAt, startedAt, finishedAt: Int
    let userFriendlyError: JSONNull?
    let triedRecordingVideo: Bool
    let videoURL: String
    let videoRemovedAt: Int
    let retriedOriginalTaskID: String
    let retriedByTaskID: JSONNull?
    let capturedDataTemporaryURL: String
    let capturedTexts: CapturedTexts
    let capturedScreenshots: CapturedScreenshots
    let capturedLists: CapturedLists
    
    enum CodingKeys: String, CodingKey {
        case id, inputParameters
        case robotID = "robotId"
        case runByUserID = "runByUserId"
        case runByTaskMonitorID = "runByTaskMonitorId"
        case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
        case videoURL = "videoUrl"
        case videoRemovedAt
        case retriedOriginalTaskID = "retriedOriginalTaskId"
        case retriedByTaskID = "retriedByTaskId"
        case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
        case capturedTexts, capturedScreenshots, capturedLists
    }
}

// MARK: - CapturedLists
struct CapturedLists: Codable {
    let companies: [Company]
}

// MARK: - Company
struct Company: Codable {
    let position, name, location, description: String
    
    enum CodingKeys: String, CodingKey {
        case position = "Position"
        case name, location, description
    }
}

// MARK: - CapturedScreenshots
struct CapturedScreenshots: Codable {
}

// MARK: - CapturedTexts
struct CapturedTexts: Codable {
    let productName, width, patternRepeat, construction: String
    let fiber: String
    let color: JSONNull?
    let mainImage: String
    
    enum CodingKeys: String, CodingKey {
        case productName = "Product Name"
        case width = "Width"
        case patternRepeat = "Pattern Repeat"
        case construction = "Construction"
        case fiber = "Fiber"
        case color = "Color"
        case mainImage = "Main Image"
    }
}

// MARK: - InputParameters
struct InputParameters: Codable {
    let originURL: String
    let companiesSkip, companiesLimit: Int
    
    enum CodingKeys: String, CodingKey {
        case originURL = "originUrl"
        case companiesSkip = "companies_skip"
        case companiesLimit = "companies_limit"
    }
}

// MARK: - Encode/decode helpers
class JSONNull: Codable, Hashable {
    
    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool
    {
        return true
    }
    
    public var hashValue: Int {
        return 0
    }
    
    public init() {}
    
    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self,  DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

and here’s a sample of JSON response

{
  "statusCode": 200,
  "messageCode": "success",
  "result": {
    "id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
    "inputParameters": {
      "originUrl": "https://www.ycombinator.com/companies/airbnb",
      "companies_skip": 0,
      "companies_limit": 10
    },
    "robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
    "runByUserId": null,
    "runByTaskMonitorId": null,
    "runByAPI": true,
    "createdAt": 1620739118,
    "startedAt": 1620739118,
    "finishedAt": 1620739118,
    "userFriendlyError": null,
    "triedRecordingVideo": true,
    "videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
    "videoRemovedAt": 1620739118,
    "retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
    "retriedByTaskId": null,
    "capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
    "capturedTexts": {
      "Product Name": "Alexis",
      "Width": "15",
      "Pattern Repeat": "PATTERN REPEAT",
      "Construction": "Hand woven",
      "Fiber": "100% Wool",
      "Color": null,
      "Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
    },
    "capturedScreenshots": {
      "top-ads": {
        "id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
        "name": "Top ads",
        "src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
        "width": 600,
        "height": 120,
        "x": 201,
        "y": 142,
        "deviceScaleFactor": 1.2,
        "full": "page",
        "comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
        "diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
        "changePercentage": 20,
        "diffThreshold": 5,
        "fileRemovedAt": 1620739118
      }
    },
    "capturedLists": {
      "companies": [
        {
          "Position": "1",
          "name": "Airbnb",
          "location": "San Francisco, CA, USA",
          "description": "Book accommodations around the world."
        },
        {
          "Position": "2",
          "name": "Coin base",
          "location": "San Francisco, CA, USA",
          "description": "Buy, sell, and manage crypto currencies."
        },
        {
          "Position": "3",
          "name": "DoorDash",
          "location": "San Francisco, CA, USA",
          "description": "Restaurant delivery."
        }
      ]
    }
  }
}

EDIT2: Upon Rob’s suggestion, I tried dotrycatch, as follows:

executeGet() { (json, error) in
    if let error = error{
        print(error.localizedDescription)
        
    }
    else if let json = json {
        print(type(of:json)) // Data
        print(json)   // 2479 Bytes
        do{
            var welcome = try JSONDecoder().decode(Welcome.self, from: json)
            print(welcome)
        }
        catch {
            print(error)
        }
        
    }
}

Which reports the error:

keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))

2

Answers


  1. Your error was reportedly:

    keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))

    That points you precisely to where the decoding failed. There is apparently no key called companies_skip in result ยป inputParameters. Now, you don’t show us the full response you actually received, so it is hard to be precise. But we can infer from this error that the response does not precisely match your sample JSON, but rather, the companies_skip key is not present.

    We might infer from the name, inputParameters, that your request URL (which, again, you have not shared with us) may possibly need to supply that parameter. Or, alternatively, perhaps that parameter name shouldn’t be marked as a required sub key of the inputParameters structure (e.g., you might want to make it an optional).

    Regardless of the particulars, this is the process. If decoding fails, look at the complete error object, and it will tell you where it had problems. Note, if there are multiple decoding problems, the error will only report the first one, so do not be surprised if it takes a few times and different queries to resolve all of the issues. The first time you start decoding a particular request, resolving all of the potential discrepancies may be an iterative process.

    Login or Signup to reply.
  2. The string response you show in your comment, means you get a valid response from the server,
    and so you should be able to decode it with the following models.

    Use @vadian answer to your previous question :
    Unable to parse JSON data properly from Alomafire

    Here are the test code and models to decode the response into a set of structs.

    Note you will have to consult the server doc to determine which properties are Optional and adjust the code (i,e put ?)
    where nessesary .

    struct ContentView: View {
        @State var welcome: WelcomeResponse?
        
        var body: some View {
            VStack {
                if let response = welcome {
                    Text(response.messageCode)
                    Text("(response.statusCode)")
                    ForEach(response.result.capturedLists.companies) { item in
                        Text(item.description)
                    }
                }
            }
            .onAppear {
                let json = """
                {
                 "statusCode": 200,
                  "messageCode": "success",
                 "result": {
                  "id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
                  "inputParameters": {
                  "originUrl": "https://www.ycombinator.com/companies/airbnb",
                  "companies_skip": 0,
                  "companies_limit": 10
                },
                "robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
                "runByUserId": null,
                "runByTaskMonitorId": null,
                "runByAPI": true,
                "createdAt": 1620739118,
                "startedAt": 1620739118,
                "finishedAt": 1620739118,
                "userFriendlyError": null,
                "triedRecordingVideo": true,
                "videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
                "videoRemovedAt": 1620739118,
                    "retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
                    "retriedByTaskId": null,
                    "capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
                "capturedTexts": {
                  "Product Name": "Alexis",
                  "Width": "15",
                  "Pattern Repeat": "PATTERN REPEAT",
                  "Construction": "Hand woven",
                  "Fiber": "100% Wool",
                  "Color": null,
                  "Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
                },
                "capturedScreenshots": {
                  "top-ads": {
                    "id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
                    "name": "Top ads",
                    "src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
                    "width": 600,
                    "height": 120,
                    "x": 201,
                    "y": 142,
                    "deviceScaleFactor": 1.2,
                    "full": "page",
                    "comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
                    "diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
                    "changePercentage": 20,
                    "diffThreshold": 5,
                    "fileRemovedAt": 1620739118
                  }
                },
                "capturedLists": {
                  "companies": [
                    {
                      "Position": "1",
                      "name": "Airbnb",
                      "location": "San Francisco, CA, USA",
                      "description": "Book accommodations around the world."
                    },
                    {
                      "Position": "2",
                      "name": "Coin base",
                      "location": "San Francisco, CA, USA",
                      "description": "Buy, sell, and manage crypto currencies."
                    },
                    {
                      "Position": "3",
                      "name": "DoorDash",
                      "location": "San Francisco, CA, USA",
                      "description": "Restaurant delivery."
                    }
                  ]
                    }
                    }
                     }
                """
                // simulated API data from the server
                let data = json.data(using: .utf8)!
                do {
                    let results = try JSONDecoder().decode(WelcomeResponse.self, from: data)
                    welcome = results
                    print("n---> results: (results) n")
                } catch {
                    print("n---> decoding error: n (error)n")
                }
            }
        }
    }
    
    // MARK: - WelcomeResponse
    struct WelcomeResponse: Codable {
        let statusCode: Int
        let messageCode: String
        let result: Result
    }
    
    // MARK: - Result
    struct Result: Codable {
        let id: String
        let inputParameters: InputParameters
        let robotID: String
        let runByUserID, runByTaskMonitorID: String?
        let runByAPI: Bool
        let createdAt, startedAt, finishedAt: Int
        let userFriendlyError: String?
        let triedRecordingVideo: Bool
        let videoURL: String
        let videoRemovedAt: Int
        let retriedOriginalTaskID: String
        let retriedByTaskID: String?
        let capturedDataTemporaryURL: String
        let capturedTexts: CapturedTexts
        let capturedScreenshots: CapturedScreenshots
        let capturedLists: CapturedLists
        
        enum CodingKeys: String, CodingKey {
            case id, inputParameters
            case robotID = "robotId"
            case runByUserID = "runByUserId"
            case runByTaskMonitorID = "runByTaskMonitorId"
            case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
            case videoURL = "videoUrl"
            case videoRemovedAt
            case retriedOriginalTaskID = "retriedOriginalTaskId"
            case retriedByTaskID = "retriedByTaskId"
            case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
            case capturedTexts, capturedScreenshots, capturedLists
        }
    }
    
    // MARK: - CapturedLists
    struct CapturedLists: Codable {
        let companies: [Company]
    }
    
    // MARK: - Company
    struct Company: Identifiable, Codable {
        let id = UUID()
        let position, name, location, description: String
        
        enum CodingKeys: String, CodingKey {
            case position = "Position"
            case name, location, description
        }
    }
    
    // MARK: - CapturedScreenshots
    struct CapturedScreenshots: Codable {
        let topAds: TopAds
        
        enum CodingKeys: String, CodingKey {
            case topAds = "top-ads"
        }
    }
    
    // MARK: - TopAds
    struct TopAds: Codable {
        let id, name: String
        let src: String
        let width, height, x, y: Int
        let deviceScaleFactor: Double
        let full, comparedToScreenshotId: String
        let diffImageSrc: String
        let changePercentage, diffThreshold, fileRemovedAt: Int
    }
    
    // MARK: - CapturedTexts
    struct CapturedTexts: Codable {
        let productName, width, patternRepeat, construction: String
        let fiber: String
        let color: String?
        let mainImage: String
        
        enum CodingKeys: String, CodingKey {
            case productName = "Product Name"
            case width = "Width"
            case patternRepeat = "Pattern Repeat"
            case construction = "Construction"
            case fiber = "Fiber"
            case color = "Color"
            case mainImage = "Main Image"
        }
    }
    
    // MARK: - InputParameters
    struct InputParameters: Codable {
        let originUrl: String
        let companiesSkip: Int?
        let companiesLimit: Int?
        
        enum CodingKeys: String, CodingKey {
            case originUrl
            case companiesSkip = "companies_skip"
            case companiesLimit = "companies_limit"
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search