Use callback to get JSON value from call to database - json

I am trying to populate a UITable with a json result. I call a function that gets the json from the server and stores the result in NSDictionary. I want to be able to use this collection, and then populate a table. I run into a problem however because for the func numberOfRowsInSection I need the count of the collection, and since my json result is within another function inside a try/catch I cant seem to return the value.
This is what I have for the function which I call in ViewDidLoad():
func getSubjects() -> NSDictionary{
let myUrl = NSURL(string: "www.mydomain.com/script.php");
let request = NSMutableURLRequest(url:myUrl as! URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil {
print("error=\(error)")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let resultValue: NSDictionary = parseJSON["subjects"] as! NSDictionary
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
}
If I print resultValue I get what I need, in this example being:
{
1 = (
Maths,
Lecture
);
2 = (
Science,
Lecture
);
3 = (
English,
Seminar
);
}
But the confusion is, how do I go about returning this value? and where? and how would I implement it in the table? If I try to return resultValue when I parse the JSON I get the error that it is unexpected non-void return in void function and if I try to return the value at the end of the function, I get an unresolved identifier error
I feel I am implementing this incorrectly. I have checked many tutorials on this, and no one seems to populate a table with a POST JSON so I don't know how to go about returning the value, or the proper method of implementation. Any help would be greatly appreciated!

The problem your having is that the dictionary hasn't been retrieved by the time you try to return it. You can use an asynchronous callback to get the dictionary after it has been retrieved from your database.
func getSubjects(callback: #escaping (NSDictionary)-> Void){
let myUrl = NSURL(string: "www.mydomain.com/script.php");
let request = NSMutableURLRequest(url:myUrl as! URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil {
print("error=\(error)")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let resultValue: NSDictionary = parseJSON["subjects"] as! NSDictionary
callback(resultValue)
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
}
and then you would call this function like...
getSubjects(callback: {(resultValue)-> Void in
print(resultValue)
//here you could set your tableView data equal to the values in the dictionary that was just received and then call self.tableView.reloadData to update your table view
})
So perhaps in the viewDidLoad() function of you UITableViewController or in the viewDidAppear(_:), depending on the life cycle of your view, you would call getSubjects(...) as i have shown above and then when the callback is called you call self.tableView.reloadData() as I have explained in the function call. If you are unsure how to setup a tableview datasource and delegate then you should probably open another question for that
EDIT
In response to your comment asking how to use the retrieved value from your server as a variable available to your whole class, you could do something like this...
class: ExampleViewController {
var resultsDictionary: [Int: [String: String]]?
override func viewDidLoad(){
getSubjects(callback: {(resultValue)-> Void in
resultsDictionary = resultValue
})
}
//Use the actual getSubjects function I have already shown you above
func getSubjects(){...}
}

Related

How to POST data from multiple view controllers to server with JSON using SWIFT

I need help with combining data collected from firstVC, secondVC, and thirdVC and serializing those in the fourthVC.
This link helps with one VC but I have to send only ONE FILE of JSON DATA to the server.
How to create and send the json data to server using swift language
The other method is passing a dictionary array from firstVC, secondVC, and thirdVC to the fourthVC and from the fourthVC convert the dictionaries into JSON. But i don't know how to do that.
I used the format from the answer provided in the link above, but if you need additional info, I will gladly cooperate. Thanks!
PS. Please give me useful comments that will help in any way. I need the code and not feedbacks like doing my own research and such cause I have been stressing about this for nearly a month now.
This is the UserDefault keys
if let AC = UserDefaults.standard.value(forKey: "Acc") as? String {
labeltext.text = "\(AC)"
}
if let TY = UserDefaults.standard.value(forKey: "Taxyear") as? String {
taxtext.text = "\(TY)"
}
if let BB = UserDefaults.standard.value(forKey: "Bsb") as? String {
bsbtext.text = "\(BB)"
}
Here is my JSON code
#IBAction func save(_ sender: Any){
typealias JSONDictionary = [String:Any]
let parameters = ["BankAccountNumber": "Acc", "Tax Year": "Taxyear", "my-bsb": "Bsb"]
let url = URL(string: "https://server:port/")! //change the url
//create the session object
let session = URLSession.shared
//now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
let valid = JSONSerialization.isValidJSONObject(parameters) // true
print (valid)
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
let alertMessage = UIAlertController(title: "Saved!", message: "We have recorded your information", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title:"Okay", style: UIAlertActionStyle.default, handler: nil)
alertMessage.addAction(action)
self.present(alertMessage, animated: true, completion: nil)
}
I solved it by first storing them in a variable
var TITLE = UserDefaults.standard.value(forKey: "Title")
var GN = UserDefaults.standard.value(forKey: "GivenNames")
var LN = UserDefaults.standard.value(forKey: "LastName")
Then I placed them in a parameter and that's done. It was so obvious that I can't believe I didn't solve it sooner
#IBAction func save(_ sender: Any){
let parameters = ["Tax Year": TaxYear, "Title": TITLE, "first-name": GN, "sur-name": LN]

Load data from server using swift 4

I try to load the user profile like below
#IBAction func Btn_LoadDataFromDataBase(_ sender: UIButton) {
let myurl = "site.com/profile.php"
LoadURL(url: myurl)
}
func LoadURL(url: String) {
do{
let appURL = URL(string: url)! // convert string to URL
let data = try Data(contentsOf: appURL)
//error here on this line below :
let json1 = try JSONSerialization.jsonObject(with: data ) as! [String: Any]
print(json1)
let query1 = json1["profile"] as! [String: Any]
print(query1)
label_email.text = "Email : (query1["email"]!)"
}catch{
print("error in url")
}
}
if I test the json via webbrowser I get it like this:
{profile : [{"0":"999","id":"999","1":"1","email":"blabla#gmail.com","2":"1111","tel":"00122222222","3":"0" ..........
php code:
print "{profile : ".json_encode($user_profile,JSON_UNESCAPED_UNICODE)."}";
mysql_close($db);
?>
Please read the JSON carefully, there are only two different collection types
{} is dictionary ([String: Any])
[] is array ([Any] but in most cases [[String: Any]])
so the result for query1 (I changed the variable names to something more descriptive) is an array and you need a loop to print all elements:
let profileData = try JSONSerialization.jsonObject(with: data ) as! [String: Any]
let profiles = profileData["profile"] as! [[String: Any]] // could be even [[String:String]]
for profile in profiles {
print("Email :", profile["email"]!")
}
I'm wondering why so many owners of web services send the PHP arrays unnecessarily with both index and key.
And never load data from a remote URL synchronously, use asynchronous URLSession
You're better using URLRequest for async requests. You will need to pass your appURL as a parameter in a URLRequest and handle the answer in its completion handler.
An example:
let urlString = "https://swift.mrgott.pro/blog.json"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
// Implement JSON decoding and parsing
do {
let articlesData = try JSONDecoder().decode([OBJECT YOU WANT].self, from: data)
} catch let jsonError {
print(jsonError)
}
}.resume()

Parsing nested Json data in swift 3

I am getting unexpected values back when i am parsing my json data from my api, i may be doing something wrong here as i'm quite new to swift but i was getting correct values before when i was receiving one "key" but now i have added two i cannot seem to parse the values properly.
This is the json collected from the address my code is receiving, (sorry if its hard to read havn't worked out how to do line breaks yet in my ruby api)(as long as its functional im not too worried at the moment)
{
"ratings":{
"elements":{"Ready Position":[{"description":"Neutral Grip","values":"1,2,3,4,5"},{"description":"Back Straight (Concave ir Convex?)","values":"1,2,3,4,5"},{"description":"Body Low \u0026 Feet a little more than sholder width apart","values":"1,2,3,4,5"},{"description":"Weight on Balls of Feet","values":"1,2,3,4,5"},{"description":"Head Up","values":"1,2,3,4,5"},{"description":"Sholder Blades Close","values":"1,2,3,4,5"},{"description":"Eyes Drilled","values":"1,2,3,4,5"}],"Split Step":[{"description":"Ready Position Conforms","values":"Yes,No"},{"description":"Body Position Low","values":"1,2,3,4,5"},{"description":"Legs Loaded/Prepared","values":"1,2,3,4,5"}]}
},
"comments":{}
}
Now, My swift code looks like this
let playerAPIurl = "http://linkcoachuat.herokuapp.com/api/v1/session/element?organisation=" + userorganisation + "&group=" + urlGroupSelected + "&sport=" + usersport
print(playerAPIurl)
var request = URLRequest(url: URL(string: playerAPIurl)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("ERROR")
}
else{
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String: AnyObject]
print(json)
And this is the output im getting from this print(json)
Optional({
comments = {
};
ratings = {
};
})
I know i shouldnt be getting anything more in the comments part, but in the ratings part there should be some data?
so after recieving the json and dealing with parsing it i need to access this part of it ["ratings"]["elements"] and after that im all good
thanks in advance and please bare in mine im very new to swift
Thanks
Try the below code. The url used in below code has your JSON data. This code is printing the output correctly.
func testApi(){
let url = URL(string: "https://api.myjson.com/bins/jfccx")
let session = URLSession.shared
let request = URLRequest(url: url!)
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard let data = data, error == nil else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print(json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}

Ambiguous reference to member `jsonObject(with:options:)` when trying to get data from json

I'm new to Swift and while making one of the tutorials (fairly old) which involves getting credentials from a server through php which returns a JSON, but I'm stuck with the error Ambiguous reference to member jsonObject(with:options:) in the json var, I've searched and trying applying the different solutions but to no avail. :(
Thank you for your time and help.
here is my code:
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
if((userEmail?.isEmpty)! || (userPassword?.isEmpty)!) {
displayMyAlertMessage(userMessage: "All Fields are required.")
return;
}
let myUrl = URL(string: "/UserLogin.php");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "email\(userEmail)&password=\(userPassword)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, URLResponse, error in
if error != nil {
//print = ("error=\(error)");
return
}
var err: Error?
var json = JSONSerialization.jsonObject(with: 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 Succesful
UserDefaults.standard.set(true, forKey:"isUserLoggedIn");
UserDefaults.standard.synchronize();
self.dismiss(animated: true, completion: nil);
}
}
}
task.resume()
There are two major issues:
The actual error occurs because the response parameter in the completion block is wrong. Rather than the type URLResponse it must be a parameter label / variable.
let task = URLSession.shared.dataTask(with: request) { data, response, error in
Since you are using Swift 3 there is no error parameter in jsonObject(with. The method does throw, you need a do - catch block. And – as always – the option .mutableContainers is completely useless in Swift. Omit the parameter.
do {
if let parseJSON = try JSONSerialization.jsonObject(with: data) as? [String:Any],
let resultValue = parseJSON["status"] as? String {
print("result: ", resultValue)
if resultValue == "Success" {
//Login Succesful
UserDefaults.standard.set(true, forKey:"isUserLoggedIn")
self.dismiss(animated: true, completion: nil)
}
}
} catch {
print(error)
}
Some other notes:
To check the text fields safely use optional binding
guard let userEmail = userEmailTextField.text, !userEmail.isEmpty, let userPassword = userPasswordTextField.text, !userPassword.isEmpty else {
displayMyAlertMessage(userMessage: "All Fields are required.")
return
}
Declare Swift constants always as let (for example resultValue)
Do not use NSArray / NSDictionary in Swift. Use native types.
Do not use parentheses around if conditions and trailing semicolons. They are not needed in Swift.
UserDefaults.standard.synchronize() is not needed either.
String.Encoding.utf8 can be reduced to just .utf8.

How to parse JSON data in Swift 3? [duplicate]

This question already has answers here:
Correctly Parsing JSON in Swift 3
(10 answers)
Closed 5 years ago.
I need to get my GPS location from mySQL by PHP in Swift 3. I tried to write the code for get data but it still not work, could you advise me?
JSON Data from PHP:
[{"id":"3752","latitude":"11.2222","longitude":"111.2222","Speed":"0.000000","Volt":"3.97","Percent":"87.000000","Dates":"2017-03-07 22:53:32"}]
Swift 3 code:
import UIKit
//-------- import google map library --------//
import GoogleMaps
import GooglePlaces
class ViewController: UIViewController , GMSMapViewDelegate {
var placesClient: GMSPlacesClient!
override func viewDidLoad() {
super.viewDidLoad()
var abc : String = String()
//-------- Google key for ios --------//
GMSServices.provideAPIKey("XXXXXXXXXX")
GMSPlacesClient.provideAPIKey("XXXXXXXXX")
//--------set URL --------//
let myUrl = URL(string: "http://www.myweb/service.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "";
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
{
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
// Now we can access value of latiutde
let latitude= parseJSON["latitude"] as? String //<---- Here , which i need latitude value
print("latitude = \(latitude)")
}
} catch {
print(error)
}
}
task.resume()
}
I tried to write the code but it show the errors on debug output
let responseString = String(data: data, encoding: .utf8 )
let str = String(data: data, encoding: .utf8 )
let data2 = str?.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data2!, options: []) as! [String: AnyObject]
if let names = json["latitude"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
Error message
Could not cast value of type '__NSSingleObjectArrayI' (0x1065fad60) to
'NSDictionary' (0x1065fb288).
Try casting the json object to a Swift representation directly for a more 'Swifty' access of the underlying data. So you don't need to fuss around with NSNumber etc.
guard let json = JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String: String]] else { return }
guard json.count > 0 else { return }
guard let lattitude = json[0]["lattitude"] else { return }
print("Lattitude received: \(lattitude)")
If you are not sure you'll have a [String: String] object array, you can replace it with a [String: Any] in the cast, then all you need to do is check the type with an optional cast in reading the lattitude. You could add a chained optional then checking for isEmpty to check whether its the lattitude value you want or something went wrong.
I would also advice to pretty much never use ! in your code, try to rely more on optional chaining and guard statements.
Guard statement introduction
Note: a single line guard statement isn't very verbose and might make it very difficult to debug your application. Consider throwing errors or some more debug printing in the body of the guard statement.