Detect file creation date on iPhone OS? - iphone

I was planning on writing some code whose logic was based upon testing the creation date of a particular file in my app's Documents folder. Turns out, when I call -[NSFileManager attributesOfItemAtPath:error:], NSFileCreationDate isn't one of the provided attributes.
Is there no way to discover a file's creation date?
Thanks.

The fileCreationDate is indeed part of the dictionary. Here's a method that gets passed a file URI and grabs some of the attributes from the file:
- (NSDictionary *) attributesForFile:(NSURL *)anURI {
// note: singleton is not thread-safe
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *aPath = [anURI path];
if (![fileManager fileExistsAtPath:aPath]) return nil;
NSError *attributesRetrievalError = nil;
NSDictionary *attributes = [fileManager attributesOfItemAtPath:aPath
error:&attributesRetrievalError];
if (!attributes) {
NSLog(#"Error for file at %#: %#", aPath, attributesRetrievalError);
return nil;
}
NSMutableDictionary *returnedDictionary =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
[attributes fileType], #"fileType",
[attributes fileModificationDate], #"fileModificationDate",
[attributes fileCreationDate], #"fileCreationDate",
[NSNumber numberWithUnsignedLongLong:[attributes fileSize]], #"fileSize",
nil];
return returnedDictionary;
}

According to Apple's reference, NSFileCreationDate is available in 2.0+:
NSFileCreationDate The key in a file
attribute dictionary whose value
indicates the file's creation date.
The corresponding value is an NSDate
object.
Available in iPhone OS 2.0 and later.
Declared in NSFileManager.h.

Related

how to add values to a dictionary inside a dictionary of a property list

