-1

I am working on an app for iOS and I'm using Swift 4 to develop it. The app will make use of REST calls to populate pickers and tables. The code below has been used to work on calls successfully, however now, I don't receive any data. The REST has been tested with postman which gives me the expected results.

typealias JSONCompletionHandler = (Data?, Int?, Error?) -> void

static function getData(completionHandler: @escaping JSONCompletionHandler){
    let url = URL(string: "http://apiurl.com")
    let task = URLSession.shared.dataTask(with: url!) { data, response, error in
        let htttResponse = response as? HTTPURLResponse

        completeionHandler(data, httResponse?,statuscode, error)
    }
    task.resume()
}

EDIT

Below is the printout of the HTTPURLResponse

Headers {
"Cache-Control" =     (
    "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
);
Connection =     (
    "Keep-Alive"
);
"Content-Type" =     (
    "application/json; charset=utf-8"
);
Date =     (
    "Tue, 01 May 2018 15:44:27 GMT"
);
Expires =     (
    "Thu, 19 Nov 1981 08:52:00 GMT"
);
"Keep-Alive" =     (
    "timeout=5, max=100"
);
Pragma =     (
    "no-cache"
);
Server =     (
    "Apache/2.2.29 (Win32) mod_ssl/2.2.29 OpenSSL/0.9.8zf"
);
"Set-Cookie" =     (
    "ZDEDebuggerPresent=php,phtml,php3; path=/"
);
"Transfer-Encoding" =     (
    Identity
);
"X-Powered-By" =     (
    "ZendServer 8.0.2"
);
} }

apache connection log

Application

  • [02/May/2018:09:51:04 +0100] "GET /public/rest/catalogue-standard-fit/ HTTP/1.1" 200 15669
  • [02/May/2018:09:51:04 +0100] "GET /public/rest/catalogue-multi-fit/ HTTP/1.1" 200 14302
  • [02/May/2018:09:51:04 +0100] "GET /public/rest/catalogue-accessory/ HTTP/1.1" 200 216

POSTMAN

10.2.13.221 - - [02/May/2018:09:52:55 +0100] "GET /public/rest/catalogue-accessory/ HTTP/1.1" 200 216

EDIT

The data is being returned and the issue is with the method used to parse the JSON data.

I'm using the Codable approach available in swift 4 and the structures used are below along with the how the JSON data output is (from postman)

//Structures for JSON
struct PartType: Codable {
    let id: String
    let name: String
}

struct Availability: Codable {
    let id: String
    let status: String
}

struct AccessorySearch: Codable {
    let part_type = [PartType]()
    let availability = [Availability]()

}

JSON data from POSTMAN

{
"part_type": [
    {
        "id": "1",
        "name": "Type 1"
    },
    {
        "id": "2",
        "name": "Type 2"
    },
    {
        "id": "3",
        "name": "Type 3"
    }
],
"availability": [
    {
        "id": "1",
        "status": "In Stock"
    },
    {
        "id": "2",
        "status": "In Development"
    },
    {
        "id": "3",
        "status": "Not in Stock"
    }
]
}

Method used

    private func jsonDecodeString(data: Data?) {


    // array of the above structures 
    paryTypeArrayStructure.insert(PartType(id: "0", name: "Select Option"), at: 0)
    availabilityArrayStructure.insert(Availability(id: "0", status: "Select Option"), at: 0)

    let decoder = JSONDecoder()
    do {

        let decodeAccessories = try decoder.decode(AccessorySearch.self, from: data!)
        print("PART TYPE: ",decodeAccessories.part_type)
        print("AVAILABILITY: ",decodeAccessories.availability)

        let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject]

        print(json)

        for item in decodeAccessories.availability {
            print(item)
            availabilityArrayStructure.append(item)
        }

        for item in decodeAccessories.part_type {
            paryTypeArrayStructure.append(item)
        }

        //print(availabilityArrayStructure)
    } catch let jsonErr {
        print ("Failed to decode: ", jsonErr)
        displayErrorMessage(message: "[RCCA003 JSONException occurred when reading the response from server]")
    }

}

I tried JSONSerilization and was able to parse the data. So now my question is, why did my initial attempt parsing fail? I apologise for any confusion, you've all been very helpful, so thank you.

EDIT - Solution found...but why did it work?

Firstly I want to say thanks to all those commenting and providing areas for research. It turns out the issue with the JSON parsing and how I setup up the structures.

//Structures for JSON
struct PartType: Codable {
    let id: String?
    let name: String?
}

struct Availability: Codable {
    let id: String?
    let status: String?
}

struct AccessorySearch: Codable {

    let part_type:  [PartType]
    let availability: [Availability]
}

The structures that will parse the part_type arrays and availability arrays have their variables set to optional. This allowed the JSON to be parsed. Can anyone shed some light on this? Why did this simple change work? Thanks again.

Patru
  • 4,481
  • 2
  • 32
  • 42
