NSURLSession downloadTaskWithResumeData giving error after relaunch app - ios

I have created a download task. I have cancelled the task in my application with cancelByProducingResumeData and saved to disk. When user relaunch application and click on resume button I have again started the download task by self.downloadTask = [session downloadTaskWithResumeData:resumableData];. But this gives error.
Invalid resume data for background download. Background downloads must use http or https and must download to an accessible file
EDIT: I have verified the tmp directory on simulator contains the downloadData but not able to access that.

After restarting the application link on the tmp file in resuminData is not valid. Change the field-"NSURLSessionResumeInfoLocalPath" in resuminData, to the correct path of the tmp file.
use:
NSMutableDictionary* resumeDictionary = [NSPropertyListSerialization propertyListWithData:resumingData options:NSPropertyListMutableContainers format:NULL error:&error];
...
NSString* badFilePath = [resumeDictionary objectForKey:#"NSURLSessionResumeInfoLocalPath"];
...
[resumeDictionary setObject:trueTmpfile forKey:#"NSURLSessionResumeInfoLocalPath"];
...
NSData* newResumingData = [NSPropertyListSerialization dataFromPropertyList:resumeDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&errorDesc];

Related

How to know a file is writing via NSFileManager

I got a problem , How to know the file is accessible ???
For example , the file is writing , but not finish yet.
And I check the file again ,
I use
[[NSFileManager defaultManager] fileExistsAtPath:filePath];
It return true ... but the file is not accessible .
How to determine the file is ready ?
Thanks
Webber
EDIT
Maybe I'm not telling my problem clearly . I mean if you start transport a video file . before transport finish . the video file is not accessible , but you still can get a part of transporting file.
Write Data in async way using GCD concept and once it is completed, the completion handler will be executed.(Where completion of a writing process shall be detected)
The dispatch I/O convenience API lets you perform asynchronous read and write operations on file descriptors. This API supports stream-based semantics for accessing the contents of the file-descriptor.
Method:
void dispatch_write ( dispatch_fd_t fd, dispatch_data_t data, dispatch_queue_t queue, void (^handler)(dispatch_data_t data, int error) );
Ref: https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_write
Basically you need to check the attributes of the file and particullary the NSFileBusy attribute:
if ([fileManager fileExistsAtPath:filePath]) {
NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
NSLog(#"File Busy %#", [attributes objectForKey:NSFileBusy]);
}
NSFileCoordinator and NSFilePresenter are created just for that. You may find interesting Advanced iCloud Document Storage video from wwdc that covers the usage of this classes. Building a Document-based App will be great to watch too.
Try to fetch data of file by using following code :
NSURL *url = [NSURL fileURLWithPath:filePath];
NSData *readData = [NSData dataWithContentsOfURL:url];

NSData is returning nil for same file at different filepaths

I stored an audio file of format .aiff in two different folders. The NSData is returning nil for one file path even though both file paths have the audio file. I double checked the file paths.
The path is like this:
//url1 file:///Users/VenkataManiteja/Library/Developer/CoreSimulator/Devices/80B8117E-D2C9-4B42-8A76-9A89A10FB1C1/data/Containers/Data/Application/FD17AD64-EAF9-4578-B50D-0B5BF6F2DEFF/Documents/28Apr15_090827AM.aif
//url2 file:///Users/VenkataManiteja/Library/Developer/CoreSimulator/Devices/80B8117E-D2C9-4B42-8A76-9A89A10FB1C1/data/Containers/Data/Application/B60CF270-73CA-4BE4-BA75-B2AC3642360D/Documents/28Apr15_090827AM.aif
NSError *err;
data = [NSData dataWithContentsOfURL:url1]; //this is working fine
audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:&err];
data = [NSData dataWithContentsOfURL:url2]; //nsdata is returning nil
Can anyone tell me why the url2 is getting the nil NSData?
You are trying to read a file outside of the running app's sandbox. While both may exist, presumably you are running the application with ID "FD17AD64-EAF9-4578-B50D-0B5BF6F2DEFF", which is why that URL is working while the other one isn't.
I would recommend watching the WWDC video A Practical Guide to the App Sandbox for more on how the security model works.

How to know if NSURLIsExcludedFromBackupKey is working?

I recently had my app rejected due to:
2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected
The reason for this is my app downloads a lot of image files from the web and writes them to the documents directory. So I do the following to try and remedy the situation:
NSURL *url = [NSURL URLWithString:stickerURL];
NSError *error = nil;
BOOL success = [url setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
if(!success)
{
NSLog(#"Error excluding %# from backup %#", [url lastPathComponent], error);
}
NSData *data = [NSData dataWithContentsOfURL:url];
My question is, how am I to know if NSURLIsExcludedFromBackupKey is working? The BOOL success is always coming back as YES. However, when I check Settings -> iCloud -> Storage & Backup -> Manage Storage -> MyiPhone5, it's making no difference in the size that is being shown underneath my app. Would this mean it's not working? If not, what can I fix in my code?
If you are checking "Manage Storage" in your device's settings, and after adding a bunch of files (eg., photos, documents, or whatever it is you're flagging to not be backed up) you notice that the backup size for you app is increasing, then the flag isn't "working" (although that shouldn't be the case, really).
One way to test it is to get rid of the key NSURLIsExcludedFromBackupKey, download a bunch of data, and then check to see if the backup size is increasing. Then, once you add the key back into your code, and once again download a bunch of data, the backup storage size should really not increase noticeably.

NSURLSession and stream upload in background

I have some problems with using NSURLSession to upload photos from Asset Library to the server.
At first NSURLSession doesn't support streaming upload. I got an exception when trying to using that:
#property (nonatomic, strong) NSURLSession *uploadSession;
...
_uploadSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
backgroundSessionConfiguration:kUploadBackgroundURLSessionIdentifier] delegate:self delegateQueue:nil];
...
NSURLSessionUploadTask *task = [self.uploadSession uploadTaskWithStreamedRequest:URLRequest];
This is an exception:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Upload tasks in background sessions must be from a file'
That's really strange because Apple's manual doesn't contain any information about using only uploadTaskWithRequest:fromFile: for background session. What if I would like to upload really huge video file from Asset Library? Should I save it previously to my tmp directory?
Looks like the only reason is to use uploadTaskWithRequest:fromFile: anyway, right? But then I have a question how server get to know what's part of the file is uploading right now if uploading process was interrupted and started to upload next part in background?
Should I manage something for that? Previously I used Content-Range for that in URL request if I wanted to continue upload part of the file which was started previously. Now I can't do that - I have to create an URL Request before creating upload task and looks like NSURLSession have to do something like that automatically for me?
Does anyone do something like that already? Thanks
Convert to NSData and copy and write in app folder
ALAsset *asset = [cameraRollUploadImages objectAtIndex:startCount];
ALAssetRepresentation *representation = [asset defaultRepresentation];
// create a buffer to hold the data for the asset's image
uint8_t *buffer = (Byte *)malloc(representation.size);// copy the data from the asset into the buffer
NSUInteger length = [representation getBytes:buffer
fromOffset:0
length:representation.size
error:nil];
// convert the buffer into a NSData object, free the buffer after
NSData *image = [[NSData alloc] initWithBytesNoCopy:buffer
length:representation.size
freeWhenDone:YES];
Right now, there is no way otherthan saving the picture to local file system or temp directory.
Following code make sure your data won't be lost with exif tags. (ALAsset => NSData)
ALAssetRepresentation *assetRepresentation = [(ALAsset *)assetBeingUploaded defaultRepresentation];
uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t)*[assetRepresentation size]);
NSUInteger buffered = 0;
if (buffer != NULL)
buffered = [assetRepresentation getBytes:buffer fromOffset:0.0 length:assetRepresentation.size error:nil];
self.imageBeingUploaded = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
The upload task in background session doesn't support completion handler. We should go for.,
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
I doubt how do we get the response headers or body in case if we are using background session & uploadtask with request using file?
A clean workaround will be to create a NSOperation which will copy the file from the asset library to your temporary folder using NSStream, so you won't get a crash in case of a huge file, when the operation completes you schedule a upload of that temporary file, when the upload finishes you delete it.
In my case, i need to send the file in multipart format so the creation of the temporary file is necessary but i encounter a problem in uploading large files, more then 2 Gb, example movies over 20 minutes.
You can't upload NSData in background you need to upload file format. You can create it by directory path

