grantvansant
grantvansant

Reputation: 45

AFNetworking POST created but blank

I am building a rails-backed ios app that uses AFNetworking to POST content to a server. A user can upload a photo with a comment - and this works. I also want to have the option to let a user upload just text- this is where I am having trouble. I have one method for saving a photo and text, and another method for saving just text. The save photo method works, but the save text method creates a post but the text is null. The save photo implementation is like this:

- (void)savePhotoAtLocation:(CLLocation *)location
                     withBlock:(void (^)(CGFloat))progressBlock completion:(void (^)(BOOL, NSError *))completionBlock {

if (!self.content) self.content = @"";

NSDictionary *params = @{
                         @"post[content]" : self.content,
                         @"post[lat]": @(location.coordinate.latitude),
                         @"post[lng]": @(location.coordinate.longitude)

                         };

NSURLRequest *postRequest = [[APIClient sharedClient] multipartFormRequestWithMethod:@"POST"                                                                               path:@"/posts" parameters:params 
    constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
                                          {
                                              [formData appendPartWithFileData:self.photoData
                                                                          name:@"post[photo]"
                                                                      fileName:@""
                                                                      mimeType:@"image/png"];
                                          }];
    AFHTTPRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:postRequest];

This method only works when there is photoData- if you don't have photoData, the app crashes. So I am wondering what is the equivalent to a multipartFormRequest- that lets you only include a string? This is what I have right now- which creates a post- but returns content: as well as the lat/lng params which should be returned with the current location. This is defined in the post model

- (void)savePostAtLocation:(CLLocation *)location
    withBlock:(void (^)(CGFloat progress))progressBlock completion:(void (^)(BOOL success, NSError *error))completionBlock {

    if (!self.content) self.content = @"";

    NSDictionary *params = @{
                             @"post[content]" : self.content,
                             @"post[lat]" : @(location.coordinate.latitude),
                             @"post[lng]" : @(location.coordinate.longitude)
                                  };

    NSURLRequest *postRequest = [[APIClient sharedClient]requestWithMethod:@"POST" path:@"/posts" parameters:params];

    AFHTTPRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:postRequest];

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

    [[APIClient sharedClient] enqueueHTTPRequestOperation:operation];
}

And in the AddPostViewController save calls this:

- (void)save:(id)sender

{
    CLLocationManager * locationManager = [[CLLocationManager alloc] init];
    self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    self.locationManager.distanceFilter = 80.0f;
    [locationManager startUpdatingLocation];
    [self getLocation];
    CLLocation * location = [locationManager location];

    Post *post = [[Post alloc] init];
    post.content = self.contentTextField.text;

    [self.view endEditing:YES];

    ProgressView *progressView = [ProgressView presentInWindow:self.view.window];
    if (location) {

    [post savePostAtLocation:self.locationManager.location withBlock:^(CGFloat progress) {
        [progressView setProgress:progress];
    } completion:^(BOOL success, NSError *error) {
        [progressView dismiss];
        if (success) {
            [self.navigationController popViewControllerAnimated:YES];
        } else {
            NSLog(@"ERROR: %@", error);
        }
    }];
    } else {
        NSLog(@"No Location");
    }
}

Here is the log after a post is created. As you can see the attributes are null- and shouldn't be.

Created, {
    post =     {
        content = "<null>";
        "created_at" = "2013-07-21T18:45:12Z";
        id = 13;
        lat = "<null>";
        lng = "<null>";

    success = 1;
}

So the fact that a post is created but the attributes are null makes me think that the problem is simply in the NSURLRequest- and that I am not fully implementing the AFNetworking protocol but I haven't been able to find a way to implement a post request that doesn't entail fileData. How do I make a post request that doesn't append fileData? Any help would be greatly appreciated. Thanks!

Upvotes: 0

Views: 312

Answers (2)

grantvansant
grantvansant

Reputation: 45

This is how I got it to work: post.h

+ (void)createNoteAtLocation:(CLLocation *)location
                 withContent:(NSString *)content
                       block:(void (^)(Post *post))block;

post.m

+ (void)createNoteAtLocation:(CLLocation *)location
                 withContent:(NSString *)content
                       block:(void (^)(Post *post))block
{
    NSDictionary *parameters = @{ @"post": @{
                                          @"lat": @(location.coordinate.latitude),
                                          @"lng": @(location.coordinate.longitude),
                                          @"content": content
                                          }
                                  };

    [[APIClient sharedClient] postPath:@"/posts" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
        Post *post = [[Post alloc] initWithDictionary:responseObject];
        if (block) {
            block(post);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (block) {
            block(nil);
        }
    }];
}

And finally in the createPostViewController:

- (void)save:(id)sender

{
    CLLocationManager * locationManager = [[CLLocationManager alloc] init];
    self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    self.locationManager.distanceFilter = 80.0f;
    [locationManager startUpdatingLocation];

    [self getLocation];
    CLLocation * location = [locationManager location];


    Post *post = [[Post alloc] init];
    post.content = self.contentTextField.text;

    [self.view endEditing:YES];

    if (location) {

        [Post createNoteAtLocation:location withContent:self.contentTextField.text block:^(Post *post) {
            NSLog(@"Block: %@", post);
            [self.navigationController popViewControllerAnimated:YES];
        }];
    }

Upvotes: 0

Wain
Wain

Reputation: 119031

You can copy your existing method but instead of using appendPartWithFileData:name:fileName:mimeType: to set file data you can convert your parameters to data and add them with appendPartWithFormData:name:.

Upvotes: 1

Related Questions