Download and parse a JSON with swift - ios

Can't figure out why this don't work...
when i run it, i have this error in the console:
[CONNECTION] OK, data correctly downloaded
[ERROR] An error has happened with parsing of json data
nil
maybe is the JSON format in the link that i pass in the salvaJson() function.
This is the viewController:
//MARK:proprietà
#IBOutlet weak var meteoLabel: UILabel!
#IBOutlet weak var descrizioneLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let data = salvaJson()
let json = json_parseData(data!)
print(json)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func salvaJson()->NSData?{
guard let url = NSURL(string: "http://www.medialweb.it/corsi_ok.json") else {
return nil
}
guard let data = NSData(contentsOfURL: url) else {
print("[ERROR] There is an unspecified error with the connection")
return nil
}
print("[CONNECTION] OK, data correctly downloaded")
return data
}
// funzione per la generazione del json a partire da un NSData
func json_parseData(data: NSData) -> NSDictionary? {
do {
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
print("[JSON] OK!")
return (json as? NSDictionary)
} catch _ {
print("[ERROR] An error has happened with parsing of json data")
return nil
}
}
I have removed the comment at the top o JSON. json_parseData()
Now print:[JSON] OK! but i still have nil instead of the printend json

Problem is your JSON cause json result must be start with array
I tested your code it is working code :
guard let url = NSURL(string: "http://pokeapi.co/api/v2/pokemon/1/") else {
return nil
}
Pokemon Webapi and test your json format
UPDATE :
how can i print the error?
do {
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
print("[JSON] OK!")
return (json as? NSDictionary)
} catch {
//print("[ERROR] An error has happened with parsing of json data")
print("\(error)")
return nil
}

The JSON doesn't seem to be valid. Here you can find a JSON validator that fails with the URL you have.

agree with Above, you have to make sure your JSON data is in valid format. Try to remove these lines in your JSON data
/**
Export to JSON plugin for PHPMyAdmin
#version 0.1
*/
// Database 'convegnoagi2016_it_db'
// convegnoagi2016_it_db.v_prenotazioni_workshop

Related

The data couldn't be read because it isn't in the correct format

Hello I am creating an app with Xcode and I am having the following problem, I created this API with mockapi.io (if you enter the link you'll see the JSON data) https://62858a2ff0e8f0bb7c057f14.mockapi.io/categorias
If you dont want to enter the link here is how it looks the JSON: (By default the JSON has an array without name as the root and that can't be modified)
[
{
"categorie":"Fruits",
"id":"1"
},
{
"categorie":"Animals",
"id":"2"
},
{
"categorie":"Vegetables",
"id":"3"
},
{
"categorie":"Juices",
"id":"4"
},
{
"categorie":"Alcohol",
"id":"5"
},
{
"categorie":"Desserts",
"id":"6"
}
]
The problem I have is that when I try to decode the data from the API it cant't be readed because is in the wrong format, I am trying to recreate the same code of this youtube video, but with my API: https://www.youtube.com/watch?v=sqo844saoC4
This is how my code looks like:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = "https://62858a2ff0e8f0bb7c057f14.mockapi.io/categorias"
getData(from: url)
}
private func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
}
catch {
print("failed to convert\(error.localizedDescription)")
}
guard let json = result else {
return
}
print(json.items.categorie) // 👈 HERE ES WHERE THE PRINT HAPPENS
})
task.resume()
}
}
// 👇 I THINK THE PROBLEM IS DEFINITELY HERE
struct Response: Codable {
let items: ResultItem
}
struct ResultItem: Codable {
let categorie: String
}
When I execute this the terminal print: "The data couldn't be read becouse it isn't in the correct format."
I am pretty sure the error comes of the way I am calling the data in the structs, so my question is...? How can I exactly call the data from my API's JSON in the code?
yes ,there is an issue in your model you don't need to use the (Response) only use the Model (ResultItem) the JSON isn't complex JSON like that it just array of (ResultItem)
private func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
do {
let result = try JSONDecoder().decode([ResultItem].self, from: data)
print(result)
}
catch {
print("failed to convert\(error.localizedDescription)")
}
})
task.resume()
}
struct ResultItem: Codable {
let categorie: String
}
The response you get is an array of ResultItems rather than a single object, so you need to decode it as an array:
result = try JSONDecoder().decode(Array<ResultItem>.self, from: data)
That said, you won't need the Response struct at all and the type of result will be [ResultItem].

