1

Brand new to iOS development, so I'm feeling a little lost here. How can we use NSURLSession to upload an image to a web server (a PHP rest API that I'll setup)?

I've already been able to write the code to the point where a user is able to select an image, but now I need to send this image to my PHP web page that will handle it. Any advice on how to do this, and the best way to have my PHP setup to handle a request like this?

Any advice/insight would be greatly appreciated!

pwoerfulafhjksdh
  • 381
  • 1
  • 3
  • 11

2 Answers2

0

Abetter choice to use when interacting with web services is AFNetworking

Here is the code to upload an image

-(void)uploadImage:(UIImage *)image{
    AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://server.url"]];
    NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
    NSDictionary *parameters = @{}; // pass parameter here
    AFHTTPRequestOperation *op = [manager POST:@"{SERVER_URL}" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        //do not put image inside parameters dictionary as I did, but append it!
        [formData appendPartWithFileData:imageData name:paramNameForImage fileName:@"photo.jpg" mimeType:@"image/jpeg"];
    } success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"Success: %@ \n%@", operation.responseString, responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@ \n%@", operation.responseString, error);
    }];
    [op start];
}
Mahmoud Adam
  • 5,772
  • 5
  • 41
  • 62
0

An NSURLSession works in conjunction with NSURLSessionTasks, which do the actual HTTP calls. These Tasks work asynchronously and deliver data in chunks, so you need some object to setup these tasks, put them to work and wait for their results. The class of this object needs to act as the delegate of the NSURLSessionTasks and therefore needs to implement some delegate protocols:

@interface MyUrlSessionTasksManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
...
@end

I have a class that does that. It keeps a list of running tasks and accumulates their retrieved chunked data until they're done. Then it returns its result to the requester, which is technically its delegate. At initialization it creates and configures an NSURLSession (I am using cookies) and an empty list of tasks:

- (id) init {
    self = [super init];
    if (self) {
        self.runningTasks = [NSMutableDictionary dictionary];
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        configuration.HTTPShouldSetCookies = YES;
        configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyAlways;
        self.session = [NSURLSession sessionWithConfiguration: configuration
                                                     delegate: self
                                                delegateQueue: [NSOperationQueue mainQueue]];
    }
    return self;
}

As mentioned, this class works with a delegate to which to report finished results. Here is the definition of the delegate:

@protocol MyUrlSessionTasksManagerDelegate <NSObject>
- (void) sessionTask: (NSURLSessionTask *) task completedWithError: (NSError *) error data: (NSData *) data;
@end

The main method of this class (for this case) is:

- (NSURLSessionTask *) startPostTaskForURL: (NSURL *) url parameters: (NSDictionary *) values {
    NSURLRequest *request = [self createPostRequestWithURL:url parameters:values];
    NSURLSessionTask *task = [self.session dataTaskWithRequest:request];
    [self scheduleTask:task];
    return task;
}

This methods takes an NSURL and an NSDictionary of parameters, creates an HTTP POST request, creates a Task with that request and schedules the task for execution. The scheduling is just putting the task in the dictionary 'runningTasks' with an associated Data object for accumulating the data received:

- (void) scheduleTask: (NSURLSessionTask *) task {
    [self.runningTasks setObject:[NSMutableData data] forKey:task];
    [task resume];
}

Whenever there is data for a task, the following TaskDelegate methods is called. All it does is lookup the task in its list of running tasks and append the data received to the tasks associated Data object:

- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    NSMutableData *runningData = [self dataForTask:dataTask];
    if (!runningData) {
        NSLog(@"No data found for task");
    }
    [runningData appendData: data];
}

When the data reception is completed the following TaskDelegate method will be called. All it does is report to its delegate, passing the whole Data object and any errors:

- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    [self.delegate sessionTask:task completedWithError:error data:[self dataForTask:task]];
}

Some internal support methods used:

- (NSMutableData *) dataForTask: (NSURLSessionTask *) task {
    return [self.runningTasks objectForKey:task];
}
- (NSString *)urlEncodedUTF8String: (NSString *) source {
    return (id)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(0, (CFStringRef)source, 0,
                                                       (CFStringRef)@";/?:@&=$+{}<>,", kCFStringEncodingUTF8));
}

- (NSString *) createHttpParameters: (NSDictionary *) parameters {
    NSMutableString *body = [NSMutableString string];
    for (NSString *key in parameters) {
        NSString *val = [parameters objectForKey:key];
        if ([body length])
            [body appendString:@"&"];
        [body appendFormat:@"%@=%@", [self urlEncodedUTF8String: [key description]],
         [self urlEncodedUTF8String: [val description]]];
    }
    return body;
}

- (NSURLRequest *)createPostRequestWithURL:(NSURL *)url
                          parameters:(NSDictionary *)parameters {

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:@"application/x-www-form-urlencoded"
   forHTTPHeaderField:@"Content-Type"];
    NSString * httpParams = [self createHttpParameters:parameters];

    [request setHTTPBody:[httpParams dataUsingEncoding:NSUTF8StringEncoding]];
    return request;
}

What is not provided for in this code is how to get the file and convert it to the proper encoding to pass it as a parameter in the POST body. Reason is this code was copied literally from one of my projects, which is not using file uploads. Still I hope it helps you.

Rudi Angela
  • 1,463
  • 1
  • 12
  • 20