Swift JSON values - json

I am new to iOS development and wanting some help here.
I have a JSON output from a webservice and I want to display the details in a custom table view cell. Actually, I am following a tutorial here: https://www.youtube.com/watch?v=ea6_a_zbQrY
In that tutorial, the JSON output is as follows:-
{
"actors": [
{
"name": "Brad Pitt",
"description": "William Bradley 'Brad' Pitt is an American actor and film producer. He has received a Golden Globe Award, a Screen Actors Guild Award, and three Academy Award nominations in acting categories",
"dob": "December 18, 1963",
"country": "United States",
"height": "1.80 m",
"spouse": "Jennifer Aniston",
"children": "Shiloh Nouvel Jolie-Pitt, Maddox Chivan Jolie-Pitt",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
"name": "Tom Cruise",
"description": "Tom Cruise, is an American film actor and producer. He has been nominated for three Academy Awards and has won three Golden Globe Awards. He started his career at age 19 in the 1981 film Endless Love.",
"dob": "July 3, 1962",
"country": "United States",
"height": "1.70 m",
"spouse": "Katie Holmes",
"children": "Suri Cruise, Isabella Jane Cruise, Connor Cruise",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
"name": "Johnny Depp",
"description": "John Christopher 'Johnny' Depp II is an American actor, film producer, and musician. He has won the Golden Globe Award and Screen Actors Guild award for Best Actor.",
"dob": "June 9, 1963",
"country": "United States",
"height": "1.78 m",
"spouse": "Lori Anne Allison",
"children": "Lily-Rose Melody Depp, John 'Jack' Christopher Depp III",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
},
My own JSON output are as follows:
[{"ID":"5662","Subject":"EXAM [JUNE 17 SEMESTER]","Course":"UNITAR","Lecturer":"EXAM OFFICER","CTime":"9:00AM-5:30PM","Venue":"10.03","TDate":"2017-09-04"},{"ID":"10314","Subject":"FAB","Course":"CAT","Lecturer":"DR CHONG","CTime":"9:00AM-12:00PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10317","Subject":"FMA","Course":"CAT","Lecturer":"GS ONG","CTime":"9:00AM-12:00PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10318","Subject":"FFA","Course":"CAT","Lecturer":"MARGARET","CTime":"1:00PM-4:00PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10319","Subject":"MA1","Course":"CAT","Lecturer":"GS ONG","CTime":"1:00PM-4:00PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10320","Subject":"P5","Course":"ACCA","Lecturer":"SPENCER","CTime":"6:15PM-9:45PM","Venue":"THEATRE ROOM 1 [LV 9]","TDate":"2017-09-04"},{"ID":"10324","Subject":"F8","Course":"ACCA","Lecturer":"MIKE KEE","CTime":"6:15PM-9:45PM","Venue":"9.02","TDate":"2017-09-04"},{"ID":"10325","Subject":"F2","Course":"ACCA","Lecturer":"GS ONG","CTime":"6:15PM-9:45PM","Venue":"9.09","TDate":"2017-09-04"},{"ID":"10326","Subject":"F4","Course":"ACCA","Lecturer":"HEMA","CTime":"6:15PM-9:45PM","Venue":"9.13","TDate":"2017-09-04"},{"ID":"11413","Subject":"M4","Course":"TG","Lecturer":"LAI WS","CTime":"7:00PM-10:00PM","Venue":"9.01","TDate":"2017-09-04"}]
Here is the code from the tutorial to parse the JSON values from the tutorial:
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "actors"))
if let actorArray = jsonObj!.value(forKey: "actors") as? NSArray {
for actor in actorArray{
if let actorDict = actor as? NSDictionary {
if let name = actorDict.value(forKey: "name") {
self.nameArray.append(name as! String)
}
if let name = actorDict.value(forKey: "dob") {
self.dobArray.append(name as! String)
}
if let name = actorDict.value(forKey: "image") {
self.imgURLArray.append(name as! String)
}
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}
How do I modify this code as I don't have "actors" key in my JSON. Can someone guide me how to change this part?

This is one of the worst codes I've ever seen. Almost everything is wrong or a very bad programming habit.
The biggest mistakes are:
No error handling at all.
The usage of Foundation (NSArray / NSDictionary) rather than native collection types.
The usage of multiple string arrays rather than one custom struct / class as data model.
The forced unwrapping of the values rather than handling the optionals safely.
The usage of valueForKey rather than dedicated objectForKey or key subscription.
First of all create a struct as data model and one array as data source
struct Schedule {
let id, subject, course, lecturer, cTime, venue, tDate : String
}
var schedules = [Schedule]()
Assuming all values won't be changed the struct members are declared as constants (let). You get the memberwise initializer for free.
Reading JSON is very easy. There are only two collection types, array ([]) and dictionary ({}).
This JSON is an array of dictionaries ([{ .. }, { ...}]) . All keys and values are strings. The appropriate (native) Swift type is [[String:String]]. The code parses the JSON and assigns an empty string in case one of the keys does not exist.
func downloadJson(with urlString : String) {
guard let url = URL(string: urlString) else { print("bad URL"); return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let connectionError = error {
print(connectionError)
return
}
do {
if let scheduleArray = try JSONSerialization.jsonObject(with: data!) as? [[String:String]] {
for item in scheduleArray {
self.schedules.append(Schedule(id: item["ID"] ?? "",
subject: item["Subject"] ?? "",
course: item["Course"] ?? "",
lecturer: item["Lecturer"] ?? "",
cTime: item["CTime"] ?? "",
venue: item["Venue"] ?? "",
tDate: item["TDate"] ?? ""))
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
In the table view in cellForRow you can simply write
let schedule = schedules[indexPath.row]
aLabel.text = schedule.id
anotherLabel.text = schedule.subject
...

First ignore the JSON contents, and instead, think of it as an array of objects called as course.
[
{
"ID": "",
"Subject": "",
"Course": "",
"Lecturer": "",
"CTime": "",
"Venue": "",
"TDate": ""
},
...
]
So first you need to parse your JSON as an array. Let's call it as coursesArray.
if let coursesArray = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray
{
for course in CoursesArray
{
// course is a json object. So get it into a dictionary so that
// we can access the values in the course.
if let courseDict = course as? NSDictionary
{
// Now we can print the remaining properties of the course
let id = courseDict.value(forKey: "ID")
let subject = courseDict.value(forKey: "Subject")
let courseName = courseDict.value(forKey: "Course")
let lecturer = courseDict.value(forKey: "Lecturer")
let cTime = courseDict.value(forKey: "CTime")
let venue = courseDict.value(forKey: "Venue")
let tDate = courseDict.value(forKey: "TDate")
// Print them, or use them in any way you like now.
}
}
}
That should do about the extraction of the data. To be able to use these, you'll need to append them to other arrays, and reload the table. I'm leaving it to you.
Hope this helps.

Try using following code , take json object as [[String:Any]] and loop through all the present dictionaries in It to get all values you require
//Two arrays to store your data and use it as result
var IDs = [String]()
var Subjects = [String]()
//Your function
func downloadJsonWithURL() {
let url = URL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [[String:Any]] {
print(jsonObj)
//Loop through all the keys present in the dictionaries inside
for key in jsonObj{
//Need to provide required key to check and loop in it
let ID = key["ID"]
let Subject = key["Subject"]
//Append values in Arrays declared above
self.IDs.append(ID as! String)
self.Subjects.append(Subject as! String)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}

In the tutorial the author's data format is dictionary of arrays and each array is of type dictionary so, in the code first author converted json data into NSDictionary then accessed array with actor keyword.
But all this steps are not necessary in your case, because your data is directly in an array of dictionaries. So first convert your data as NSArray and then convert each record of it as NSDictionary as shown in below code snippet.
func downloadJsonWithURL() {
let url = URL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [[String:String]] {
print(jsonObj)
for student in jsonObj{
if let studentDict = student as? [String:String] {
if let id = studentDict["ID"] ?? "" {
self.idArray.append(id)
}
if let subject = actorDict["Subject"] ?? "" {
self.subArray.append(subject)
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}

Related

Parsing a local JSON file into Coredata leads to an error

I'm trying to parse a local JSON file which contains 3 dictionaries and each has its own array in Coredata, but I keep getting the following error:
Could not cast value of type '__NSDictionaryM' (0x7fff86b93f10) to 'NSArray' (0x7fff86b92430).
I'm using the following code:
private func preloadData() {
// Get a reference to User Defaults
let defaults = UserDefaults.standard
// Get a reference to the persitent container
let context = persistentContainer.viewContext
// Check if this is the first launch
if defaults.bool(forKey: Constants.PRELOAD_DATA) == false {
// Parse the Json
let path = Bundle.main.path(forResource: "JSONProjectFinal", ofType: "json")
// Check that path isn't nil
guard path != nil else { return }
// Create a URL
let url = URL(fileURLWithPath: path!)
do {
// Get the data
let data = try Data(contentsOf: url)
// Attempt to parse the Json Array
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [[String:Any]]
print(jsonArray)
// Loop through the Json array
for d in jsonArray {
// Create the book objects and populate them
// Populate the Fantasy books
let f = Fantasy(context: context)
f.name = d["name"] as? String
f.imagename = d["imagename"] as? String
f.date = d["date"] as? String
f.summary = d["summary"] as? String
// Populate the Adventure books
let a = Adventure(context: context)
a.name = d["name"] as? String
a.imagename = d["imagename"] as? String
a.date = d["date"] as? String
a.summary = d["summary"] as? String
// Populate the SciFi books
let sci = SciFi(context: context)
sci.name = d["name"] as? String
sci.imagename = d["imagename"] as? String
sci.date = d["date"] as? String
sci.summary = d["summary"] as? String
}
} catch { return }
And here's a snippet of the JSON file
{
"fantasy": [
{
"name": "Fellowship of the Ring",
"imagename": "lotr",
"date": "24 July 1964",
"summary": "The Fellowship of the Ring is the first of three volumes in The Lord of the Rings, an epic set in the fictional world of Middle-earth. The Lord of the Rings is an entity named Sauron, the Dark Lord, who long ago lost the One Ring that contains much of his power. His overriding desire is to reclaim the Ring and use it to enslave all of Middle-earth"
},
{
"name": "Harry Potter and the Deathly Hallows",
"imagename": "harrypotter",
"date": "21 July 2007",
"summary": "Without the guidance and protection of their teachers, Harry, Ron and Hermione set out on a mission to destroy the horcruxes, which are the sources of Voldemort's immortality. Although they must trust each other more than ever, forces of darkness threaten to separate them. Voldemort's Death Eaters have taken control of the Ministry of Magic and Hogwarts, and they are looking for Harry as he and his friends prepare for the final confrontation."
}
]
}
So, how should I go about this? Should I cast this as a dictionary of arrays?
I'm fairly new to programming in general so any help would be greatly appreciated!
Your root is a dictionary not an array so
guard let res = try JSONSerialization.jsonObject(with: data, options:[:]) as? [String:Any] ,
let jsonArray = res["fantasy"] as? [[String:Any]] else { return }

parsing YouTube json with JSONSerialization

I have trouble parsing json data from YouTube api with JSONSerialization. when I try to fetch I return error. this is my code
this is json data I want to parse, I want to get video id, url, title, and desctiption
{
"kind": "youtube#searchListResponse",
"etag": "\"j6xRRd8dTPVVptg711_CSPADRfg/mBDPbwkuU2lLUxWHYPI1X54CUwQ\"",
"nextPageToken": "CAUQAA",
"regionCode": "ID",
"pageInfo": {
"totalResults": 3552,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"j6xRRd8dTPVVptg711_CSPADRfg/73cXngXOrGm_Bt7McNY945A6koc\"",
"id": {
"kind": "youtube#video",
"videoId": "-0ZZzOuuV3c"
},
"snippet": {
"publishedAt": "2018-09-20T08:00:01.000Z",
"channelId": "UCjHoMXZXAIx_QHgk9qsAJ-Q",
"title": "HADIST-HADIST PALSU TAPI POPULER - Ustadz Adi Hidayat LC MA",
"description": "\"Kebersihan sebagian dari iman\". Sering dogn mendengar ucapan ini. Sebagian orang mengatakan ini hadist dari Rasulullah. Tapi taukah kamu, bahwa ini ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/-0ZZzOuuV3c/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Audio Dakwah",
"liveBroadcastContent": "none"
}
}
This is my code to parse json I create it in youtubeAPI struct, when I try to run it invalidJSONData
static func videos(fromJSON data: Data) -> VideoResults {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
guard
let jsonDictionary = jsonObject as? [AnyHashable: Any],
let itemsArray = jsonDictionary["items"] as? [[String: Any]]
else {
return .failure(YoutubeError.invalidJSONData)
}
var finalItems = [Video]()
for itemJSON in itemsArray {
if let item = video(fromJSON: itemJSON) {
finalItems.append(item)
}
}
if finalItems.isEmpty && !itemsArray.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
}
private static func video(fromJSON json: [String: Any]) ->Video? {
guard
let videoID = json["videoID"] as? String,
let title = json["title"] as? String,
let description = json["description"] as? String,
let stringURL = json["url"] as? String,
let url = URL(string: stringURL)
else {
return nil
}
return Video(videoID: videoID, title: title, description: description, url: url)
}
with the question I have post. finally I have the answer for my problem. hopefully this will help other developer who want to parse json with JSONSerialization, with such a complex data like YouTube api or other api. so this is my answer.
Since items is an array and inside item have another nested data such as snippet, so I need to iterate snippet to get the data. This is the code.
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
var finalItems = [Video]()
if let items = jsonObject?["items"] as? Array<Dictionary<String, Any>> {
for item in items {
if
let id = item["id"] as? [String: String],
let snippet = item["snippet"] as? [String: Any] {
if
let videoID = id["videoId"],
let description = snippet["description"] as? String,
let title = snippet["title"] as? String,
let thumbnails = snippet["thumbnails"] as? [String: Any],
let medium = thumbnails["medium"] as? [String: Any],
let urlString = medium["url"] as? String,
let url = URL(string: urlString) {
finalItems.append(Video(videoID: videoID, title: title, description: description, url: url))
}
}
}
}
print(finalItems.count)
if finalItems.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
And the the next step is to clean up the code for parsing that iterate the item, and put it in extension in Video Model, I was found a good article from apple developer website to clean up the code so the code not bloated in my videos function. if you are curious check out this link https://developer.apple.com/swift/blog/?id=37. so the extension for video model is like this.
extension Video {
init(dict: [String: Any]) throws {
guard
let id = dict["id"] as? [String: String],
let videoID = id["videoId"]
else {
throw SerializationError.missing("id")
}
guard
let snippet = dict["snippet"] as? [String: Any],
let description = snippet["description"] as? String,
let title = snippet["title"] as? String,
let thumbnails = snippet["thumbnails"] as? [String: Any],
let medium = thumbnails["medium"] as? [String: Any],
let urlString = medium["url"] as? String,
let url = URL(string: urlString) else { throw SerializationError.missing("snippet") }
self.videoID = videoID
self.title = title
self.description = description
self.url = url
}
}
and then update the videos function, and add do catch block when you try to append since the extension have potential error to parse json data. this is the update method
static func videos(fromJSON data: Data) -> VideoResults {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
var finalItems = [Video]()
if let items = jsonObject?["items"] as? Array<Dictionary<String, Any>> {
for item in items {
do {
try finalItems.append(Video(dict: item))
} catch let error {
print("Failed to append video: \(error)")
}
}
}
print(finalItems.count)
if finalItems.isEmpty {
return .failure(YoutubeError.invalidJSONData)
}
return .success(finalItems)
} catch let error {
return .failure(error)
}
}
and voila the code is safe now, why I not use JSONDecoder? since a lot of people recommended to use JSONDecoder. the reason is, this is my personal reason by the way other might not agree with me. I know how to use JSONDecoder but the thing is i don't want to create a lot struct to decode the json data, since with this JSONSerialization doesn't have to create struct it help me reduce a file and step up my learning curve. and hey even a library like Alamofire have a choice to parse using JSONSerialization. it's up to you now if you want to use JSONDecoder or JSONSerialization I hope this help :).

How can I fix this? Cast from '[String]' to unrelated type '[String : AnyObject]' always fails

I can't seem to find the solution to this, there are hundreds of answers but none that I can find related to this simple problem.
Whatever I do to try to put the dictionary into a string, fails?
Original Text:
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
let jsonString = (jsonResult as AnyObject).components(separatedBy: "")
let jsonDict = jsonString as! [String: AnyObject]
//let jsonDict = jsonString as! [String: AnyObject]
//let jsonDict = jsonString as! Dictionary<String,String>
//Cast from '[String]' to unrelated type 'Dictionary<String, String>' always fails
The full code, now sort of working after #Vadian's fix.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
searchForMovie(title: "pulp_fiction")
}
func searchForMovie(title: String){
//http://www.omdbapi.com/?t=pulp+fiction
if let movie = title.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed){
let url = URL(string: "https://www.omdbapi.com/?t=\(movie)&i=xxxxxxxx&apikey=xxxxxxxx")
// this url contains the omnbapi keys which are free/
let session = URLSession.shared
let task = session.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
} else {
if data != nil {
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String {
let jsonArray = jsonResult.components(separatedBy: "")
print(jsonArray)
} else {
print("This JSON is (most likely) not a string")
}
let jsonDict = jsonResult as! [String: Any]
DispatchQueue.main.async {
print(jsonDict)
}
} catch {
}
}
}
})
task.resume()
}
}
}
The Result of this is:
This JSON is (most likely) not a string
["Poster": https://m.media-
amazon.com/images/M/MV5BNGNhMDIzZTUtNTBlZi00MTRlLWFjM2ItYzViMjE3YzI5MjljXkEyXkFqcGdeQXVyNzkwMjQ5NzM#._V1_SX300.jpg, "BoxOffice": N/A, "Language": English, Spanish, French, "Year": 1994, "Metascore": 94, "Director": Quentin Tarantino, "Rated": R, "Runtime": 154 min, "Genre": Crime, Drama, "imdbVotes": 1,548,861, "Ratings": <__NSArrayI 0x604000256a10>(
{
Source = "Internet Movie Database";
Value = "8.9/10";
},
{
Source = "Rotten Tomatoes";
Value = "94%";
},
{
Source = Metacritic;
Value = "94/100";
}
)
, "Released": 14 Oct 1994, "imdbRating": 8.9, "Awards": Won 1 Oscar. Another 62 wins & 69 nominations., "Actors": Tim Roth, Amanda Plummer, Laura Lovelace, John Travolta, "Response": True, "Country": USA, "Plot": The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption., "DVD": 19 May 1998, "Title": Pulp Fiction, "Writer": Quentin Tarantino (stories), Roger Avary (stories), Quentin Tarantino, "Production": Miramax Films, "imdbID": tt0110912, "Website": N/A, "Type": movie]
First of all never use AnyObject for JSON values in Swift 3+. They are all Any.
The error occurs because the result of components(separatedBy is an array ([String]), but you cast it to a dictionary ([String:Any(Object)]). Don't cast at all, the compiler knows the type.
And don't use .mutableContainers in Swift, never. This option is pointless.
components(separatedBy makes only sense if the JSON is a string. If so you have to pass the option .allowFragments
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String {
let jsonArray = jsonResult.components(separatedBy: "")
print(jsonArray)
} else {
print("This JSON is (most likely) not a string")
}
Edit:
According to your added result the received object is apparently an dictionary, so as? String as well as components(separatedBy: and .allowFragments is wrong. Try this
if let jsonDictionary = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
for (key, value) in jsonDictionary {
print(key, value)
}
} else {
print("This JSON is not a dictionary")
}

Correct parse JSON in swift

This is JSON what I want to parse and use in my app
{
"status": "ok",
"source": "the-next-web",
"sortBy": "latest",
-"articles": [
-{
"author": "Mix",
"title": "Social media giants like Facebook liable to €50M ‘hate speech’ fines in Germany",
"description": "Germany will soon punish social media giants like Facebook, Twitter and YouTube with fines of up to €50 million (approximately $57 million) if they fail to take down illegal or offensive ...",
"url": "https://thenextweb.com/facebook/2017/06/30/facebook-youtube-twitter-germany-50m/",
"urlToImage": "https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2017/03/facebook.jpg",
"publishedAt": "2017-06-30T15:10:58Z"
},
-{
"author": "Abhimanyu Ghoshal",
"title": "Google’s new Android app makes it easy to save mobile data on the go",
"description": "The good folks at Android Police have spotted a new app from Google called Triangle; it lets you control which other apps can use your mobile data. It's a handy little tool if you're ...",
"url": "https://thenextweb.com/apps/2017/06/30/googles-new-android-app-makes-it-easy-to-save-mobile-data-on-the-go/",
"urlToImage": "https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2017/06/Triangle-hed.jpg",
"publishedAt": "2017-06-30T13:16:12Z"
},
-{
This is my methods to parse the Json:
typealias GetWeatherSuccess = (_ result: NSDictionary) -> Void
typealias GetWeatherFailure = (_ error: Error?) -> Void
func requestNewsForToday() -> URLRequest {
let urlText = NewsManager.shared.weatherRequest()
return URLRequest(url: URL(string: urlText)! as URL)
}
func getNews(successHandler: GetWeatherSuccess?,
failureHandler: GetWeatherFailure?) {
let session = URLSession.shared
let dataTask = session.dataTask(with: requestNewsForToday()) { data, _, error in
if error == nil {
if let dic = try? JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
successHandler?(dic!)
}
} else {
failureHandler?(error)
}
}
dataTask.resume()
}
And usage of methods:
NetworkManager.shared.getNews(successHandler: { (json: NSDictionary) in
let articles = json.object(forKey: "articles") as! NSArray
print(articles[0])
}) { (_:Error?) in
print("error")
}
}
And result is oblivious:
{
author = "Romain Dillet";
description = "\U201cWe called this building Station F, like Station France, Station Femmes [ed. note: women in French], Station Founders or Station Freyssinet because..";
publishedAt = "2017-07-01T09:47:38Z";
title = "A walk around Station F with Emmanuel\U00a0Macron";
url = "https://techcrunch.com/2017/07/01/a-walk-around-station-f-with-emmanuel-macron/";
urlToImage = "https://tctechcrunch2011.files.wordpress.com/2017/06/station-f-emmanuel-macron-9.jpg?w=764&h=400&crop=1";
}
How to take authors String for example?
Or make full array of data from json and use it?
I am going to use Swift 4 Decodable protocol to parse JSON the simple and easy way! It's really a very powerful tool that Apple has given in this update!
By looking at the JSON, I can infer I need two structs which will conform to the Decodable protocol:-
struct jsonData: Decodable {
let status: String
let source: String
let sortBy: String
let articles: [Articles]
}
struct Articles: Decodable {
let author: String
let title: String
let description: String
let url: String
let urlToImage: String
let publishedAt: String
}
Pay attention that I have exactly named the variables same as your keys in the JSON. This is where the Decodable protocol magic happens. It automatically infers which key value/pair belongs to which variable on the basis of your variable name (meaning your key name in JSON, should be the variable name in swift), provided you named them correctly, otherwise it will throw an error.
Now, making a network connection as usual:-
URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
do {
let item = try JSONDecoder().decode(jsonData.self, from: data)
print(item.status) // prints "ok"
print(items.articles) // prints the 2 arrays in the json, provided in your Question
}
catch let jsonErr {
print("serialisation error", jsonErr)
}
}

How to sort posts read from JSON server file in Swift

I want to sort the posts I read from JSON file on the server.
I want to sort via push of a button, sorted by name or time
My code
func ReadFromJson()
{
let path="www.xxxxxxxxxxx.com/.json"
let url=NSURL(string: path)
let session=NSURLSession.sharedSession()
let task=session.dataTaskWithURL(url!, completionHandler: { (data, response, err) -> Void in
if (err == nil)
{
let dic =
NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.AllowFragments, error: nil)
as NSDictionary
self.arr=dic["events"] as? NSArray
self.MyCollection.reloadData()
self.Myind.stopAnimating()
}
})
task.resume()
}
We would need to see more detail on the structure of the events array, but if you're going to use NSArray, then you'd use one of the many NSArray sorting methods.
First, let's assume that the JSON looked like:
{
"events": [
{
"name": "Storming of the Bastille",
"date": "July 7, 1789"
},
{
"name": "american revolution",
"date": "July 4, 1776"
},
{
"name": "Guy Fawkes Night",
"date": "November 5, 1605"
}
]
}
If you wanted to sort that by name, you could:
var error: NSError?
if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? NSDictionary {
if let unsortedEvents = json["events"] as? NSArray {
let descriptor = NSSortDescriptor(key: "name", ascending: true, selector: "caseInsensitiveCompare:")
self.events = unsortedEvents.sortedArrayUsingDescriptors([descriptor])
self.collectionView.reloadData()
self.activityIndicatorView.stopAnimating()
}
}
Clearly, if the dates were in a form that could be sorted alphabetically, you could do something like the above. I picked formats that were not in an easily sorted form, to illustrate a more complicated combination of NSDateFormatter and sortedArrayUsingComparator
let formatter = NSDateFormatter()
formatter.dateFormat = "MMM d, YYYY"
var error: NSError?
if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? NSDictionary {
if let unsortedEvents = json["events"] as? NSArray {
self.events = unsortedEvents.sortedArrayUsingComparator() { (obj1, obj2) -> NSComparisonResult in
let event1 = obj1 as NSDictionary
let event2 = obj2 as NSDictionary
let date1 = formatter.dateFromString(event1["date"] as NSString)
let date2 = formatter.dateFromString(event2["date"] as NSString)
return date1!.compare(date2!)
}
self.collectionView.reloadData()
self.activityIndicatorView.stopAnimating()
}
}