I'm trying to preview a document with UIDocumentInteractionController.
The document is an .xls file which my app downloads from the web. I keep getting this error:
'UIDocumentInteractionController: invalid scheme (null). Only the file scheme is supported.'
I use the next code:
- (void) webServiceController:(WebServiceController *)webServiceController returnedArray:(NSMutableArray *)array {
if ([webServiceController.webRequest isEqualToString: #"generateExcelFileForEmployee"]) {
// start previewing the document at the current section index
NSString *link = [[NSString stringWithString:array[0]] stringByReplacingOccurrencesOfString:#"AdminFunctions.php" withString:#"AdminFunctions.xls"];
link = [[[link stringByReplacingOccurrencesOfString:#"\\" withString:#""]stringByReplacingOccurrencesOfString:#"/public/sites/" withString:#"http://"]stringByReplacingOccurrencesOfString:#"\"\\\"" withString:#""];
NSLog(#"%#", link);
NSData *urlData = [NSData dataWithContentsOfURL:[NSURL URLWithString:link]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"AdminFunctions.xls"];
BOOL isWriteSuccess = [urlData writeToFile:filePath atomically:YES];
NSLog(#"%#", filePath);
if(isWriteSuccess) //success
{
file = [NSURL fileURLWithPath:filePath];
self.fileView = [self setupControllerWithURL:file usingDelegate:self];
[self.fileView presentPreviewAnimated:YES];
}
else
{
NSLog(#"file not written");
}
}
}
- (UIDocumentInteractionController *) setupControllerWithURL: (NSURL*) fileURL usingDelegate: (id <UIDocumentInteractionControllerDelegate>) interactionDelegate {
UIDocumentInteractionController *interactionController =
[UIDocumentInteractionController interactionControllerWithURL: fileURL];
interactionController.delegate = interactionDelegate;
return interactionController;
}
- (UIViewController *) documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *) controller {
return self;
}
Both methods are implemented in the viewcontroller in which I want to present the preview. I know by hard i'm getting the data in the urlData object, because when I put in breakpoints, the console says that it contains 6144 bytes, which is exactly the size of the document to download.
I have been looking for this for quiet a while now, but I can't seem to figure out how to solve this. I already tried putting in the link object as a source for the documentInteractionController, but then the error changes to:
'UIDocumentInteractionController: invalid scheme (http). Only the file scheme is supported.'
Any comments on how I can overcome this issue would be highly appreciated
Try to use
// objC
file = [NSURL fileURLWithPath:filePath];
// swift
file = URL.init(fileURLWithPath: filePath)
instead of
//objC
file = [NSURL URLWithString:filePath];
// swift
file = URL.init(string: filePath)
Related
I'm getting base64-encoded file content from server. Then I'm writing data into file. I checked that file is not empty after that, it means data was successfully written to file. But when I'm trying to present this file, all I get is an empty view. So what am I doin' wrong?
enter image description here
[f getFile:^(NSString * _Nonnull string) {
NSString *docsDir;
NSArray *dirPaths;
NSData *content = [string dataUsingEncoding:NSUTF8StringEncoding];
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"hill.jpg"]];
[content writeToFile:databasePath atomically:YES];
if ([[NSFileManager defaultManager] fileExistsAtPath:databasePath]){
NSURL *url = [NSURL fileURLWithPath:databasePath];
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:url];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}
} Error:^(NSString * _Nonnull errorMessage) {
}];
Edited your code snippet, Try it
[f getFile:^(NSString * _Nonnull string) {
NSString *docsDir;
NSArray *dirPaths;
NSData *content = [[NSData alloc] initWithBase64EncodedString: string options:0];
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"hill.jpg"]];
[content writeToFile:databasePath atomically:YES];
if ([[NSFileManager defaultManager] fileExistsAtPath:databasePath]){
NSURL *url = [NSURL fileURLWithPath:databasePath];
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:url];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}
} Error:^(NSString * _Nonnull errorMessage) {
}];
From your code, I see you save a string into a an image file (hill.jpg). The UIDocumentInteractionController read file base on their extension, so they can't read this file. I think if you change your file name to hill.txt, your code could work. Hope this helps.
As you say, the content gets from the server are base64-encode, so you need to decode it.
NSData *imageContent = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions];
if (imageContent) {
// save to file
}
I am downloading the resources from server and writing it to the documents directory successfully by using this code below
NSString *fileName = [[[NSURL URLWithString:url] path] lastPathComponent];
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folder = [pathArr objectAtIndex:0];
NSString *filePath = [folder stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
NSLog(#"File written");
NSError *writeError = nil;
[webData writeToURL: fileURL options:0 error:&writeError];
if( writeError) {
NSLog(#" Error in writing file %#' : \n %# ", filePath , writeError );
return;
}
NSLog(#"%#",fileURL);
Now I want to access those files to use it.I have tried this code
-(UIImage *) loadImage:(NSString *)fileName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath
{
UIImage * result = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%#.%#", directoryPath, fileName, extension]];
return result;
}
Now method used is like this:
UIImage *imageFromWeb = [self loadImage:#"metaioman" ofType:#"png" inDirectory:folder];
_imgview.image=imageFromWeb;
But no luck...please help
Thank you
Firstly check file exists using NSFileManager.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathForFile = [NSString stringWithFormat:#"%#/%#.%#", directoryPath, fileName, extension]
UIImage * result = nil;
if ([fileManager fileExistsAtPath:pathForFile]){
//file exists
result = [UIImage imageWithContentsOfFile:pathForFile];
}
else
{
//file doesnot exists or wrong filename.
}
return result;
Reference : http://beageek.biz/save-and-load-uiimage-in-documents-directory-on-iphone-xcode-ios/
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
}
}
I'm having difficulty debugging this error. I get an EXC_BAD_ACCESS error after I click on Adobe or Nook as the selected app to open the download PDF. I've gone through with the debugger and it breaks somewhere outside of the methods I wrote. Here is the code in question.
//download protocol
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *storedEmail = [defaults stringForKey:#"email"];
NSString *storedPassword = [defaults stringForKey:#"password"];
NSString *projectID = self.study.ProjectID;
NSString *urlString = [NSString stringWithFormat:#"http://service.pharmatech.com/Document/projectprotocol/%#/%#/%#", storedEmail, storedPassword, projectID];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
if (data) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, [NSString stringWithFormat:#"Protocol_%#.pdf", projectID]];
[data writeToFile:filePath atomically:YES];
UIDocumentInteractionController *docController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
docController.delegate = self;
docController.UTI = #"com.adobe.pdf";
self.navigationController.toolbarHidden = NO;
BOOL isValid = [docController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
NSLog([NSString stringWithFormat:#"DISPLAYED? %d", isValid]);
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:NULL];
for (int count = 0; count < (int)[directoryContent count]; count++) {
NSLog(#"File %d: %#", (count + 1), [directoryContent objectAtIndex:count]);
}
[self.navigationController setToolbarHidden:YES animated:YES];
The popup shows with the apps capable of showing pdfs. After I click one the Bad Access error occurs, but not until after leaving the method this is called in, which is just a button click action.
If I run the app w/o the debugger is simply closes the my app and opens nothing. Anyone have any tips. I'm still very new to this ios stuff.
EDIT: For those who visit this later and don't read the comments, the answer is that the UIDocumentInteractionController must be an instance variable, not local.
It seems to be a problem with "docController", is a local variable and, i think, must be a instance variable.
Change
UIDocumentInteractionController *docController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
for
docController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
and in your .h declare
UIDocumentInteractionController *docController;
I have a CSV file that I'm downloading from an S3 account and I would like to show it in my ios app by using the Quicklook framework.
The error I'm getting is in my console. It says
QLPreviewController's datasource shouldn't be nil at this point.
This appears after this line of code runs // Set data source
[previewer setDataSource:self];
here's all the code for downloading the file, saving it and then loading with quicklook
-(void)showDocument
{
NSString *stringURL = #"http://jornada.s3.amazonaws.com/Dust.csv";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:nil];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"tempfile.csv"];
//[urlData writeToFile:filePath atomically:YES];
BOOL newFile = [[NSFileManager defaultManager] createFileAtPath:filePath contents:urlData attributes:nil];
arrayOfDocuments = [[NSArray alloc] initWithObjects:
filePath, nil];
QLPreviewController *previewer = [[QLPreviewController alloc] init];
[self addSubview:previewer.view];
// Set data source
[previewer setDataSource:self];
// Which item to preview
[previewer setCurrentPreviewItemIndex:0];
}
}
/*---------------------------------------------------------------------------
*
*--------------------------------------------------------------------------*/
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [arrayOfDocuments count];
}
/*---------------------------------------------------------------------------
*
*--------------------------------------------------------------------------*/
- (id <QLPreviewItem>)previewController: (QLPreviewController *)controller previewItemAtIndex:(NSInteger)index
{
// Break the path into it's components (filename and extension)
NSArray *fileComponents = [[arrayOfDocuments objectAtIndex: index] componentsSeparatedByString:#"."];
NSArray *filePaths = [[fileComponents objectAtIndex:0] componentsSeparatedByString:#"/"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:[((NSString *)[filePaths objectAtIndex:[filePaths count]-1]) stringByAppendingString:(NSString*)[fileComponents objectAtIndex:1]]];
// Use the filename (index 0) and the extension (index 1) to get path
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if (fileExists) {
//
}
return [NSURL fileURLWithPath:filePath isDirectory:NO];
}
You don't add a preview controller's view to your view - you present the preview controller. And you should do that after setting the data source! So, in this order:
QLPreviewController* preview = [QLPreviewController new];
preview.dataSource = self;
[self presentViewController:preview animated:YES completion:nil];