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!
}
Related
I have question about tasks. I need to restore my request, I change link and fetch some new data to that link and show them on my table view. User can change link according to picker view, I have a variable for it and I replaced in link and thats working correct too but in second request can make a thread I assume it is very bad question but I am new in swift. How can I make second or more request in one function.
Here my function code :
func fetchArticles(){
let urlRequest = URLRequest(url: URL(string: "my_api_link_is_here")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (Data,URLResponse,Error) in
if Error != nil {
print(Error!)
}
self.articles = [Article]()
do{
let json = try JSONSerialization.jsonObject(with: Data!, options: .mutableContainers) as! [String: AnyObject]
if let articlesFromJson = json["articles"] as? [[String:AnyObject]] {
for articleFromJson in articlesFromJson {
let article = Article()
let source = articleFromJson["source"] as![String: AnyObject]
let name = source["name"]
if let title = articleFromJson["title"] as? String, let author = name as? String , let desc = articleFromJson["description"] as? String, let url = articleFromJson["url"] as? String, let imageToUrl = articleFromJson["urlToImage"] as? String {
article.author = author as String
if articleFromJson.index(forKey: "description") != nil {
article.desc = desc as String
}else{
article.desc = "empty"
}
article.headline = title as String
article.imageUrl = imageToUrl as String
article.url = url as String
}
self.articles?.append(article)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch let Error {
print(Error)
}
}
task.resume()
}
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)
}
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
How to get the multiple data inside the json curly braces in swift3?
Can i use this code to get multiple data? (get "crew_id","crew_name","crew_email")
if let crew = user!["crew"] as? [String:Any], let crewName = crew["crew_name"] as? String {
print(crewName)
JSON
crew ={
"crew_avatar" = "http://ec2-52-221-231-3.ap-southeast-1.compute.amazonaws.com/gv/images/profile_image/Pang_Kang_Ming_916210_0e9.jpg";
"crew_contact" = 0123456789;
"crew_email" = "pang#xover.com.my";
"crew_gender" = Male;
"crew_id" = PP000001;
"crew_name" = "Pang Kang Ming";
"crew_preferred_name" = PKM;
"crew_qrcode" = "images/qrcode/qrcode_085960293a5378a64bec6ebfa3c89bb7.png";
};
message = "Login Sucessfully";
result = success;
Yes you can, just add the values you want to unwrap as below, just be aware that if one of the optional binding does not unwrap, even if others unwrap if statement will not be executed, consider separating the if statements.
Depends on all being returned in the json.
if let crew = user!["crew"] as? [String:Any],
let crewName = crew["crew_name"] as? String,
let crewId = crew["crew_id"] as? String {
print(crewName)
print(crewId)
}
Recommended way, even if some values are not present in the json response, you will be able to get the other values.
if let crew = user!["crew"] as? [String:Any] {
if let crewName = crew["crew_name"] as? String {
print(crewName)
}
if let crewId = crew["crew_id"] as? String {
print(crewId)
}
}
if let file = Bundle.main.url(forResource: "yourJsonFileName", withExtension: "json") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
let jsonData = json as! [[String:Any]]
DispatchQueue.main.async {
let projectName = jsonData.flatMap { $0["crew_avatar"] as? String }
self.crewAvatarArray = projectName
print(self.crewAvatarArray)
let subTitle = jsonData.flatMap { $0["crew_contact"] as? String }
self.crewContactArray = subTitle
let startDate = jsonData.flatMap { $0["crew_email"] as? String }
self.crewEmailArray = startDate
}
}
Try this code
How would I be able to check whether or not the downloaded JSON content contains an error message rather than the expected content? I've tried to validate the URL but that cannot work due to how a false subdomain (location in this case) still returns an Error Message through the JSON content. I'd appreciate it if anybody could help me out. (Note: I want to check for an invalid location entered by the user and I'm using OpenWeatherMap API.)
func downloadData(completed: #escaping ()-> ()) {
print(url)
//UIApplication.shared.openURL(url as URL)
Alamofire.request(url).responseJSON(completionHandler: {
response in
let result = response.result
if let dict = result.value as? JSONStandard, let main = dict["main"] as? JSONStandard, let temp = main["temp"] as? Double, let weatherArray = dict["weather"] as? [JSONStandard], let weather = weatherArray[0]["main"] as? String, let name = dict["name"] as? String, let sys = dict["sys"] as? JSONStandard, let country = sys["country"] as? String, let dt = dict["dt"] as? Double {
self._temp = String(format: "%.0f °F", (1.8*(temp-273))+32)
self._weather = weather
self._location = "\(name), \(country)"
self._date = dt
}
completed()
})
}
Assuming the resulting JSON has different content when there is an error, check dict for the error content. Below is an example assuming there is a key named error. Adjust as needed based on what you really get when there is an error.
Alamofire.request(url).responseJSON(completionHandler: {
response in
let result = response.result
if let dict = result.value as? JSONStandard {
if let error = dict["error"] {
// parse the error details from the JSON and do what you want
} else if let main = dict["main"] as? JSONStandard, let temp = main["temp"] as? Double, let weatherArray = dict["weather"] as? [JSONStandard], let weather = weatherArray[0]["main"] as? String, let name = dict["name"] as? String, let sys = dict["sys"] as? JSONStandard, let country = sys["country"] as? String, let dt = dict["dt"] as? Double {
self._temp = String(format: "%.0f °F", (1.8*(temp-273))+32)
self._weather = weather
self._location = "\(name), \(country)"
self._date = dt
} else {
// Unexpected content, handle as needed
}
}
completed()
})
You should also provide a parameter to your downloadData completion handler so you can pass back an indication of success or failure so the caller can handle the result appropriately.