In Swift, changing in function doesn't work in outside - json

I have a tableview. cekilecek_data contains tableview datas. I get some datas from JSON and I want to append this datas to tableview. But I have to do this inside of jsonGetir(). However, it does not work. kodJSON and kodlarJSON are nil in viewDidLoad(). Also, cekilecek_data.append(kodJSON[1]) it doesn't add the datas to the table.
How do I fix it?
var cekilecek_data = ["Fenerbahçe", "Chelsea", "Arsenal"]
var kodlarJSON:String = ""
var kodJSON:[String] = []
func jsonGetir(){
let urls = NSURL(string: "http://gigayt.com/mackolik/deneme.php")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){ print(error) }
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
kodlarJSON = jsonResult["kodlar"] as! String //101,102,103
kodJSON = kodlarJSON.componentsSeparatedByString(",")
cekilecek_data.append(kodJSON[1]) //Here doesn't work!
}
}
catch { print(error) }
}
sessions.resume()
}

Reload your tableview after you got data from server this way:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
And your final code will be:
func jsonGetir(){
let urls = NSURL(string: "http://gigayt.com/mackolik/deneme.php")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){ print(error) }
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
self.kodlarJSON = jsonResult["kodlar"] as! String //101,102,103
self.kodJSON = self.kodlarJSON.componentsSeparatedByString(",")
self.cekilecek_data.append(self.kodJSON[1]) //Here doesn't work!
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData() //Reload tableview here.
}
}
catch { print(error) }
}
sessions.resume()
}

With kodJSON = kodlarJson.componentsseparatedByString(",") you are creating an Array of only one object. Then with cekilecke_data.append(kodJSON[1]) you are trying to append your data with the second object from this array that you have only set one object to.
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
kodlarJSON = jsonResult["kodlar"] as! String
kodJSON = kodlarJSON.componentsSeparatedByString(",")
// this line sets kodJSON:[String] to ["kodlar"]
cekilecek_data.append(kodJSON[1]) //Here doesn't work!
// this tries to append cekilecek_data from index [1] or second slot of kodJSON which only has one entry
}
}
if you change line to kodlarJSON = jsonResult["kod,lar"] as! String, it would work because kodJSON[1] would equal "lar"

Related

How do I get my Swift code to read a specific piece of information from the JSON file

I am trying to read the data for "Name" in a JSON file I am hosting using Swift and I seem to only be able to read the whole JSON file and not able to pick out specific data. My JSON file contains this:
[{"Email":"Admin#admin.com","Password":"password","Name":"Admin"}]
The swift code I am using is this:
override func viewDidLoad() {
super.viewDidLoad()
//to get data from external DB
let url = URL(string: "http://localhost/Projects/Test_mobileAPI/test_userInfo.php?email=Admin#admin.com")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil{
print("Error Occured")
}
else{
print("Okie")
if let content = data {
do{
//Array
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print("this part works")
print(myJson)
if let diction = myJson as? NSDictionary
{
if let name = myJson["Name"]{
print(name as Any)
}
}
}
catch{
print(error)
}
}
}
}
task.resume()
}
The output I keep getting is this:
Okie
this part works
(
{
Email = "Admin#admin.com";
Name = Admin;
Password = password;
}
)
But I do not get just the value for "Name". Can anyone help me get the value for "Name" (i.e "Admin")
Can you try
if let myJson = try JSONSerialization.jsonObject(with: content) as? [[String:Any]] {
if let first = myJson.first {
print(first["Name"])
}
}
The problem is that the JSON that you're receiving back isn't a Dictionary, it's a single element array with a dictionary in it. So when you do
if let diction = myJson as? NSDictionary
the myJson object is failing the cast to NSDictionary. If you unwrap the array first, you should then be able to reference the dictionary and pick off any keyed values you want:
if let array = myJson as? NSArray
{
if let myDict = array[0] as? NSDictionary
{
if let name = myDict ["Name"]{
print(name as Any)
}
}
}

Trouble getting data from TheMovieDB API with Swift JSON

