I am writing a simple application for iOS in swift that makes an ajax call to a server of mine. Here is the relevant swift code:
class Request : NSObject {
func send(url: String, f: (NSData)-> ()) {
var request = NSURLRequest(URL: NSURL(string: url)!)
var response: NSURLResponse?
var error: NSErrorPointer = nil
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)
//var reply = NSString(data: data!, encoding: NSUTF8StringEncoding)
f(data!)
}
}
class myObj: NSObject {
let baseURL: String
var message: String
init() {
self.baseURL = XXXXXXXXXXXXX
self.message = "No Message"
}
func check() -> Bool {
let locationURL = self.baseURL
self.message = locationURL
var request = Request()
request.send(locationURL, f: {(result: NSData)-> () in
let jsonData: NSData = result
var error: NSError?
let jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as! NSDictionary
self.message = jsonDict["title"] as! String
})
return true
}
}
Here is the server side code that I return dummy JSON with:
<?php
header('Content-Type: application/json; charset=utf-8');
if(isset($_GET['location'])) {
echo json_encode(Array( 'success' => true, 'message' => 'I came from the server.', 'title' => 'tittttt'));
} else if(isset($_POST['message'])) {
echo json_encode(Array( 'success' => true, 'message' => 'message received', 'title' => 'ttt'));
} else {
echo json_encode(Array( 'success' => false, 'message' => 'invalid params', 'title' => 'title from server'));
}
?>
When I switch out my URL for a dummy json url (I am using http://jsonplaceholder.typicode.com/posts/1?a=b) it works; when I use my own URL it fails with the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
What am I doing wrong here?
EDIT: Here is the actual JSON response from the server:
{
success: true,
message: "I came from the server.",
title: "tit-le" }
Have u tried adding
header("Access-Control-Allow-Origin: *");
To your PHP
Related
This is how I picked a file from device
onPressed: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: [
'jpg',
'pdf',
'doc'
],
);
List<File> files = result!.paths
.map((path) => File(path!))
.toList();
context
.read<Dropper>()
.fileCheck(result: result);
myfile = files[0];
},
Then I Converted to Uint8List :
Uint8List imgbytes = await myFile.readAsBytes();
Now I am sending that file to Php database
final url = "http://10.0.2.2:8000/api/addSurvey";
final uri = Uri.parse(url);
final response = await api.post(uri, headers: {
'Authorization': token,
}, body: {
"bill_image": imgbytes
}
It throws error msg like this : type 'Uint8List' is not a subtype of type 'String' in type cast
var url = "http://10.0.2.2:8000/api/addSurvey";
Map<String, String> headers = {"Content-Type": "application/json",
'Authorization': token,};
var request = http.MultipartRequest("POST", Uri.parse(url));
if(_image != null){
var pic = await http.MultipartFile.fromBytes('bill_image', imgbytes , filename: 'photo.jpg');
request.files.add(pic);
}
request.send().then((result) async {
http.Response.fromStream(result).then((response) async {
if (response.statusCode == 200)
{
}
else{
}
return response.body;
});
}).catchError((err) => print('merror : '+err.toString())).whenComplete(()
{
});
Try it using multipart request.
if php is accepting parameter as file type File then you need to send image as multipart request i guess.
var headers = {
'Authorization': 'token'
};
var request = http.MultipartRequest('POST', Uri.parse('http://10.0.2.2:8000/api/addSurvey'));
request.files.add(await http.MultipartFile.fromPath('bill_image', 'path to your file'));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
}
else {
print(response.reasonPhrase);
}
I am trying to create simple login and signup in swift with php mysql in backend. I was following "https://github.com/EMacco/ios-tutorials/tree/yummie-network-connection/Yummie" this project
I get the following error
The response is: {"data":[{"response":"Success","username":"Mohammad Tariq Shamim"}]} Error Response could not be decoded
I am getting response from the server but JSON is not getting decoded. Following is my code
enum Route {
static let baseurl = "http://arabscholar.net/android/"
case login
var description: String {
switch self {
case .login: return "login.php"
}
}
}
struct User: Decodable{ let username: String? let response: String? }
struct ApiResponse<T: Decodable>: Decodable {
let status: Int
let data: T?
let error: String?
}
AppError Swift file
enum AppError: LocalizedError {
case errorDecoding
case unknownError
case invalidUrl
case serverError(String)
var errorDescription: String? {
switch self {
case .errorDecoding:
return "Response could not be decoded"
case .unknownError:
return "Unknown error"
case .invalidUrl:
return "Url invalid"
case .serverError(let error):
return error
}
}
}
Main networking struct file
struct NetworkService {
static let shared = NetworkService()
private init() {}
func signin(username: String, password: String, completion: #escaping(Result<User, Error>) -> Void) {
let params = ["login": "user", "email": username, "password": password]
request(route: .login, method: .post, parameters: params, completion: completion)
}
private func request<T: Decodable>(route: Route, method: Method, parameters: [String: Any]? = nil, completion: #escaping(Result<T, Error>) -> Void) {
guard let request = createRequest(route: route, method: method, parameters: parameters) else {
completion(.failure(AppError.unknownError))
return
}
URLSession.shared.dataTask(with: request) { data, response, error in
var result: Result<Data, Error>?
if let data = data {
result = .success(data)
let responseString = String(data: data, encoding: .utf8) ?? "Could not stringify our data"
print("The response is: (responseString)")
} else if let error = error {
result = .failure(error)
print("The error is " + error.localizedDescription)
}
DispatchQueue.main.async {
self.handleResponse(result: result, completion: completion)
}
}.resume()
}
private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
guard let result = result else {
completion(.failure(AppError.unknownError))
return
}
switch result {
case .success(let data):
let decoder = JSONDecoder()
guard let response = try? decoder.decode(ApiResponse.self, from: data) else {
completion(.failure(AppError.errorDecoding))
return
}
if let error = response.error {
completion(.failure(AppError.serverError(error)))
return
}
if let decodedData = response.data {
completion(.success(decodedData))
} else {
completion(.failure(AppError.unknownError))
}
case .failure(let error):
completion(.failure(error))
}
}
private func createRequest(route: Route, method: Method, parameters: [String: Any]? = nil) -> URLRequest? {
let urlString = Route.baseurl + route.description
guard let url = urlString.asUrl else {return nil}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = method.rawValue
if let params = parameters {
switch method {
case .get:
var urlComponent = URLComponents(string: urlString)
urlComponent?.queryItems = params.map { URLQueryItem(name: $0, value: "($1)")}
urlRequest.url = urlComponent?.url
case .post:
let bodyData = try?JSONSerialization.data(withJSONObject: params, options: [])
urlRequest.httpBody = bodyData
}
}
return urlRequest
}
}
My PHP code
$postdata = json_decode( file_get_contents( "php://input" ), TRUE );
//$id = $postdata[ "email" ];
//$name = $postdata[ "password" ];
$result[ 'response' ] = "Success";
$result[ 'username' ] = "Mohammad Tariq Shamim";
// Store values in an array
$returnValue = array( $result );
$array['data'] = $returnValue;
// Send back request in JSON format
echo json_encode( $array );
//echo json_encode( array( 'response' => 'No data found for ' . $_POST[ 'email' ] ) );
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).
I get the following error when i try to get data. In the internet i read that its because the php script is invalid and don't return json data. But the php script runs fine and outputs the right data.
Error Message :
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
I tried to allow fragments but then i get just another error message.
Here is the swift code where i try to get the data :
let myUrl = NSURL(string: "http://xxxxxxxxxxx.xxx/xxxxxxxx.php")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "userEmail=\(userEmail!)&userPassword=\(userPassword!)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
dispatch_async(dispatch_get_main_queue())
{
if(error != nil)
{
var alert = UIAlertController(title: "Achtung", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
}
print("1")
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json {
let userId = parseJSON["userId"] as? String
if( userId != nil)
{
print("SUCESS FUCKER")
let mainView = self.storyboard?.instantiateViewControllerWithIdentifier("main") as! FlickrPhotosViewController
let mainPageNavi = UINavigationController(rootViewController: mainView)
//open mainView
let appdele = UIApplication.sharedApplication().delegate
appdele?.window??.rootViewController = mainPageNavi
} else {
let userMassage = parseJSON["message"] as? String
let myAlert = UIAlertController(title: "Alert", message: userMassage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
}
} catch{
print(error)
print("FAILED CATCHED")
}
}
}).resume()
and this is the important part of the php file :
$userSecuredPassword = $userDetails["user_password"];
$userSalt = $userDetails["salt"];
if($userSecuredPassword === sha1($userPassword . $userSalt))
{
$returnValue["status"]="200";
$returnValue["userFirstName"] = $userDetails["first_name"];
$returnValue["userLastName"] = $userDetails["last_name"];
$returnValue["userEmail"] = $userDetails["email"];
$returnValue["userId"] = $userDetails["user_id"];
} else {
$returnValue["status"]="403";
$returnValue["message"]="User not found";
echo "failed";
echo json_encode($returnValue);
return;
}
echo json_encode($returnValue);
$returnValue returns this when i print it:
Array ( [status] => 200 [userFirstName] => Paul [userLastName] => Heinemeyer [userEmail] => paul_heine [userId] => 63 )
When you properly format your PHP code, you will see, that in the else part you have
echo "failed";
echo json_encode($returnValue);
which results in
failed{...}
As the error message already says, this "JSON text did not start with array or object ..."
Maybe there is similar output for the other if part.
i have php file to check a value passed from swift ios, i print json data, this is the code:
<?php
$said = $_REQUEST['sa_id'];
if($said == '123456') {
$returnValue = array("said" => "true");
}else{
$returnValue = array("said" => "false");
}
echo json_encode($returnValue);
?>
Also i wrote a swift function to check the returned said value, my code is work success in second click, i want it from first click:
class ViewController: UIViewController {
var saidResult = false;
#IBOutlet var saidField: UITextField!
#IBOutlet var labelField: UILabel!
override func viewDidLoad(){
super.viewDidLoad()
}
#IBAction func checkSAID(sender: UIButton) {
if ( isValidSAID(saidField.text) == false ) {
labelField.text = "SAID is Invalid"
} else {
labelField.text = "Done"
}
}
func isValidSAID(said2Test: String) -> Bool {
let myUrl = NSURL(string: "http://*****.com/said.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
// Compose a query string
let postString = "sa_id=\(said2Test)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, error) in
if error != nil {
println("error=\(error)")
return
}
// You can print out response object
println("response = \(response)")
// Print out response body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString = \(responseString)")
//Let's convert response sent from a server side script to a NSDictionary object:
var err: NSError?
var myJSON = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error:&err) as? NSDictionary
if let parseJSON =myJSON {
// Now we can access value of First Name by its key
var saidValue = parseJSON["said"] as? String
println("saidValue: \(saidValue)")
if ((saidValue) == "true" ) {
self.saidResult = true;
}
println("saidResult: \(self.saidResult)");
}
}
task.resume()
println("saidResult: \(self.saidResult)");
if ( self.saidResult == true ){
return true
} else {
return false
}
}
}
As i say, in first click the value of saidResult is false but after that it is take the true value
How i can solve this issue, or is there another way to improve my code?
I think this is probably because the http request is not answered on the first click but is on the second click.
you could try replacing your checkSAID function with this.
#IBAction func checkSAID(sender: UIButton) {
let saidQueue = dispatch_queue_create("saidQueue", DISPATCH_QUEUE_CONCURRENT)
// Do the checking on a background thread.
dispatch_async(saidQueue, {
if ( isValidSAID(saidField.text) == false ) {
// As the UI updates are performed on the main queue, update the label on the main queue.
dispatch_async(dispatch_get_main_queue()) {
labelField.text = "SAID is Invalid"
})
} else {
dispatch_async(dispatch_get_main_queue()) {
labelField.text = "Done"
})
}
})
}
Finally, after 4 days of testing i solved my problem.
I changed isValidSAID function code:
func isValidSAID(said2Test: String) -> Bool {
var status: String = "";
let myUrl = NSURL(string: "http://*****.com/said.php?sa_id=\(said2Test)");
let data = NSData(contentsOfURL: myUrl!)
let json = NSHSONSerialization.JSONObjectWithData(data!, option: nil, error: nil) as! NSDictionary
status = (json["said"] as? String)!
println(status)
return ( status == "true" ) ? true : false
}
and solved my issue.