Upload image to my server via PHP using Swift - ios

I am using the code below to upload image to my server, the code send the request but the response always from the API is (there was an error).
In the same time of uploading the image also some information of this image will be stored in mySql.
Here is my code, I am using Xcode 10.1 and Swift 4.2
#IBAction func uploadImage(_ sender: Any) {
self.showActivityIndicator()
//Post URL
let url = "https://website.com/folder/include/upload.php"
//Getting text from textFiled!
let name = nameField.text!
let age = ageField.text!
//Call Parameters
let params: Parameters = ["name": name,"age": age]
//Checking image place holder
let image = UIImage(named: "map.png")
//Checking if empty name or age fileds
if name.isEmpty || age.isEmpty{
self.hideActivityIndicator()
myAlert(title: "Error", msg: "Make sure you enter all the required information!")
}
//Checking if image is not selected!!
else if imageView.image == image
{
self.hideActivityIndicator()
myAlert(title: "Error", msg: "Make sure you choose an image!")
}else{
let imageToUpload = self.imageView.image!
Alamofire.upload(multipartFormData:
{
(multipartFormData) in
multipartFormData.append(imageToUpload.jpegData(compressionQuality: 0.75)!, withName: "image", fileName: self.generateBoundaryString(), mimeType: "image/jpeg")
for (key, value) in params
{
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, to:url,headers:nil)
{ (result) in
switch result {
case .success(let upload,_,_ ):
upload.uploadProgress(closure: { (progress) in
//Print progress
self.showActivityIndicator()
})
upload.responseJSON
{ response in
//print response.result
if let result = response.result.value {
//Calling response from API
let message = (result as AnyObject).value(forKey: "message") as! String
let status = (result as AnyObject).value(forKey: "status") as! String
//Case Success
if status == "1" {
self.hideActivityIndicator()
print("Your Results are ====> ",result)
self.myAlert(title: "Data Upload", msg: message)
self.imageView.image = UIImage(named: "map.png")
self.nameField.text = ""
self.ageField.text = ""
}else{
self.hideActivityIndicator()
self.myAlert(title: "Error Uploading", msg: message)
}
}
}
case .failure(let encodingError):
print(encodingError)
break
}
}
}
}
}
Here is the PHP file code:
<?php
include 'include/connect.php';
//Get Param Data
$name = $_POST["name"];
$age = $_POST["age"];
$xName = mysqli_real_escape_string($conn, $name);
$xAge = mysqli_real_escape_string($conn, $age);
//Results Array
$result = array();
//Image setup
$uploads_dir = 'img';
$tmp_name = $_FILES["image"]["tmp_name"];
$image_name = basename($_FILES["image"]["name"]);
$supported_image = array('gif','jpg','jpeg','png');
$ext = strtolower(pathinfo($image_name, PATHINFO_EXTENSION));
if(empty($xName) || empty($xAge)|| empty($image_name))
{
// Send some dummy result back to the iOS app
$result["message"] = "Sorry, there was an error uploading your file.";
$result["status"] = "0";
$result["post"] = $_POST;
$result["files"] = $_FILES;
}
if (! in_array($ext, $supported_image))
{
// Send some dummy result back to the iOS app
$result["message"] = "Sorry, Image extension is not Allowed!";
$result["status"] = "0";
$result["post"] = $_POST;
$result["files"] = $_FILES;
}
else
{
$query ="INSERT INTO images (name, age, image) VALUES ('$xName', '$xAge','$image_name')";
if (mysqli_query($conn, $query)) {
move_uploaded_file($tmp_name,"$uploads_dir/$image_name");
// Send some dummy result back to the iOS app
$result["message"] = "Data has been uploaded successfully.";
$result["status"] = "1";
$result["post"] = $_POST;
$result["files"] = $_FILES;
}
}
echo json_encode($result);
?>
The response from the API seems to be missing of some information, but I am filling the two fields with informations needed, which are the (name and age).
I do not what I am missing to complete uploading the image and its informations.
Thanks

try this in your button click event--->
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let addPhotos = UIAlertAction(title: "Choose Photo", style: .default) { (addPhoto) in
self.imgPicker.sourceType = .photoLibrary
self.imgPicker.allowsEditing = false
self.present(self.imgPicker, animated: true, completion: nil)
}
let camera = UIAlertAction(title: "Camera Photo", style: .default) { (CameraPhoto) in
self.imgPicker.sourceType = .camera
self.imgPicker.allowsEditing = false
self.present(self.imgPicker, animated: true, completion: nil)
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (Cencel) in
self.dismiss(animated: true, completion: nil)
}
alert.addAction(addPhotos)
alert.addAction(camera)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)

