8

What's the correct way to code a multipart PUT request using AFNetworking on iOS? (still Objective-C, not Swift)

I looked and seems like AFNetworking can do multipart POST but not PUT, what's the solution for that?

Thanks

Rob
  • 415,655
  • 72
  • 787
  • 1,044
mindbomb
  • 1,642
  • 4
  • 20
  • 35

6 Answers6

15

You can use multipartFormRequestWithMethod to create a multipart PUT request with desired data.

For example, in AFNetworking v3.x:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

NSError *error;
NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    NSString *value = @"qux";
    NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
    [formData appendPartWithFormData:data name:@"baz"];
} error:&error];

NSURLSessionDataTask *task = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
    if (error) {
        NSLog(@"%@", error);
        return;
    }

    // if you want to know what the statusCode was

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
        NSLog(@"statusCode: %ld", statusCode);
    }

    NSLog(@"%@", responseObject);
}];
[task resume];

If AFNetworking 2.x, you can use AFHTTPRequestOperationManager:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

NSError *error;
NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    NSString *value = @"qux";
    NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
    [formData appendPartWithFormData:data name:@"baz"];
} error:&error];

AFHTTPRequestOperation *operation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];

[manager.operationQueue addOperation:operation];

Having illustrated how one could create such a request, it's worth noting that servers may not be able to parse them. Notably, PHP parses multipart POST requests, but not multipart PUT requests.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Tried above code but getting folowing error { "image": [ "The submitted data was not a file. Check the encoding type on the form." ] } – Pinank Lakhani Oct 13 '16 at 09:19
  • Did you use `appendPartWithFormData` or `appendPartWithFileData`? It sounds like you want the latter. – Rob Oct 13 '16 at 15:24
  • @Rob I've implemented your solution but I'm getting bytes data as responseObject. Can you address to this issue here: http://stackoverflow.com/questions/40424323/afnetworking-3-afmultipartformdata-getting-data-in-response – Chaudhry Talha Nov 04 '16 at 14:20
  • @Rob please how can I get the returned status code from the session task? – Ne AS Aug 04 '17 at 11:17
  • @Llg - It's part of the `response` object. So, check to see if it is a `NSHTTPURLResponse` object, and if so, grab its `statusCode`. See revised example above. – Rob Aug 04 '17 at 16:28
  • @Rob can you please provide the same example for AFNetworking v3.0 but for the multipart/form-data? I'm trying to upload an image to the server but I can't get it work – Ne AS Sep 05 '17 at 21:00
  • First, if it's a multipart/form-data, its exceeding unlikely to be `PUT`. It's generally `POST`, in which case you use the standard AFNetworking API for posting of multipart data, not the silliness above. Second, my first example _is_ AFNetworking 3 `PUT` with multipart/form-data. I must not understand your question. – Rob Sep 05 '17 at 21:03
3

Here is code for Afnetworking 3.0 and Swift that worked for me. I know its old thread but might be handy for someone!

    let manager: AFHTTPSessionManager = AFHTTPSessionManager()

    let URL = "\(baseURL)\(url)"        

    let request: NSMutableURLRequest = manager.requestSerializer.multipartFormRequestWithMethod("PUT", URLString: URL, parameters: parameters as? [String : AnyObject], constructingBodyWithBlock: {(formData: AFMultipartFormData!) -> Void in
        formData.appendPartWithFileData(image!, name: "Photo", fileName: "photo.jpg", mimeType: "image/jpeg")
        }, error: nil)

    manager.dataTaskWithRequest(request) { (response, responseObject, error) -> Void in

        if((error == nil)) {
            print(responseObject)
            completionHandler(responseObject as! NSDictionary,nil)
        }
        else {
            print(error)
            completionHandler(nil,error)
        }

        print(responseObject)
        }.resume()
MJ_iOSDev
  • 145
  • 10
0

You can create an NSURLRequest constructed with the AFHTTPRequestSerialization's multipart form request method

NSString *url = [[NSURL URLWithString:path relativeToURL:manager.baseURL] absoluteString];
id block = ^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileData:media
                                name:@"image"
                            fileName:@"image"
                            mimeType:@"image/jpeg"];
};

NSMutableURLRequest *request = [manager.requestSerializer
                                multipartFormRequestWithMethod:@"PUT"
                                URLString:url
                                parameters:nil
                                constructingBodyWithBlock:block
                                error:nil];

[manager HTTPRequestOperationWithRequest:request success:successBlock failure:failureBlock];
Kaey
  • 4,615
  • 1
  • 14
  • 18
