How do I get QuickLook to show offline files? - iphone

Our iPad app can show Documents and save them offline when needed.
I've got a QLPreviewController subclass named DocumentViewController (named DVC from now on) for showing them.
Workflow of the app:
- The user clicks a name of a document and the DVC is pushed on to show the document.
- The DVC downloads the file offline and shows it when done.
(So the HTTP URL is downloaded, stored offline, and an offline URL is returned)
The weird thing is, is that only PDF files are working with the offline URL, and the rest crashes.. (it works with online links though)
I did some tests and when I put file:// before the offline link the app does not crash but the DVC is ging me some information about the file (like that it is a excel 97-2004 document).
So some info is transferred, but I can't figure out what the problem is.
Here are some screenshots and after that some code.
code:
Note that Document is a model class with document properties like id, name, file type and url.
//DVC QLPreviewController dataSource method for returning url
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger)index
{
[SaveHelper saveDocumentFileAndPropertyWithDocument:document];
//[SaveHelper getDocumentFileWithDocument:document]; without file://
//if I return document.documentUrl it is working with all files except iworks files)
return [SaveHelper getDocumentFileAsPathWithDocument:document]; //with file://
}
//SaveHelper methods
+ (NSString *)documentFilePathWithDocument:(Document *)document
{
return [[self documentFilePath] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#%d.%#", DOCUMENT_FILE_PREFIX, document.documentId, document.documentType]];
}
+ (NSURL *)saveDocumentFileAndPropertyWithDocument:(Document *)document
{
if([self saveDocumentPropertyWithDocument:document])
{
return [self saveDocumentFileWithDocument:document];
}
return nil;
}
+ (NSURL *)saveDocumentFileWithDocument:(Document *)document
{
NSData *data = [NSData dataWithContentsOfURL:document.documentURL];
NSString *fullPath = [self documentFilePathWithDocument:document];
if([[NSKeyedArchiver archivedDataWithRootObject:data] writeToFile:fullPath atomically:YES])
{
return [NSURL fileURLWithPath:fullPath];
}
return nil;
}
+ (NSURL *)getDocumentFileWithDocument:(Document *)document
{
return [NSURL fileURLWithPath:[self documentFilePathWithDocument:document]];
}
+ (NSURL *)getDocumentFileAsPathWithDocument:(Document *)document
{
return [NSURL fileURLWithPath:[#"file://" stringByAppendingPathComponent:[[self getDocumentFileWithDocument:document] absoluteString]]];
}
If more code needed, just say.
EDIT:
When logging the URL passed trough the 'getDocumentFileAsPathWithDocument' method:
url: file:/var/mobile/Applications/xx-xx/Documents/documentFiles/file_20.pdf
url: file:/var/mobile/Applications/xx-xx/Documents/documentFiles/file_80.docx
Where the PDF file is working and the docx not
When I try to load an image(jpg) from local storage I get a black screen with this error message:
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/System/Library/Frameworks/QuickLook.framework/DisplayBundles/Image.qldisplay/Image (file not found).
warning: No copy of Image.qldisplay/Image found locally, reading from memory on remote device. This may slow down the debug session.
EDIT:
The webview does not work either with the local urls. PDF is fine but the office files gives an message "Unable to read Document, the file format is invalid". The iWorks documents give the same error as the quicklook. I think its somewhere at the save and load of the format, I savve them as a NSDATA but after that there is no hint for the iPad to see if it is for example a word document (only the extension).

You haven't posted your download code, but I believe that the problem is there. Files from Pages (.pages extension) aren't actual files, they are bundles, i.e. directories that contain files and show as a single item in Finder (look up a .pages file in Finder, right-click it and select 'Show contents'). Downloading a .pages file is actually like downloading a directory: it depends on the web server what kind of result you get but it's most likely an error page.
You could detect that it's a .pages file and try to download all of its contents manually, but you'd have to study the structure of the files to see if that's possible because it's unlikely that you can request the contents of the directory from a web server.
The results for the .ppt and .xls files look normal to me; I think it unlikely that the iPad can preview MS Office documents at all.
Edit: apologies, I just read that iOS can preview MS Office documents. Perhaps the documents get somehow corrupted during download? Have you tried to set your download location to the app's documents folder and enable iTunes file sharing? That way you can download some documents, pull them off your device and then try to open it on your PC to see if that works.

We finally found the solution!
I was right that the problem was with saving the document.
I needed to change the save method in:
+ (NSURL *)saveDocumentFileWithDocument:(Document *)document
{
NSData *data = [NSData dataWithContentsOfURL:document.documentURL options:NSDataReadingUncached error:nil];
NSString *fullPath = [self documentFilePathWithDocument:document];
if([[NSFileManager defaultManager] createFileAtPath:fullPath contents:data attributes:nil])
{
return [NSURL fileURLWithPath:fullPath];
}
//OLD CODE
// if([[NSKeyedArchiver archivedDataWithRootObject:data] writeToFile:fullPath atomically:YES])
// {
// return [NSURL fileURLWithPath:fullPath];
// }
return nil;
}
SO saving it with the filemanager and not with a keyedarchiver.

Did you check if the size of the files is the same both online and offline? It is possible that the file download wasn't complete
Try using the URL of the MS Office documents with a normal NSURL object and opening in a UIWebView. Does it work then (so we know if its the document or your class)?
Try using NSURL's fileURLWithPath: method in the getDocumentFileAsPathWithDocument: It is possible that the URL being returned is incorrect (though doesn't look like it from the logs but doesn't hurt to try)

first of all, use this code to make sure your documents are there,because i think the error cause by your documents path.
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath:fullPath]){
NSLog(#"%# exsit! ",fullPath);
}else{
NSLog(#"%# not exsit! ",fullPath);
}

If any of one have same problem even though you did everything suggestions above.
(I had same problem, when I downloaded some files from google drive.)
Try this!
Put 'x' end of your file extension to be recognized as a new version of format.
(it's working only for 'doc' and 'ppt' files, not for 'xls' files)
Yes, I know this is not a appropriate way to solve this problem, but
it's worth to try it.
Believe me I tried everything!
Hope you help.

Related

DropBox Core API Downloading Images for UICollectionView

Im new to dropbox Core API. Previously i have some experience with making calls to instagram api's with NSURLSession on the treehouse course I'm on. However I'm stuck here. Iv called the method
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if (metadata.isDirectory) {
NSLog(#"Folder '%#' contains:", metadata.path);
for (DBMetadata *file in metadata.contents) {
[self.pathsArray addObject:file.path];
NSLog(#"----%#", pathsArray);
}
}
//reloads view to see images
[self.collectionView reloadData];
}
Which has given me an array of the file path location. I then wrote
NSString *paths = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[self.filePaths lastPathComponent]]];
`for (NSString *file in self.filePaths) {
[self.restClient loadFile:file intoPath:paths];
}`
which downloads the images correctly however, because the loaded file method returns null i am unsure how create an array of the downloaded images to display in my UICollectionViews cell?
The loadFile method in the Dropbox for iOS Core SDK says:
/* Loads the file contents at the given root/path and stores the result into destinationPath */
- (void)loadFile:(NSString *)path intoPath:(NSString *)destinationPath;
This means the downloaded content (for the file on Dropbox at path) is saved to the local filesystem at the local path destinationPath.
You can use this delegate method to know when this is done:
- (void)restClient:(DBRestClient*)client loadedFile:(NSString*)destPath;
To get the content for the downloaded file once you've been notified it is done, you need to read the downloaded file from the local file system. For example, you might load it using NSString stringWithContentsOfFile:encoding:error, or whatever the relevant next step is for you.

Objective-C: Create text file on device and easily retrieve the file

I am wanting to write my own logs to a text file on my iPhone. I wrote up a quick method that writes a string to a file. Right now it saves it into the Documents directory, which, if on the device is going to be a pain to get off, since I can't just browse to it. Is there a better way to quickly get this file off the device after I have written to it?
/**
* Logs a string to file
*
* #version $Revision: 0.1
*/
+ (void)logWithString:(NSString *)string {
// Create the file
NSError *error;
// Directory
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"log.txt"];
// Get the file contents
NSData *localData = [NSData dataWithContentsOfFile:filePath];
if (localData) {
NSString *logString = [[NSString alloc] initWithData:localData encoding:NSUTF8StringEncoding];
string = [logString stringByAppendingFormat:#"%#\n", string];
[logString release];
}
// Write to the file
[string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
}//end
Add Application supports iTunes file sharing to your application target's build info in Xcode:
Then, you can easily browse, retrieve and delete any files created by the app from iTunes, right under Devices > Your device > Apps > File Sharing:
You may have to capture what number of logs you have created so far and create a new name for each log biased on that.
So you might save your last made logs name as a string in NSUserDefaults and get the number off the end of that and add one onto that captured int ready for the next name.
So if you have #"Log4" you can get the 4 out of that and make it 5 so that the next log is named "Log5"
Just my 2 cents :P
With regard to the 'How to get the file' part of the question
iExplorer, previously iPhone Explorer allows you to view your apps, including their documents folder without jailbreaking your devices.
In my experience (albeit of an older version), getting the files from the phone can be a little temporamental (i.e. I drag a file onto my desktop and although it creates the file, it doesn't write any of the data), you can get the files from your device.

replaceItemAtURL fails without error on iOS but works fine on OSX

I'm implementing a manually-triggered migration process for a CoreData-based app, and after the migration completes successfully, I'm trying to move the migrated DB back over the top of the original one using replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:.
The problem is that on iOS, nothing I do will make this method return YES, however it also never puts anything into the error pointer to allow you to see what's going wrong.
I'd read things elsewhere (e.g. http://www.cocoabuilder.com/archive/cocoa/287790-nsdoc-magic-file-watcher-ruins-core-data-migration.html) indicating that not shutting down all the CoreData objects (e.g. NSMigrationManager, NSManagedObjectModel etc) before attempting the replace might be the cause, but that wasn't it. I even implemented a little two file create-and-swap thing that didn't involve CoreData DBs at all to verify that the CoreData stuff didn't have anything to do with it.
I then noticed in the official documentation that the newitemURL is supposed to be in a directory deemed appropriate for temporary files. I assumed that that meant a directory returned by URLForDirectory:inDomain:appropriateForURL:create:error: using NSItemReplacementDirectory as the search path.
That didn't work either! I ended up falling back to implementing the replacement logic using separate operations, but this is non-atomic and unsafe and all that bad stuff.
Does anyone have a working snippet of code that runs on iOS that either return YES from a call to replaceItemAtURL or actually puts error information into the error pointer?
Any help much appreciated.
EDIT - Test code included below. This runs in application:didFinishLaunchingWithOptions: on the main thread.
NSFileManager *fm = [[NSFileManager alloc] init];
NSError *err = nil;
NSURL *docDir = [NSURL fileURLWithPath:[self applicationDocumentsDirectory]];
NSURL *tmpDir = [fm URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask
appropriateForURL:docDir
create:NO
error:&err];
NSURL *u1 = [docDir URLByAppendingPathComponent:#"f1"];
NSURL *u2 = [tmpDir URLByAppendingPathComponent:#"f2"];
NSURL *repl = nil;
[fm createFileAtPath:[u1 path]
contents:[[NSString stringWithString:#"Hello"]
dataUsingEncoding:NSUTF8StringEncoding]
attributes:nil];
[fm createFileAtPath:[u2 path]
contents:[[NSString stringWithString:#"World"]
dataUsingEncoding:NSUTF8StringEncoding]
attributes:nil];
BOOL test = [fm replaceItemAtURL:u1 withItemAtURL:u2 backupItemName:#"f1backup"
options:0 resultingItemURL:&repl error:&err];
// At this point GDB shows test to be NO but error is still nil
I have experienced issues with all the NSFileManager methods using an URL on iOS. However, all the methods using Path work. So I think you should use removeItemAtPath:error:and copyItemAtPath:toURL:error: for that purpose.
Hope it helps
In mac file system is not case sensitive, but in IOS it. Even though you cant have two files with same name but with different case at one location, the path is case sensitive. So if file is has .JPEG and in your code you are passing link with .jpeg it will fail.
It may not be the case with you but just what to share
Although strangely it should give you error.

MPMoviePlayer load and play movie saved in app documents

I am writing an application that stores the movies in the photo roll into the local documents folder for the app. I can play remote movies using the MPMoviePlayer, however trying to play a movie stored in the documents folder for the app always returns MPMovieLoadStateUnknown.
The notifications are all getting sent and received from the default notification center (MPMoviePlayerLoadStateDidChangeNotification, MPMoviePlayerPlaybackDidFinishNotification).
An alert box shows up shortly after getting the loadStateUnknown message in the console, saying that the movie could not be played and the app then receives the movie playback completed notification.
I think that it may be a case that the filename (MPMoviePlayer can only take a URL as the asset location) cannot be found. Has anyone dealt with this issue or similar?
Given that none of the other answers seem to resolve the problem, I'm inclined to think that the problem might be with the Documents file path you are producing.
You should be using the following method to generate the path to the application's documents folder:
NSString *userDocumentsPath = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0)
{
userDocumentsPath = [paths objectAtIndex:0];
}
have you tried converting the local file path to a url
[NSURL URLWithString: [NSString stringWithFormat:#"file:/%#//",filePath]];
if there are spaces in you file path, you will have to convert them prior to creating the URL
filePath = [filePath stringByReplacingOccurrencesOfString:#"/" withString:#"//"];
filePath = [filePath stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
The Core Data template converts a file path to a URL like this:
NSURL *storeUrl = [NSURL fileURLWithPath:whatever];
That seems to do all of the correct space escaping and stuff because I'm using this to load a file with a space in the path.
I am not sure if this will help you.
In this tutorial there are some code lines which probably will bring you on the right path:
// Unless state is unknown, start playback
if ([mp loadState] != MPMovieLoadStateUnknown)
...
You will find this snippet in following method:
- (void) moviePlayerLoadStateChanged:(NSNotification*)notification
here is the link to the tutorial:
Tutorial
Hopefully this will help you...

NSFileManager - Copying Files at Startup

I need to copy a few sample files from my app's resource folder and place them in my app's document folder. I came up with the attached code, it compiles fine but it doesn't work. All the directories I refer to do exist. I'm not quite sure what I am doing wrong, could someone point me in the right direction please?
NSFileManager*manager = [NSFileManager defaultManager];
NSString*dirToCopyTo = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString*path = [[NSBundle mainBundle] resourcePath];
NSString*dirToCopyFrom = [path stringByAppendingPathComponent:#"Samples"];
NSError*error;
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyFrom error:nil];
for (NSString *file in files)
{
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:dirToCopyTo error:&error];
if (error)
{
NSLog(#"%#",[error localizedDescription]);
}
}
EDIT: I just edited the code the way it should be. Now however there's another problem:
2010-05-15 13:31:31.787 WriteIt
Mobile[4587:207] DAMutableDictionary.h
2010-05-15 13:31:31.795 WriteIt
Mobile[4587:207] FileManager
Error:Operation could not be
completed. File exists
EDIT : I have fixed the issue by telling NSFileManager the names of the copied files's destinations.
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:[dirToCopyTo stringByAppendingPathComponent:file] error:&error];
I think the problem is in this line:
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyTo error:nil];
You are listing files in a destination directory instead of the source. Change it to something like:
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyFrom error:nil];
And you should be fine.
I think the problem is that yo are reading the files to copy from dirToCopyTo and I think you meant dirToCopyFrom
Also to get the documents directory you should be using NSDocumentDirectory with - (NSArray *)URLsForDirectory:(NSSearchPathDirectory)directory inDomains:(NSSearchPathDomainMask)domainMask
Please note that lengthy operation on startup must be avoided:
Not a good User Experience (delay and croppy behavior)
Watchdog in iOS can kill your app as if it were stuck.
So perform copy in a secondary thread (or operation... or whatever uses a different execution path).
Another problem will arise if You need data to populate your UI: in that case:
Disable UI elements
Start an async / threaded operation
In the completion call back of copying (via a notification, a protocol.. or other means)
notify to the UI interface it can start fetching data.
For example we copy a ZIP file and decompress it, but it takes some time so we had to put it in a timer procedure that will trigger UI when done.
If You need an example, ket me know.
PS:
Copying using ZIP file is MORE efficient as:
Only call to file system
Far less bytes to copy
The bad news: you must use a routine do decompress zip file, but you can find them on the web.
Decompressing Zip files should be more efficient as these calls are written in straight C, and not in Cocoa with all the overhead.
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:dirToCopyTo error:&error];
The destination path is the path you want the copy to have, including its filename. You cannot pass the path to a directory expecting NSFileManager to fill in the name of the source file; it will not do this.
The documentation says that the destination path must not describe anything that exists:
… dstPath must not exist prior to the operation.
In your case, it's the path to the destination directory, so it does exist, which is why the copy fails.
You need to make it a path to the destination file by appending the desired filename to it. Then it will not exist (if not previously copied), so the copy will succeed.