I am currently creating a controller class for my plist.in this plist I have a root dictionary that has several types in it (Number, String and Dictionary), In my controller class I check for a plist then add it to the documents so I can read and write to it.
From here I read whats in my current plist and pass those values over to tempvars I have set up in this class.
This is what my read method looks like in my plist controller class.
-(void) readPlistData
{
// Data.plist code
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
self.protocolSignature = [temp objectForKey:#"Protocol"];
self.requestNumber = [temp objectForKey:#"RequestNumber"];
//How do I add the dictionary values here?
}
The reason I put the data into variables is because latter I am going to use these values to test against checks I want to perform against my db.. making sure of things like i am receiving the correct request number etc.
UPDATE:: my idea to add them to the dictionary inside the root dictionary would be something like this. which i think is not even close but it might give you a better clue to what I am trying to do.
self.cacheValue = [temp objectForKey:#"Cache Value"];
self.manufacturers = [cacheValue objectForKey:#"Manufacturers"];
self.models = [cacheValue objectForKey:#"Model"];
self.subModels = [cacheValue objectForKey:#"SubModels"];
any help would be greatly appreciated.
I believe you want to do the following:
Define your cacheValue property in the .h as a mutable dictionary.
NSMutableDictionary *cacheValue;
Serialize the plistXml as a NSMutableDictionary:
// This is the root Dictionary
NSMutableDictionary *temp = (NSMutableDictionary *)[NSPropertyListSerialization propertyListWithData:plistXML options:NSPropertyListMutableContainersAndLeaves format:NSPropertyListXMLFormat_v1_0 error:&error];
Since everything is mutable, you can now read, update, insert, delete any part of the dictionary or its subcontents. For instance, grabbing the Mutable Dictionary "Cache Value" is just:
self.cacheValue = [temp objectForKey:#"Cache Value"];
Remember to check that the object is not nil in case there isn't a value for the key. The key needs to be exactly as it appears in the plist.
Updating a value in the Mutable Dictionary is easy:
[self.cache setValue:#"New Value" forKey:#"Sub"];
And finally, to save the changes in the root Mutable Dictionary back to the plist:
/*
The flag "atomically" specifies whether the file should be written atomically or not.
If flag is YES, the receiver is written to an auxiliary file, and then the auxiliary file is renamed to path.
If flag is NO, the dictionary is written directly to path.
The YES option guarantees that path will not be corrupted even if the system crashes during writing.
*/
[self.temp writeToFile:plistPath atomically:YES];
Hope this helps, cheers!

Reading from and writing to plists in xcode 4.0.2 - issue retrieving from dictionary

I have been following this Tutorial on how to read from a plist. However i cannot read the string with key name. i tried putting NSLog("%#", [temp objectForKey:#"Name"]); but it returns null. i had a similar issue before which involved opening the xml file and manually changing dict to array. How can i fix this?
the hierarchy in the xml file is what confuses me. the dictionary i created named root with string John Doe is part of a dictionary itself? Clarification appreciated!
From the tutorial linked above:
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
NSLog(#"%#", [temp objectForKey:#"Name"];
Most likely this is because there is no object with key #"Name" (remember that Xcode is case sensitive!) in the (presumably) NSDictionary named temp.
For a more specific answer, you'll need to post the code where you define temp.

Not able to write into text file

I need to write a string into a file. For that, my code is:
-(void)writeToFile:(NSString *)fileName: (NSString *)data {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// the path to write file
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
[data writeToFile:appFile atomically:YES];
}
I am calling this method like this
ownServices *obj = [[ownServices alloc]init];
[obj writeToFile:#"iphone.txt" :#"this is mahesh babu"];
but it didn't write into the text file.
What's the reason? Can anyone please help me.
Thank u in advance.
The most likely problem is that the documents directory does not exist. Create it if it doesn't, then write to it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *parentDir = [paths objectAtIndex:0];
/* Create the parent directory.
* This is expected to fail if the directory already exists. */
(void)[[NSFileManager defaultManager]
createDirectoryAtPath:parentDir
withIntermediateDirectories:YES
attributes:nil error:NULL];
NSString *path = [parentDir stringByAppendingPathComponent:fileName];
/* Now write, and if it fails, you'll know why thanks to the |error| arg. */
NSError *error = nil;
BOOL ok = [data writeToFile:path options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: Failed to write to %#: %#", __func__, path, error);
}
Even simpler would be to use the latest API, which will create the directory for you if it doesn't already exist:
NSError *error = nil;
NSURL *parentURL = [[NSFileManager defaultManager]
URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask
appropriateForURL:nil create:YES error:&error];
if (!parentURL) {
NSLog(#"%s: *** Failed to get documents directory: %#", __func__, error):
return;
}
NSURL *furl = [parentURL URLByAppendingPathComponent:fileName];
error = nil;
BOOL ok = [data writeToURL:furl options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: *** Failed to write to %#: %#", __func__, furl, error);
}
Firstly, you are calling your method strangely. Rename the method to
-(void)writeString:(NSString *) data toFile:(NSString *)fileName
and call it like so:
[obj writeString:#"this is mahesh babu" toFile:#"iphone.txt"];
Secondly, writeToFile:atomically: is deprecated, use writeToFile:atomically:encoding:error::
NSError *error = nil;
BOOL success = [data writeToFile:appFile atomically:YES encoding:NSUTF8Encoding error:&error];
if (!success) {
NSLog(#"Error: %#", [error userInfo]);
}
This way, you also see what the error is.
Your code looks OK. Use the debugger (or an NSLog statement) to verify the values of data and appFile. If data is nil, nothing will happen (including no errors) because sending a message to nil is a no-op. It's also possible that appFile is not the path you think it is.
Check the permissions of the directory you are trying to write to (ls -la). On the device you can't, but on the simulator you can. Is it read-only for you? Is it owned by another user?
Assuming that isn't the problem, try calling with atomically:NO. Atomic file writing is performed by writing a file, then renaming it to replace the old one. If the problem is there, that will isolate the problem.
Bonus Style Critique
Class names should start with an uppercase letter: OwnServices instead of ownServices
Although your method name is perfectly valid, it's unusual to have two parameters with no words to separate them. A name like writeToFile:string: would be better.
Don't name a variable data if it is meant to point to an instance of something other than NSData. It's confusing, and there's almost a better (more specific) word you can use beside "data".

iPhone - properties of a file [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
iPhone - file properties
Hi all. i m creating an application which makes the iphone work as a pendrive for easy file sharing purpose.
In the first stage, i have some files(png, pdf, jpg, zip) in a directory and i made them display in the tableview in the form of mutable array without the extensions of each file.
In the second stage i have a detailedViewController which then displays the detailed view of the files like
file size
file type
if it is a image, it should open in imageView
if it is a song, it should play it
So i need to retrieve the properties like filePath, fileType, fileSize.. of each files. Now i got stuck in getting those properties like fileSize and fileType... Please help me proceed with a sample source code.
Here is my code.
- (void)listFiles {
NSFileManager *fm =[NSFileManager defaultManager];
NSError *error = nil;
NSString *parentDirectory = #"/Users/akilan/Documents";
NSArray *paths = [fm contentsOfDirectoryAtPath:parentDirectory error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
error = nil;
}
directoryContent = [[NSMutableArray alloc] init];
for (NSString *path in paths){
documentsDirectory = [[path lastPathComponent] stringByDeletingPathExtension];
NSLog(#"%#", documentsDirectory);
[directoryContent addObject:documentsDirectory];
}
Thanks in advance..
you should use the method attributesOfItemAtPath:error: of your filemanager instance.
Have a look at the documentation.

Why doesen't it work to write this NSMutableArray to a plist?

edited.
Hey, I am trying to write an NSMutableArray to a plist.
The compiler does not show any errors, but it does not write to the plist anyway.
I have tried this on a real device too, not just the Simulator.
Basically, what this code does, is that when you click the accessoryView of a UITableViewCell, it gets the indexPath pressed, edits an NSMutableArray and tries to write that NSMutableArray to a plist. It then reloads the arrays mentioned (from multiple plists) and reloads the data in a UITableView from the arrays.
Code:
NSIndexPath *indexPath = [table indexPathForRowAtPoint:[[[event touchesForView:sender] anyObject] locationInView:table]];
[arrayFav removeObjectAtIndex:[arrayFav indexOfObject:[NSNumber numberWithInt:[[arraySub objectAtIndex:indexPath.row] intValue]]]];
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"arrayFav.plist"];
NSLog(#"%# - %#", rootPath, plistPath);
[arrayFav writeToFile:plistPath atomically:YES];
// Reloads data into the arrays
[self loadDataFromPlists];
// Reloads data in tableView from arrays
[tableFarts reloadData];
CFShow() on the array after removing one of them shows this:
<CFArray 0x6262110 [0x2c810a0]>{type = mutable-small, count = 4, values = (
0 : <CFNumber 0x6502e10 [0x2c810a0]>{value = +3, type = kCFNumberSInt32Type}
1 : <CFNumber 0x6239de0 [0x2c810a0]>{value = +8, type = kCFNumberSInt32Type}
2 : <CFNumber 0x6239dc0 [0x2c810a0]>{value = +10, type = kCFNumberSInt32Type}
3 : <CFNumber 0x6261420 [0x2c810a0]>{value = +40, type = kCFNumberSInt64Type}
DEBUG-INFO: writeToPlist shows YES, I have tried to release all the arrays before filling them up again, setting them to nil, set atomically to NO.
As discussed in the comments below, the actual problem here is that the plist is being read from and written to two different locations. Somewhere in the app, there is code that reads the file into the array similar to this:
NSString *plistFavPath = [[NSBundle mainBundle] pathForResource:#"arrayFav"
ofType:#"plist"];
arrayFav = [[NSMutableArray alloc] initWithContentsOfFile:plistFavPath];
This logic reads the array from the application's bundle, which is a read-only location and part of the distributed app. Later when the edited array is persisted, code similar to this is used:
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) objectAtIndex:0];
NSString *plistPath = [rootPath
stringByAppendingPathComponent:#"arrayFav.plist"];
NSLog(#"%# - %#", rootPath, plistPath);
[arrayFav writeToFile:plistPath atomically:YES];
The result here is that the updated file gets written to the app's documents directory, but it is never read from there, giving the appearance that the file is not being saved correctly. To correct this, you should change the code that reads the file to use the same path that you are writing to.
If you need to distribute a default version of the plist for use on the initial launch before the array has been edited, you could continue to include a version of the file in your bundle and then add code to your app delegate that check if the file exists in the documents directory and if it is not present, copies the bundle's default version of the file to the proper place.
[yourMutableArray writeToFile:fileName atomically:YES];
This should work. NSMutableArray inherits from NSArray which has a method to write to a plist.
writeToFile:atomically: won't work if your array contains custom objects.
If your array contains custom objects that are not Plist objects (NSArray, NSDictionary, NSString, NSNumber, etc), then you will not be able to use this method. This method only works on Plist objects.
Another option would be to use the NSCoding protocol, and write your objects to disk that way.
Yes
Look at the Property List Programming Guide.
phoneNumbers is a NSMutableArray
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
NSString *error;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: personName, phoneNumbers, nil]
forKeys:[NSArray arrayWithObjects: #"Name", #"Phones", nil]];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error];
if(plistData) {
[plistData writeToFile:plistPath atomically:YES];
}
else {
NSLog(error);
[error release];
}
return NSTerminateNow;
}