Invalid conversion from throwing function type (_,_,_) throws to non-throwing function type (Data?, Response?, URLResponse?) Void

I am new at Swift programming and I am getting an error on a "let task = URLSession. I have researched and found some of those errors but none of the answers worked for me. Here is my code and line 44 throws this error
import UIKit
class loginViewController: UIViewController {
#IBOutlet weak var userLoginTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func loginButtonTapped(_ sender: Any) {
let user_login = userLoginTextField.text;
let user_passwd = userPasswordTextField.text;
if((user_passwd?.isEmpty)! || (user_passwd?.isEmpty)!) { return; }
//Send user data to server
let myUrl = NSURL(string: "https://www.pcpusa.net/legacy/iOS/userLogin.php");
var request = URLRequest(url: myUrl! as URL);
request.httpMethod = "POST";
let postString = "login=\(String(describing: user_login))&password=\(String(describing: user_passwd))";
request.httpBody = postString.data(using: String.Encoding.utf8);
**let task = URLSession.shared.dataTask(with: request){**
data,response,error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
var json = try JSONSerialization.JSONObjectWithData(data, options: .mutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
//Login is successful
NSUserDefaults.standardUserDefaults().setBool(true, value(forKey: "isUserLoggedIn"));
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion:nil);
}
}
}
task.resume()
}
}
URLSession.dataTask expects a non-throwing completion handler and you are passing it a closure that throws. Because of this it fails to infer the method (in other words: it can’t find a method with that name that accepts a closure that throws).
To solve this you should catch and handle the errors that can happen when you decode the JSON.
The key to that error message is "throwing function type". In Swift, whether or not a function can throw an error is part of its method signature. URLSession is expecting a closure/function that will not throw an error.
Therefore, if you throw an error, you must catch it.
So, you basically have two options here:
Wrap the throwable try in a do-try-catch block.
Use optionals to ignore possible errors and safely unwrap the result.
Example of option 1:
var json: [String: Any]!
do {
json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]
} catch let error { // `let error` is optional. It is the default.
// Handle error here
}
Example of option 2:
(Also, I suggest using the guard statement for situations like this. This is my preferred method most of the time, and is considered by many to be more "Swifty")
let task = URLSession.shared.dataTask(with: request) {
data,response,error in
guard error != nil else {
print("error=\(String(describing: error))")
return
}
guard
let data = data,
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers),
let json = jsonObject as? [String: Any]
else {
print("Could not parse JSON")
return
}
if let status = json["status"] as? String {
print(status)
}
}
EDIT: Adds surrounding context to option 2.
More on guard. It basically acts as an inside-out if-else statement.
You can use guard to test regular booleans. Or, like if, you can combine it with let to make a guard-let statement.
The difference between the two is:
You use if-let when you say, "If this exists, I'll do something with it. Then I'll continue."
You use guard-let when you say, "If I don't have this thing, then I can't continue".
Because of this, you must stop your function in its else statement, usually by returning or throwing an error (which you can't do here).
The problem is you try to decode (which can throw). Since you don't catch anywhere URLSession.dataTask complains because it doesn't expect that closure to throw. (If a method or closure can throw it will be marked with the throws keyword, this is an Apple provided method that does not have that keyword)
You just need to wrap in a do catch like so:
let task = URLSession.shared.dataTask(with: request){ (data,response,error) in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
var json = try JSONSerialization.JSONObjectWithData(data, options: .mutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
...
} catch {
//print("Error: unable to serialize data: \(err)")
}
}

Invalid value around character 0 swift

I hope you are having a great day. I encountered an error that I did some research about. The error I think means that my JSON object is not a proper one to be serialized by the JSONSerialization class on swift 3.0. I verified that my json object is not valid by using the method .isValidJSONObject of the JSONSerialization class.
I checked that my json object is valid. The error occure at the line where "JSONSerialization.jsonObject" method execute. I would love if you can help me figure out how to solve this problem. Feel free to ask for more code parts or project settings. Thanks in advance.
Here is the code used:
import UIKit
class ViewController: UIViewController, NSURLConnectionDataDelegate {
lazy var receivedData = NSMutableData()
override func viewDidLoad() {
super.viewDidLoad()
var url = NSURL(string:"http://localhost:8080/OurServer/webresources/server")!
var request = NSURLRequest(url: url as URL)
var connection = NSURLConnection(request: request as URLRequest, delegate: self, startImmediately: false)!
connection.start()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func connection(_ connection: NSURLConnection, didReceive data: Data)
{
receivedData.append(data)
var temp1 = receivedData as NSMutableData
do
{
var temp3 = JSONSerialization.isValidJSONObject(receivedData)
var jsonResult = try JSONSerialization.jsonObject(with: receivedData as Data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary
print("\n")
print(jsonResult)
}
catch let error as NSError
{
print("\n" + "\(error)")
}
}
}
As mentioned in the comment NSURLConnection is outdated.
This is a modern, Swift 3 compliant version of your code using URLSession
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string:"http://localhost:8080/OurServer/webresources/server")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
print("\n", jsonResult)
} catch {
print("\n", error)
}
}
task.resume()
}
}
After checking, it appears i made a not very smart mistake. The mistake was with my url. excuse me for that guys.

