Accessing *remote* JSON deeply nested objects in Swift - json

Remote JSON parsing in Swift is new for me and I've spent weeks trying to figure this one out.
The JSON I'm pulling from is this guy:
http://www.odysseynewsmagazine.net/wp-json/wp/v2/posts?_embed
I'm trying to get to that "source_url" for an image for each post but it's nested within "media_details" which is nested within "wp:featuredmedia" which is nested within "_embedded" and I just keep getting errors.
The code I've written looks like this:
func parseData() {
fetchedSlug = []
//from odyssey site
let url = "http://www.odysseynewsmagazine.net/wp-json/wp/v2/posts?_embed"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Error")
}
else {
do {
let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
//Json objects to variables
for eachFetchedSlug in fetchedData {
let eachSlug = eachFetchedSlug as! [String: Any]
let slug = eachSlug["slug"] as! String
let link = eachSlug["link"] as! String
self.fetchedSlug.append(Slug(slug: slug, link: link))
}
self.slugTableView.reloadData()
}
catch {
print("Error2")
}
}
}
task.resume()
}
}//end of VC Class
class Slug {
//define variables
let slug: String?
let link: String?
init(slug: String?, link: String?) {
self.slug = slug
self.link = link
}
//creating dictionaries from Json objects
init(slugDictionary: [String : Any]) {
self.slug = slugDictionary["slug"] as? String
link = slugDictionary["link"] as? String
}
}
I'm also going to need the title of each post which is found in "rendered" within "title".
All of this info is populating labels within a reusable custom cell within a tableView. I can populate the slug and link labels, but not any of the nested info.
What's up with the underscore preceding "embedded"? Is that why I can't get to anything? Can I make it go away? I'm not allowed to download plugins or run custom scripts until I show them a working app.

Please check below code :
for eachFetchedSlug in fetchedData {
let eachSlug = eachFetchedSlug as! [String: Any]
let slug = eachSlug["slug"] as! String
let link = eachSlug["link"] as! String
self.fetchedSlug.append(Slug(slug: slug, link: link))
let title = eachSlug["title"] as! [String: Any]
let rendered = String(describing: title["rendered"])
print(rendered) // this is title
let embedded = eachSlug["_embedded"] as! [String: Any]
let wpfeaturedmedias = embedded["wp:featuredmedia"] as! [Any]
for wpfeaturedmedia in wpfeaturedmedias {
let featuredmedia = wpfeaturedmedia as! [String: Any]
let mediaDetails = featuredmedia["media_details"] as! [String: Any]
let mediaDetailsSize = mediaDetails["sizes"] as! [String: Any]
let mediaDetailsSizeThumbnail = mediaDetailsSize["thumbnail"] as! [String: Any] // getting only thumbnail. Based on you requirement change this to
let image = String(describing: mediaDetailsSizeThumbnail["source_url"])
print(image) // this is image
}
}
I added the code for only retrieving thumbnail. In the sizes so many types(medium,medium_large ...) are there. Based on your requirement, change the value.
Its better to add if let check for optionals. Because so many conversions are there. If it fails in any conversion, it will crash.

install Better REST API Featured Images plugin

Related

Get item out of array in Swift

