Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4 JSONSerialization.jsonObject

Tags:

json

swift

swift4

I am using Xcode 9.2 and Swift 4. How can I check if the returned json data is null? Now it gives me error Type 'Any' has no subscript members at line if json[0]["data"]

var json: NSMutableArray = []
var newsArray: NSMutableArray = []

let url = URLFactory()
var data = try! NSData(contentsOf: url.getURL()) as Data
do {
    json = try JSONSerialization.jsonObject(with: data, options: []) as! NSMutableArray
    if json[0]["data"] {
        // data is not null
    }
} catch let error as NSError {
    // handle error
}

My JSON returns something like this:

{
   "data":
   [
      {
         "news_id":123,
         "title":"title",
         "news_date":"2017-02-08 21:46:06",
         "news_url":"url",
         "short_description":"description",
         "category_id":4,
         "category_name":"Health",
         "latlng":
         [
            {
               "lat":"43.003429",
               "lng":"-78.696335"
            }
         ]
      }
      { ....
}
like image 549
Jumana Alhaddad Avatar asked Jan 06 '18 19:01

Jumana Alhaddad


People also ask

What is Jsonserialization in Swift?

An object that converts between JSON and the equivalent Foundation objects.

What is JSON object in Swift?

JSON stands for JavaScript Object Notation. JSON is a lightweight format for storing and transporting data. The JSON format consists of keys and values. In Swift, think of this format as a dictionary where each key must be unique and the values can be strings, numbers, bools, or null (nothing).

What kind of Jsonserialization have Readingoptions?

Reading OptionsSpecifies that leaf strings in the JSON object graph are mutable. Specifies that the parser allows top-level objects that aren't arrays or dictionaries. Specifies that reading serialized JSON data supports the JSON5 syntax.


1 Answers

If you're using Swift 4, I might suggest JSONDecoder:

First, define the types to hold the parsed data:

struct ResponseObject: Codable {
    let data: [NewsItem]
}

struct NewsItem: Codable {
    let newsId: Int
    let title: String
    let newsDate: Date
    let newsURL: URL
    let shortDescription: String
    let categoryID: Int
    let categoryName: String
    let coordinates: [Coordinate]

    // because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys

    enum CodingKeys: String, CodingKey {
        case newsId = "news_id"
        case title
        case newsDate = "news_date"
        case newsURL = "news_url"
        case shortDescription = "short_description"
        case categoryID = "category_id"
        case categoryName = "category_name"
        case coordinates = "latlng"
    }
}

struct Coordinate: Codable {
    let lat: String
    let lng: String
}

And then you can parse it:

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
    let responseObject = try decoder.decode(ResponseObject.self, from: data)
    print(responseObject.data)
} catch {
    print(error)
}

Clearly, if your JSON is different, you might need to change your objects accordingly (e.g. it struck me odd that latlng was an array of coordinates). Also, you can define custom init(from:) methods if you want to convert some of these strings into numbers, but I'd rather fix the JSON, instead (why are latlng returning string values rather than numeric values).

For more information, see Encoding and Decoding Custom Types.


As an aside, I'd advise against this pattern (note, this is your network request logic, but excised of NSData):

let data = try! Data(contentsOf: url.getURL())

That will retrieve the data synchronously, which can be problematic because

  • the app will be frozen while the data is being retrieved resulting in a poor UX;
  • you risk having your app killed by the watchdog process which looks for frozen apps; and
  • you don't have robust error handling and this will crash if the network request fails.

I'd suggest using URLSession:

let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
    guard let data = data, error == nil else {
        print(error ?? "Unknown error")
        return
    }

    // now parse `data` like shown above

    // if you then need to update UI or model objects, dispatch that back
    // to the main queue:

    DispatchQueue.main.async {
        // use `responseObject.data` to update model objects and/or UI here
    }
}
task.resume()
like image 129
Rob Avatar answered Sep 21 '22 22:09

Rob