iOS8: UIDocumentPickerViewController get NSData - ios

I have implement UIDocumentPickerViewController according docs and now trying to get NSData from picked file in delegate method, but [[NSData alloc] initWithContentsOfURL:] returns nil:
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{
NSData* documentData = [[NSData alloc] initWithContentsOfURL:url];
//documentData is nil
documentData = [[NSData alloc] initWithContentsOfFile:[url path]];
//documentData is still nil :(
}
I'm using Xcode6 beta6, iPhone simulator, document picker mode is UIDocumentPickerModeImport.
Trying to retrieve documents saved to iCloude Drive.

Elaborating on #cescofry's answer a bit here regarding iWork files (.pages, .numbers, .key) so others won't have to rediscover the issue. (This will work for non iWork files as well.)
If you are pulling iWork files from iCloud, you need to worry about two primary things before you can get a valid NSData object. A) Security scope through a NSFileCoordinator object (as covered by #cescofry) and B) that iWork files are actually directories/bundles not single files. The options parameter you want for coordinateReadingItemAtURL: is NSFileCoordinatorReadingForUploading. This will read in single files as if you had used 0, but will turn directories into zip files automatically. Strip off the .zip that is added on and you'll have a valid Pages/Numbers/Keynote file. (It's valid with it on too.)
[url startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *fileData;
[coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
// File name for use in writing the file out later
NSString *fileName = [newURL lastPathComponent];
NSString *fileExtension = [newURL pathExtension];
if([fileExtension isEqualToString:#"zip"]) {
if([[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"pages"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"numbers"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"key"] ) {
// Remove .zip if it is an iWork file
fileExtension = [[newURL URLByDeletingPathExtension] pathExtension];
fileName = [[newURL URLByDeletingPathExtension] lastPathComponent];
}
}
NSError *fileConversionError;
fileData = [NSData dataWithContentsOfURL:newURL options:NSDataReadingUncached error:&fileConversionError];
// Do something with the file data here
}
[url stopAccessingSecurityScopedResource];
Relevant Apple documentation on the NSFileCoordinator options here:
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSFileCoordinator_class/#//apple_ref/c/tdef/NSFileCoordinatorReadingOptions

A URL from a document picker needs to be accessed through a file coordinator. Further more the url needs to be looked in scope:
[url startAccessingSecurityScopedResource];
__block NSData *data
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
NSError *error;
[coordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
data = [NSData dataWithContentsOfURL:url];
}];
[url stopAccessingSecurityScopedResource];
More from the Apple documentation

The problem was that actually Page documents (*.pages) are not files, but folders. So when I have tried to get NSData from folders path it returns nil.
Default Mail.app attaches documents as zip archives.

Related

Reading NSData in parts

I am implementing extensions in ios8. In this import function returns a url. I need to read data from it and save in my app.
Here is the code.
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *data;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
data = [NSData dataWithContentsOfURL:newURL];
//saving in document directory
}];
My question is, if file is too big, dataWithContentsOfURL results in crash due to out of memory.
I wanted a method to read data from that url in parts, save in my documents, then read next part and keep appending. Thus it won't have memory issue.
Can someone help.
Found two solutions.
1- Used NSURLConnection. Using NSFileHandle wrote data in file.
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1{
// [data appendData:data1];
[self.currentFileHandle seekToEndOfFile];
[self.currentFileHandle writeData:data1];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self.currentFileHandle closeFile];
self.currentFileHandle = nil;
}
Instead of converting it in data and then saving. Used following to copy file.
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:nil byAccessor:^(NSURL *newURL) {
NSFileManager * fileManager = [[NSFileManager alloc] init];
NSError * error;
NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *urlDestination = [paths lastObject];
urlDestination = [urlDestination URLByAppendingPathComponent:[url lastPathComponent]];
if([[NSFileManager defaultManager] fileExistsAtPath:[urlDestination relativePath]]){
[[NSFileManager defaultManager] removeItemAtPath:[urlDestination relativePath] error:nil];
}
BOOL success = [fileManager copyItemAtURL:url toURL:urlDestination error:&error];
if (success) {
}
}];
I used 2nd solution. Sorry for wrong alignment. I tried a lot, but not
coming properly.

Attaching iWork documents from iCloud Document Picker does not work because they are directories not files