E.Mattu
  • 1
  • 4
  • Ummh, this is a really abstract question. Could you post some of your `JSONCompletionHandler` or better yet the printout of `error`, `data` (in `String` form) and `response`? – Patru May 01 '18 at 16:27
  • This code doesn't even compile. – vadian May 01 '18 at 16:30
  • There are a few issues with the above code, but as you are using HTTP have you set `Allow Arbitrary Loads` to YES in info.plist under App Transport Security Settings? https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http – rbaldwin May 01 '18 at 16:33
  • Thanks for the quick replies. @Patru the issues is that the task completes with a status 200. however the returned JSON data is empty and when I make the same call in Postman I get the data I want. Unfortunately, I can't give you the URL to test the API for yourselves, so I am checking to see if there are any errors in the above code. – E.Mattu May 01 '18 at 16:41
  • @rbaldwin this error hasn't appeared, the issue I'm experiencing is the empty returned JSON data – E.Mattu May 01 '18 at 16:46
  • Are you sure that your request is the exact same? E.g. are you meant to be doing a `POST` or `GET` request? Use something like AFNetworking/Alamofire to help you perform such requests and make sure that they contain everything you need. – Guy Kogus May 01 '18 at 17:08
  • You need to set the verb ("POST", "GET", etc) and the mime type ("application/json") in your session. – u84six May 01 '18 at 18:11
  • Could you check with the server log if the request from Postman is the same (verb, URL, parameters) as your iOS request? There is probably something different on the server if he does not react in the same way. – Patru May 01 '18 at 19:21
  • @u84six I have added the print of the HTTPURLResponse above. I assumed that using the URLSession would default to a GET call – E.Mattu May 02 '18 at 08:15
  • @Patru I think I've found the logs you're looking for. The last bit appears to be the bytes returned and they are exactly the same for Postman and the app connections. I have tested the REST code and see the data being retrieved however the data is not received by the app. Could this be an issue with JSON parsing? – E.Mattu May 02 '18 at 09:09
  • Potentially. I strongly recommend using something like https://github.com/Alamofire/Alamofire to make sure that you're performing the request correctly. With that framework included you can call `SessionManager.default.request(url).responseJSON { ... }` and get clean error/success responses. – Guy Kogus May 02 '18 at 09:20
  • @GuyKogus I will look at using Alamofire for a future project. The issue was the method used to parse the data. I have added the further information above. – E.Mattu May 02 '18 at 10:25

2 Answers2

0

I used the following Playground to illustrate your (small) glitch:

import Cocoa

let jsonData = """
{
"part_type": [
    {
        "id": "1",
        "name": "Type 1"
    },
    {
        "id": "2",
        "name": "Type 2"
    },
    {
        "id": "3",
        "name": "Type 3"
    }
],
"availability": [
    {
        "id": "1",
        "status": "In Stock"
    },
    {
        "id": "2",
        "status": "In Development"
    },
    {
        "id": "3",
        "status": "Not in Stock"
    }
]
}
""".data(using: .utf8)!

struct PartType: Codable {
    let id: String
    let name: String
}

struct Availability: Codable {
    let id: String
    let status: String
}

struct AccessorySearch0: Codable {
    let part_type = [PartType]()
    let availability = [Availability]()
}

struct AccessorySearch: Codable {
    let part_type: [PartType]
    let availability: [Availability]
}

do {
    let accessory = try JSONDecoder().decode(AccessorySearch.self, from: jsonData)
    print(accessory)
} catch {
    print(error)
}

I used your AccessorySearch from your second version combined with PartType and Availability from your first version. This parses perfectly, so I think all is well.

However your first version of AccessorySearch (which I put as AccessorySearch0 for easy comparison) is a different animal. It does not just define a class, it also initializes it. Since it uses let to define its properties this first assignment cannot be changed anymore and all you will get from the JSONDecoder will an object with empty arrays.

As I said, this was just a small glitch.

Patru
  • 4,481
  • 2
  • 32
  • 42
  • that explains the glitch I was getting. Thanks so much, I'll be careful of how I define variables in the future. – E.Mattu May 08 '18 at 13:18
  • It is customary on StackOverflow to accept an answer if it solves your problem (and it will even earn you some points the first time :-) – Patru May 08 '18 at 18:59
  • sorry about that – E.Mattu May 09 '18 at 15:02
  • No need to be sorry. Stackoverflow is a site wich promotes learning. Ask questions, get better and start answering some. Everybody around her understands that it takes time to do so. – Patru May 09 '18 at 19:03
0

Please you should isolate your problem. First try to parse the response.. Using debugger data may be indicate zero bytes , do not take this a a reference. Just parse the response inside try block

do {
  //full dictionary response
  let json = 
  try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as?Dictionary<String, AnyObject>
  //doe son parse correctly ?
catch {
  print(error.localizedDescription)
}

if you inspect the instance of URLResponse with expectedContentLength variable you should see the content of the response.

Boris Ch.F
  • 156
  • 5