1

So I'm trying to do a multi-part post request in Swift using the following format:

user_id 3232424234
photo *PHOTO DATA*

I set up my request as shown at the bottom of this post and am getting the following error:

Optional(Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSUnderlyingError=0x7f86235cfe40 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=ENDPOINTURL, NSErrorFailingURLKey=ENDPOINTURL, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-4, NSLocalizedDescription=The network connection was lost.})

This error seems extremely odd to me because I can make it go away by omitting the boundary requirements for the POST request, but the server than explodes with a 500 and lets the client know it omitted the boundary reqs, so yah. I am probably doing something wrong in Swift. Below is my code to make the request. Let me know if more info is needed, thx guru's of the world.

//Inputs in this scope are an "id" and a "photo"
let url = NSURL(string: "**URL**")
let boundary = NSUUID()
let request = NSMutableURLRequest(URL: url!)

request.HTTPMethod = "POST"
request.setValue("multipart/form-data; boundary=--\(boundary)", forHTTPHeaderField: "Content-Type")

let parameterBody:NSDictionary = [
    "user_id": id,
]

let data:NSMutableData = NSMutableData()

parameterBody.enumerateKeysAndObjectsUsingBlock { (parameterKey: AnyObject, parameterValue: AnyObject, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
    data.appendData(NSString(string: "--\(boundary)\r\n").dataUsingEncoding(NSUTF8StringEncoding)!)
    data.appendData(NSString(string: "\(parameterKey) \(parameterValue)\r\n").dataUsingEncoding(NSUTF8StringEncoding)!)
}

data.appendData(NSString(string: "--\(boundary)\r\n").dataUsingEncoding(NSUTF8StringEncoding)!)
data.appendData(NSString(string: "photo").dataUsingEncoding(NSUTF8StringEncoding)!)
data.appendData(photo.imageData as! NSData)
data.appendData(NSString(string: "--\(boundary)--\r\n").dataUsingEncoding(NSUTF8StringEncoding)!)

request.setValue("\(data.length)", forHTTPHeaderField: "Content-Length")

let task = NSURLSession.sharedSession().uploadTaskWithRequest(request, fromData: data) { (responseJSON: AnyObject?, response: NSURLResponse?, error: NSError?) -> Void in
    //Here I handle the response and check for errors.
}

task.resume()
Unome
  • 6,750
  • 7
  • 45
  • 87

1 Answers1

1

There are a ton of issues here:

  1. The boundary should be NSUUID().UUIDString.

  2. When you add the boundary to the Content-Type header, you should not add -- there.

  3. The individual parts of the multipart request are not well-formed. For example, you're missing the Content-Disposition.

  4. It's not critical, but you do not need to set Content-Length of the request. That's done for you.

I'd suggest you refer to this answer which provides example of how to form multipart request: Upload image with parameters in Swift. Or consider using Alamofire, which takes care of all of this for you.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Awesome, I will make these adjustments, and comment back when I've had a chance to review and implement them. – Unome Jan 13 '16 at 18:14
  • @Unome - FYI, I have uploaded a more generalized `NSURLSession` extension that prepares multipart requests, https://github.com/robertmryan/NSURLSessionMultipart. Frankly, though, I'd reiterate that you really consider Alamofire, which is a more elegant implementation. – Rob Jan 14 '16 at 07:46
  • Yes, I've used Alamofire in the past and it works pretty nice. We've been trying to avoid it for this project, so I'll checkout your github first. Thx! I implemented the change you recommended and followed that post, but am still getting back a 503 error from the server, and I know the server is working because we have a webclient that uses the same endpoint and it works fine. – Unome Jan 14 '16 at 17:06
  • After looking at your Github I have a question. The app I am making downloads an image from Facebook, and then allows the user to modify the image (scale/crop). I have the image/data for the modified image, but not a file path on the device. How could I leverage your framework to handle that? – Unome Jan 14 '16 at 17:09
  • That code doesn't contemplate that. (I was simply trying to generalize that code that was in that other Stack Overflow question.) You could easily refactor that code so that instead of an array of file URLs, it would take an array of `NSData` and an array of file names. – Rob Jan 14 '16 at 17:11
  • Sweet, I will look into that. – Unome Jan 14 '16 at 17:13
  • Success! Reworking your code it worked, I'm still a little ruffled about what in the end I was missing, most likely some small syntax with boundary stuff, but it works great. Thx! – Unome Jan 14 '16 at 17:24