I have made a struct that is helping me fetching info from a twitter json. The json has values such as text, which I am able to fetch without a problem, but it also has a dictionary names user and it has the string screen_name inside it.
How can I access that string?
Here is how I access the text string and how I fetch the user dictionary:
func parseTwitterJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let twitterLocations = NSMutableArray()
for i in 0 ..< jsonResult.count{
jsonElement = jsonResult[i] as! NSDictionary
let twitterLocation = twitterLocationModel()
//the following insures none of the JsonElement values are nil through optional binding
if let lang = jsonElement["lang"] as? String,
let text = jsonElement["text"] as? String,
let user = jsonElement["user"] as? NSDictionary
{
twitterLocation.lang = lang
twitterLocation.text = text
twitterLocation.user = user
}
twitterLocations.add(twitterLocation)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.twitterDownloaded(items: twitterLocations)
})
}
To access the value of a key from a dictionary you would use
let myDictName : [String:String] = ["keyName" : "value"]
let value = myDictName["keyName"]
//value will be equal to "value"
Since you have the element user as an dictionary, you can just say
let userName = user["screen_name"]
Related
Hi I'm trying to get data from a certain JSON API. I can gat a snapshot of all values from the API, which is shown below. But I can't manage to put a specifiek row in a variable. This is the JSON form which I get. I want to print the "Description" value.Can someone help me with this?
And Hier is my code:
func apiRequest() {
let config = URLSessionConfiguration.default
let username = "F44C3FC2-91AF-5FB2-8B3F-70397C0D447D"
let password = "G23#rE9t1#"
let loginString = String(format: "%#:%#", username, password)
let userPasswordData = loginString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData?.base64EncodedString()
let authString = "Basic " + (base64EncodedCredential)!
print(authString)
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
var running = false
let url = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session.dataTask(with: url! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let array = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(array)
if let items = array["items"] {
if let description = items["Description"] as? [[String:Any]]{
print(description as Any)
}
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task.resume()
while running {
print("waiting...")
sleep(1)
}
}
First of all the array is not an array and not AnyObject, it's a dictionary which is [String:Any] in Swift 3.
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
I don't know why all tutorials suggest .mutableContainers as option. That might be useful in Objective-C but is completely meaningless in Swift. Omit the parameter.
The object for key itemsis an array of dictionaries (again, the unspecified JSON type in Swift 3 is Any). Use a repeat loop to get all description values and you have to downcast all values of a dictionary from Any to the expected type.
if let items = dictionary["items"] as? [[String:Any]] {
for item in items {
if let description = item["Description"] as? String {
print(description)
}
}
}
Looks like items is an array that needs to be looped through. Here is some sample code, but I want to warn you that this code is not tested for your data.
if let items = array["items"] as? [[String: AnyObject]] {
for item in items {
if let description = item["Description"] as? String{
print("Description: \(description)")
}
}
}
This code above, or some variation of it, should get you on the right track.
use the SwiftyJSON and it would be as easy as json["items"][i].arrayValue as return and array with items Values or json["items"][i]["description"].stringValue to get a string from a row
I'm looking to try and reference all "titles" within this json (link here) in xcode 8. The issue is there's an object and array that need to be referenced (i believe) before I can pull the title data, and I'm not sure how to do that.
So far this is what i've got:
func fetchFeed(){
let urlRequest = URLRequest(url: URL(string: "http://itunes.apple.com/us/rss/topalbums/limit=10/json")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error)
return
}
self.artists = [Artist]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let feedFromJson = json["feed"]?["entry"] as? [[String : AnyObject]] {
for feedFromJson in feedsFromJson {
let feed = Feed()
if let entry = feedFromJson["entry"] as? String, let author = feedFromJson["domain"] as? String {
feed.entry = entry
article.headline = title
}
self.articles?.append(article)
}
}
DispatchQueue.main.async {
self.tableview.reloadData()
And thank you for your help in advance!
I'm working hard to try to understand what you need. If you want to get an Article array where the headline is the title label for the entry, here is how I cheated it out.
func articles(from json: Any?) -> [Article] {
guard let json = json as? NSDictionary, let entries = json.value(forKeyPath: "feed.entry") as? [NSDictionary] else {
return []
}
return entries.flatMap { entry in
guard let title = entry.value(forKeyPath: "title.label") as? String else {
return nil
}
var article = Article()
article.headline = title
return article
}
}
you call it as such
self.articles = articles(from: json)
NSDictionary has the method value(forKeyPath:) that is near magic. Calling json.value(forKeyPath: "feed.entry") returns an array of dictionaries. Each dictionary is an "entry" object in the json. Next, I map each entry to call entry.value(forKeyPath: "title.label") which returns a string.
If this is something more than a quick solution, then I would consider adding SwiftyJSON to your project.
func articles(from json: Any?) -> [Article] {
return JSON(json)["feed"]["entry"].arrayValue.flatMap { entry in
guard let title = entry["title"]["label"].string else {
return nil
}
var article = Article()
article.headline = title
return article
}
}
There is two kinds of titles.
the "feed" and the "entry".
if let entry = feedFromJson["entry"] as? String, let author = feedFromJson["domain"] as? String {
The practice of iOS is not this.
feedFromJson["entry"] is nil ,not a string . I guess you try to get the "feed" title.
if let entry = (json["feed"] as Dictionary)?["title"]
To get the "entry" title. Just traverse the array, and get the title.
let titleDict = feedFromJson["title"] as? Dictionary
let title = titleDict["title"] as? String
article.headline = title
Better to know the structure of the JSON data.
It's too quick.
if let feedFromJson = json["feed"]?["entry"] as? [[String :
AnyObject]] {
You should step by step.
if let feedFromJson = (json["feed"] as Dictionary)?["entry"] as? [[String : AnyObject]] {
I have made the following function in Swift 3:
func parseJSON() {
var JsonResult: NSMutableArray = NSMutableArray()
do {
JsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement:NSDictionary=NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for i in 0 ..< JsonResult.count
{
jsonElement = JsonResult[i] as! NSDictionary
let location = Parsexml()
if let title = jsonElement["Title"] as? String,
let body = jsonElement["Body"] as? String,
let userId = jsonElement["UserId"] as? Int,
let Id = jsonElement["Id"] as? Int
{
location.title = title
location.body = body
location.userId = userId
location.id = Id
}
locations.add(location)
}
DispatchQueue.main.async { () -> Void in
self.delegate.itemsDownloaded(items: locations)
}
When i call this function from another method, i get the following error:
Could not cast value of type '__NSArrayI' (0x105d4fc08) to 'NSMutableArray' (0x105d4fcd0).
It points me towards the element here:
JsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
Where it exits with a SIGBRT..
What have i missed here?
You are trying to convert an NSArray into an NSMutable array which is what the warning is complaining about.
Take the array it provides you, and then convert it into a mutable one.
let jsonArray = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
jsonResult = jsonArray.mutableCopy() as! NSMutableArray
Unrelated, but you may also want to user a lower case value for the JsonResult to fit with normal iOS style guidelines. It should instead be jsonResult.
Another way to improve your code:
You are not mutating your JsonResult, so you have no need to declare it as NSMutableArray:
var JsonResult = NSArray()
do {
JsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
And some steps to improve your code...
enum MyError: Error {
case NotArrayOfDict
}
func parseJSON() {
do {
guard let jsonResult = try JSONSerialization.jsonObject(with: self.data as Data) as? [[String: Any]] else {
throw MyError.NotArrayOfDict
}
let locations: NSMutableArray = NSMutableArray()
for jsonElement in jsonResult {
let location = Parsexml()
if let title = jsonElement["Title"] as? String,
let body = jsonElement["Body"] as? String,
let userId = jsonElement["UserId"] as? Int,
let Id = jsonElement["Id"] as? Int
{
location.title = title
location.body = body
location.userId = userId
location.id = Id
}
locations.add(location)
}
DispatchQueue.main.async { () -> Void in
self.delegate.itemsDownloaded(items: locations)
}
} catch let error {
print(error)
}
}
as! casting sometimes crashes your app, use it only when you are 100%-sure that the result is safely converted to the type. If you are not, using guard-let with as? is safer.
Use Swift types rather than NSSomething as far as you can.
Specifying .allowFragments is not needed, as you expect the result as an Array.
And if you can modify some other parts of your code, you can write your code as:
func parseJSON() {
do {
//If `self.data` was declared as `Data`, you would have no need to use `as Data`.
guard let jsonResult = try JSONSerialization.jsonObject(with: self.data) as? [[String: Any]] else {
throw MyError.NotArrayOfDict
}
var locations: [Parsexml] = [] //<-Use Swift Array
for jsonElement in jsonResult {
let location = Parsexml()
if let title = jsonElement["Title"] as? String,
let body = jsonElement["Body"] as? String,
let userId = jsonElement["UserId"] as? Int,
let Id = jsonElement["Id"] as? Int
{
location.title = title
location.body = body
location.userId = userId
location.id = Id
}
locations.append(location)
}
DispatchQueue.main.async { () -> Void in
self.delegate.itemsDownloaded(items: locations)
}
} catch let error {
print(error)
}
}
let urlAsString = "https://drive.google.com/uc?export=download&id=0B2bvUUCDODywWTV2Q2IwVjFaLW8"
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Okay so here i am receiving data from online json then storing it as NSDictionary in jsonresult . I need to get all keys and values as into two separate arrays ?
Basically i want this
jsonresult.allkeys --> String array
jsonresult.allvalues --> String array
You can use:
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 }
It is quite simple because you are using jsonResult as NSDictionary.
let dict: NSDictionary = ["Key1" : "Value1", "Key2" : "Value2"]
let keys = dict.allKeys
let values = dict.allValues
In you're case
let keys:[String] = dict.allKeys as! [String]
var values:[String]
if let valuesSting = dict.allValues as? [String] {
values = valuesSting
}
For anyone trying it with newer version Swift please use compactMap()instead of flatMap()
let keys = jsonResult.compactMap(){ $0.0 as? String }
let values = jsonResult.compactMap(){ $0.1 }
I've been trying to get a JSON String to a Dictionary Value, and I can't get it to work, I'm getting an empty value. Before I tried to pull the data I checked that I got the all JSON, so obviously I'm doing something wrong here
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
var items = [[String:String]()]
var item:AnyObject
var authorDictionary:AnyObject
for var i = 0; i < jsonResult["item"]?.count; i++ {
items.append([String:String]())
item = (jsonResult["items"] as? [[NSObject:AnyObject]])!
items[i]["content"] = item["content"] as? String
items[i]["title"] = item["title"] as? String
items[i]["publishedDate"] = item["published"] as? String
authorDictionary = item["author"] as! NSDictionary
items[i]["author"] = item["displayName"] as? String
}
println(items)
That's what I got as a result:
[[:]]
I am new to JSON, can someone explain me what I should do and what I did wrong?
You can iterate directly over jsonResult["items"] if it has the right type declared (array of dictionaries).
Then inside the loop you have to create a new dictionary each time, fill this dictionary with the data you grab from the JSON response, then you append the new dictionary to your items array of dictionaries:
var items = [[String:String]]()
for item in jsonResult["items"] as! [[String:AnyObject]] {
var newDict = [String:String]()
newDict["content"] = item["content"] as? String
newDict["title"] = item["title"] as? String
newDict["publishedDate"] = item["published"] as? String
newDict["author"] = item["displayName"] as? String
items.append(newDict)
}
As for authorDictionary, since it's a simple dictionary and not an array, if you assign it a value in the loop, it will be overwritten each time and all you'll have in the end is the author from the last object.
Check this out
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
var items = [[String:String]()]
var item:AnyObject
var authorDictionary:AnyObject
for var i = 0; i < jsonResult["items"]!.count; i++
{
items.append([String:String]())
item = (jsonResult["items"] as! [NSDictionary])[i]
items[i]["content"] = item["content"] as! NSString as String
items[i]["title"] = item["title"] as! NSString as String
items[i]["publishedDate"] = item["published"] as! NSString as String
authorDictionary = item["author"] as! NSDictionary
items[i]["author"] = authorDictionary["displayName"] as! NSString as String
}
println(items)