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 Episode Source Code AFNetworking FAQ - "How do I upload a file?" KKGridView 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]; };