I am trying to make a request to a PHP server from my swift app. For some reason php is showing an empty array as the $_REQUEST variable. I have looked through stack overflow and implemented everything I can find that might help, but still getting an empty array in php. Here is the relevant swift code...
func connect(_ pin: String, completion: #escaping(Result<ConnectResponse?, Error>) -> ()) {
let params: [String : Any] = [
"mobile_pin_connect": pin,
"device_info": UIDevice().model,
"additional_info": UIDevice().systemVersion
]
doRequest(params: params) { (data) in
if let data = data {
do {
let res = try JSONDecoder().decode(Dictionary<String, String>.self, from: data)
completion(.success(
ConnectResponse(success: (res["success"] == "true"), connect_id: res["connect_id"] ?? nil, error: res["error"] ?? nil)))
} catch {
completion(.failure(error))
}
} else {
print("in else block")
}
}
}
fileprivate func doRequest(params: [String: Any], completion: #escaping (Data?) -> ()) {
let body = createJsonBody(params)!
self.request.httpBody = body
print("Sending request with thw following variables")
print(String(data: body, encoding: .utf8)!)
print(String(data: self.request.httpBody!, encoding: .utf8))
URLSession.shared.dataTask(with: self.request) { (data, response, error) in
if let error = error {
print("Error in request: \(error)")
completion(nil)
}
let stringResult = String(data: data!, encoding: .utf8)!
let properResult = String(stringResult.map {
$0 == "." ? "=" : $0
})
let decodedData = Data(base64Encoded: properResult)
completion(decodedData)
}.resume()
}
fileprivate func createJsonBody(_ params: [String: Any]) -> Data? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: params)
let body = Data(jsonData).base64EncodedData()
return body
} catch {
print("Unable to create json body: " + error.localizedDescription, error)
return nil
}
}
That sends the request to the server, the setup for the request is in the static var setup...
private static var sharedConnector: ApiConnector = {
let url = URL(string: "https://mywebsiteURLhere.com/api/mobile/challenge")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let connector = ApiConnector(request)
return connector
}()
So I have the right header values for application/json I have the request method set to post, I am base64encoding the json data and in PHP I have the setup getting php://input...
$rawRequest = file_get_contents("php://input");
and dumping the $_REQUEST variable to an error log, but I always get array\n(\n)\n
it is just showing an empty array
I even did
error_log("Raw request from index.php");
error_log(print_r($rawRequest, true));
and it logs a completely empty line.
I can't figure out why PHP is getting nothing in the request, from everything I have seen online I am doing the request correctly in swift. Any help is really appreciated. Thank you
As per your Swift Code, Can you please replace the following method.
fileprivate func createJsonBody(_ params: [String: Any]) -> Data? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: params)
let body = Data(jsonData)
return body
} catch {
print("Unable to create json body: " + error.localizedDescription, error)
return nil
}
}
You need to replace this line let body = Data(jsonData) with
let body = Data(jsonData).base64EncodedData()
Without seeing your PHP code, it is difficult to determine the entire picture. However, whatever steps you perform to encode your data via the client (Swift) you must reverse to successfully decode the message on the server.
For example, if you prepare and send the request from your client as follows.
Client:
JSON encode data
base-64 encode
send data
The your server must reverse the steps to successfully decode the data.
Server:
recv data
base-64 decode data
JSON decode data
Unless your server requires it, I would remove the base-64 encode step, as it only complicates your encode / decode process.
I have created a working example: https://github.com/stuartcarnie/stackoverflow/tree/master/q59329179
Clone it or pull down the specific code in your own project.
To test, open up a terminal and run the php server:
$ cd q59329179/php
$ php -S localhost:8080 router.php
PHP 7.3.9 Development Server started at Thu Dec 19 10:47:58 2019
Listening on http://localhost:8080
Document root is /Users/stuartcarnie/projects/stackoverflow/q59329179/php
Press Ctrl-C to quit.
Test it works with curl in another terminal session:
$ curl -XPOST localhost:8080 --data-binary '{"string": "foo", "number": 5}'
Note you should see output in the php session:
[Thu Dec 19 11:33:43 2019] Array
(
[string] => foo
[number] => 5
)
Run the Swift test:
$ cd q59329179/swift
$ swift run request
Note again, decoded output in php session:
[Thu Dec 19 11:20:49 2019] Array
(
[string] => string value
[number] => 12345
[bool] =>
)
Your request is probably not arriving through the POST structure, but is kept in the request body.
Try running this as your first PHP operation:
$raw = file_get_contents('php://input');
and see what, if anything, is now into $raw. You should see a Base64 encoded string there, that you need to decode - like this, if you need an array:
$info = json_decode(base64_decode($raw), true);
I've tested your code and it's working fine. The issue might be at your PHP end. I've tested the following code on local server as well as on httpbin
The output from a local server (recent version of XAMPP (php 7.3.12)):
Sending request with thw following variables
eyJhZGRpdGlvbmFsX2luZm8iOiIxMy4yLjIiLCJtb2JpbGVfcGluX2Nvbm5lY3QiOiIxMjM0IiwiZGV2aWNlX2luZm8iOiJpUGhvbmUifQ==
result eyJhZGRpdGlvbmFsX2luZm8iOiIxMy4yLjIiLCJtb2JpbGVfcGluX2Nvbm5lY3QiOiIxMjM0IiwiZGV2aWNlX2luZm8iOiJpUGhvbmUifQ==
message ["additional_info": "13.2.2", "mobile_pin_connect": "1234", "device_info": "iPhone"]
Code:
ApiConnector.swift
import Foundation
import UIKit
class ApiConnector{
var request: URLRequest
private init(request: URLRequest) {
self.request = request
}
public static var sharedConnector: ApiConnector = {
let url = URL(string: "http://localhost/post/index.php")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let connector = ApiConnector(request: request)
return connector
}()
func connect(_ pin: String, completion: #escaping(Result<Dictionary<String, String>, Error>) -> ()) {
let params: [String : Any] = [
"mobile_pin_connect": pin,
"device_info": UIDevice().model,
"additional_info": UIDevice().systemVersion
]
doRequest(params: params) { (data) in
if let data = data {
do {
let res = try JSONDecoder().decode(Dictionary<String, String>.self, from: data)
completion(.success(res))
} catch {
completion(.failure(error))
}
} else {
print("in else block")
}
}
}
fileprivate func doRequest(params: [String: Any], completion: #escaping (Data?) -> ()) {
let body = createJsonBody(params)!
self.request.httpBody = body
print("Sending request with thw following variables")
print(String(data: body, encoding: .utf8)!)
URLSession.shared.dataTask(with: self.request) { (data, response, error) in
if let error = error {
print("Error in request: \(error)")
completion(nil)
}
let stringResult = String(data: data!, encoding: .utf8)!
print("result \(stringResult)")
let properResult = String(stringResult.map {
$0 == "." ? "=" : $0
})
let decodedData = Data(base64Encoded: properResult)
completion(decodedData)
}.resume()
}
fileprivate func createJsonBody(_ params: [String: Any]) -> Data? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: params)
let body = Data(jsonData).base64EncodedData()
return body
} catch {
print("Unable to create json body: " + error.localizedDescription, error)
return nil
}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
let session = URLSession.shared
override func viewDidLoad() {
super.viewDidLoad()
ApiConnector.sharedConnector.connect("1234") { (result) in
switch result {
case .success(let message):
print("message \(message)")
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
index.php
echo file_get_contents("php://input");
You can verify your code by doing a request to https://httpbin.org/post
output:
Sending request with thw following variables
eyJkZXZpY2VfaW5mbyI6ImlQaG9uZSIsImFkZGl0aW9uYWxfaW5mbyI6IjEzLjIuMiIsIm1vYmlsZV9waW5fY29ubmVjdCI6IjEyMzQifQ==
result {
"args": {},
"data": "eyJkZXZpY2VfaW5mbyI6ImlQaG9uZSIsImFkZGl0aW9uYWxfaW5mbyI6IjEzLjIuMiIsIm1vYmlsZV9waW5fY29ubmVjdCI6IjEyMzQifQ==",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-us",
"Content-Length": "108",
"Content-Type": "application/json; charset=utf-8",
"Host": "httpbin.org",
"User-Agent": "SessionTest/1 CFNetwork/1120 Darwin/19.0.0"
},
"json": null,
"origin": "122.173.135.243, 122.173.135.243",
"url": "https://httpbin.org/post"
}
in else block
If you are running an older version of PHP then You might need HTTP_RAW_POST_DATA
Have look at this SO for more info on PHP side.
Related
I have an app in xcode where I will be taking notes and uploading them to a server on the web. I have the following code in xcode to POST. I'm getting an error
The data couldn’t be read because it isn’t in the correct format.
Here are my parameters to upload to php
let parameters: [String: Any] = ["title": title, "post": post]
Here is my create Post code
func createPost(parameters: [String: Any]) {
guard let url = URL(string: "\(prefixUrl)/post.php") else {
print("Did not find url")
return
}
let data = try! JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request ) { (data, res, error) in
if error != nil {
print("error", error?.localizedDescription ?? "")
return
}
do {
if let data = data {
let result = try JSONDecoder().decode([postDataBase].self, from: data)
DispatchQueue.main.sync {
print(result)
}
}else {
print("No Data")
}
} catch let JsonError {
print("fetch json error:", JsonError.localizedDescription)
}
}.resume()
}
The create post is wrapped in a class ViewDataBase with other CRUD commands...
I have a struct postDataBase where i know the error The data couldn’t be read because it isn’t in the correct format. could be happening as well. I've changed these to various data types with no success
struct postDataBase: Decodable {
var ID: String
var title: String
var post: String
}
My php code is here. I use bluehost to support my database so i can have a public domain. If you need my username and password let me know i can change it later.
<?php
$connection=mysqli_connect("localhost","****","****");
$ID = $_POST['ID'];
$title = $_POST['title'];
$post = $_POST['post'];
if(!$connection)
{
die('Connection Failed');
}
else
{
$dbconnect = #mysqli_select_db('mlbroadv_PlantAssistDB', $connection);
if(!$dbconnect)
{
die('Could not connect to Database');
}
else
{
$query = "INSERT INTO Notes (title, post) VALUES ( '$title', '$post');";
mysqli_query($query, $connection) or die(mysqli_error());
echo 'Successfully added.';
echo $query;
}
}
?>
My goal is getting my swift data uploaded to php and into mySQL database. There seems to be a disconnect somewhere...
I think the error lies in the following code where the php files is read in to connect with mySQL and to upload my variables...
EDIT:
let data = try! JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = data
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request ) { (data, res, error) in
EDIT 2: Still Trying to get it
For this line...
let parameters: [String: Any] = ["title": "Fgh", "post": "Fgg"
I get...
Param raw data is coded as: ["post": "Fgg", "title": "Fgh"]
For this line...
let dataParam = try! JSONSerialization.data(withJSONObject: parameters)
I Get...
json serialization request is coded as: 28 bytes
For this line...
URLSession.shared.dataTask(with: request as URLRequest ) { (data, res, error) in
if error != nil {
print("urlsession error is coded as: ", error?.localizedDescription ?? "")
return
}
I Get for the data varaible...
urlsession data is coded as: 235 bytes
and for the res variable, I get the following repsonse...
urlsession results is coded as: <NSHTTPURLResponse: 0x282ea64c0> { URL: https://mlbroadvisions.com/post.php } { Status Code: 200, Headers {
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"text/html; charset=UTF-8"
);
Date = (
"Fri, 22 Jul 2022 16:07:03 GMT"
);
Server = (
cloudflare
);
Vary = (
"Accept-Encoding"
);
"cf-cache-status" = (
DYNAMIC
);
"cf-ray" = (
"72ed6d53f9072be1-ORD"
);
"expect-ct" = (
"max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""
);
"host-header" = (
"c2hhcmVkLmJsdWVob3N0LmNvbQ=="
);
} }
during the ...
catch let error as NSError {
print(error.localizedDescription)
It gives me the error...
The data couldn’t be read because it isn’t in the correct format.
json data is coded as: 235 bytes
What part of my code do i need to change to get it into the correct format i've tried a lot of things none of which worked.
I hope you all are doing well and staying safe! :)
I am new to iOS programming. I am trying to post values from a textfield to a MYSQL Database. As a first step, I tried to just print the values received on the PHP end.
The values are printed on the Swift end but on the PHP end, an empty string(null) is received. Could you please help and let me know why the string received on PHP end is null? Please help! I am stuck and unsure of what to do next.
I have tried retrieving values from the database and that code works perfectly fine.
Thanks in advance!
EDITED:
This has been now fixed. The correct code is below -
Swift Code:
struct DatatoPost : Codable{
var name : String
init() {
name = "Empty String"
}
}
var myDatatoPost = DatatoPost()
//Protocol created for this structure so that it can be applied to multiple class (View Controllers) that create a delegate for it(ManageDataDelegate)
protocol ManageDataDelegate {
func updateViewController(_ myManager : ManageData, saveOutputData: OutputData)
func didFailError(error : Error)
}
/* what action is expected to be taken on data reached through PHP file*/
struct ManageData {
var delegate : ManageDataDelegate?
var data : Data
init() {
data = Data()
}
//create POST URL based on function - this is the URL to post data to the backend.
mutating func postPHPData(){
let urlPostString = myConstant.baseURL + myConstant.postPHPValue
print(urlPostString)
performPostOperation(urlPostString)
}
//Steps to create URL and other related objects - prepare data for parsing.
mutating func performPostOperation(_ urlPostString : String) {
//1. Create URL
if let url = URL(string: urlPostString){
//Create request variable
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
do{
let params : [String : Encodable] = ["name": myDatatoPost.name]
data = try JSONSerialization.data(withJSONObject: params, options: .init())
let body = Data(data).base64EncodedData()
print("Data is: ")
let dataString = String(data: data, encoding: .utf8)
print(dataString)
request.httpBody = body
let requestString = String(data: request.httpBody!, encoding: .utf8)
print("Request String is:")
print(requestString)
//2. Create URLSession
let session = URLSession(configuration: .default)
//3. Give the session a task
let task = session.dataTask(with: request as URLRequest, completionHandler: handlePOST(data:response:error:))
//4. Start the session
task.resume()
}catch{
print(error)
}
}
}
//Function that lists the activities that need to be completed in the URL session - GET Data.
func handlePOST(data : Data?, response : URLResponse?, error: Error?){
if error != nil {
delegate?.didFailError(error: error!)
print("error is")
print(error!)
return
}
if let safeData = data{
let dataString = String(data: safeData, encoding: .utf8)
print(dataString)
}
print(response)
}
}
PHP Code:
<?php
echo "we are here";
// Create connection
$con = mysqli_connect("localhost","username","password","dbname");
echo "we are here again";
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: ";
}
$postdata = json_decode(file_get_contents("php://input"),TRUE);
$decoded = base64_decode($postdata);
echo "decoded!";
print_r($decoded,true);
//this value is not printed
echo "postdata";
echo $postdata->name;
//this value is not printed
$name= $postdata["name"];
echo "Name";
echo $postdata["name"];
//this value is not printed
echo "Base 64";
echo base64_decode($postdata["name"]);
echo base64_decode($postdata->name);
//This value is not printed
if (empty($postdata["name"])){
echo "String is empty";
//this is printed
}
if (empty($postdata->name)){
echo "String is empty";
//This is printed
}
echo "Its done";
// Close connections
mysqli_close($con);
?>
You have specified your request’s Content-Type to be application/json. So it should be JSON, free of base64 encoding. The only time you should use base64-encoding is if you are trying to send binary data in JSON. (And even then, in that case, we would reach for application/x-www-form-urlencoded request, not application/json request.)
Anyway, to send JSON request and parse JSON response:
protocol ManageDataDelegate: class {
func didFailError(error: Error)
func didReceiveResponse(object: Any)
}
/* what action is expected to be taken on data reached through PHP file*/
struct ManageData {
weak var delegate: ManageDataDelegate? // delegates should always be weak to avoid strong reference cycle; obviously `ManageDataDelegate` must be class protocol
//create POST URL based on function - this is the URL to post data to the backend.
func postPHPData(name: String) {
let urlPostString = URL(string: MyConstant.baseURL)!.appendingPathComponent(MyConstant.postPHPValue)
print(urlPostString)
performPostOperation(urlPostString, name: name)
}
//Steps to create URL and other related objects - prepare data for parsing.
func performPostOperation(_ url: URL, name: String) {
//Create request variable
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
do {
let params = ["name": name]
let body = try JSONSerialization.data(withJSONObject: params)
// 1. for diagnostic purposes, let's look at the JSON
if let json = String(data: body, encoding: .utf8) {
print("JSON is:", json)
}
request.httpBody = body
//2. Create URLSession
let session = URLSession(configuration: .default)
//3. Give the session a task
let task = session.dataTask(with: request, completionHandler: handlePOST(data:response:error:))
//4. Start the session
task.resume()
//5. If creating a URLSession locally, make sure to invalidate it when the request is done or else you will leak memory; better, create one `URLSession` and reuse it for all of your requests (and then you don't need/want to invalidate it)
session.finishTasksAndInvalidate()
} catch {
print(error)
}
}
enum ManageDataError: Error {
case unknownError(Data?, URLResponse?, Error?)
case notJsonResponse(String)
}
//Function that lists the activities that need to be completed in the URL session - GET Data.
func handlePOST(data: Data?, response: URLResponse?, error: Error?) {
guard let responseData = data, error == nil else {
let error = error ?? ManageDataError.unknownError(data, response, error)
delegate?.didFailError(error: error)
print("error is")
print(error)
return
}
if let object = try? JSONSerialization.jsonObject(with: responseData) {
delegate?.didReceiveResponse(object: object)
} else if let string = String(data: responseData, encoding: .utf8) {
delegate?.didFailError(error: ManageDataError.notJsonResponse(string))
} else {
delegate?.didFailError(error: ManageDataError.unknownError(data, response, error))
}
print(response ?? "No response")
}
}
There are a variety of refinements buried in the above (if creating a session, you must invalidate it when finished; passing responses to the delegate; making sure delegate reference is weak to avoid strong reference cycles; etc.), but hopefully this illustrates the idea: No base64-encoding is needed or desired with JSON requests.
Note, you have specified the request’s Accept to also be application/json so I would make sure it did precisely that:
<?php
header("Content-Type: application/json");
// Create connection
$con = mysqli_connect("localhost","username","password","dbname");
// Check connection
if (mysqli_connect_errno())
{
$result = [
"success" => false,
"message" => "Failed to connect to MySQL: " . mysqli_connect_error()
];
echo json_encode($result, JSON_PRETTY_PRINT);
exit();
}
$postdata = json_decode(file_get_contents("php://input"), true);
if (isset($postdata["name"]))
{
$result = [
"success" => true,
"name" => $postdata["name"]
];
}
else
{
$result = [
"success" => false,
"message" => "'name' not found",
"postdata" => $postdata
];
}
echo json_encode($result, JSON_PRETTY_PRINT);
// Close connections
mysqli_close($con);
?>
So, this parses the JSON and checks for name, but returning a JSON response, which is what the client was looking for.
There are other refinements I would suggest (e.g. I would use JSONEncoder and JSONDecoder rather than JSONSerialization; not create new URLSession for every request; usually a “network controller” like this would be a reference type so that you can use the same one wherever needed; I would use a framework such as Laravel in the server code, etc.), but I did not want to stray too far from the question at hand.
i am new to swift programming and i am working on a login and registry xcode example i found online.
it said, it works with all sort of backends. so i changed it to work with my login.php file. but i come only so far ...
func handleResponse(for request: URLRequest,
completion: #escaping (Result<[User], Error>) -> Void) {
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
guard let unwrappedResponse = response as? HTTPURLResponse else {
completion(.failure(NetworkingError.badResponse))
return
}
print(unwrappedResponse.statusCode)
switch unwrappedResponse.statusCode {
case 200 ..< 300:
print("success")
default:
print("failure")
}
if let unwrappedError = error {
completion(.failure(unwrappedError))
return
}
if let unwrappedData = data {
do {
let json = try JSONSerialization.jsonObject(with: unwrappedData, options: [])
print(json)
if let users = try? JSONDecoder().decode([User].self, from: unwrappedData) {
completion(.success(users))
} else {
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: unwrappedData)
completion(.failure(errorResponse))
}
} catch {
completion(.failure(error))
}
}
}
}
task.resume()
}
i have this request function.
func request(endpoint: String,
loginObject: Login,
completion: #escaping (Result<User, Error>) -> Void) {
guard let url = URL(string: baseUrl + endpoint) else {
completion(.failure(NetworkingError.badUrl))
return
}
var request = URLRequest(url: url)
do {
let loginData = try JSONEncoder().encode(loginObject)
request.httpBody = loginData
print(loginObject)
} catch {
completion(.failure(NetworkingError.badEncoding))
}
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
handleResponse(for: request, completion: completion)
}
and then got this error
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
so i was searching online, mostly in here, what went wrong. i tested the code and found out where the error comes from and i found out, i should change my code to...
if let unwrappedData = data {
do {
let json = try JSONSerialization.jsonObject(with: unwrappedData, options: [])
print(json)
if let users = try? JSONDecoder().decode([User].self, from: unwrappedData) {
completion(.success(users))
} else {
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: unwrappedData)
completion(.failure(errorResponse))
}
} catch {
completion(.failure(error))
}
}
so i thought that is the proper fix for my problem. but unfortunately i got another error after this change...
Member 'success' in 'Result<User, Error>' produces result of type 'Result<Success, Failure>', but context expects 'Result<User, Error>'
and i cant even build and run the code anymore. can anybody help me?
if necessary i change my login.php from an array to dictionary.
is this the completion closure?
enum MyResult<T, E: Error> {
case success(T)
case failure(E) }
func handleResponse(for request: URLRequest,
completion: #escaping (Result<User, Error>) -> Void) {
... }
enum NetworkingError: Error {
case badUrl
case badResponse
case badEncoding }
ErrorResponse.swift
import Foundation
struct ErrorResponse: Decodable, LocalizedError {
let reason: String
var errorDescription: String? { return reason } }
Login.swift
import Foundation
struct Login: Encodable {
let username: String
let password: String }
User.swift
import Foundation
struct User: Decodable {
let user_id: Int
let username: String
let password: String
let firstname: String
let surname: String
let activated: Int
let reg_time: String
}
print(json) ...
({
activated = 1;
firstname = Thomas;
password = Maggie;
"reg_time" = "0000-00-00 00:00:00";
surname = Ghost;
"user_id" = 2;
username = "testuser";
})
now i am almost back in business. i found out that my user_id in mysql is int(3) but swift 5 doesnt take it as it should. when i take out let user_id: int, i finally get rid off the last error message. but now i can log in with any click of a button and any user and password, whether its right or wrong.
I believe the issue lies in the signature of your completion closure. Looks like you are using Swift.Result but varying the generic Success and Failure types. Can you post the enclosing function where you are passing completion closure?
OK, try changing handleResponse to:
unc handleResponse(for request: URLRequest,
completion: #escaping (Result<[User], Error>) -> Void) {
... }
And ensure that ErrorResponse implements Error
I want to post some data to my php page, I've tested two ways to do so but non of them worked :
let parameters = [
"name": "test",
]
Alamofire.request(URL(string: "http://www.tameshkshop.ir/11.php")!, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [:]).responseJSON { (response) in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
let responseString = String(data: response.data!, encoding: .utf8)
print(responseString)
}
the second way :
var request = URLRequest(url: URL(string: "http://www.tameshkshop.ir/11.php")!)
request.httpMethod = "POST"
let postString = "name=\(FinallFactorViewController.name)"
request.httpBody = postString.data(using: .)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data, encoding: .utf8)
let index = responseString?.index((responseString?.startIndex)!, offsetBy: 4)
let done = responseString?.substring(to: index!)
in the php page, I get the posted value and echo them like this :
echo $_POST['name'];
but it I get nothing in return .
what is wrong ? where am I doing is wrong ?
Your POST service is waiting for for httpBody parameters, not JSON. I did check this on postman. Use the following code to make your request:
let parameters = ["name":"Name"]
Alamofire.request(.POST, "http://tameshkshop.ir/11.php", parameters: parameters)
.responseString(completionHandler: { response in
print(response.result.value)
})
The output:
"test : Name |"
EDIT:
You can't make a request using HTTP since iOS 9. Instead use HTTPS or Allow arbitrary loads in your .plist https://stackoverflow.com/a/33712228/5006492 Before going to production disable Allow arbitrary loads, is a security risk use HTTP. Get a SSL certificate for your site.
I'm trying to do is submit the device IMEI to be inserted into the database.
However, the returned JSON output from the database shows the IMEI as null.
Here's what's been implemented:
Requester
class Requester
{
....
func postRequest(_ url: URL, headers : Dictionary<String,String>?, data: Data?, callback : #escaping (_ response: HTTPResponseWithData) -> Void) -> Void
{
let request = Factory.httpRequest(url, method: "POST", headers: headers, data: data)
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {
data, response, error in
print("RESPONSE: \(response)");
})
task.resume()
}
....
}
Factory
class Factory
{
func httpRequest(_ url: URL, method: String, headers: Dictionary<String, String>?, data: Data?) -> URLRequest
{
var request = URLRequest(url: url)
request.httpMethod = method
if headers != nil
{
for (field, value) in headers!
{
request.addValue(value, forHTTPHeaderField: field)
}
}
if data != nil
{
request.httpBody = data
}
return request
}
}
MainVC
let requester = Requester()
#IBAction func sendRequest(_ sender: Any)
{
var json: Dictionary<String, Any> = [:]
json["imei"] = myIMEI
do
{
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
post(theData: data)
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
func post(theData: Data) -> Void
{
self.requester.postRequest("www.url.com", headers: nil, data: theData, callback: {(response: HTTPResponseWithData) -> Void in
if response.statusCode == 200 && response.data != nil && HTTPHeader.isContentTypeJSON(response.mimeType)
{
print(response.data!)
do
{
if let test = try JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions()) as? Dictionary<String, Any>
{
print("test = \(test)")
}
}
catch
{
print("ERROR parsing data")
}
}
else
{
}
});
}
What I get back from the output is:
test = ["imei": <null>]
I've looked at numerous questions and answers on SO regarding this, and besides my implementation being in different classes, I don't see what could possibly be wrong.
Here's some snippet of the PHP code:
header("Content-Type: application/json");
$imei = $_POST["imei"];
$something_else = $_POST["something_else"];
$mysqli = new mysqli($host, $userid, $password, $database);
if ($mysqli->connect_errno)
{
echo json_encode(array("success" => false, "message" => $mysqli->connect_error, "sqlerrno" => $mysqli->connect_errno));
exit();
}
echo json_encode( array('imei'=>$imei) );
What exactly is wrong with my POST request implementation that is not allowing me to submit the IMEI to the database?
If it helps, the RESPONSE output is:
RESPONSE: Optional( { URL:
http://www.url.com } { status code: 200, headers {
Connection = "Keep-Alive";
"Content-Type" = "application/json";
Date = "Mon, 02 Jan 2017 08:07:54 GMT";
"Keep-Alive" = "timeout=2, max=96";
Server = Apache;
"Transfer-Encoding" = Identity; } })
UPDATE: After further testing, I replaced the above php code after the header with the following code, and now the imei is reported:
$handle = fopen("php://input", "rb");
$raw_post_data = '';
while (!feof($handle))
{
$raw_post_data .= fread($handle, 8192);
}
fclose($handle);
$request_data = json_decode($raw_post_data, true);
$imei = $request_data["imei"];
I'm confused, why is it the case that the updated php code works but the one involving $_POST does not?
See the $_POST documentation which says it is:
An associative array of variables passed to the current script via the HTTP POST method when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type in the request.
But you're not doing x-www-form-urlencoded request. You're performing an application/json request. So you can't use $_POST. Use php://input (e.g., as discussed here: iOS Send JSON data in POST request using NSJSONSerialization).