Is there a way to get the last opened files of an application? I know with LSSharedFileListCreate you can get an array of the global recent documents of the user but I was wondering if it was possible to get the recent documents for a specific application, such as Xcode, using cocoa or objective-c.
I don't think there is a public API for getting recent documents of apps other than your own. The recent files are saved as plists in ~/Library/Preferences though, so you could just read those directly.
Note however that the format of these plists might change with future OS upgrades, so you should ideally not rely on this.
Here's an example for reading the recent documents of Xcode:
NSString *bundleID = #"com.apple.dt.Xcode";
NSString *prefPath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"Preferences"];
NSString *recentFilesFileName = [bundleID stringByAppendingPathExtension:#"LSSharedFileList.plist"];
NSString *recentFilesPath = [prefPath stringByAppendingPathComponent:recentFilesFileName];
NSArray *recentDocumentItems = [[[NSDictionary dictionaryWithContentsOfFile:recentFilesPath] objectForKey:#"RecentDocuments"] objectForKey:#"CustomListItems"];
for (NSDictionary *recentDocumentItem in recentDocumentItems) {
NSString *name = [recentDocumentItem objectForKey:#"Name"];
NSData *bookmarkData = [recentDocumentItem objectForKey:#"Bookmark"];
NSError *error = nil;
NSURL *bookmarkURL = [NSURL URLByResolvingBookmarkData:bookmarkData options:NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:NULL error:&error];
if (bookmarkURL) {
NSLog(#"File name: %#", name);
NSLog(#"URL: %#", bookmarkURL);
} else {
NSLog(#"Could not resolve URL for file %#: %#", name, error);
}
}
This won't work in a sandboxed app of course.
You can use -[NSDocumentController recentDocumentURLs], which retrieves an array of URLs of the most recently opened documents:
NSArray *array = [[NSDocumentController sharedDocumentController] recentDocumentURLs];
Related
I've made an app that is relying on reading and writing a plist-file. This works well when I'm running the app in the iPhone simulator, but doesn't work at all when I'm testing it on my iPhone. I've also made a pre made text file in .txt format with demo data. The app works when I'm running this file.
All the reading and writing is done in a class that looks like this:
-(void)saveArray:(NSMutableArray *)inputArray
{
albumArray = inputArray;
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [path objectAtIndex:0];
NSString *filePath = [documentFolder stringByAppendingFormat:#"albums.plist"];
[albumArray writeToFile:filePath atomically:YES];
}
Update: Changed the string from "stringByAppendingFormat" to "stringByAppendingPathComponent" and it seems to work now. Thanks a lot! You guys made my day made.
Are you sure, that the folders already exist?
Here is a function i'm using to get the path to my file:
- (NSString*) pathToSavedAlbums
{
NSURL *applicationSupportURL = [self applicationDataDirectory];
if (! [[NSFileManager defaultManager] fileExistsAtPath:[applicationSupportURL path]])
{
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:[applicationSupportURL path]
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error)
{
NSLog(#"error creating app support dir: %#", error);
}
}
NSString *path = [[applicationSupportURL path] stringByAppendingPathComponent:#"albums.plist"];
return path;
}
Check the spelling of the plist name as well as the case, device is case sensitive for docs but simulator isn't. Also try deleting the app from the device and reinstalling it ?
writeToFile:atomically: returns a bool, so check that to see if it fails to even write to the path. Check the file path string and ensure this is where you want it to go.
Learning to use coreData. Currently looking at Stanford's CS193P Lecture 14, which is very helpful. I have successfully set up a working app with core data persistence, using a ManagedDocument.
This code below is run every time the app starts. My confusion is: how do we know that the url for the document is correct? How does it know that "lastObject" will always be the URL for the saved document?
if (!myManagedDocument) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"theDatabase"];
myManagedDocument = [[UIManagedDocument alloc]initWithFileURL:url];
}
This code below will open the document, or create/save it if it has not already been saved previously.
if (![[NSFileManager defaultManager] fileExistsAtPath:[myManagedDocument.fileURL path]]) {
[myManagedDocument saveToURL:myManagedDocument.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL sucess) {
[self getInfoFromDatabase];
}];
} else if (myManagedDocument.documentState == UIDocumentStateClosed) {
[myManagedDocument openWithCompletionHandler:^(BOOL sucess) {
[self getInfoFromDatabase];
}];
} else if (myManagedDocument.documentState == UIDocumentStateNormal) {
[self getInfoFromDatabase];
}
Depending on the directory and domainMask argument, URLsForDirectory can return
an array of several URLs. For example, on OS X,
NSArray *urls = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSAllDomainsMask];
returns
(
file://localhost/Users/<user>/Library/Application%20Support/,
file://localhost/Library/Application%20Support/,
file://localhost/Network/Library/Application%20Support/
)
But in your case, on iOS,
NSArray *urls = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
returns an array of exactly one URL, which is the document directory inside the
application sandbox. On the simulator, this would look like
(
file://localhost/Users/<user>/Library/Application%20Support/iPhone%20Simulator/5.0/Applications/AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE/Documents/
)
So it does not matter if you take the first or last object of that array.
The code just assumes that the managed document is saved in the document directory
of the application sandbox.
I´m writing certain values to a file. See Write Operations below.
This works fine when using iPad 6.1 Simulator.
When trying the same thing on my iPad it fails. I think it´s something with sandboxing. I haven´t found out yet which path is best on iOS Devices to write stuff for internal use.
Any ideas?
#pragma mark Write Operations to Tmp Folder
- (BOOL) psWriteFileWithName: (NSString*) fileName
withString:(NSString*) string {
NSString *fileName = #"artistNumber";
NSError * error = NULL;
NSString *filePath = [NSString stringWithFormat:#"/tmp/%#.txt",fileName];
[string writeToFile:filePath
atomically:YES
encoding: NSUTF8StringEncoding
error:&error];
return YES;
}
You cannot write to /tmp since this is outside of your app sandbox.
However your app also has a temp directory, which can be referenced with the NSTemporaryDirectory() function:
Which works like:
NSString *tempfilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
Here is you method with the correct NSTemporaryDirectory() implementation, also edit some error handling:
#pragma mark Write Operations to Tmp Folder
- (BOOL) psWriteFileWithName: (NSString*) fileName
withString:(NSString*) string {
NSString *fileName = #"artistNumber";
NSError *error = nil;
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
if (![string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error] ) {
NSLog(#"Error writing file: %#", error);
return NO;
}
return YES;
}
I currently have a plist file in my iOS Project which is downloaded from the web when updates are available and it contains a list of news articles along with images.
The application caches the images on the iPhone for offline access, I am currently trying to write a function which will clean the cached files every so often.
Currently I have this code which looks in the Temp folder for images and then deletes them, however for each image found I would like it to check if the file name exists as a value in the plist stored as NSDictionary before deleting, however I am not sure of a quick method to search the NSDictionary without the need for a for statement.
Any tips would be great.
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TMP error:nil];
if (files == nil) {
// error...
NSLog(#"no files found");
}
for (NSString *file in files) {
NSString *uniquePath = [TMP stringByAppendingPathComponent: file];
if([file rangeOfString: #".png" options: NSCaseInsensitiveSearch].location != NSNotFound)
{
NSLog(#"%#", file);
if ([[NSFileManager defaultManager] removeItemAtPath: uniquePath error: NULL] == YES)
NSLog (#"Remove successful");
else
NSLog (#"Remove failed");
}
}
EDIT
I have currently added this not sure if its the best way to do it but it works.
NSArray *newsArray = [self.newsData allValues];
// Convert the Array into a string
NSString *newsString = [newsArray description];
// Perform Range Search.
NSRange range;
range = [newsString rangeOfString : filename];
if (range.location != NSNotFound) {
NSLog(#"The file exists in the plist %#", filename);
} else {
// Delete the file
}
You could reduce the array so that it only contains the objects you are interested in by using a NSPredicate, then quickly loop over the objects which you wish to delete. Like so:
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TMP error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF CONTAINS[cd] '.png'"];
NSArray *filteredArray = [files filteredArrayUsingPredicate:thePredicate];
for (NSString *file in filteredArray) {
NSString *uniquePath = [TMP stringByAppendingPathComponent:file];
if ([[NSFileManager defaultManager] removeItemAtPath: uniquePath error: NULL])
NSLog (#"Remove successful");
else
NSLog (#"Remove failed");
}
This will mean that the for loop is only looping over objects you are interested in.
Since you do not care about the sequence of files in plist or folder and you obviously won't have duplication, use NSSet rather than NSArray and then use intersect method (intersectsSet:) to find intersection.
I seem to have stumbled over a problem regarding saving an xml file from a string (this is done on the iPhone)
The file itself exists and included in the project (hence within the workspace), and all indications I get from the code snippet which follows passes without any errors on the emulator and fail on the iPhone (error 513), but in either case the file is not saved!
{
Hits = config->Hits;
NSString* filenameStr = [m_FileName stringByAppendingFormat: #".xml" ];
NSString* pData = [self getDataString]; // write xml format - checked out ok
NSError *error;
/* option 2 - does not work as well
NSBundle *mainBundle = [NSBundle mainBundle];
NSURL *xmlURL = [NSURL fileURLWithPath:[mainBundle pathForResource: m_FileName ofType: #"xml"]];
if(![pData writeToURL: xmlURL atomically: true encoding:NSUTF8StringEncoding error:&error])
{
NSLog(#"Houston - we have a problem %s#\n",[error localizedFailureReason]);
return false;
}
*/
if(![pData writeToFile: filenameStr atomically: FALSE encoding:NSUTF8StringEncoding error:&error])
{
NSLog(#"Houston - we have a problem %s#\n",[error localizedFailureReason]);
return false;
}
return true;
}
Any help would be appreciated,
-A
You should not write to files included in the application package. On a real iPhone, you may be prevented from doing this because these files are digitally signed.
Even if you can modify a packaged file, it is not a good place to store data. Re-installing the application from an App Store upgrade or an Xcode build will overwrite the file with the original.
Instead, store your XML into the Documents directory. You can get the path like this:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* leafname = [m_FileName stringByAppendingFormat: #".xml" ];
NSString* filenameStr = [documentsDirectory
stringByAppendingPathComponent:leafname];
If your file needs some initial state that you don't want to generate in your code, have your app check that it is present in the documents directory the first time it is needed and, if it is missing, copy it from the template in the package.
An alternative to storing structured data is to use user defaults. For example:
[[NSUserDefaults standardUserDefaults] setObject:foo forKey:FOO_KEY];