Issue with printing the value of JSON data from POST request - ios

Essentially I have the following function that responds to POST Request and displays JSON data.
I would like to just print the results of this data by printing the value of del_tex
At the top of the ViewController I define the variable structure as:
var structure = [NotesStructure]()
NotesStructure is the structure of the JSON received:
import UIKit
struct NotesStructure: Codable {
let del_tex: String
}
The following is the JSON function that fetches and processes the data. I try to print the value of del_tex but get the error that structure has no value del_tex
private func fetchJSON() {
guard let url = URL(string: "https://example.com/example/example"),
let value = driverName.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "person=\(driverName)&serial=\(peronNum)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.structure = try JSONDecoder().decode([NotesStructure].self,from:data)
DispatchQueue.main.async {
print(self.structure.del_tex)
}
}
catch {
print(error)
}
}.resume()
}

Your result is an array so you first need to access the array, either only the first element
do {
self.structure = try JSONDecoder().decode([NotesStructure].self,from:data)
if let first = self.structure.first {
print(first.del_tex)
}
...
or print the whole array
do {
self.structure = try JSONDecoder().decode([NotesStructure].self,from:data)
for item in self.structure {
print(item.del_tex)
}
...

Related

Keep getting the error “Expected to decode Array<Any> but found a dictionary | Swift

I have the following JSON that is formatted like this:
{
"error":false
}
I understand that is not an array because it does not include square brackets on both sides, but I cannot seem to understand how to properly get Swift to interpret this correctly.
This is the structure I am using:
struct CheckStruct: Decodable {
let error: String?
}
And the following is the function that should read the JSON:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.CheckRecord = try JSONDecoder().decode(Array<CheckStruct>.self,from:data)
DispatchQueue.main.async {
// Do something
}
}
catch {
print(error)
}
}.resume()
}
UPDATE:
If I were to use the results of the function to create an if else statement, how would this look?
For example if results are true do this..
else do this...
Your model should be like this:
struct CheckStruct: Codable {
let error: Bool?
}
And your function should be like this:
private func JSONFunc() {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
print(myData.error)
} catch {
print(error)
}
}.resume()
}
BONUS
//Create Typealias
typealias BoolHandler = ((Bool?) -> Void)
//Create Function with Completion
private func fetchData(_ completion: #escaping BoolHandler) {
guard let url = URL(string: "https://example.com/example/example.php"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "number=\(number)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
do {
let myData= try JSONDecoder().decode(CheckStruct.self, from:data)
DispatchQueue.main.async {
completion(myData.error)
}
} catch {
DispatchQueue.main.async {
completion(nil)
}
}
}.resume()
}
//Call Method
fetchData { isSuccess in
if isSuccess {
// Do something
} else {
// Do something
}
}
I hope it will work for you.
Enjoy.

Why will the object not append to the array?

class NetworkManager{
var articleList = [Article]()
func downloadJsonData() -> Void{
let jsonUrl = "someUrl"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
self.articleList = apiResults.articles
} catch let err{
print(err)
}
}.resume()
}
}
I have also tried to use a for loop to append to the array and that didn't work either. Any help will be appreciated.
First thing I would check is that the data returned is correct.
Is the guard block triggering the return or is the data fine?
Is the JSON able to decode the response correctly?
Are the articles in the apiResults object populated.
The next thing is you are not attempting to append the contents of apiResults.articles to your list, instead you are making your list become what ever apiResults.articles is.
Try the following and see how it runs:
class NetworkManager{
// better declaration syntax
var articleList: [Article] = []
func downloadJsonData() {
let jsonUrl = "https://newsapi.org/v2/everything?sources=nfl-news&apiKey=mykey"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
//appends contents instead of assignment
self.articleList.append(contentsOf: apiResults.articles)
} catch let err{
print(err)
}
}.resume()
}
}

Parsing the Json in tableview using Swift

