Episode #31

Posting Multi-part Forms with AFNetworking

31 minutes
Published on August 30, 2012

This video is only available to subscribers. Get access to this video and 586 others.

In this episode, I take an existing app and add the ability to post information to a server, including photo uploads. We report on the progress of the upload and configure AFNetworking to do a proper muliti-part HTTP form post. In addition, I cover how to build a standalone static TableViewController to represent a form using Storyboards.

Episode Links

Saving the Data


- (void)save:(id)sender {
    BLLatte *latte = [[BLLatte alloc] init];
    latte.location = self.locationTextField.text;
    latte.submittedBy = self.authorTextField.text;
    latte.comments = self.commentsTextView.text;
    latte.photoData = UIImagePNGRepresentation(self.imageView.image);

    [self.view endEditing:YES];

    BLProgressView *progressView = [BLProgressView presentInWindow:self.view.window];

    // save it
    [latte saveWithProgress:^(CGFloat progress) {
        [progressView setProgress:progress];
    } completion:^(BOOL success, NSError *error) {
        [progressView dismiss];
        if (success) {
            [self.navigationController popViewControllerAnimated:YES];
        } else {
            NSLog(@"ERROR: %@", error);
        }
    }];
}

Then, in the model...

- (void)saveWithProgress:(void (^)(CGFloat progress))progressBlock completion:(void (^)(BOOL success, NSError *error))completionBlock {

    //make sure none of the parameters are nil, otherwise it will mess up our dictionary
    if (!self.location) self.location = @"";
    if (!self.submittedBy) self.submittedBy = @"";
    if (!self.comments) self.comments = @"";

    NSDictionary *params = @{
    @"latte[location]" : self.location,
    @"latte[submitted_by]" : self.submittedBy,
    @"latte[comments]" : self.comments
    };

    NSURLRequest *postRequest = [[BLAPIClient sharedClient] multipartFormRequestWithMethod:@"POST"
                                                                                      path:@"/lattes"
                                                                                parameters:params
                                                                 constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
                                                                        [formData appendPartWithFileData:self.photoData
                                                                                                    name:@"latte[photo]"
                                                                                                fileName:@"latte.png"
                                                                                                mimeType:@"image/png"];
                                                                 }];

    AFHTTPRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:postRequest];
    [operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
        CGFloat progress = ((CGFloat)totalBytesWritten) / totalBytesExpectedToWrite;
        progressBlock(progress);
    }];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        if (operation.response.statusCode == 200 || operation.response.statusCode == 201) {
            NSLog(@"Created, %@", responseObject);
            NSDictionary *updatedLatte = [responseObject objectForKey:@"latte"];
            [self updateFromJSON:updatedLatte];
            [self notifyCreated];
            completionBlock(YES, nil);
        } else {
            completionBlock(NO, nil);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        completionBlock(NO, error);
    }];

    [[BLAPIClient sharedClient] enqueueHTTPRequestOperation:operation];
};