NSFileWrapper fails when from iCloud and works from local directory

I have a problem syncing NSFileWrapper documents with iCloud. I am able to create my wrapper and save it to my ubiquitous container.
When I try to read it from the device that created it, it works. When I try to read form another device that got it from iCloud, it crashes.
Some code:
This function to add a wrapper container with a NSString
- (void) addNSString:(NSString*)_string toFileWrapper:(NSFileWrapper*)_wrapper forKey:(NSString*)_key {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_string];
if(data) {
[_wrapper addRegularFileWithContents:data preferredFilename:_key];
}
}
And then here is how I decode it:
- (id) unarchiveObjectFromWrappers:(NSDictionary*)_wrappers withKey:(NSString*)_key {
id value = nil;
NSFileWrapper *wrapper = [_wrappers valueForKey:_key];
if(wrapper) {
NSData *data = [wrapper regularFileContents];
if(data) {
value = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
}
return value;
}
The decoding part works on one device and not on the others (EXC_BAD_ACCESS when the NSKeyedUnarchiver tries to unarchive from the NSData. The NSData seems good, it has the proper length and everything but when I try to log its datas for example it crashes).
My guess is that the NSFileWrapper doesn't download its full content, only its structure and that I have to do something to make it available. But I don't know what.
Any ideas?
========
Edit:
NSURLUbiquitousItemIsDownloadedKey says that the file is downloaded BUT if I try to copy it to the sandbox it fails with this error: "The operation couldn’t be completed. Bad file descriptor"
So the file is either not uploaded properly to iCloud or not downloaded properly...
It drove me crazy too. The solution is rather simple, yet totally undocumented by Apple. You must download the file specifically. Only the file wrapper is downloaded automatically, but not its contents. That's why the check says the file exists.
Before copying the file over, call something like this:
[[NSFileManager defaultManager]startDownloadingUbiquitousItemAtURL:cloudURL error:nil];
Related: Cannot sync simple text file with iCloud (bad file descriptor)

Resources