i am developeing share extension for my iOS application. i had really done every thing but the problem is that my code is only working for small images but when i upload image taken from device camer then uploading fails and only text get uploded.
- (void)performUploadWith:(NSDictionary *)parameters imageFile:(UIImage *)image{
NSString *boundary = #"SportuondoFormBoundary";
// NSURLSessionConfiguration *configuration= [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationName];
// NSURLSession *session=[NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
// NSMutableURLRequest *request;//[NSURLRequest pho]
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.HTTPAdditionalHeaders = #{
#"api-key" : #"55e76dc4bbae25b066cb",
#"Accept" : #"application/json",
#"Content-Type" : [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary]
};
NSURLSession *session=[NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSMutableData *body = [NSMutableData data];
for (NSString *key in parameters)
{
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"%#\r\n", [parameters objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
}
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
NSLog(#"imageDATE, %#", imageData);
if (imageData)
{
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"image.jpg\"\r\n", #"file"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Data uploading task. We could use NSURLSessionUploadTask instead of NSURLSessionDataTask if we needed to support uploads in the background
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",kURLBase,kURLAddPostDL]];
NSLog(#"url %#",url);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10000];
request.HTTPMethod = #"POST";
request.HTTPBody = body;
NSURLSessionUploadTask *upload=[session uploadTaskWithRequest:request fromData:request.HTTPBody];
[upload resume];
}
i update my code to this and i check on server my request never reaches to server. I am using this code
NSInteger randomNumber = arc4random() % 1000000;
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[NSString stringWithFormat:#"testSession.foo.%d", randomNumber]];
config.sharedContainerIdentifier=kGroupNameToShareData;
session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL URLWithString:[NSString stringWithFormat:#"file://%#", dataSrcImagePath]]];
[uploadTask resume];
Extensions aren't allowed their own cache disk space. Need to share with application
Uploading large image need cache disk space,so you upload failed.
You need create url session with following codes:
let configName = "com.shinobicontrols.ShareAlike.BackgroundSessionConfig"
let sessionConfig = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(configName)
// Extensions aren't allowed their own cache disk space. Need to share with application
sessionConfig.sharedContainerIdentifier = "group.ShareAlike"
let session = NSURLSession(configuration: sessionConfig)
and then setting the app group both extension target and containing app.
and more infomations you can refer following link:
http://www.shinobicontrols.com/blog/posts/2014/07/21/ios8-day-by-day-day-2-sharing-extension
You should be using a background NSURLSession since you are meant to dismiss the sharing UI as soon as possible by calling completeRequestByReturningItems on your NSExtensionContext instance.
The problem is i am not using background session
NSURLSessionConfiguration *configuration= [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationName];
- (void)performUploadWith3:(NSDictionary *)parameters imageFile:(UIImage *)image
{
NSString *fileName = #"image.file";
NSString *boundary= #"Boundary+2EB36F87257DBBD4";
NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kGroupNameToShareData];
storeURL=[storeURL URLByAppendingPathComponent:fileName];
NSLog(#"sotre url %#",storeURL);
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
imageData =[self appendParams:parameters andImage:image bondary:boundary];
if (!([imageData writeToFile:[storeURL path] atomically:YES]))
{
NSLog(#"Failed to save file.");
}
NSString *string=#"URL here";
NSURL *url = [NSURL URLWithString:string];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField:#"Content-Type"];
for (NSString *key in parameters)
{
[request setValue:[parameters objectForKey:key] forHTTPHeaderField:key];
}
if (session == nil)
{
//If this is the first upload in this batch, set up a new session
//Each background session needs a unique ID, so get a random number
NSInteger randomNumber = arc4random() % 1000000;
sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[NSString stringWithFormat:#"and unique name"]];
sessionConfiguration.sharedContainerIdentifier=kGroupNameToShareData;
// config.HTTPMaximumConnectionsPerHost = 1;
session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
//Set the session ID in the sending message structure so we can retrieve it from the
//delegate methods later
// [sendingMessage setValue:session.configuration.identifier forKey:#"sessionId"];
NSLog(#"config %#",sessionConfiguration.sharedContainerIdentifier);
}
if([[NSFileManager defaultManager]fileExistsAtPath:[storeURL path]])
{
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:storeURL];
[uploadTask resume];
}
else
NSLog(#"file does not exsit");
NSLog(#"desc %#",[session sessionDescription]);
// NSLog(#"state %ld", [uploadTask state]);
}
Related
Is there any way to upload , download to a server read the .sqlite file present in document directory from your iPhone application and use it as your database.
I tried by converting to binary data and uploading but the file is getting encrypted I cannot read or write it after restoring/downloading it from server.
Any suggestion?
Uploading task fetching the file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *chatDatabasePath = [documentsDirectory stringByAppendingPathComponent:#"ChatDB.sqlite"];
NSString *contactDatabasePath = [documentsDirectory stringByAppendingPathComponent:#"ContactDB.sqlite"];
NSURL*ChatDB_url = [NSURL fileURLWithPath:chatDatabasePath];
NSURL*ContactDB_url = [NSURL fileURLWithPath:contactDatabasePath];
self.ChatDBData = [NSData dataWithContentsOfURL:ChatDB_url];
self.ContactDBData = [NSData dataWithContentsOfURL:ContactDB_url];
Uploading
-(void)UploadDbToServer:(NSData*)DB_Data ForDbName:(NSString*)dbName forExtension:(NSString*)extension andCallback:(void (^)(id))callback{
NSString *urlString = [[NSString alloc]initWithString:[NSString stringWithFormat:#"%#chat-backup-upload",baseUrl]];
urlString=[urlString stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:100];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"7MA4YWxkTrZu0gW";
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
// post body
NSMutableData *body = [NSMutableData data];
// add DB data
if (DB_Data) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"DB\"; filename=\"%#.%#\"\r\n",dbName,extension] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: file/sqlite\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:DB_Data];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
NSURLSessionConfiguration *sessionConf = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConf];
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
id jsonResponse= [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
callback ((id) jsonResponse);
}];
[task resume];}
Downloading
(void)startDownload:(NSURL*)url {
NSURL *Url= [NSURL URLWithString:[NSString stringWithFormat:#"%#",url]];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue setMaxConcurrentOperationCount:5];
self.activeUrlString=[NSString stringWithFormat:#"%#",Url];
if (!self.session) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"image"];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:queue];
}
NSURLRequest* request = [NSURLRequest requestWithURL:Url];
self.task = [self.session downloadTaskWithRequest:request];
[self.task resume];}
Writing the downloaded file
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
//saving the files
NSData *DBData = [NSData dataWithContentsOfURL:location];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *chatDatabasePath = [documentsDirectory stringByAppendingPathComponent:#"ChatDB.sqlite"];
NSString *contactDatabasePath = [documentsDirectory stringByAppendingPathComponent:#"ContactDB.sqlite"];
if ([self.activeDownload isEqualToString:#"chat"]){
[DBData writeToFile:chatDatabasePath atomically:YES];
self.activeDownload = #"contact";
[self startDownload:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#",pingovaURL,self.ContactDB_URL]]];
}else{
[DBData writeToFile:contactDatabasePath atomically:YES]; }}
I have implemented code for nginx upload file to server using NSURLSession for speed test. This is working properly on iOS 8 but on iOS 10 it is giving status code 413 (request entity too large).
File size is approx 30MB, If file size is less then 1MB it's working properly. Here is my code
NSString *BoundaryConstant = #"----------V2ymHFg03ehbqgZCaKO6jy";
NSString* FileParamConstant = #"file";
NSURL* requestURL = [NSURL URLWithString:kPathForUploadCheck];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:600];
[request setHTTPMethod:#"POST"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", BoundaryConstant];
[request setValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *body = [NSMutableData data];
NSString *path = [[NSBundle mainBundle] pathForResource:#“myfile” ofType:#"psd"];
NSData *imageData22 = [NSData dataWithContentsOfFile:path];
NSString *base64String = [imageData22 base64EncodedStringWithOptions:0];
NSData *imageData = [base64String dataUsingEncoding:NSUTF8StringEncoding];
if (imageData) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"image.jpg\"\r\n", FileParamConstant] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[body length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setURL:requestURL];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *backgroundSeesion = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];
NSURL* tempUrl = [NSURL fileURLWithPath:path];
uploadTask = [backgroundSeesion uploadTaskWithRequest:request fromFile:tempUrl];
uploadTask = [backgroundSeesion uploadTaskWithRequest:request fromData:imageData];
[uploadTask resume];
Hi i am trying to upload an image through multipart to server in objective c using NSURLSession. But i am getting few errors like
org.apache.commons.fileupload.FileUploadException: Header section has more than 10240 bytes (maybe it is not properly terminated)
Below is my objective c code:
{
NSString *boundaryConstant = #"----------V2ymHFg03ehbqgZCaKO6jy";
NSString *fileParamConstant = #"photo";
NSMutableURLRequest *req = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:url]];
[req setHTTPMethod:#"POST"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data;boundary=%#",boundaryConstant];
[req setValue:contentType forHTTPHeaderField:#"Content-Type"];
NSMutableData *body = [NSMutableData data];
if (imageData) {
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"%#\"\r\n", fileParamConstant, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
NSString *bodyLength = [NSString stringWithFormat:#"%zu",[body length]];
[req setValue:bodyLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPBody:body];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:req fromData:body completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(#"STRING %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[uploadTask resume];
}
}
Any help would be greatly appreciated.
Thanks
I have been stucked in this from last week. But I did not get the solution.
I want to access all photos from camera roll and want to save all photos on remote server with the help of NSURLSessionUploadTask. For this I am using following code:
NSString *boundary = #"----WebKitFormBoundarycC4YiaUFwM44F6rT";
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"%#.jpeg\"\r\n", #"upload",self.pictureName] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:self.imageData];
[body appendData:[[NSString stringWithFormat:#"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPMaximumConnectionsPerHost = 1;
config.HTTPAdditionalHeaders = #{
#"api-key" : #"55e76dc4bbae25b066cb",
#"Accept" : #"application/json",
#"Content-Type" : [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary]
};
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURL *url = self.uploadURL;
NSLog(#"URL=%#",url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"PUT"];
[request setHTTPBody:body];
// 3
self.uploadTask = [upLoadSession uploadTaskWithRequest:request fromData:nil];
// self.uploadTask = [upLoadSession uploadTaskWithRequest:request fromFile:tmpFileUrl];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// 5
[_uploadTask resume];
All thing is working fine in foreground. But as I enter in background, It stop working. Where is the problem?
What I will have to do for uploading images in background using NSURLSessionUploadTask?
Your session config is not specified to run in the background.
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:#"com.test.backgroundSession"];
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
Implement as above.
For upload task to work in background, you have to use uploadTaskWithRequest:fromFile: instead of uploadTaskWithRequest:fromData:
If you use NSData to supply the files, it will work while the app is active and will stop once the app is in background.
edit:
I need to upload a file asynchronously from an iPhone to a Python server-side process. I'd like to do the request asynchronously so that I can display a busy animation while it's working.
The request needs to include the username, password and file as 'multipart/form-data'.
I can get it working synchronously using NSURLConnection with the code looking like this::
-(void) uploadDatabase{
Database *databasePath = [[Database alloc] init];
NSString *targetPath = [databasePath getPathToDatabaseInDirectory];
NSData *dbData = [NSData dataWithContentsOfFile:targetPath];
NSString *url = #"http://mydomain.com/api/upload/";
//NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
NSString *username = #"user";
NSString *password = #"pass";
NSMutableURLRequest *request = [self createRequestForUrl:url withUsername:username andPassword:password andData:dbData];
NSURLResponse *response;
NSError *error;
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *stringResult = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
NSLog(#"**server info %#", stringResult);}
// Request construction
-(NSMutableURLRequest*) createRequestForUrl: (NSString*)urlString withUsername:(NSString*)username andPassword:(NSString*)password andData:(NSData*)dbData
{NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"BOUNDARY_STRING";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request addValue:contentType forHTTPHeaderField:#"Content-Type"];
NSMutableData *body = [NSMutableData data];
if(dbData != NULL)
{
//only send these methods when transferring data as well as username and password
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"file\"; filename=\"dbfile\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:dbData]];
}
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"username\"\r\n\r\n%#", username] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"password\"\r\n\r\n%#", password] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
return request;}
However, when I try to do this asynchronously using NSURLSession it doesn't seem to work properly. The code with NSURLSession looks like this:
-(void)uploadDatabase{
Database *databasePath = [[Database alloc] init];
NSString *targetPath = [databasePath getPathToDatabaseInDirectory];
NSURL *phonedbURL = [NSURL URLWithString:targetPath];
NSString *url = #"http://mydomain.com/api/upload/";
NSString *username = #"user";
NSString *password = #"pass";
NSMutableURLRequest *request = [self createRequestForUrl:url withUsername:username andPassword:password andData:NULL];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
self.uploadSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:Nil];
NSLog(#"the url = %#",url);
NSURLSessionUploadTask *uploadTask = [self.uploadSession uploadTaskWithRequest:request fromFile:phonedbURL];
[uploadTask resume];}
I'm struggling to see what I'm doing differently though as it seems this should work.
Is using NSURLSession the right way to do asynchronous requests? and I'm new to NSURLSession so do I have to change my NSURLMutableRequest for NSURLSession requests rather than NSURLConnection?
Thanks in advance for any help!
You are correct, that if you just want to make your request asynchronous, you should retire sendSynchronousRequest. While we once would have recommended sendAsynchronousRequest, effective iOS 9, NSURLConnection is formally deprecated and one should favor NSURLSession.
Once you start using NSURLSession, you might find yourself drawn to it. For example, one can use a [NSURLSessionConfiguration backgroundSessionConfiguration:], then have uploads progress even after the app has gone into background. (You have to write a few delegate methods, so for simplicity's sake, I've stayed with a simple foreground upload below.) It's just a question of your business requirements, offsetting the new NSURLSession features versus the iOS 7+ limitation it entails.
By the way, any conversation about network requests in iOS/MacOS is probably incomplete without a reference to AFNetworking. It greatly simplifies creation of these multipart requests and definitely merits investigation. They have NSURLSession support, too (but I haven't used their session wrappers, so can't speak to it). But AFNetworking is undoubtedly worthy of your consideration. You can enjoy some of the richness of the delegate-base API (e.g. progress updates, cancelable requests, dependencies between operations, etc.), offering far greater control that available with convenience methods (like sendSynchronousRequest), but without dragging you through the weeds of the delegate methods themselves.
Regardless, if you're really interested in how to do uploads with NSURLSession, see below.
If you want to upload via NSURLSession, it is a slight shift in thinking, namely, separating the configuration of the request (and its headers) in the NSMutableURLRequest from the creation of the the body of the request (which you now specify during the instantiation of the NSURLSessionUploadTask). The body of the request that you now specify as part of the upload task can be either a NSData, a file, or a stream (I use a NSData below, because we're building a multipart request):
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *boundary = [self boundaryString];
[request addValue:[NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary] forHTTPHeaderField:#"Content-Type"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSData *fileData = [NSData dataWithContentsOfFile:path];
NSData *data = [self createBodyWithBoundary:boundary username:#"rob" password:#"password" data:fileData filename:[path lastPathComponent]];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSAssert(!error, #"%s: uploadTaskWithRequest error: %#", __FUNCTION__, error);
// parse and interpret the response `NSData` however is appropriate for your app
}];
[task resume];
And the creation of the NSData being sent is much like your existing code:
- (NSData *) createBodyWithBoundary:(NSString *)boundary username:(NSString*)username password:(NSString*)password data:(NSData*)data filename:(NSString *)filename
{
NSMutableData *body = [NSMutableData data];
if (data) {
//only send these methods when transferring data as well as username and password
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"file\"; filename=\"%#\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Type: %#\r\n\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:data];
[body appendData:[#"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"username\"\r\n\r\n%#\r\n", username] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"password\"\r\n\r\n%#\r\n", password] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
return body;
}
You hardcoded the boundary and the mime type, which is fine, but the above happens to use the following methods:
- (NSString *)boundaryString
{
NSString *uuidStr = [[NSUUID UUID] UUIDString];
// If you need to support iOS versions prior to 6, you can use
// Core Foundation UUID functions to generate boundary string
//
// adapted from http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections
//
// NSString *uuidStr;
//
// CFUUIDRef uuid = CFUUIDCreate(NULL);
// assert(uuid != NULL);
//
// NSString *uuidStr = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
// assert(uuidStr != NULL);
//
// CFRelease(uuid);
return [NSString stringWithFormat:#"Boundary-%#", uuidStr];
}
- (NSString *)mimeTypeForPath:(NSString *)path
{
// get a mime type for an extension using MobileCoreServices.framework
CFStringRef extension = (__bridge CFStringRef)[path pathExtension];
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL);
assert(UTI != NULL);
NSString *mimetype = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType));
assert(mimetype != NULL);
CFRelease(UTI);
return mimetype;
}