0

I came up with a solution that can handle any supported method. This is a solution for PUT, but you can replace it with POST as well. This is a method in a category that I call on the base model class.

    - (void)updateWithArrayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key params:(NSDictionary *)params success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {

        id block = [self multipartFormConstructionBlockWithArayOfFiles:arrayOfFiles forKey:key failureBlock:failure];

        NSMutableURLRequest *request = [[self manager].requestSerializer
                                        multipartFormRequestWithMethod:@"PUT"
                                        URLString:self.defaultURL
                                        parameters:nil
                                        constructingBodyWithBlock:block
                                        error:nil];

       AFHTTPRequestOperation *operation = [[self manager] HTTPRequestOperationWithRequest:request success:success failure:failure];
       [operation start];
    }

    #pragma mark multipartForm constructionBlock

    - (void (^)(id <AFMultipartFormData> formData))multipartFormConstructionBlockWithArayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
        id block = ^(id<AFMultipartFormData> formData) {
            int i = 0;
            // form mimeType
            for (FileWrapper *fileWrapper in arrayOfFiles) {
                NSString *mimeType = nil;
                switch (fileWrapper.fileType) {
                    case FileTypePhoto:
                        mimeType = @"image/jpeg";
                        break;
                    case FileTypeVideo:
                        mimeType = @"video/mp4";
                        break;
                    default:
                        break;
                }
                // form imageKey
                NSString *imageName = key;
                if (arrayOfFiles.count > 1)
                    // add array specificator if more than one file
                    imageName = [imageName stringByAppendingString: [NSString stringWithFormat:@"[%d]",i++]];
                // specify file name if not presented
                if (!fileWrapper.fileName)
                    fileWrapper.fileName  = [NSString stringWithFormat:@"image_%d.jpg",i];
                NSError *error = nil;

                // Make the magic happen
                [formData appendPartWithFileURL:[NSURL fileURLWithPath:fileWrapper.filePath]
                                           name:imageName
                                       fileName:fileWrapper.fileName
                                       mimeType:mimeType
                                          error:&error];
                if (error) {
                    // Handle Error
                    [ErrorManager logError:error];
                    failure(nil, error);
                }
            }
        };
        return block;
    }

Aso it uses FileWrapper Interface

    typedef NS_ENUM(NSInteger, FileType) {
        FileTypePhoto,
        FileTypeVideo,
    };


@interface FileWrapper : NSObject

@property (nonatomic, strong) NSString *filePath;
@property (nonatomic, strong) NSString *fileName;
@property (assign, nonatomic) FileType fileType;

@end
Igor Kovryzhkin
  • 2,195
  • 1
  • 27
  • 22
0

For RAW Body :

NSData *data = someData; NSMutableURLRequest *requeust = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self getURLWith:urlService]]];

[reqeust setHTTPMethod:@"PUT"];
[reqeust setHTTPBody:data];
[reqeust setValue:@"application/raw" forHTTPHeaderField:@"Content-Type"];

NSURLSessionDataTask *task = [manager uploadTaskWithRequest:requeust fromData:nil progress:^(NSProgress * _Nonnull uploadProgress) {

} completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

}];
[task resume];
Badre
  • 710
  • 9
  • 17
0

.h

+ (void)PUT:(NSString *)URLString
 parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
   progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
    success:(void (^)(NSURLResponse *response, id responseObject))success
    failure:(void (^)(NSURLResponse * response, NSError *error))failure
      error:(NSError *__autoreleasing *)requestError;

.m:

+ (void)PUT:(NSString *)URLString
 parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
    success:(void (^)(NSURLResponse * _Nonnull response, id responseObject))success
    failure:(void (^)(NSURLResponse * _Nonnull response, NSError *error))failure
error:(NSError *__autoreleasing *)requestError {

    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]
                                    multipartFormRequestWithMethod:@"PUT"
                                    URLString:(NSString *)URLString
                                    parameters:(NSDictionary *)parameters
                                    constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                    error:(NSError *__autoreleasing *)requestError];
    AFURLSessionManager *manager = [AFURLSessionManager sharedManager];//[AFURLSessionManager manager]
    NSURLSessionUploadTask *uploadTask;
    uploadTask = [manager uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                               progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                                          if (error) {
                                              failure(response, error);
                                          } else {
                                              success(response, responseObject);
                                          }
                                      }];

    [uploadTask resume];
}

Just like the classic afnetworking. Put it to your net work Util :)

刘俊利
  • 358
  • 4
  • 12