I am new in parsing json.I made a single view application .I got json from url using this function .
func parseData(){
//created URL
guard let requestURL = URL(string: "https://machla.bh/api-category2") else {return }
//creating URLRequest
var request = URLRequest(url: requestURL)
//setting the method to post
request.httpMethod = "POST"
//creating the post parameter by concatenating the keys and values from text field
var postParameters = ""
postParameters += "key=LpfyirxoNOfP8wPns4nZqTw6DQ4wY A2q6yvpKsof6gkYDTykVXSEonTO2VB HE2zRdqrvsyoyMVyRagWtKAtVuOuNs c7QW5KrgbXS8SqPZ7sIDlPEvhBWyo5 NoObAcor3GlO87nRSaFdxhKDRTiBkK 3pFsTQyffzuBdIBiM8zFra6Yh8NbbC QQaratgFFE2hzLouNEIHq88xaSqum1 C0z7g325i3hixT5oLSo5tvhpvvdTJO WohfqGSakeGz7hsAU"
postParameters += "&path=59"
postParameters += "&language_id=1"
//adding the parameters to request body
request.httpBody = postParameters.data(using: .utf8)
//creating a task to send the post request
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
guard error == nil else {
print("error is \(error!.localizedDescription)")
return
}
guard let data = data else {
print("No data was returned by the request!")
return
}
// print data from request
let str = String(data: data, encoding: .utf8)!
print(str)
for eachFetechedCountry in str
{
let eachCountry=eachFetechedCountry as! [String:Any]
let category = eachCountry["categories"] as! String
let product=eachCountry["products"] as! String
self.fetchcountry.append(Country(categories: category, products: product))
}
self.countryTableView.reloadData()
}
//executing the task
task.resume()
}
I am trying to read categories and products from json using following statement in above function.These statement are not working.How to read categories and products from json?
for eachFetechedCountry in str
{
let eachCountry=eachFetechedCountry as! [String:Any]
let category = eachCountry["categories"] as! String
let product=eachCountry["products"] as! String
self.fetchcountry.append(Country(categories: category, products: product))
}
For populating categories and product into table view i created a class named country
class Country{
var product:String
var category:String
init(categories :String, products:String)
{
self.category=categories
self.product=products
}
}
For populating categories and product into table view i coded
func tableView(_ tableViewr: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = countryTableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text=fetchcountry[indexPath.row].category
cell?.detailTextLabel?.text=fetchcountry[indexPath.row].product
return cell!
}
how to populate categories and product into table view ?
From this link you can download sample project for correction?https://drive.google.com/file/d/0B5pNDpbvZ8SnY3RicXpGN1FYbXc/view?usp=sharing
You should use feature of Swift 4 - Decodable protocol.
let task = session.dataTask(with: request) { data, response, error in
guard error == nil else {
print("error is \(error!.localizedDescription)")
return
}
guard let data = data else {
print("No data was returned by the request!")
return
}
do {
let country = try JSONDecoder().decode(Country.self, from: data)
// do what you want here with Country array
// ...
} catch let error {
// catch error and handled it here
}
DispatchQueue.main.async {
self.countryTableView.reloadData()
}
}
//executing the task
task.resume()
Always update tableview in Main thread!!!
And use those structs:
struct Country: Decodable{
var categories: [Category]
}
struct Category: Decodable {
var name: String
var products: [Product]
}
struct Product: Decodable {
var name: String
}

Parse JSON response with SwiftyJSON without crash

My iOS app is getting JSON response from server
let myURL = NSURL(string: SERVER_URL);
let request = NSMutableURLRequest(URL:myURL!);
request.HTTPMethod = "POST";
let postString = ""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
dispatch_async(dispatch_get_main_queue(),{
var json = JSON(data: data!)
let someInt = json["someInt"].int
let message = json["message"].stringValue
Sometimes server is down or there may be errors in JSON so there will be no such values (message, someInt) and I want to handle it without app crash - what can I do?
With SwiftyJSON, non-optional getters end with Value, and optional getters don't.
So to test if the value is here you can use optional binding with if let:
if let someInt = json["someInt"].int,
message = json["message"].string {
// someInt and message are available here
} else {
// someInt and message are not available
}
Or with guard:
guard let someInt = json["someInt"].int,
message = json["message"].string else {
// error, someInt and message are not available
return
}
// someInt and message are available here
Very simple, probably you already know it, you could protect your code with:
if let someInt = json["someInt"].int {
// do whatever you want with someInt
}
if let message = json["message"].string {
// do whatever you want with message
}
Try this approach:
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if let data = data,
jsonString = NSString(data: data, encoding: NSUTF8StringEncoding)
where error == nil {
var json = JSON(data: data!)
// use some protection as explained before..
} else {
print("error=\(error!.localizedDescription)")
}
}
task.resume()
Let me post my answer too =)
first of all you can implement small extension for failure JSON initializer:
extension JSON {
init?(_ data: NSData?) {
if let data = data {
self.init(data: data)
} else {
return nil
}
}
}
You may put it in global scope with SwiftyJSON imported and forget about forcing unwrap your data before use it in JSON. Same fail initializers can be written for other data types if you use them. Its only for a bit shorter and readable code in future. With many routes or in some cases, for example when you wait from json some single fields, this extension can make your code looks extremely easy and readable:
guard let singleMessage = JSON(data: data)?["message"].string else {return}
Then you need to check for nil in way that you need (in fact explained in previous answers). Probably you searching for fully valid data, so use if-let chain:
let myURL = NSURL(string: SERVER_URL);
let request = NSMutableURLRequest(URL:myURL!);
request.HTTPMethod = "POST";
let postString = ""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
dispatch_async(dispatch_get_main_queue()) {
if let json = JSON(data: data),
someInt = json["someInt"].int,
message = json["message"].string,
// ...
{
// all data here, do what you want
} else {
print("error=\(error)")
return
}
}
}
The best would be to handle using try catch
request.HTTPBody = postdata.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
(data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
var jsondata: AnyObject?
do
{
let jsondata = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(jsondata)
// your code here
}
catch
{
print("Some Error Found")
}
})
}
task.resume()
If you encounter any error, you will receive a message in the console, thus preventing the application from crashing

