I am trying to get login/register on my iOS app to work. I am using mySQL and PHP besides Swift 2.0. For some reason when I try to send my HTTP POST to the PHP-scripts I keep geting the error: "The data couldn’t be read because it isn’t in the correct format."
I am using MAMP for the mySQL server.
let request = NSMutableURLRequest(URL: NSURL.fileURLWithPath("/Users/robin/Programming/xcode/Projects/Quix/php_scripts/userRegister.php"))
request.HTTPMethod = "POST"
let postString = "email=\(userEmail)&password=\(userPassword)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
print(postString)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if (error != nil) {
print("error=\(error)")
}
do {
if let parseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let resultValue = parseJSON["status"] as? String
print("result: \(resultValue)")
var isUserRegistered:Bool = false
if (resultValue == "Success") { isUserRegistered = true }
var messageToDisplay:String = parseJSON["message"] as! String!
if (!isUserRegistered) {
messageToDisplay = parseJSON["message"] as! String!
}
dispatch_async(dispatch_get_main_queue(), {
let myAlert = UIAlertController(title: "Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) { action in
self.dismissViewControllerAnimated(true, completion: nil)
}
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
});
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
task.resume()
In my database I have the table 'users' and the parameters id (auto_increment), email and password. I am using port: 3306 for mySQL aswell. The standard IP for MAMP is 127.0.0.1, should I use the IP: 127.0.0.1 or localhost:3306 aswell?
You are using a file URL with a NSURLSession request. You should use a https:// or http:// request. NSURLSession is for making network requests, not local file system requests. So, if you're running this on the iOS simulator, you can use http://localhost/.... But when you run it on an device, you'll have to supply it a host name that will resolve to your machine running MAMP.
By the way, if you use http, you may need to add a NSExceptionDomains entry as outlined in https://stackoverflow.com/a/31254874/1271826.
Related
Hi all I am trying my hands on Swift and I am trying to post users registration data. I know how to do it firebase but my main project is in php mysql so I want to connect it with swift
#IBAction func signUp(_ sender: Any) {
//check textfield data
checkTextFields()
//create user
let url = NSURL(string: "http://localhost:8888/helo/register.php")
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
//apending body to url
let body = "Fullname=\(name.text!.lowercased())&userName=\(userName.text!.lowercased())&emailAddress=\(emailAddress.text!.lowercased())&password=\(password.text!.lowercased())"
request.httpBody = body.data(using: String.Encoding.utf8)
//lunching
URLSession.shared.dataTaskWithRequest(request as URLRequest, completionHandler: { (data:NSData?, response:URLResponse?, error:NSError?) in
if error == nil{
dispatch_async(dispatch_get_main_queue(),{
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as?
NSDictionary
guard let parseJSON = json else{
print("Error while parsing")
return
}
let id = parseJSON["id"]
if id != nil {
print(parseJSON)
}
}catch{
print("Caugth an error: \(error)")
}
})
}else{
print("error: \(error)")
}
} )
}
I am getting an error on the line where I have commented as as lunching which say
Cannot convert value of type '(NSData?, URLResponse?, NSError?) -> ()' to expected argument type '(Data?, URLResponse?, Error?) -> Void'
I am new to Swift any help is welcome thank you all. I am using Xcode 9
After enough reading, I just realised I was doing a very tedious and using orthodox method when things have improved. I removed the whole code and did everything with Alamofire. Its really easy and straight forward. I will post the code below to help others who encounter similar problems later on.
//Constant that holds the URL for our web servicer
let URL_USER_REGISTER = "http://localhost:8888/members/register.php?"
Alamofire.request(URL_USER_REGISTER, method: .post, parameters: parameters).responseJSON{
response in
//printing response
print(response)
//getting json value from the server
if let result = response.result.value {
//converting it as NSDictionary
let jsonData = result as! NSDictionary
//displaying the message in label
self.lableMessage.text = jsonData.value(forKey: "message") as! String?
}
}
you have to first import Alamofire.
This is my code and I am unable to compile it .
I am trying to login to my server but it doesnt allow me so
Sorry I am new at programming and I've researched on stackoverflow, regarding this error , I can only parse Dictionary or Array. but I've googled by copying my JSON response but it doesnt work.
Any Suggestions would be helpful !!
import UIKit
class Login: UIViewController {
#IBOutlet var Username: UITextField!
#IBOutlet var Password: UITextField!
#IBAction func Login(sender: UIButton) {
let username=Username.text
let password=Password.text
let URL_LOGIN="http://172.22.95.116/SoilCondition/app/getLogin.php?USERNAME=" + username! + "&PASSWORD=" + password!;
let requestURL = NSURL(string: URL_LOGIN)
let request = NSMutableURLRequest(URL: requestURL!)
request.HTTPMethod = "POST"
let postParameters = "username="+username!+"&password="+password!;
request.HTTPBody = postParameters.dataUsingEncoding(NSUTF8StringEncoding)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
data, response, error in guard let data = data where error == nil
else {
print("error: \(error)")
return
}
do{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
if let parseJSON = myJSON{
var msg: String!
msg = parseJSON["message"] as! String?
print(msg)
}
/* if let parseJSON = myJSON {
var msg : String!
msg = parseJSON["message"] as! String?
print(msg)
}*/
/*if data != nil {
json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
println("json: \(json)")
if let dictionary = parseJSON(jsonString) {
println("dictionary: \(dictionary)")
}*/
} catch let parseError{
print(parseError)
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I found this question regarding error code 3840.
As it says there, the problem could be that your server doesn't return valid JSON to you.
Now, you say:
I can only parse Dictionary or Array
I don't know if that means that you are able to actually parse the response you receive from the server into valid JSON here:
let myJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
But if it doesn't then a good place to start could be to verify whether your server actually returns valid JSON to you.
To do so, you could try calling your server directly from cURL or postman and see what you get in return.
Hope that helps you.
So basically I have a login script on my server that returns a result depending on if the user credentials are correct or wrong, and I have an iOS App that sends data to that login script to return the correct or wrong result.
Here is the relevant part of my login page that shows the return code ($userDetails being the TRUE or FALSE check of correct or wrong credentials) :
$userDetails = $dao->getUserDetailsWithHashedPassword($email,$password);
if($userDetails===TRUE) {
$returnValue["status"] = "Success";
$returnValue["message"] = "User logged in !";
echo json_encode($returnValue);
} else {
$returnValue["status"] = "error";
$returnValue["message"] = "User not found";
echo json_encode($returnValue);
}
If anyone needs to see what that getUserDetailsWithHashedPassword() does, click here
Using Postman to test the HTTP POST, everything works fine, I get the correct result when posting email#email.com & testpassword in the body and using the correct Content-Type (application/x-www-form-urlencoded) :
{"status":"error","message":"User not found"}
Now my iOS is supposed to interpret this with this code :
#IBAction func loginButtonPressed(_ sender: AnyObject) {
let userEmail = emailLoginField.text
let userPassword = passwordLoginField.text
// Check for empty fields
if((userEmail?.isEmpty)! || (userPassword?.isEmpty)!) {
// Display alert message
displayMyAlertMessage(userMessage: "All fields are required");
return ;
}
// Send user data to server side
let myUrl = URL(string: "https://support.vincentsolutions.ca/userLogin.php");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let postString = "email=\(userEmail!)&password=\(userPassword!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
URLSession.shared.dataTask(with: request, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
if error != nil {
print ("error=\(error)")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
var resultValue = parseJSON["status"] as? String
print("result: \(resultValue)")
var isUserLoggedIn:Bool = false;
if(resultValue=="Success") {
// Login is successful
UserDefaults.standard.set(true, forKey: "isUserLoggedIn");
UserDefaults.standard.synchronize();
self.performSegue(withIdentifier: "loginSuccesful", sender: self)
}
var messageToDisplay:String = parseJSON["message"] as! String!;
if(!isUserLoggedIn) {
messageToDisplay = parseJSON["message"] as! String!;
}
DispatchQueue.main.async(execute: {
// Display alert message with confirmation.
var myAlert = UIAlertController(title: "Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){ action in
self.dismiss(animated: true, completion: nil);
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil);
});
}
} catch let error as NSError {
print("An error occured: \(error)")
}
}).resume()
Now I'm getting this error when I run the code from the iOS App :
An error occured: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text
did not start with array or object and option to allow fragments not
set."
Does anyone see what could be wrong here ? I've tried looking for that error here on SO and on the internet but couldn't find anything related to my situation.
That is because the response you are receiving from the URL probably not in correct JSON format. I will suggest you to do 2 things -
Try to NSLog the data and response
Try this
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as! NSDictionary
I'm fairly new to programming with Swift in Xcode so I have been recently following the fantastic php sql sign up tutorial by Sergey Kargopolov located here http://swiftdeveloperblog.com/store-user-information-in-mysql-database/.
Unfortunately the tutorial was slightly outdated and needed to be updated for Swift 2.0. I managed to substitute the problematic code by implementing a do/catch statement which seems to work great except when i receive a response from the server side php script I can't seem to navigate to different view controllers based on the result. For example if the response is "Registration Successful" the user is directed to the protected page however if the email address already exists in the database, I want it to stay on the same page.
Currently I have the code working to the point where the alert appears (either user already exists or successful) but when the user clicks "Ok", they are directed to the protected page regardless of the result. I figured it would be a simple case of telling it to go to the next view based on the result in this "if" statement:
if (resultValue == "Success") {
isUserRegistered = true
let next = self.storyboard?.instantiateViewControllerWithIdentifier("ProtectedViewController") as! ProtectedViewController
self.presentViewController(next, animated: true, completion: nil)
}
but it doesn't seem to work. Hopefully it makes sense what i'm trying to do and any help is greatly appreciated.
here is my code:
let myUrl = NSURL(string: "http://localhost/PhpProject1/scripts/registerUser.php")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "userEmail=\(userEmail!)&userFirstName=\(userFirstName)&userLastName=\(userLastName)&userPassword=\(userPassword!)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json {
var resultValue = parseJSON["status"] as! String!
print("result: \(resultValue)")
var isUserRegistered: Bool = false
if (resultValue == "Success") {
isUserRegistered = true
}
var messageToDisplay: String = parseJSON["message"] as! String!
if (!isUserRegistered)
{
messageToDisplay = parseJSON["message"] as! String!
}
dispatch_async(dispatch_get_main_queue(), {
var myAlert = UIAlertController(title: "Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default) { action in
// self.dismissViewControllerAnimated(true, completion: nil)
let next = self.storyboard?.instantiateViewControllerWithIdentifier("ProtectedViewController") as! ProtectedViewController
self.presentViewController(next, animated: true, completion: nil)
}
myAlert.addAction(okAction)
}
)}
} catch { print(error)}
}
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)
}
}
I should also add that when the user registers successfully, I get "Result: 200" in the output window but when the user email already exists I get "Result: 400". I just don't know how to take advantage of this.
Thanks in advance!
From what I can see from your JSON response, parseJSON["status"] is the HTTP statusCode, which is a numerical status code. 200 being success, 400 being bad request, 404 being Not found, 500 being an error.
You could either read the message from the dictionary instead or depending on how your PHP code is working you could cast the status as an Integer and only segue when status is 200. For any other status code you likely want to show the message to the user.
This depends on whether you are intentionally returning status codes based on the result, for example a status code of 401 is un-authorised.
Using the status code properly would be much more reliable than comparing strings.
EDIT:
import Foundation
let jsonData = "{ \"message\": \"Successfully registered new user\", \"status\": 200, \"userEmail\": \"abc\", \"userFirstName\": \"a\", \"userId\": 22, \"userLastName\": \"a\" }".dataUsingEncoding(NSUTF8StringEncoding)
do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json {
let statusCode = parseJSON["status"] as! Int
switch statusCode {
case 200:
print("successfully logged in")
case 400, 401:
print("access denied")
default:
print("error occurred")
}
}
}
Above, I have added an example of how you might approach this in Swift, you just need to change the behaviour depending on which statusCodes you want to deal with and how
Well, I stayed up a little longer and thanks to you Scriptable I was able to figure it out.
Basically all I did was create a new variable:
var statuscheck: Int!
And then after the "if let parseJSON = json {" statement I cast the "status" as an Int like this:
self.statuscheck = (parseJSON["status"]! as! NSString).integerValue
Then after the line beneath the "dispatch_async(dispatch_get_main_queue()":
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default) { action in
I did the check:
if (self.statuscheck == 200) {
let next = self.storyboard?.instantiateViewControllerWithIdentifier("ProtectedViewController") as! ProtectedViewController
self.presentViewController(next, animated: true, completion: nil)
}
This seems to work great now.
Thanks again for all your help Scriptable. You definitely pointed me in the right direction!
i need help with my app login code.
I try to make a Http Request to my local server, a simple login request with mail and password but i've a issue and i don't know how to fix it.
#IBAction func clickConnexionBtn(sender: AnyObject)
{
if(!mailTf.text!.isEmpty && !passwordTf.text!.isEmpty)
{
let mail = mailTf.text!
let password = passwordTf.text!
let param = String(format: "mail=%#&password=%#", arguments: [mail, password]);
let url = NSURL(string: "http://localhost:8888/MoneyManager/login.php")!
RequestObject.sharedInstance.prepareRequest(param, _url: url, _method: "POST")
let task = NSURLSession.sharedSession().dataTaskWithRequest(RequestObject.sharedInstance.request)
{ data, response, error in
if error != nil
{
let alertView = UIAlertController(title: "Error server", message: "Could not connect to server", preferredStyle: UIAlertControllerStyle.Alert)
self.showViewController(alertView, sender: self)
}
else
{
let serverResponse = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Server response: \(serverResponse)")
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? NSDictionary {
print(json)
}
} catch let error2 as NSError {
print(error2.description)
}
}
}
task.resume()
}
}
So this is the action when i click on the 'connexionButton'.
Unfortunately when i try to Serialize the JSON, i have this error:
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}
My PHP code is like:
if($result)
{
$return = array("Success" => 1, "Message" => "Insert user done", "user_id" => $_SESSION['id'], "api_key" => $_SESSION['api_key']);
echo json_encode($return);
}
Here is the code of my 'print("Server ....")'
Server response: Optional(Connexion successfully {"Success":1,"Message":"Insert user done","user_id":"9","api_key":"aa3b0c16fc63efe207be1a7471ac234a"})
Its all the data that i want to get and turn into JSON object
I look many subject but no one of them could help me cause most of the people use Alamofire to perform request.
So please if you have an idea, i listening
Thanks all.