Converting any iWork documents (Pages, Keynote, Numbers) into NSData from the UIDocumentPickerViewController does not seem to be working since they are not files but directories.
Here is my code:
NSString *fileName = [[url path] lastPathComponent];
NSData *fileData = [[NSFileManager defaultManager] contentsAtPath:[docPickerUrl path]];
NSString *fileExtension = [docPickerUrl pathExtension];
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
fileData is always nil since NSFileManager can't convert directory to data.
I get url from - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
I recommend going through a NSFileCoordinator object, which is AFAIK required for accessing security-scoped files such as those through Dropbox and iCloud. (Uncomment the first and next-to-last lines in the code below in that case.) The options parameter you want for NSFIleCoordinator's coordinateReadingItemAtURL: is NSFileCoordinatorReadingForUploading. This will read in single files normally, but will turn directories into zip files automatically. Strip off the .zip that is added on and you'll have a valid Pages/Numbers/Keynote file. (It's valid with it on too.)
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
//[url startAccessingSecurityScopedResource]; // Necessary for security-scoped files
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *fileData;
[coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
// File name for use in writing the file out later
NSString *fileName = [newURL lastPathComponent];
NSString *fileExtension = [newURL pathExtension];
// iWork files will be in the form 'Filename.pages.zip'
if([fileExtension isEqualToString:#"zip"]) {
if([[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"pages"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"numbers"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"key"] ) {
// Remove .zip if it is an iWork file
fileExtension = [[newURL URLByDeletingPathExtension] pathExtension];
fileName = [[newURL URLByDeletingPathExtension] lastPathComponent];
}
}
NSError *fileConversionError;
fileData = [NSData dataWithContentsOfURL:newURL options:NSDataReadingUncached error:&fileConversionError];
// Do something with the file data here
}
//[url stopAccessingSecurityScopedResource]; // Necessary for security-scoped files
}
Relevant Apple documentation on the NSFileCoordinator options here:
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSFileCoordinator_class/#//apple_ref/c/tdef/NSFileCoordinatorReadingOptions
I have the same issue here: iOS8: UIDocumentPickerViewController get NSData
The solution is to add iWork documents as zip archives, the simplest way is to use SSZipArchive library, also available as cocoa pod:
pod 'SSZipArchive', '~> 0.3'
Did you get anywhere with this, i've tried compressing the package using something like SSZipArchive or ZipArchive but get a crash.
This is where've i've got to which might help spark some ideas.
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
if (controller.documentPickerMode == UIDocumentPickerModeImport) {
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:url includingPropertiesForKeys:[NSArray arrayWithObject:NSURLNameKey] options:0 error:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *newDocDirectory = [paths objectAtIndex:0];
NSString *docDirectory = [dirContents objectAtIndex:0];
BOOL isDir=NO;
NSArray *subpaths;
NSString *exportPath = docDirectory;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:exportPath isDirectory:&isDir] && isDir){
subpaths = [fileManager subpathsAtPath:exportPath];
}
NSString *archivePath = [newDocDirectory stringByAppendingString:#".zip"];
ZipArchive *archiver = [[ZipArchive alloc] init];
[archiver CreateZipFile2:archivePath];
for(NSString *path in subpaths)
{
NSString *longPath = [exportPath stringByAppendingPathComponent:path];
if([fileManager fileExistsAtPath:longPath isDirectory:&isDir] && !isDir)
{
NSLog(#"adding file %#", path);
[archiver addFileToZip:longPath newname:path];
}
}
NSData *documentData = [NSData dataWithContentsOfFile:archivePath];
//post documentDate as zip file to server
}
}

how to download jpg,png,pdf,doc.ppt,rtf,etc file from the google drive in objective c

I am new iPhone developer.I am developing iPhone app in this app Google drive integration it is successfully.but I want to download jpg,png,pdf,doc.ppt,rtf,etc file are download.
all file and folder display in Table view.I want do download the selected file from the table view.so how can do this ?
I am read this link https://developers.google.com/drive/v2/reference/files/get but i can not understand.here how to call all this function.
I want to download file's from Google Drive and save in document directory.
I am try this code.
NSString *downloadedString = file.downloadUrl; // file is GTLDriveFile
NSLog(#"%#",file.downloadUrl);
GTMHTTPFetcher *fetcher = [self.driveService.fetcherService fetcherWithURLString:downloadedString];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error)
{
if (error == nil)
{
if(data != nil)
{
GTLDriveFile *file = [driveFiles objectAtIndex:indexPath.row];
NSString *filename=file.title;
//this variable globalObj.documentPath is global variable for document directory path.
filename = [globalObj.documentPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",filename]];
filename=[filename stringByReplacingOccurrencesOfString:#"file:/private" withString:#"file:///"];
NSLog(#"File name : %#",filename);
NSURL *targetURL=[[NSURL alloc] initFileURLWithPath:downloadedString];
NSData* Data = [[NSData alloc]initWithContentsOfURL:targetURL];
[Data writeToFile:filename atomically:YES];
NSLog(#"my path:%#",filename);
}
}
else
{
NSLog(#"Error - %#", error.description);
}
}];
so places help me.I want to download file and save in document directory.
any link for code places suggestion me.
Thanks
you does not have to do all this stuff.
here is just simple code
image url is from dropbox.
NSString *stringURL = #"https://dl.dropboxusercontent.com/s/pquhnr5pt37uk6q/backkk.jpeg?dl=1&token_hash=AAEXBejAXoD__RPMBom6nL2F5_Uhu62ed0puhtLIt2FGug";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"yourfile.png"];
[urlData writeToFile:filePath atomically:YES];
}
and finally you can represent it any way you want.
If you want to download large data.then use Threading concept.
if you use imageview programetically then write below code..... myimg=[[UIImageView alloc]initWithFrame:CGRectMake(30, 100, 100, 100)];
myimg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://dl.dropboxusercontent.com/s/pquhnr5pt37uk6q/backkk.jpeg?dl=1&token_hash=AAEXBejAXoD__RPMBom6nL2F5_Uhu62ed0puhtLIt2FGug"]]];
[self.view
addSubview:myimg]; it's working

How can I have UIDocumentInteractionController show Calendar as an option for opening a .ics file?

I am intercepting a type of URL in the webview I use in my app in order to download the file that it links to instead of just trying to open it in the webview. The link is to a .ics file. So I download that file into the temporary directory and then bring up the file in a UIDocumentInteractionController instance, but Calendar is not shown as one of the apps to open that .ics file in, just Mail, DropBox and "Copy".
As you can see in the commented out line in the code below I've tried making the link open with webcal:// at the front instead of http://, and also manually setting the UIDocumentInteractionController's UTI to no avail. Is there some reason my local .ics file would not show Calendar as an option for opening it?
//Test for link to an ics file
if ([urlToLoad rangeOfString:#"GetSingleEventCalendarFile" options:NSCaseInsensitiveSearch].location != NSNotFound)
{
//urlToLoad = [urlToLoad stringByReplacingOccurrencesOfString:#"http" withString:#"webcal"];
//NSData *contact = [NSData dataWithContentsOfURL:[NSURL URLWithString:webCalProtocol]];
//return NO;
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:urlToLoad]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *resp, NSData *respData, NSError *error){
NSString *urlString = [req.URL absoluteString];
NSString *actIdAndRest = [urlString substringFromIndex:[urlString rangeOfString:#"="].location + 1];
NSString *actId = [actIdAndRest substringToIndex:[actIdAndRest rangeOfString:#"&"].location];
NSString *fileName = [[#"activity" stringByAppendingString:actId] stringByAppendingString:#".ics"];
NSString * path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSError *errorC = nil;
BOOL success = [respData writeToFile:path
options:NSDataWritingFileProtectionComplete
error:&errorC];
if (success) {
NSBundle * bundleContaining = [NSBundle bundleWithPath:NSTemporaryDirectory()];
NSLog(#"%#", bundleContaining);
NSURL *fileURL = [bundleContaining URLForResource:fileName withExtension:#"ics"];
documentController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
//documentController.UTI = #"ics";
[documentController presentOptionsMenuFromRect:CGRectZero inView:self.view animated:YES];
} else {
NSLog(#"fail: %#", errorC.description);
}
}];
}

Issue saving to NSApplicationSupportDirectory

I'm having a strange issue saving to NSApplicationSupportDirectory on my iPad app.
I've been using this data structure for a long time with no issues, it only appears during the first two loads of the program.
The first time the app opens, I open up a file from the mainBundle and re-save it as a default settings file, which is then used to store any changes to settings in the application.
The second time I open the app, the file doesn't seem to exist yet, so it creates it again! The third time and after, all is well. I can't figure out why it isn't saving it the first time.
Sorry if this is messy - it's a part of hundreds of pages of code!
Here's my initialization:
///inside the init of the object
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSError* err = nil;
NSURL* dir = [fileManager URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&err];
if (err) {
NSLog(#"error finding app support directory %#", err);
}
currentSettingsURL = [NSURL URLWithString:kCurrentSettingsFilename relativeToURL:dir];
if(![fileManager fileExistsAtPath:[currentSettingsURL path]])[self createDefaultFile];
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[dir path] error:&err];
NSLog(#"files %#", files);
where the file is created if it doesn't exist.
-(void)createDefaultFile{
NSURL* readURL = [[NSBundle mainBundle] URLForResource:#"smooth" withExtension:nil];
NSMutableData* tempData = [NSMutableData dataWithContentsOfURL:readURL];
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:tempData];
///re archive
[self setCurrentPresets:[unarchiver decodeObjectForKey:#"presetData"]];
}
-(void)setCurrentPresets:(NSMutableArray*)presets{
currentPresets = presets;
[self saveCurrentSettings];
}
here is where the file is saved
-(void)saveCurrentSettings{
data = [[NSMutableData alloc] init];
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:currentPresets forKey:kCurrentSettingsFilename];
[archiver finishEncoding];
if(![data writeToURL:currentSettingsURL atomically:YES])NSLog(#"error saving file");
}
I simply needed to tell it my NSApplicationSupportDirectory was a folder..
NSURL* dir = [fileManager URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&err];
if (err) {
NSLog(#"error finding app support directory %#", err);
}
NSURL* folder = [[NSURL alloc] initFileURLWithPath:[dir path] isDirectory:YES];
currentSettingsURL = [NSURL URLWithString:kCurrentSettingsFilename relativeToURL:folder];

Resources