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"
}
]
}
{ ....
}
An object that converts between JSON and the equivalent Foundation objects.
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).
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.
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
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With