Making a re-useable function of JSON URL fetching function in SWIFT 2.0

I am stuck in a problem. I think it is all due to my weak basics. I am sure someone can help me easily and put me in the right direction.
I have different segues and all get the data from JSON via remote URL.
So in-short all segues need to open URL and parse JSON and make them into an ARRAY
I have made the first segue and it is working fine.
Now i plan to use the functions where it download JSON and turns it into ARRAY as a common function
I read in another page on stackoverflow that I can declare all common functions outside the class in ViewController
I hope everyone is with me this far.
now in ViewController i declare a function
getDataFromJson(url: String)
This function code looks like following
func getJsonFromURL(url: String)
{
// some class specific tasks
// call the common function with URL
// get an array
let arrJSON = getJsonArrFromURL(url)
for element in arrJSON
{
// assign each element in json to ur table
print("Element: \(element)")
}
// some class specific tasks
}
and this will call the common function declared outside the score of class
getArrFromJson(url: String) -> NSArray
This common function is just very generic.
Take a URL, call it, open it, parse its data into ARRAY and return it back.
The problem i am stuck is where to put the return
It returns empty array as the task is not finished and i am clueless
func getJsonArrFromURL(var url: String) -> NSArray
{
var parseJSON : NSArray?
if ( url == "" )
{
url = self.baseURLHomepage
}
print("Opening a JSON URL \(url)")
let myUrl = NSURL(string: url);
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "GET";
let postString = "";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if ( error != nil )
{
print("Error open JSON url \n\(error)")
return
}
do
{
parseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
}
catch
{
self.showAlert("Error", msg: "Error occurred while trying to process the product information data")
print("Error occured in JSON = \(error)")
}
}
task.resume()
return parseJSON!
}
You can probably add a method like below in any of your class
func post(url: String, info: String, completionHandler: (NSString?, NSError?) -> ()) -> NSURLSessionTask {
let URL = NSURL(string: url)!
let request = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "GET"
let bodyData = info
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
dispatch_async(dispatch_get_main_queue()) {
guard data != nil else {
print("response String is nil")
completionHandler(nil, error)
return
}
if let dataNew = data {
completionHandler(NSString(data: (NSData(base64EncodedData: dataNew, options: NSDataBase64DecodingOptions([])))!, encoding: NSASCIIStringEncoding), nil)
}
}
}
task.resume()
return task
}
and access it anywhere like
let url = "your URL String"
let info = "The data you would like to pass"
yourClassName.post(url, info: info) { responseString, error in
guard responseString != nil else {
print("response String is nil")
print(error)
return
}
do {
if !(responseString as? String)!.isEmpty {
let json = try NSJSONSerialization.JSONObjectWithData((responseString as! String).data, options: NSJSONReadingOptions.init(rawValue: 0))
//process your json here
}
} catch {
print("Error\n \(error)")
return
}
}
Extend your string like follows
extension String {
var data:NSData! {
return dataUsingEncoding(NSUTF8StringEncoding)
}
}

Resources