I am brand new to using JSON and wanted to get started with a simple app to provide a movie overview when you type in a title. My below code returns everything in one big string. How do I get just one piece of information like the overview or year?
With my below attempt, print(obj["overview"] as Any)) prints "nil" and print(obj) looks like this:
{
page = 1;
results = (
{
adult = 0;
"backdrop_path" = "/A0aGxrCGRBuCrDltGYiKGeAUect.jpg";
"genre_ids" = (
53,
80
);
id = 680;
"original_language" = en;
"original_title" = "Pulp Fiction";
overview = "A burger-loving hit man, his philosophical partner, a drug-addled gangster's moll and a washed-up boxer converge in this sprawling, comedic crime caper. Their adventures unfurl in three stories that ingeniously trip back and forth in time.";
Current Code:
let query = "Pulp+Fiction"
let urlString = "https://api.themoviedb.org/3/search/movie?api_key={MYAPIKEY}&query=\(query)"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error as Any)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as Any
if let obj = parsedData as? NSDictionary {
print(obj["overview"] as Any)
print(obj)
}
} catch {
print("error")
} }
}.resume()
}
// write this extension anywhere in your any swift file
extension String{
func toDictionary() -> NSDictionary {
let blankDict : NSDictionary = [:]
if let data = self.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
} catch {
print(error.localizedDescription)
}
}
return blankDict
}
}
//now in your code modify as
if data != nil {
let responseString = String(data: data!, encoding: .utf8)!
if(responseString != "")
{
//convert response string into dictionary using extended method
let responseValues = responseString.toDictionary()
//access value using keyPath using
let value = responseValues.value(forKeyPath: "key.key2")
//where key2 is the target key which is inside the value of key
}
}
First of all JSON results are never Any. As already mentioned in the comments the root object is a dictionary
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
The key overview is in array for key results
let results = parsedData["results"] as? [[String:Any]] {
You have to iterate over the array to get the values for key overview
for result in results {
print(result["overview"] as? String ?? "no value for key overview")
}
}
It's highly recommended to use the Codable protocol and custom structs in Swift 4.

parsing a JSON array in Swift

