title: uploading image to server with swift (php, json, swift)
body: <p>I am working on a project wherein I'd like to select an image and then upload it to the server. I am able to click the upload image button, select an image, and change the image in the iOS simulator but the upload fails. When I include a <code>print(request)</code> after the <code>request.httpBody = creatBodyWithParams()</code> statement, it shows that the original url has not changed. I am using XAMPP to set up a virtual server and php to communicate with it. </p>
  
  <p>My code:</p>
  
  <pre><code>// upload image button clicked
  @IBAction func edit_click(_ sender: Any) {
  
      // select image
      let picker = UIImagePickerController()
      picker.delegate = self
  
      //picker.sourceType = UIImagePickerControllerSourceType.photoLibrary
      picker.allowsEditing = true
      self.present(picker, animated: true, completion: nil)
  }
  
  // selected image
  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
      avaImg.image = info[UIImagePickerControllerEditedImage] as? UIImage
      self.dismiss(animated: true, completion: nil)
  
      // call function of uploading image file to server
      uploadImage()
  }
  
  // create body of HTTP request to upload image file
  func createBodyWithParams(parameters: [String : String]?, filePathKey: String?, imageDataKey: NSData, boundary: String) -&gt; NSData {
  
      let body = NSMutableData();
  
      if parameters != nil {
          for (key, value) in parameters! {
              body.appendString(string: "--\(boundary)\r\n")
              body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
              body.appendString(string: "\(value)\r\n")
          }
      }
  
      let filename = "image.jpg"
  
      let mimetype = "image/jpg"
  
      body.appendString(string: "--\(boundary)\r\n")
      body.appendString(string: "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
      body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
      body.append(imageDataKey as Data)
      body.appendString(string: "\r\n")
  
      body.appendString(string: "--\(boundary)--\r\n")
  
      return body
  }
  
  // upload image to server
  func uploadImage() {
  
      let id = user!["id"] as! String
  
      let address = URL(string: "http://localhost/project/uploadImage.php")!
      var request = URLRequest(url: address)
      request.httpMethod = "POST"
      let param = ["id" : id]
  
      let boundary = "Boundary-\(NSUUID().uuidString)"
  
      request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
  
      let imageData = UIImageJPEGRepresentation(avaImg.image!, 0.5)
  
      if imageData == nil {
          return
      }
  
      request.httpBody = createBodyWithParams(parameters: param, filePathKey: "file", imageDataKey: imageData! as NSData, boundary: boundary) as Data
  
      // launch session
      let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
  
          // get main queue to communicate back to user
          DispatchQueue.main.async {
  
              if error == nil {
  
                  do {
  
                      let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
  
                      guard let parseJSON = json else {
  
                          print("error while parsing")
                          return
  
                      }
  
                      print(parseJSON)
  
                  } catch {
  
                      print("Caught an error: \(error)")
  
                  }
  
              } else {
  
                  print(error)
  
              }
          }
      })
  
      task.resume()
  
  }
  </code></pre>
  
  <p>With the following extension:</p>
  
  <pre><code>// creating protocol of appending string to var of type data
  extension NSMutableData {
  
      appendString(string : String) {
  
          let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
          append(data!)
  
      }
  }
  </code></pre>
  
  <p>I have consulted the following pages for help and have tried changing my code to reflect the differences in their approaches but I am left banging my head against the wall.</p>
  
  <p><a href="https://stackoverflow.com/questions/35008594/uploading-image-with-other-parameters-in-swift">Uploading image with other parameters in SWIFT</a></p>
  
  <p><a href="https://stackoverflow.com/questions/26162616/upload-image-with-parameters-in-swift/26163136#26163136">Upload image with parameters in Swift</a></p>
  
  <p><a href="http://www.kaleidosblog.com/how-to-upload-images-using-swift-2-send-multipart-post-request" rel="nofollow noreferrer">http://www.kaleidosblog.com/how-to-upload-images-using-swift-2-send-multipart-post-request</a></p>
  
  <p>EDIT 1:</p>
  
  <p>Forgot error message (dur): <code>Caught an error: 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.}</code></p>
  
  <p>Error is occurring somewhere in the <code>do</code> statement. I think something is going on with the <code>request</code> because testing <code>http://localhost/project/uploadImage.php</code> in my browser returns the array <code>{"message":"Missing required information"}</code></p>
  
  <p>EDIT 2: PHP Code</p>
  
  <p>uploadImage.php</p>
  
  <pre><code>&lt;?php
  
  // report all errors except E_NOTICE
  error_reporting(E_ALL &amp; ~E_NOTICE);
  
  // Part 1: Upload File
  // Step 1: check data passed to this php file
  if (empty($_REQUEST["id"])) {
  
      $returnArray["message"] = "Missing required information";
      print json_encode($returnArray);
      return;
  }
  
  // request user id
  $id = htmlentities($_REQUEST["id"]);
  
  // step 2: create a folder for user with id as folder name
  $folder = "/Applications/XAMPP/xamppfiles/htdocs/project/image/" . $id;
  
  // if folder doesn't exist
  if (!file_exists($folder)) {
  
      mkdir($folder, 0777, true);
  
  }
  
  // step 3: move uploaded file
  $folder = $folder . "/" . basename($_FILES["file"]["name"]);
  
  if (move_uploaded_file($_FILES["file"]["tempname"], $folder)) {
      $returnArray["status"] = "200";
      $returnArray["message"] = "The file has been uploaded successfully.";
  } else {
      $returnArray["status"] = "300";
      $returnArray["message"] = "Error while uploading.";
  }
  
  
  // Part 2: Updating image path
  // build secure connection
  
  $file = parse_ini_file("../../../project.ini");
  
  // store variable information from ini file in php as variables
  $host = trim($file["dbhost"]);
  $user = trim($file["dbuser"]);
  $pass = trim($file["dbpass"]);
  $name = trim($file["dbname"]);
  
  // include access.php to call connect function from access.php file
  require ("secure/access.php");
  $access = new access($host, $user, $pass, $name);
  $access-&gt;connect();
  
  // STEP 5: save path to uploaded file in database
  $path = "http://localhost/project/image/" . $id . "/ava.jpg";
  $access-&gt;updateImagePath($path, $id);
  
  // STEP 6: get new user information after updating
  $user = $access-&gt;selectUserViaID($id);
  
  $returnArray["id"] = $user["id"];
  $returnArray["username"] = $user["username"];
  $returnArray["fullname"] = $user["fullname"];
  $returnArray["email"] = $user["email"];
  $returnArray["image"] = $user["image"];
  
  // Step 7. Close Connection
  $access-&gt;disconnect();
  
  // Step 8. feedback array to app
  echo json_encode($returnArray);
  
  ?&gt;
  </code></pre>