Json response error handling - ios

My app has a search bar that sends a query to a MySQL database and then receives a JSON response and displays it in a view. This works great when the response is success (there's an actual array and rows in MySQL). However, when the response is empty I would like to display an alertbox with a simple error.
My PHP code on the server is set to echo No rows when the query response is empty, how can I achieve this in my app?
This is what I have going right now:
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
if(searchBar.text!.isEmpty)
{
return
}
doSearch(searchBar.text!)
}
func doSearch(searchWord: String)
{
mySearchBar.resignFirstResponder()
//created NSURL
if let requestURL = NSURL(string: URL_GET_coffees) {
//creating NSMutableURLRequest
let request = NSMutableURLRequest(URL: requestURL)
//setting the method to post
request.HTTPMethod = "POST"
//getting values from search bar text field
let searchText=mySearchBar.text
//creating the post parameter from search bar text field
let postParameters = "searchQuery="+searchText!;
//adding the parameters to request body
request.HTTPBody = postParameters.dataUsingEncoding(NSUTF8StringEncoding)
//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)")
self.displayAlertMessage(error!.localizedDescription)
return;
}
//parsing the response
do {
guard let coffees = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray else {
//Doesn't exist, or isn't an NSArray
return
}
var newcoffees=[face]()
for coffee in coffees {
//getting the data at each index
let coffeeName = coffee["name"] as! String
let coffeeDirectLink = coffee["calculation_toemail_"] as! String
let coffeeImage = coffee["calculation_toemail_"] as! String
let newface = face(name:coffeeName,
directLink: coffeeDirectLink,
image:coffeeImage)
newcoffees.append(newface)
//displaying the data
print("name -> ", coffeeName)
print("direct_link -> ", coffeeDirectLink)
print("image -> ", coffeeImage)
print("===================")
print()
}
dispatch_async(dispatch_get_main_queue(),{
self.faces = newcoffees
self.collectionview.reloadData()
})
let errorMessage = newcoffees["No rows"] as? String
if(errorMessage != nil)
{
// display an alert message
self.displayAlertMessage(errorMessage!)
}
} catch {
print(error)
}
}
//executing the task
task.resume()
}
}
func displayAlertMessage(userMessage: String)
{
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
But what's going on is I'm getting an Cannot subscript a value of type '[face]' with an index of type 'String' where I tell it what the error response is. What can I be doing wrong?

Related

How to know when asynchronous request has completed using a closure?

I am using Alamofire to download data and parse it with JSON. I know that in order to announce that data is available we should use closures and no the NotificationCenter. I do not understand closures though. How would I use a closure to reload a table view once the request has completed?
Here is the code.
func downloadEvents() {
let coreDataObject = CoreDataMethods()
Alamofire.request(URL(string:URL)!)
.responseJSON { response in
switch response.result {
case .success:
// we create the model object
let downloadedEvent = EventModel()
/* we have new data remove old data from core data*/
coreDataObject.deleteData(entityArgument: "Event")
events.removeAll() // remove the data from array this is no longer needed FIX
for JSONData in response.result.value as! [Dictionary<String, Any>] {
/* get the data from JSON and store in the event model*/
downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
/* if the event has an image save the url*/
if let image = JSONData[downloadedEvent.imageString] as? String {
downloadedEvent.eImageURL = image
} else {
/* else we save the default image url */
downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
}
coreDataObject.save(eventParam: downloadedEvent)
}
/* post notification to reload table view FIX */
NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
case .failure(let error):
print("ALAMO REQUEST FIALED: \(error)")
}
}
}
Here is the downloadEvents function with the ability to notify the caller it was successful:
func downloadEvents(completion: #escaping (Bool, String?)-> Void) {
let coreDataObject = CoreDataMethods()
Alamofire.request(URL(string:URL)!)
.responseJSON { response in
switch response.result {
case .success:
// we create the model object
let downloadedEvent = EventModel()
/* we have new data remove old data from core data*/
coreDataObject.deleteData(entityArgument: "Event")
events.removeAll() // remove the data from array this is no longer needed FIX
for JSONData in response.result.value as! [Dictionary<String, Any>] {
/* get the data from JSON and store in the event model*/
downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
/* if the event has an image save the url*/
if let image = JSONData[downloadedEvent.imageString] as? String {
downloadedEvent.eImageURL = image
} else {
/* else we save the default image url */
downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
}
coreDataObject.save(eventParam: downloadedEvent)
}
completion(true, nil)
/* post notification to reload table view FIX */
//NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
case .failure(let error):
print("ALAMO REQUEST FIALED: \(error)")
completion(false, "ALAMO REQUEST FIALED: \(error)")
}
}
}
You would then call the function like this:
func reloadTable(){
downloadEvents { (success, errMsg) in
if success{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
else{
let alertMessage: String
if let err = errMsg{
alertMessage = err
}
else{
alertMessage = "An unknown error occurred."
}
let alert = UIAlertController.init(title: "Request Failed", message: alertMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.cancel, handler: nil))
DispatchQueue.main.async {
self.present(alert, animated: true, completion: nil)
}
}
}
}

Swift 3.0 and web service

Attempting to read our web service into a UITableViewController and it only returns the first record to the simulator. So hoping that someone will be able to look at the code and guide me down the correct path. Ultimate goal is to get it into a UITableViewCell so I can format nicely but just looking to get all the records.
Here is a view of the partial json file that will be returned.
{
"Count":11518,
"Result":[
{
"cuName": "#1",
"charter_Num":
"16328","City":
"Jonesboro",
"State_id": "GA",
"cuName_location": "#1 - Jonesboro, GA"
},
{
"cuName": "#lantec Financial",
"charter_Num": "7965",
"City": "Virginia Beach",
"State_id": "VA",
"cuName_location": "#lantec Financial - Virginia Beach, VA"
}]
}
Here is the code that reads in the json web service and attempts to parse and put in the table.
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
//Commented out the following lines because it doesn't return anything when using the modified code that works
//
// guard let data_list = json as? NSArray else
// {
// return
// }
//This code works but only gives me the 1st record back
if let cu_list = try? json as? [String:Any],
let result = cu_list?["Result"] as? [[String:Any]],
let charter_num = result[0]["charter_Num"] as? String,
let value = result[0]["cuName_location"] as? String, result.count > 0 {
TableData.append(value + " (" + charter_num + ")")
} else {
print("bad json - do some recovery")
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
You are referring the 0th index element from result object and it will only return the 1st record from JSON data. You need to run thru the loop and append the data to array which you need to use for populating the data in UITableView.
func GetCategoryData(){
//get_high_score.php?from=1472409001482&number=10&to=1493657787867
let checklaun = UserDefaults.standard.integer(forKey: "Language")
if checklaun == 1 {
EZLoadingActivity.show("Loading...", disableUI: false)
}else{
EZLoadingActivity.show("جار التحميل...", disableUI: false)
}
DispatchQueue.global(qos: .background).async {
let myUrl = URL(string: GlobleUrl.BASEURL + "advertise_list.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "GET"
// let fromvalue = 1472409001482
// let numbervalue = 10
// let tovalue = 1493657787867
let postString = ""//"from=" + fromvalue + "&" + "number=" + numbervalue + "&" + "to=" + tovalue
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
EZLoadingActivity.hide(false, animated: true)
print("error=\(error)")
let checklaun = UserDefaults.standard.integer(forKey: "Language")
var titlemsga : String = String()
var okmsg : String = String()
if checklaun == 1 {
titlemsga = "Something going wrong"
okmsg = "Ok"
}else{
titlemsga = "حدث خطأ ما"
okmsg = "حسنا"
}
let alert = UIAlertController(title: "", message: titlemsga, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: okmsg, style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
print("response = \(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
DispatchQueue.main.async {
if let parseJSON = json {
print(parseJSON)
let status = parseJSON["status"] as! Bool
if status == true{
EZLoadingActivity.hide(true, animated: false)
self.categoryDataArray = parseJSON["data"] as! NSMutableArray
print("\(self.categoryDataArray)")
self.filterarray = parseJSON["fixed_cat"] as! NSMutableArray
let teamp = self.categoryDataArray .value(forKey: "main_image") as AnyObject
print("\(teamp)")
self.categoryImageArray.setArray(teamp as! [Any])
print("\( self.categoryImageArray)")
// let teamp = self.data .value(forKey: "name") as AnyObject
// print("\(teamp)")
// self.categoryNameArray.setArray(teamp as! [Any])
self.allcategoryTableViewCell.reloadData()
self.allcategoryfilterlistview.reloadData()
// self.tblscoreList.reloadData()
}else{
EZLoadingActivity.hide(false, animated: true)
}
}
}
}
catch {
EZLoadingActivity.hide(false, animated: true)
let checklaun = UserDefaults.standard.integer(forKey: "Language")
var titlemsga : String = String()
var okmsg : String = String()
if checklaun == 1 {
titlemsga = "Something going wrong"
okmsg = "Ok"
}else{
titlemsga = "حدث خطأ ما"
okmsg = "حسنا"
}
let alert = UIAlertController(title: "", message: titlemsga, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: okmsg, style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
print(error)
}
}
task.resume()
}
}

Get value from alert textField

I'm currently trying to get a value from an alert box in swift 3.
The below code is used to prompt the alert and save the data, however, im having trouble with calling back the data and manipulating it so it's just a basic string.
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store it
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
This method is called here:
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
I'm trying to call it and assign it in between a string as:
let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
However, this gives me the output:
http://Optional():3000/unLock
When I try to print it.
Any nudge in the correct direction would be greatly appreciated.
Class Added:
class ViewController: UIViewController {
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store your data
//this could be lock unique key name etc in future
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
//view did appear for the alert
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
//to post to an /unLock it must be put in the URL
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
//let url_to_lock:String = "http://\(textField):3000/Lock"
let url_to_unlock:String = "http://10.73.195.218:3000/unLock"
let url_to_lock:String = "http://10.73.195.218:3000/Lock"
override func viewDidLoad() {
super.viewDidLoad()
}
var Timestamp: String {
return "\(NSDate().timeIntervalSince1970 * 1000)"
}
func un_lock()
{
print(url_to_unlock)
let url:URL = URL(string: url_to_unlock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=unLocking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
func lock()
{
let url:URL = URL(string: url_to_lock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=Locking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
#IBAction func lock(_ sender: UIButton) {
lock()
}
#IBAction func unLock(_ sender: Any) {
un_lock()
}
}
Thank you.
The value for this is optional:
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
try:
let url = UserDefaults.standard.value(forKey: "userIP")!
let url_to_unlock:String = "http://\(url):3000/unLock"

How to use closures to return a JSON value from a NSURLSession

I am trying to return a result from a JSON object but unable to do so. I am new to Swift so kindly explain me how to do so. In the below code I want to return json_level_number in the return of function fetchNumberOfSections () where i have hard coded as 5 right now return 5. If i declare a variable json_level_number just above the reachability code it sort of solves the problem but then it is returning '0' for the first time. The API returns 2 each time.
Code as below:
func fetchNumberOfSections () -> Int {
if Reachability.isConnectedToNetwork() == true {
// Below code to fetch number of sections
var urlAsString = "http://themostplayed.com/rest/fetch_challenge_sections.php"
urlAsString = urlAsString+"?apiKey="+apiKey
print (urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
do {
let jsonResult = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
let json_level_number: String! = jsonResult["level_number"] as! String
//
dispatch_async(dispatch_get_main_queue(), {
// self.dateLabel.text = jsonDate
// self.timeLabel.text = jsonTime
print(json_level_number)
// self.activityIndicatorStop()
})
}
catch let errorJSON {
print (errorJSON)
// alert box code below
let alert = UIAlertController(title: "JSON Error!", message:"Error processing JSON.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
self.dismissViewControllerAnimated(true, completion: {})
}
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
// alert box code end
}
})
jsonQuery.resume()
// End
}
else {
print("Internet connection FAILED")
self.performSegueWithIdentifier("nointernet", sender: nil)
}
return 5
}

Swift Convert string into UIIMAGE

I would like to load an image from an api web service asynchronously into a uitableview with swift for iOS 9. Below is the code from my Playlist controller. Thanks in advance.
import UIKit
class PlaylistViewController: UITableViewController {
var playlists = [[String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://xxxxxxx.xxx/api/v1/players/1/playlists?api_key=xxxxxxxxxxxx"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = JSON(data: data)
if json != nil {
parseJSON(json)
} else {
showError()
}
} else {
showError()
}
} else {
showError()
}
}
func showError() {
let ac = UIAlertController(title: "Loading error", message: "There was a problem loading the feed; please check your connection and try again.", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
func parseJSON(json: JSON) {
for result in json["playlists"].arrayValue {
let title = result["title"].stringValue
let id = result["id"].stringValue
let cover_url = result["cover_url"].stringValue
let obj = ["title": title, "id": id, "cover_url" : cover_url]
playlists.append(obj)
}
tableView.reloadData()
}
Use NSURLSession dataTaskWithURL for asynchronously task:
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://xxxxxxx.xxx/api/v1/players/1/playlists?api_key=xxxxxxxxxxxx"
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
var task = session.dataTaskWithURL(url) { (data, response, error) -> Void in
if let err = error {
showError(err)
} else {
let json = NSString(data: data, encoding: NSUTF8StringEncoding)
// json is a String, you should handle this String as JSON
parseJSON(json)
}
}
}
Your tableView.reloadData() should be executed in main thread (because the NSURLSession dataTaskWithUrl result is in background thread)
dispatch_async(dispatch_get_main_queue(), {
tableView.reloadData()
})

Resources