then add this function in your code...
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let img = info[UIImagePickerControllerOriginalImage] as? UIImage{
self.imgProfileImage.image = img
let imgData = UIImageJPEGRepresentation(img, 0.5)!
let parameters = [String : Any]
Alamofire.upload(multipartFormData: { multipartFormData in
multipartFormData.append(imgData, withName: "Folder Name",fileName: "PicName.jpg", mimeType: "image/jpg")
for (key, value) in parameters {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
} //Optional for extra parameters
},
to:"Your API is Here.")
{ (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print("Upload Progress: \(progress.fractionCompleted)")
})
upload.responseJSON { response in
print(response.result.value)
}
case .failure(let encodingError):
print(encodingError)
}
}
}
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}

Related

Send a message and dismiss keyboard at same time in Swift

Blue up Arrow button is for sending a message
I am using the following method to present and dismiss keyboard and shift my text field with keyboard
NotificationCenter.default.addObserver(self, selector:#selector(self.keyboardNotification(notification:)),name: UIResponder.keyboardWillChangeFrameNotification,object: nil)
#objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
self.view1.frame.origin.y = endFrameY - self.view1.frame.height
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
The problem I am facing is that I can't send the message and dismiss the keyboard at same time .I have a send button in the view which when pressed only dismisses the keyboard and doesn't send the message.
How can I send message as well as dismiss the keyboard at once?
EDIT:
This is my upload button action
#IBAction func uploadBtn(_ sender: UIButton)
{
if photoArray.count > 0 || self.audioRecorded == true || enteredTF.text != ""{
CustomAlert.showWait()
var i : Int = 0
let baseUrl = config.baseURL
parameters = [
"SKEY":self.skey,
"FID":"NEWMESSAGE",
"HKEY":"5673126578",
"LONGITUDE" : longitude as? String ?? "",
"LATITUDE" : latitude as? String ?? "",
"IPADDRESS" : "\(getIPAddress())",
"MESSAGE" : enteredTF.text!,
"USERID" : Uname,
"PHONEID" : phoneID,
"TIMEZONE" : getCurrentTimeZone()]
print(parameters)
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in self.parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: "\(key)" )
}
for (image) in self.photoArray {
// print(i)
if let imageData = image.jpegData(compressionQuality: 0.5) {
OperationQueue.main.addOperation {
multipartFormData.append(imageData, withName: "files", fileName: "image\(i).jpeg", mimeType: "image/jpeg")
i=i+1
}
}
}
if self.audioRecorded == true
{
multipartFormData.append(self.getFileUrl(), withName: "myRecording.m4a")
}
else
{
multipartFormData.append("".data(using: String.Encoding.utf8)!, withName: "Voice",mimeType: "m4a")
//i = i+1
}
}, usingThreshold: UInt64.init(), to: baseUrl, method: .post) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseString { response in
print("Succesfully uploaded = \(response)")
CustomAlert.hideWait()
let msg = SCLAlertView()
msg.showSuccess("", subTitle: "Your message has been successfully sent")
self.enteredTF.text = ""
OperationQueue.main.addOperation {
self.viewAllMessages()
self.photoArray.removeAll()
self.imageCollectionV.reloadData()
}
if let err = response.error{
print(err)
return
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}
// }
}
}
Instead of using UIResponder.keyboardWillChangeFrameNotification, you can use
UIResponder.keyboardWillShowNotification as observer when keyboard will show and
UIResponder.keyboardWillHideNotification as observer when keyboard will dismissed / hide.
On UIResponder.keyboardWillHideNotification's handler you can add your code to configure UIView and call your method to send message
Here's some example code :
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(showKeyboard(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(hideKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func showKeyboard(_ notification:NSNotification) {
if let userInfo = notification.userInfo {
//TODO: Do your UI Stuff
}
}
#objc func hideKeyboard() {
DispatchQueue.main.async {
//TODO: Do your UI Stuff
}
//TODO: Send Message
}

Unable to send base64 String to server in Swift on iOS

In my application I have pick the image using UIImagePickerController from photos and then compress and convert to base64 string. Finally I upload the base64 string to server. Here the server was not accept the base64 string and does not work, but in Android and Postman it works well. I couldn't find the mistake in my code.
Here I mention the UIImagePickerControllerDelegate:
// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
selectedImage = selectedImage.resizeWithWidth(width: 700)!
picker.dismiss(animated: true, completion: nil)
DispatchQueue.main.async {
self.collPhotos.reloadData()
}
}
Here I mention the Base64 Conversion and post the parameter string to server:
func addImagesApiCAll() {
let imagedata1:UIImage = selectedImage as! UIImage
let compressData = UIImagePNGRepresentation(imagedata1)
let base64 = compressData?.base64EncodedString(options: .lineLength64Characters)
print("charCount",base64!.count)
if Reachability()!.isReachable {
let id = Singleton.sharedInstance.selectedCategory!
let parameterStr = "property_id=\(self.PropertyID)&photos=\(base64!)&lang_code=\(lanuguage_selection.value(forKey: "language") ?? "en")&base_id=\(id)&user_id=\(login_session.value(forKey: "UserId")!)"
Network.shared.POSTRequest(withParameterString: parameterStr, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
} else {
showInformation(title: "Network Error", message: "Please check your internet connection")
}
}
extension AddPhotoViewController: HTTP_POST_STRING_REQUEST_PROTOCOL {
func httpPostRequest(APIKEY: String, requestURL: String, responseDict: NSDictionary, errorDict: String) {
ListingActivityDelegate.hideActivity()
if APIKEY == "SAVE_PHOTO_LISTING"{
if errorDict.count == 0 {
print(responseDict)
let mod = RentYourSpaceModel(fromDictionary: responseDict as! [String : Any])
if mod.status! != 0 {
Singleton.sharedInstance.rentYourSpace = mod
if Singleton.sharedInstance.rentYourSpace.result[0].step5.productImage.count == 0{
imageFromResponse = "NO"
} else {
imageFromResponse = "YES"
}
}
self.showInformation(title: "Application", message: mod.message)
}
else {
}
}
}
}
Here I mention the code for Select Image from Camera or Gallery:
#IBAction func act_AddPhoto(_ sender: UIButton) {
let actionSheet = UIAlertController(title: "Home Stay", message: "Choose Image", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
self.openCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Photos", style: .default, handler: { _ in
self.openGallary()
}))
actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
//If you want work actionsheet on ipad then you have to use popoverPresentationController to present the actionsheet, otherwise app will crash in iPad
switch UIDevice.current.userInterfaceIdiom {
case .pad:
actionSheet.popoverPresentationController?.sourceView = sender
actionSheet.popoverPresentationController?.sourceRect = sender.bounds
actionSheet.popoverPresentationController?.permittedArrowDirections = .up
default:
break
}
self.present(actionSheet, animated: true, completion: nil)
}
//MARK: - Open the camera
func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)){
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
//If you dont want to edit the photo then you can set allowsEditing to false
imagePicker.allowsEditing = true
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
else{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
//MARK: - Choose image from camera roll
func openGallary(){
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
//If you dont want to edit the photo then you can set allowsEditing to false
imagePicker.allowsEditing = true
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
Here I give the POSTRequest Method:
//MARK:- Post request with parameter String.
func POSTRequest(withParameterString: String , serviceURL: String , APIKEY: String)
{
var RESPONSE_ERROR = String()
var RESPONSE_DATA = NSDictionary()
let Url = String(format: serviceURL)
guard let serviceUrl = URL(string: Url) else { return }
var request = URLRequest(url: serviceUrl)
let postString = withParameterString
// print(postString)
request.httpBody = postString.data(using: String.Encoding.utf8);
//request.addValue("application/json", forHTTPHeaderField: "content-type")
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request, completionHandler: {
data, response, error in
if let response = response {
print(response)
}
if let resdata = data {
do {
// print(response)
let json = try JSONSerialization.jsonObject(with: resdata, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
//print(json)
if parseJSON.object(forKey: "status") as! NSInteger == 1 {
if error != nil {
RESPONSE_ERROR = (error?.localizedDescription)!
}
DispatchQueue.main.async {
RESPONSE_DATA = parseJSON
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
} else {
DispatchQueue.main.async {
RESPONSE_DATA = parseJSON
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
} else {
DispatchQueue.main.async {
RESPONSE_ERROR = "No Data"
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
}catch {
DispatchQueue.main.async {
RESPONSE_ERROR = "Check your input datas"
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
} else {
DispatchQueue.main.async {
RESPONSE_ERROR = (error?.localizedDescription)!
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
})
task.resume()
}
It looks like you are trying to send base64 string as query string. It would be too long to be a query string. You need to share details for POSTRequest method. For more information.
Network.shared.POSTRequest(withParameterString: parameterStr, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
And you need to avoid using exclamation (!) in your application. Your code would crash in many points.
For example:
if Reachability()?.isReachable
You can use optional values to prevent crashes.
let parameters = ["property_id":self.PropertyID,
"photos":base64 ?? "",
"lang_code":lanuguage_selection.value(forKey: "language") ?? "en",
"base_id":id,
"user_id":login_session.value(forKey: "UserId") ?? ""];
Network.shared.POSTRequest(parameters: parameters, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
in method add json data:
if let jsonData = try? JSONSerialization.data(withJSONObject: parameters) {
request.httpBody?.append(jsonData)
}

why i get different image when downloading to the same path?

I am trying to debug a chunk of code used to upload an image and download that image from my own server.
The image path is "http://localhost/Twitter/Avatar/52/avatar.jpeg"
as we can see, there are two images in that folder, same image but different name. I got a weird result when I hard coded the path when downloading the image
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
when I write let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
I go the same image as the path, like this
but when I change to let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
I got different image, like this
I once used that image actually when the first time uploading an image, but I don't know why that image appears again. I have not implemented caching image yet. why this happens?
here is the full source code
import UIKit
class HomepageVC: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var avatarImage: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var fullnameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var editAvatarButton: UIButton!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating()
// mendeklarasikan variable user yang berasal dari superglobal variable di appdelegate
let username = userInfo?["username"] as! String
let fullname = userInfo?["fullname"] as! String
let email = userInfo?["email"] as! String
let avatarPath = userInfo?["avatar"] as? String
// update user interface text & Label
usernameLabel.text = username.uppercased()
fullnameLabel.text = fullname.capitalized
emailLabel.text = email
// update user interface avatar
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
#IBAction func logoutButtonDidPressed(_ sender: Any) {
//menghapus data userDefault yang sudah ada
UserDefaults.standard.removeObject(forKey: "parsedJSON")
UserDefaults.standard.synchronize()
//menuju ke login page dengan modal segue
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewController(withIdentifier: "loginVC")
present(loginVC, animated: true, completion: nil)
}
#IBAction func editProfilePictureButtonDidPressed(_ sender: Any) {
// user akan memilih photo dari library atau dari camera nya
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
let actionSheet = UIAlertController(title: "Photo Source", message: "please choose your source", preferredStyle: .actionSheet)
// action camera
let actionCamera = UIAlertAction(title: "Camera", style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
} else {
self.showAlert(alertTitle: "Opppss", alertMessage: "camera can't be used / not available", actionTitle: "OK")
print("camera can't be used / not available")
}
}
// action photo library
let actionPhotoLibrary = UIAlertAction(title: "Photo Library", style: .default) { (action) in
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}
//action cancel
let actionCancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(actionCamera)
actionSheet.addAction(actionPhotoLibrary)
actionSheet.addAction(actionCancel)
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
avatarImage.image = image
picker.dismiss(animated: true, completion: nil)
// call func of uploading file to server
uploadAvatar()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// custom HTTP request body to upload image file
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
// kita set agar image yang di upload kemudian berformat .jpg
let filename = "avatar.jpeg"
let mimetype = "image/jpeg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageDataKey)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body as Data
}
// uploading image ke server
func uploadAvatar() {
// mendapatkan ID dari User Default variable
let id = userInfo!["id"] as! String
// membuat request
let url = URL(string: "http://localhost/Twitter/uploadAvatar.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// parameter yang akan dikirim di dalam request body
// parameter ini dibutuhkan karena uploadAvatar.php membutuhkan inputan ID
let param = ["id" : id]
// membuat Boundary
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// mengassign image yang akan di upload dan melakukan kompresi
let imageData = UIImageJPEGRepresentation(avatarImage.image!, 0.5)
// if not compressed, return ... do not continue to code
if imageData == nil {
return
}
// constructing http body
request.httpBody = createBodyWithParams(param, filePathKey: "file", imageDataKey: imageData!, boundary: boundary)
// filePathKey berupa 'file' agar nanti di PHP $_FILES bisa didentifikasi, contohnya $_FILES['file'][tmp_name]
// launc session
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
// maka tampilkan $returnArray dari PHP (response message from server)
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
// declare new parseJSON to store json
guard let parsedJSON = json else {
print("Error while parsing")
return
}
print(parsedJSON)
// get id from $returnArray["id"] in PHP - parseJSON["id"]
let id = parsedJSON["id"]
// successfully uploaded
if id != nil {
// save user information yang berasal dari server
UserDefaults.standard.set(parsedJSON, forKey: "parsedJSON")
userInfo = UserDefaults.standard.object(forKey: "parsedJSON") as? NSDictionary
// jika tidak ada "id" kiriman dari server, maka ada error message
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = parsedJSON["message"] as! String
self.showAlert(alertTitle: "opppps", alertMessage: message, actionTitle: "OK")
})
}
// error ketika melakukan JSON serialization
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error.localizedDescription
self.showAlert(alertTitle: "SorryBroooo", alertMessage: message, actionTitle: "OK")
})
}
// error ketika koneksi ke server
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
self.showAlert(alertTitle: "oppps", alertMessage: message, actionTitle: "OK")
})
}
})
}.resume()
}
}
// extend data
extension Data {
mutating func appendString(_ string : String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
When you use this code
let session = URLSession(configuration: .default)
you had automatically signed up for default caching policies, it uses persistent disk based cache as specified in this link:
https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411560-default
if you want to remove all the caching policies, use this code instead
let session = URLSession(configuration: .ephemeral)

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)
}
}
}
}

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
}

Resources