JSON parsing in its own separate function is not working

All the tutorials that I have seen or read about Swift 3 JSON parsing include placing JSON parsing code inside viewDidLoad() func/method and it works great. I want to place JSON parsing code in its own function and call it from viewDidLoad(). Check below example:
class ViewController: UIViewController {
var ArrayImages = [String]();
override func viewDidLoad() {
super.viewDidLoad()
var json = ParseJson();
print("My Array = \(ArrayImages)");
}
// NEW FUNCTION
func ParseJson() {
let url = URL(string: "http://etasawoq.com/go_categories.php")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if (error != nil){
print("Error Found Creating URLSession : \(error)")
} else {
if let ParsedJson = data {
do {
let json = try JSONSerialization.jsonObject(with: ParsedJson, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
for x in json {
let row = x as! NSDictionary;
let imageUrl = row["image_url"] as! String
self.ArrayImages.append(imageUrl);
}
} catch {
print("Json Processing failed \(error)");
}
}
}
}.resume() // start session
}
}
Unfortunately, the "ArrayImages" is not being populated when calling the ParseJson function. The print output is "My Array = []". Why is that? How can I solve this problem while keeping JSON parsing code in a separate function?
Your response getting through block so you need to print array in side the block not outside as below .
do {
let json = try JSONSerialization.jsonObject(with: ParsedJson, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
for x in json {
let row = x as! NSDictionary;
let imageUrl = row["image_url"] as! String
self.ArrayImages.append(imageUrl);
}
print("My Array = \(ArrayImages)");
}

Connect to an online database (mySQL) from Swift, using PHP. (XCODE)

I want to connect my xcode app to online database and get the data from it and display in my app + write the data into online database using my app. I've already done with app but now it gives me an error.
ERROR :
I have my online database in my web page and i have uploaded two php files into the file manager in my web. One php file retrieving all the data in my database and encoding them to json. And second php file doing the query to write data into my online database from my app.
As in above pic im getting json output successfully but when i try to get the data into an array in xcode it gives me that error.
This is my code
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableview: UITableView!
#IBOutlet var inputFriendName: UITextField!
#IBOutlet var inputFriendInfo: UITextField!
var data: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
data = dataOfJson("http://bishanonline.com/extra/serviceselect.php")
println(data)
}
#IBAction func reload() {
data = dataOfJson("http://bishanonline.com/extra/serviceselect.php")
self.tableview.reloadData()
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: additionInfoCell = self.tableview.dequeueReusableCellWithIdentifier("customCell") as additionInfoCell
var maindata = (data[indexPath.row] as NSDictionary)
cell.friendName!.text = maindata["Name"] as? String
cell.friendInfo!.text = maindata["Additional Info"] as? String
return cell
}
#IBAction func uploadToDatabase() {
var url: NSString = "http://bishanonline.com/extra/servicequery.php?x=\(inputFriendName.text)&y=\(inputFriendInfo.text)"
url = url.stringByReplacingOccurrencesOfString(" ", withString: "%20")
url = url.stringByReplacingOccurrencesOfString("/n", withString: "%0A")
var data = NSData(contentsOfURL: NSURL(string: url)!)
var result = NSString(data: data!, encoding: NSUTF8StringEncoding)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Issue is in this code lines
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
Please help me to get json data into array. Appreciate any help.
Finally problem resolved.First i am going to elaborate the exact problem then the solution will be posted.
The code you were doing was totally fine but the real problem was your backend
For serviceselect.php
The code you have done for fetching records is
func dataOfJson(url: String) -> NSArray
{
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
This above method is returing NSArray but the data you are getting from the server is kinda messed up because along with JSON data some garbage data is included as well.Check out the below image
So when try to generate JSON data from above string we are getting crashes and errors.
May be due to free hosting service we are getting this message (Not sure)
Solution
func getallrecords(){
let url = NSURL(string: "http://bishanonline.com/extra/serviceselect.php")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
var d = NSString(data: data, encoding: NSUTF8StringEncoding)
var arr = d!.componentsSeparatedByString("<") // spliting the incoming string from "<" operator because before that operator is our required data and storing in array
var dataweneed:NSString = arr[0] as NSString // arr[0] is the data before "<" operator and arr[1] is actually no use for us
if let data = NSJSONSerialization.JSONObjectWithData(dataweneed.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSArray
// JSONObjectWithData always have first argument of NSData but our dataweneed is actually NSString so we are actually converting NSString to NSData
{
for dd in data{
var name : String = dd["Name"]! as String
var info : String = dd["Additional Info"]! as String
println("Name is : \(name)") // MainDeveloper for 0 and BestBuddy for 1 index
println("Info is : \(info)") // Bishan for 0 and AkilaPrabath for 1 index
}
}
}
task.resume()
}
Final output
For servicequery.php
func addrecord(x:String,y:String){
let request = NSMutableURLRequest(URL: NSURL(string: "http://bishanonline.com/extra/servicequery.php")!)
var postString : String = "x="+x+"&y="+y
request.HTTPMethod = "GET"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
if jsonResult as String == "Successfully added "
{
// Show an alert to notify user
}
}
task.resume()
}
Also remove "echo $query;" on line 30 of servicequery.php
Try this code to parse JSON from server
//created NSURL
let requestURL = NSURL(string: URL_GET_TEAMS)
//creating NSMutableURLRequest
let request = NSMutableURLRequest(URL: requestURL!)
//setting the method to post
request.HTTPMethod = "GET"
//creating a task to send the post request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
//exiting if there is some error
if error != nil{
print("error is \(error)")
return;
}
//parsing the response
do {
//converting resonse to NSArray
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
//looping through all the json objects in the array teams
for i in 0 ..< myJSON.count{
myJSON[i]["object key here"]
}
} catch {
print(error)
}
}
//executing the task
task.resume()
Source: json parsing in ios swift

Resources