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
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
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.
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()
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];
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
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];
.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 :)