objective-c check if copied file ready - objective-c

I'm monitoring a folder, when a user copies a file into the folder, I need to perform an action. However, when I perform the action the file has no contents yet. Is there a way to check that the copied file is ready?

NSFileManager got a method - (BOOL)fileExistsAtPath:(NSString *)path
So your code would look like:
([[[NSFileManager alloc] init] fileExistsAtPath:myPath] == true)
{ do sth.; }
Instead of the if clause you might want to choose a while loop, but i suppose you know how to deal with that ;)
Hope I could help.

I finally got it. I can use NSPasteboard to check what file is currently being copied.
With this information, I can compare the file size between the source and destination to know when the copy is complete.
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classes = [[NSArray alloc] initWithObjects:[NSURL class], nil];
NSDictionary *options = [NSDictionary dictionaryWithObject: [NSNumber numberWithBool:YES] forKey:NSPasteboardURLReadingFileURLsOnlyKey];
NSArray *copiedItems = [pasteboard readObjectsForClasses:classes options:options];

Related

Plist won't write to file

When i try this code:
if ([[NSFileManager defaultManager] fileExistsAtPath:#"~/Library/Application Support/Staying Afloat/stats/static-stats.plist"] == NO) {
NSMutableDictionary* tempDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:
[[NSString alloc] initWithString:#"0"],#"completedGames",
[[NSString alloc] initWithString:#"0"],#"floatsCollected",
[[NSString alloc] initWithString:#"0"],#"sec",
[[NSString alloc] initWithString:#"0"],#"subScore",
[[NSString alloc] initWithString:#"0"],#"highScore",
[[NSString alloc] initWithString:#"0"],#"longestSec", nil];
[tempDict writeToFile:#"~/Library/Application Support/Staying Afloat/stats/static-stats.plist" atomically:YES];
NSLog(#"written file");
}
and this outputs with
2012-04-14 19:15:10.009 Staying Afloat[3227:9c07] written file
so the loop has run, but the file isn't written?
can anyone point my in the right dirrection for saving plists to non-localized places?
EDIT: this is for mac
You need to expand your path using -(NSString *)stringByExpandingTildeInPath when you write and check if the file exist.
Edit: Read the method writeToFile:automatically in the NSDictionary documentation. It says
If path contains a tilde (~) character, you must expand it with stringByExpandingTildeInPath before invoking this method.
So just do something like
[tempDict writeToFile:[[NSString stringWithString:#"~/Library/Application Support/Staying Afloat/stats/static-stats.plist"] stringByExpandingTildeInPath] atomically:YES];
first you can't just write :
[tempDict writeToFile:#"~/Library/Application Support/Staying Afloat/stats/static-stats.plist" atomically:YES];
NSLog(#"written file");
because you don't check if the file was really written with success or not.
you should write :
if([tempDict writeToFile:#"~/Library/Application Support/Staying Afloat/stats/static-stats.plist" atomically:YES]) {
NSLog(#"written file");
} else {
NSLog(#"file not written");
}
WriteToFile method returns a Boolean.
I always use NSUserDefaults which takes care of everything for you with things like user prefs or storing app states between running. Off the top of my head I'd suggest you haven't got the right privileges to write a plist and what happens if you accidentally destroy a vital key and value for the os? Have a look at
Best practices for handling user preferences in an iPhone MVC app?
Are you sure -writeToFile:atomically: returns YES?

Write in Main Bundle directory. Is it allowed?

I was pretty sure that writing in the main Bundle isn't possible in iOS ... for example an operation like :
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
.....something
[xmlData writeToFile:path atomically:YES];
Why does the first example in Apple's documentation use this exact code?
The example is for OS X, which isn't quite as strict with permissions as iOS.
I'd be surprised if you are able to do that for much longer (if you can at all now) in a Mac App Store application bundle, though.
It could be worth filing a bug regarding the documentation.
That's not a link to the main bundle. That's a path to the resources folder, and a plist within that folder.
Hence the function name pathForResource...
Everything in the main bundle is cryptographically signed when you compile the app. The resources folder isn't though. You can write to and from that freely.
for #jrturton
// we need to get the plist data...
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Symptoms" ofType:#"plist"];
NSMutableArray *dataArray = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
// add a new entry
NSDictionary *addQuestion = [[NSDictionary alloc] initWithObjectsAndKeys:#"Blank.png",#"Icon",
[NSString stringWithFormat:#"%i",r],#"ID",
[titleTextField text],#"Title",
[questionTextField text],#"Text",
qnType,#"Type",
#"1",#"Custom",
[NSArray arrayWithObjects:#"Yes",#"No",nil],#"Values",
[unitsTextField text],#"Units",
nil];
[dataArray addObject:addQuestion];
[addQuestion release];
// rewrite the plist
[dataArray writeToFile:plistPath atomically:YES];

Write to file not working

I'm trying to combine images in my app into one file and write it to disk.
NSMutableArray *array = [NSMutableArray arrayWithObjects:
[NSData dataWithContentsOfFile:#"0.png"],
[NSData dataWithContentsOfFile:#"1.png"],
[NSData dataWithContentsOfFile:#"2.png"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
or
NSArray *array = [NSArray arrayWithObjects:
[NSImage imageNamed:#"0"],
[NSImage imageNamed:#"1"],
[NSImage imageNamed:#"2"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
But both produce a file that is 4KB (empty). If I NSLog the error it is (null). Am I making the data the wrong way?
Edit: If I open the resulting file with a text editor, it looks like this:
I wrote a quick example:
Missing: memory management / error handling / proper file handling
// Archive
NSMutableArray *array = [[NSMutableArray alloc] init];
NSString * input = #"/Users/Anne/Desktop/1.png";
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSString *path = #"/Users/Anne/Desktop/archive.dat";
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive
NSMutableArray *archive = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSData * firstObject = [archive objectAtIndex:0];
NSString * output = #"/Users/Anne/Desktop/2.png";
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:output];
[firstObject writeToURL:fileURL atomically:YES];
You can also add NSImages to the NSMutableArray:
NSString * input = #"/Users/Anne/Desktop/1.png";
NSImage *image = [[NSImage alloc] initWithContentsOfFile: input];
[array addObject:image];
But that will significantly increase the file size.
Response to the following comment:
So if I only need to access an image at runtime (in the archive), is there a way to access that image at an index without unarchiving the whole thing? Seems like unnecessary overhead to me.
I assume you're still struggling with this problem?
Hiding (or encrypting) app resources?
Like i mentioned earlier, combining all files into one big file does the trick.
Just make sure you remember the file-length of each file and file-order.
Then you can extract any specific file you like without reading the whole file.
This might be a more sufficient way if you only need to extract one file at the time.
Quick 'dirty' sample:
// Two sample files
NSData *fileOne = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/1.png"];
NSData *fileTwo = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/2.png"];
// Get file length
int fileOneLength = [fileOne length];
int fileTwoLength = [fileTwo length];
// Combine files into one container
NSMutableData * container = [[NSMutableData alloc] init];
[container appendData:fileOne];
[container appendData:fileTwo];
// Write container to disk
[container writeToFile:#"/Users/Anne/Desktop/container.data" atomically:YES];
// Read data and extract sample files again
NSData *containerFile = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/container.data"];
NSData *containerFileOne =[containerFile subdataWithRange:NSMakeRange(0, fileOneLength)];
NSData *containerFileTwo =[containerFile subdataWithRange:NSMakeRange(fileOneLength, fileTwoLength)];
// Write extracted files to disk (will be exactly the same)
[containerFileOne writeToFile:#"/Users/Anne/Desktop/1_extracted.png" atomically:YES];
[containerFileTwo writeToFile:#"/Users/Anne/Desktop/2_extracted.png" atomically:YES];
// Only extract one file from the container
NSString * containerPath = #"/Users/Anne/Desktop/container.data";
NSData * oneFileOnly = [[NSFileHandle fileHandleForReadingAtPath:containerPath] readDataOfLength:fileOneLength];
// Write result to disk
[oneFileOnly writeToFile:#"/Users/Anne/Desktop/1_one_file.png" atomically:YES];
Tip:
You can also save the 'index' inside the container file.
For example: The first 500 bytes contain the required information.
When you need a specific file: Read the index, get the file position and extract it.
You are archiving a NSMutable array of NSImage. This two classes conform to the NSCoding protocol required by NSKeyedArchiver, so I don't see where would be your problem.
So, here are many ideas to test.
First, are you sure that the data you think you have are valid? In your first code snippet, you write [NSData dataWithContentsOfFile:#"0.png"]. This method expects an absolute file path.
Assuming the problem is not in your code, just in your question, let's continue:
Do you have something different than nil in the variable data after your archiving? Ie, after the assignement to data, can you add this code. If the assertion fail, you will get an exception at runtime:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSAssert(nil != data, #"My object data is nil after archiving");
If the problem was not here, what is the return of the line [data writeToFile:path options:NSDataWritingAtomic error:&error];
(Not the variable error, but the return value of the call to the method - writeToFile: options: error:)
What happens if you simplify your code and just do this:
result = [NSKeyedArchiver archiveRootObject:data
toFile:archivePath];
If everything was ok, have you tried to unarchive your file with NSKeyedUnarchiver?
The problem is that [NSData dataWithContentsOfFile:#"0.png"] looks for the file "0.png" in the current directory, but what the application thinks of as the current directory is probably not the place you're expecting. For graphical apps, you should always either use an absolute path or a path relative to some place that you can get the absolute path of (e.g. your app bundle, the application support directory, some user-selected location).
For command-line tools, using the current directory is more common. But I doubt that's the case here.
Another thing I noticed on Mavericks and up is that the folders in the path must be in existence. Meaning you must create the folder structure prior to saving into that folder. If you try to write to a folder on the desktop or elsewhere, even with sandboxing off, it will fail if the folder does not exist. I know this has been answered already, but I found that my issue continued regardless, but once I make sure that the folder structure was in place, I could do my writing to that folder.
On a side note: I'm sure that you could do this from NSFileManager, and I'll be doing that myself once I finalize my app structure, but hope this helps someone else lost in the sauce.

Change from a NSMutableArray to a .plist

I have been working through several tutorials on uitableviews.
I have put, as instructed, all the info into a 'listofitems' as below
listOfItems = [[NSMutableArray alloc] init];
NSArray *countriesToLiveInArray = [NSArray arrayWithObjects:#"Iceland", #"Greenland", #"Switzerland", #"Norway", #"New Zealand", #"Greece", #"Rome", #"Ireland", nil];
NSDictionary *countriesToLiveInDict = [NSDictionary dictionaryWithObject:countriesToLiveInArray forKey:#"Countries"];
NSArray *countriesLivedInArray = [NSArray arrayWithObjects:#"India", #"U.S.A", nil];
NSDictionary *countriesLivedInDict = [NSDictionary dictionaryWithObject:countriesLivedInArray forKey:#"Countries"];
[listOfItems addObject:countriesToLiveInDict];
[listOfItems addObject:countriesLivedInDict];
This creates a sectioned table view. I would like to know how to change it into a .plist instead of typing it all out into the RootViewController.m. I would still like it to be in a sectioned tableview.
Is there a simple method for changing from this NSMutableArray,NSArray and NSDictionary to a plist?
There's a simple method for this writeToFile:atomically::
[listOfItems writeToFile:destinationPath atomically:YES];
This will automatically create a file with plist inside it.
that sorta depends on what you want in a plist, and what you put into it. if the entries and contents are all CFPropertyList types (CFString,CFDate,CFData,CFDictionary,CFArray,CFNumber...) then just create it with something like CFPropertyListCreateDeepCopy.
if you have non-convertible custom objects (e.g., your own NSObject subclasses), then see the cocoa archiving topics.
This is the simple function end hear relization
This is function is updating NSArray
- (void) WriteRecordToFile:(NSMutableDictionary*)countDict {
// Here to write to file
databasePathCallCount = #"plist path";
NSMutableArray *countArray = [NSMutableArray arrayWithContentsOfFile:databasePathCallCount];
if(countArray)
[countArray addObject:countDict];
[countArray writeToFile:databasePathCallCount atomically:NO];
}

Plists and connections

I have an app that needs to connect and receive data, different each time that you click in one tab.
Then to show the data to the user, i use a "element.plist" where i have one array of dictionaries( each dictionary has the info in different strings: name, category, ...). I load the info from this plist.
I would like then, to continue using the same structure. Each time i receive the connection data:
delete the content in the plist
save the new content (I can do this in the parser method, each time that i have one object with all the information)
Read the info like i'm doing now.
The step that i can't do is the second.
thanks
I'm not sure I completely understand your question,
but I'll try to help.
below is some apple sample code that saves a plist when an application is exiting.
the second line sets the name of the plist file:
NSString *bundlePath = the application directory + "Data"
the third line defines a dictionary with all the data to be saves:
NSDictionary *plistDict
the fourth line formats this dictionary as property list data:
NSData *plistData
which then gets saved as Data.plist
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
NSString *errorDesc;
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"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:&errorDesc];
if (plistData)
{
[plistData writeToFile:bundlePath atomically:YES];
}
else {
NSLog(errorDesc);
[errorDesc release];
}
return NSTerminateNow;
}
You can find this information in the Property list programming guide
Mey,
I'm not sure that I understand your statement about having an empty plist. I assume that you mean that if you read back the plist file that you created, it is null when you print it out. Suggesting that you are writing out an empty file or not reading correct or ...
I further assume that your intent is to replace the existing plist contents by a new plist while keeping the same name.
And caveat emptor - I'm new to Objective C etc. Here is a way to do that which I think you are trying to do.
// Implement viewDidLoad to do additional setup after loading the view,
// typically from a nib.
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"TmpPList" ofType:#"plist"]; //Not NARC
//NSLog(#"plistPath : %#", plistPath);
//My plist is a simple array, but it could be an array of dictionary objects etc
NSMutableArray *arrayFromPList = [[NSMutableArray alloc] initWithContentsOfFile:plistPath]; //NARC
//NSLog(#"arrayFromPList : %#", arrayFromPList);
//Delete the arrays contents and put new contents
[arrayFromPList removeAllObjects];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
//[arrayFromPList addObjectsFromArray:[NSArray arrayWithObjects:#"A", #"B", "#C", nil]];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
[arrayFromPList setArray:[NSMutableArray arrayWithObjects:#"A", #"B", #"C", #"D", #"E", #"F", nil]];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
/* */
//Write it out to the original file name
[arrayFromPList writeToFile:plistPath atomically:YES];
NSMutableArray *newArray = [[NSMutableArray alloc] initWithContentsOfFile:plistPath]; //NARC
NSLog(#"newArray : %#", newArray);
[arrayFromPList release];
[newArray release];
}