I have this working but it seems like a very manual process and I can't work out how to loop inside a loop (or if I should). Right now I am just testing this with 3 variables, but there will ultimately be about 100. Here's my playground. Is there a way to simplify this so I don't have to manually add each array name?
import Foundation
var json_data_url = "216.92.214.107/data_test.json"
var LRKSFOweekdayDep : [String] = [String]()
var LRKSFOweekendDep : [String] = [String]()
var SFOLRKweekdayDep : [String] = [String]()
let journeysURL:NSURL = NSURL(string: json_data_url)!
let data = NSData(contentsOfURL: journeysURL)!
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
print(json)
if let dep_obj = json as? NSDictionary {
if let array_journey = dep_obj["journey"] as? NSArray{
if let journies = array_journey[0] as? NSDictionary {
if let array_dep = journies["LRKSFOweekdayDep"] as? NSDictionary{
if let dep = array_dep["dep"] as? NSArray {
for var i = 0; i < dep.count; ++i
{
let add = dep[i] as! String
LRKSFOweekdayDep.append(add)
}
print(LRKSFOweekdayDep)
}
}
}
if let journies = array_journey[1] as? NSDictionary {
if let array_dep = journies["LRKSFOweekendDep"] as? NSDictionary{
if let dep = array_dep["dep"] as? NSArray {
for var i = 0; i < dep.count; ++i
{
let add = dep[i] as! String
LRKSFOweekendDep.append(add)
}
print(LRKSFOweekendDep)
}
}
}
if let journies = array_journey[2] as? NSDictionary {
if let array_dep = journies["SFOLRKweekdayDep"] as? NSDictionary{
if let dep = array_dep["dep"] as? NSArray {
for var i = 0; i < dep.count; ++i
{
let add = dep[i] as! String
SFOLRKweekdayDep.append(add)
}
print(SFOLRKweekdayDep)
}
}
}
}
}
} catch {
print("error serializing JSON: \(error)")
}
You might want to look at using SwiftyJSON to make the parsing easier.
Right now, you have something like:
if let dep = array_dep["dep"] as? NSArray {
for var i = 0; i < dep.count; ++i {
let add = dep[i] as! String
LRKSFOweekendDep.append(add)
}
}
That can be simplified to:
LRKSFOweekendDep = array_dep["dep"] as? [String]
That assumes of course, that you define LRKSFOweekendDep to be optional. If it's not optional, you can do:
LRKSFOweekendDep = array_dep["dep"] as? [String] ?? []
But, it should be optional.
In a comment, you say that there are going to be 100 of these. Rather than having a variable for each, I would have thought that you'd rather keep an array of objects. For example, consider:
struct Journey {
let name: String
let departures: [String]
}
Then, to parse your JSON, you could iterate through the results:
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
var journeys = [Journey]()
if let results = json as? [String: AnyObject], let array = results["journey"] as? [[String: AnyObject]] {
for dictionary in array {
for (name, departures) in dictionary {
if let departureDictionary = departures as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
journeys.append(Journey(name: name, departures: departureList))
}
}
}
}
Finally, I would advise against NSData(contentsOfURL:), because that's synchronous. Use NSURLSession's dataTaskWithURL, which is asynchronous. Also, if you use data! pattern, first check to make sure it's not nil. Otherwise, if data was nil for any reason outside of your control (e.g. the web server is down, internet is temporarily interrupted, etc.), the app will crash rather than handling it gracefully.
Putting that all together, you get something like:
func retrieveJourneys(completionHandler: ([Journey]?, NSError?) -> ()) {
let task = NSURLSession.sharedSession().dataTaskWithURL(journeysURL) { data, response, error in
guard error == nil && data != nil else {
completionHandler(nil, error)
return
}
var json: [String: AnyObject]?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject]
} catch let parseError as NSError {
completionHandler(nil, parseError)
}
var journeys = [Journey]()
if let array = json!["journey"] as? [[String: AnyObject]] {
for dictionary in array {
for (name, departures) in dictionary {
if let departureDictionary = departures as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
journeys.append(Journey(name: name, departures: departureList))
}
}
}
}
completionHandler(journeys, nil)
}
task.resume()
}
And then you'd use it like so:
var journeys: [Journey]?
override func viewDidLoad() {
super.viewDidLoad()
retrieveJourneys { journeys, error in
guard error == nil && journeys != nil else { // make sure it didn't have network problem
print(error)
return
}
dispatch_async(dispatch_get_main_queue()) { // now update model on main queue
self.journeys = journeys
// and, for giggles and grins, this is how you might grab the first one and examine it:
let someJourney = self.journeys![0]
print(someJourney.name)
print(someJourney.departures)
}
}
}
Now, the above assumes that you wanted an ordered list of journeys, sorted by the order you received them.
On the other hand, if you didn't care about the order, but wanted an efficient way to retrieve the departures associated with a given key, you might use a dictionary, instead:
func retrieveDepartures(completionHandler: ([String: [String]]?, NSError?) -> ()) {
let task = NSURLSession.sharedSession().dataTaskWithURL(journeysURL) { data, response, error in
guard error == nil && data != nil else {
completionHandler(nil, error)
return
}
var json: [String: AnyObject]?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject]
} catch let parseError as NSError {
completionHandler(nil, parseError)
}
var departures = [String: [String]]()
if let array = json!["journey"] as? [[String: AnyObject]] {
for dictionary in array {
for (name, departureObject) in dictionary {
if let departureDictionary = departureObject as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
departures[name] = departureList
}
}
}
}
completionHandler(departures, nil)
}
task.resume()
}
And then:
var departures: [String: [String]]?
override func viewDidLoad() {
super.viewDidLoad()
retrieveDepartures { departures, error in
guard error == nil && departures != nil else {
print(error)
return
}
dispatch_async(dispatch_get_main_queue()) {
self.departures = departures
// and, for giggles and grins, this is how you might grab a list of departures given a particular key
let departureTimes = self.departures!["LRKSFOweekdayDep"]
print(departureTimes)
}
}
}

Swift JSON Duration

I get datas from the url with JSON. When I printing the variable of berabereOran, it shows in 1 or 2 seconds on the Xcode console. But it shows in uilabel in 8 or 10 seconds. I don't know because of what.
Note: In my other project, I used tableView, because of this, I added this code: dispatch_async(dispatch_get_main_queue()) { self.tableView.reloadData() }
But this time, I don't use tableView. What should I write here?
MY CODES:
let urls = NSURL(string: "http://gigayt.com/mackolik/?code=101")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){ print(error) }
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let evsahibiLogo:String = jsonResult["ev-sahibi-logo"] as! String
let deplasmanLogo:String = jsonResult["deplasman-logo"] as! String
let berabereOran:String = jsonResult["berabere-oran"] as! String
print(berabereOran) //shows in 1-2 seconds
self.macsonusifirLabel.text = "(\(berabereOran))" //shows in 8-10 seconds
}
dispatch_async(dispatch_get_main_queue()) {
//self.tableView.reloadData()
}
}
catch { print(error) }
}
sessions.resume()
Try this one :-
let urls = NSURL(string: "http://gigayt.com/mackolik/?code=101")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){ print(error) }
do {
dispatch_async(dispatch_get_main_queue()) {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let evsahibiLogo:String = jsonResult["ev-sahibi-logo"] as! String
let deplasmanLogo:String = jsonResult["deplasman-logo"] as! String
let berabereOran:String = jsonResult["berabere-oran"] as! String
print(berabereOran) //shows in 1-2 seconds
self.macsonusifirLabel.text = "(\(berabereOran))" //shows in 8-10 seconds
}
}
}
catch { print(error) }
}
sessions.resume()
}