I'm very new to Swift and have spent several hours just trying to pull the photo_url key out of a JSON response.
I'm using this for the reading the JSON:
let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
Then:
if let eventsDictionary = jsonDictionary {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
} else {
completion(nil)
}
Here is my (failed) attempt to pull out the key:
init(eventsDictionary: [String : Any]) {
//photoUrl = eventsDictionary[EventKeys.photoUrl] as? String
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let url: String = groups[0]
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
I changed "[String: Any]" to [String: AnyObject] and now i get this...
There are problems casting Any to NSArray. Just make your Init method taking [String:AnyObject]. But, better use Array instead of NSArray here
Try to get url use following code.
let firstObj = groups[0] as! [String: String] // use if let to unwrap is better
let url = firstObj["photo_url"]
To get "photo_url" from the json file in your photo,
it looks like this:
init(eventsDictionary: [String : Any]) {
if let groups = eventsDictionary["groups"] as? [NSDictionary]{
/*
// Get All URL
var urls : [String] = []
for group in groups{
if let url = group.value(forKey: "photo_url"){
urls.append(url)
}
}
*/
// Groups[0] url
let url: String = groups[0].value(forKey: "photo_url") as! String
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
}
You need to read json as `[String: Any].
if let eventsDictionary = json as? [String: Any] {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
}
Then, init your UpcomingEvents model like this
init(eventsDictionary: [String : Any]) {
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let group1 = groups[0] as! NSDictionary
let photoURL = group1["photo_url"] as! String
print(photoURL)
}

dealing with nested json array swift

I am trying to convert some json data which i receive from a get request into a usable array or something like this
the json data i recieve looks like this
{
"elementlist":{
"Ready Position":{
"Neutral Grip":["1,2,3,4,5"],"
Back Straight (Concave ir Convex?)":["1,2,3,4,5"],"
Body Low & Feet a little more than sholder width apart":["1,2,3,4,5"],"
Weight on Balls of Feet":["1,2,3,4,5"],"
Head Up":["1,2,3,4,5"],"
Sholder Blades Close":["1,2,3,4,5"],"
Eyes Drilled":["1,2,3,4,5"]
},
"Split Step":{"
Ready Position Conforms":["Yes,No"],"
Body Position Low":["1,2,3,4,5"],"
Legs Loaded/Prepared":["1,2,3,4,5"]
}
}
}
this is the swift i am using
let playerAPIurl = "http://linkcoachuat.herokuapp.com/api/v1/session/element?organisation=5&group=green&sport=tennis"
var request = URLRequest(url: URL(string: playerAPIurl)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("ERROR")
}
else{
do{
print("hello")
let myJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
// Convert myJson into array here
print(myJson)
}
catch
{
}
}}
What i would like to be able to do is get an array of the names of the nested arrays so elementarray = ["Ready Position","Split Step"] and then be able to access the arrays by saying myJson[elementarray[0]] or something similar
im a bit of a swift noob so any help is appreciated please try and explain the answers so they are easily understood
thank you for any help
You can try to downcast that json same way you've already made:
let myJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
//creating the new array of additional elements
let elementArray: [[String: Any]] = []
//making myJson parsing for additional items
if let readyPosition = myJson?["Ready Position"] as? [String: Any] {
elementArray.append(readyPosition)
}
if let splitStep = myJson?["Split Step"] as? [String: Any] {
elementArray.append(splitStep)
}
make print(elementArray) to be sure that all was parsed correctly.
Honestly, I prefer to use objects (custom classes or structs) to store values and have an ability to make related instances or values, but up to you

"Expression Implicity Coerced from 'String?' to Any" JSON Swift 3

Hi I have the below JSON code I would like to parse
"data":{
"date":"November 30th, 2016",
"personality":"Larry",
"comment":"Saw the homie today"
},
I'm doing this in my viewDidLoad
let url=URL(string:"http://IP-ADDRESS/info.php")
do {
let allNotificationsData = try Data(contentsOf: url!)
let allNotication = try JSONSerialization.jsonObject(with: allNotificationsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
if let arrJSON = allNotication["notifications"] {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as? [String: AnyObject]
//let name = aObject?["data"]!
if let jsonResponse = aObject,
let info = jsonResponse["data"] {
// Makes sense to check if count > 0 if you're not sure, but...
let transaction_id: String? = info["personality"] as? String
print(transaction_id)
// Do whatever else you need here
}
Which seems to be fine but console returns below. Not sure while "nil" but I just want it show me "date" in the JSON file itself only in the console. Eventually I'll need it to catch an array of dates, not sure how I'll do that but I'm working on it. Let me know if you know what I'm doing wrong. It has to be something with optional.
Assuming the parent object of data is an array (your code suggests that) you can get all data objects in an array with:
do {
let allNotificationsData = try Data(contentsOf: url!)
let allNotification = try JSONSerialization.jsonObject(with: allNotificationsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
if let arrJSON = allNotification["notifications"] as? [[String:Any]] {
let infoArray = arrJSON.flatMap { $0["data"] }
}
...
}
The benefit of flatMap is it ignores nil values.
If you want to access the comment value of the first item in the array write
let comment = (infoArray[0] as! [String:Any])["comment"] as! String

Strange behaviour of optionals in Swift 3

I have experienced a strange behaviour when parsing JSON data using Swift 3.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! NSDictionary
let items:[AnyObject] = (json["items"] as? [AnyObject])!
for item in items {
let id:String = item["id"] as! String
print("ID: \(id)")
let info = item["volumeInfo"] as AnyObject
print(info)
let title = info["title"]
print(title)
}
} catch {
print("error thrown")
}
This produces the following output. Notice that info is an optional but if I try to unwrap it it states it is not an optional! The script crashes on let title = info["title"] As a result I can't access the title key. This behaviour has changed since Swift 2.
ID: lbvUD6LUyV8C
Optional({
publishedDate = 2002;
publisher = "Sams Publishing";
title = "Java Deployment with JNLP and WebStart";
})
You can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as! [String: Any]
let items = json["items"] as! [[String: Any]]
for item in items {
let id = item["id"] as! String
let info = item["volumeInfo"] as! [String: Any]
let title = info["title"] as! String
print(id)
print(info)
print(title)
}
} catch {
print("error thrown: \(error)")
}
I might suggest excising the code of the ! forced unwrapping (if the JSON was not in the form you expected, do you really want this to crash?), but hopefully this illustrates the basic idea.
The runtime type of info is Optional<Something>, but the compile time type (as you explicitly cast it) is AnyObject. How is the compiler supposed to know that the AnyObject will happen to be an Optional<Something> unless you tell it (in the form of a cast)?
Try:
let info = item["volumeInfo"] as AnyObject?
Now the compiler knows it's an Optional<AnyObject>, so you can unwrap it.
In Swift 3 the compiler must know the types of all subscripted objects if it's an array or dictionary. AnyObject – which has been changed to Any in Swift 3 – is not sufficient.
Since you know that the value for key volumeInfo is a dictionary cast it accordingly preferably using optional bindings
let info = item["volumeInfo"] as? [String:Any] {
print(info)
let title = info["title"] as! String
print(title)
}
This should do:
guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject],
let items = json["items"] as! Array<AnyObject>? else {
return
}
for item in items {
let id = item["id"] as! String
if let info = item["volumeInfo"] as? [String: AnyObject] {
let title = info["title"] as! String
} else {
// do something
}
}

JSON unwrap not running

I have a JSON fetch happening and then I do stuff with the data. My JSONObject is created and then I go about working with the data. A sample can be seen here: https://openlibrary.org/api/books?bibkeys=1593243987&f&jscmd=data&format=json
My first block to extract the author name is working perfectly, however the second to extract the cover url as a string isn't even running and I have no idea why.
If I set a breakpoint at if let thumbs = bookDictionary["cover"] as? NSArray {it stops, but then when I 'step through' the code, it jumps to the end and moves on, not even running anything inside the block.
I would appreciate any help anyone can offer. I'm using Swift 2.0 / Xcode 7b6.
let requestURL = ("https://openlibrary.org/api/books?bibkeys=" + lookUpID + "&f&jscmd=data&format=json")
let url = NSURL(string: requestURL)
let req = NSURLRequest(URL: url!)
let dataTask = session.dataTaskWithRequest(req) {
(data, response, error) in
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
if let bookDictionary: AnyObject = jsonObject!["\(self.lookUpID)"] {
// Retrieve the author name
var names = [String]()
if let authors = bookDictionary["authors"] as? NSArray {
for author in authors {
if let author = author as? NSDictionary,
let name = author["name"] as? String {
names.append(name)
}
}
}
// Retrieve cover url
var coverThumbURL: String = ""
if let thumbs = bookDictionary["cover"] as? NSArray {
// This code isn't running at all.
for thumb in thumbs {
if let thumb = thumb as? NSDictionary,
let thumbnail = thumb["medium"] as? String {
coverThumbURL = thumbnail
}
}
}
}
Thanks for the help. I did some looking around & fixed the casting.
var coverThumbURL: String = ""
if let thumbs = bookDictionary["cover"] as? NSDictionary {
let thumbnail = thumbs.valueForKey("medium") as? String
coverThumbURL = thumbnail!
}