Asynchronous Issue JSON Swift

let task = session.dataTaskWithURL(url!, completionHandler: {
data, response, error -> Void in
if (error != nil) {
println(error)
} else {
let jsonresult = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var dummyfeed:AnyObject
//println(jsonresult)
for var i = 0; i < jsonresult["feed"]!.count; i++ {
self.feeds.append([String:String]())
dummyfeed = jsonresult["feed"]![i] as NSDictionary
self.feeds[i]["id"] = dummyfeed["id"] as? String
self.feeds[i]["name"] = dummyfeed["name"] as? String
self.feeds[i]["status"] = dummyfeed["status"] as? String
self.feeds[i]["profilePic"] = dummyfeed["profilePic"] as? String
self.feeds[i]["timeStamp"] = dummyfeed["timeStamp"] as? String
self.feeds[i]["url"] = dummyfeed["url"] as? String
}
}
})
task.resume()
So Feeds is a global variable, so that I display the picture of each entry in Feeds on a table view. But it's calling asynchronously println(self.feeds) inside the task variable and println(feeds) outside of the task variable are differnent. How do I make it synchronously?
Do not make it run synchronously. Run it asynchronously, but then synchronize the interaction with feeds. The simplest way to achieve that it to dispatch the updating of the feeds back to the main queue and reloadData for the table view. This eliminates the possibility that you'll be using it from the main queue while it's mutating in the background, but avoids the horrible UX of doing this network request synchronously:
let task = session.dataTaskWithURL(url!) { data, response, error in
if (error != nil) {
println(error)
} else {
var parseError: NSError?
if let jsonresult = NSJSONSerialization.JSONObjectWithData(data, options:nil, error: nil) as? NSDictionary {
if let receivedFeeds = jsonresult["feed"] as? [[String: AnyObject]] {
dispatch_async(dispatch_get_main_queue()) {
self.feeds = [[String: String]]()
for receivedFeed in receivedFeeds {
var outputFeed = [String : String]()
outputFeed["id"] = receivedFeed["id"] as? String
outputFeed["name"] = receivedFeed["name"] as? String
outputFeed["status"] = receivedFeed["status"] as? String
outputFeed["profilePic"] = receivedFeed["profilePic"] as? String
outputFeed["timeStamp"] = receivedFeed["timeStamp"] as? String
outputFeed["url"] = receivedFeed["url"] as? String
self.feeds.append(outputFeed)
}
self.tableView.reloadData()
}
} else {
println("did not find `feed`")
}
} else {
println("problem parsing JSON: \(parseError)")
}
}
}
task.resume()
That should be a little more robust handling errors and employs asynchronous pattern of letting request run asynchronously, but dispatch updating of model object and UI back to the main thread.
let task = session.dataTaskWithURL(url!, completionHandler: {
data, response, error -> Void in
if (error != nil) {
println(error)
} else {
let jsonresult = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var dummyfeed:AnyObject
//println(jsonresult)
for var i = 0; i < jsonresult["feed"]!.count; i++ {
self.feeds.append([String:String]())
dummyfeed = jsonresult["feed"]![i] as NSDictionary
dispatch_async(dispatch_get_main_queue()) {
self.feeds[i]["id"] = dummyfeed["id"] as? String
self.feeds[i]["name"] = dummyfeed["name"] as? String
self.feeds[i]["status"] = dummyfeed["status"] as? String
self.feeds[i]["profilePic"] = dummyfeed["profilePic"] as? String
self.feeds[i]["timeStamp"] = dummyfeed["timeStamp"] as? String
self.feeds[i]["url"] = dummyfeed["url"] as? String
}
}
self.tableView.reloadData()
}
})
task.resume()
Hey Rob, I did what I think you tell me to do, and feeds is still empty :(
I have same problem my code is was working fine, but now, using dataTaskWithURL it didn't return any data, or even error. I think issue is iOS 8.2 I upgraded.