Archiving object to a read-only file - objective-c

I have my app saving some objects into .sav files using NSKeyedArchiver archiveRootObject: toFile:; however, I realized that if a user were to open up one of the .sav files in textedit and change it at all, the app would fail to unarchive the objects next time it opens, and would stop working.
Is there any way I can archive the root objects to read-only file or otherwise stop users from editing them? They're buried in application support, so not super accessible, but I'd like to play it safe.

Your application should be able to handle that kind of error.
Also, suppose you did archive the data and then set the file to be read-only. What would stop a determined user from making it read-write again?
You could use some kind of checksum to verify file integrity, though, but you would probably have to roll your own in that case.

I don't think there is a way for you to avoid potentially losing the saved state (in the end the user could simply delete the file), however if you are worried about the user manipulating the data, you should look at NSSecureCoding.
I believe that is a way to avoid unarchiving "corrupt" data and guaranteeing the integrity, I have not explored the topic further so I can't say for sure whether that would allow a scenario in which the contents of specific fields are changed (i.e object type is the same, but value is different).
In conclusion, I think it is better/safer to build your system with the idea of someone trying to circumvent your security in mind and instead of trying to stop the user from manipulating/deleting the data, just making sure invalid data is not loaded in. For example in case of invalid/corrupt/missing data just reverting back to the default values (i.e as if the app is launching for the first time).

Related

Saving data so it is persistent in Objective-C

Over many different Objective-C iOS coding projects I have frequently come across the issue of having data be accessible after I initially got it.
For example, currently I am reading from the stackoverflow API. I do this with a session and get a dictionary back (my JSON response).
But outside the scope of the session, the dictionary is unavailable! I can't copy the contents to a different dictionary that I've defined globally, or anything. It's like it disappears outside of the session.
So I am wondering, what's the best way to save this data that I want to use? From what I've been reading it seems like NSUserDefaults or maybe creating a plist file, although admittedly I've been having trouble with both options. If there is a method that is best for this then I can concentrate on that.
Thank you!
It depends on how persistent you want to be.
If you save this dictionary into a global variable, it is stored in the part of device's RAM that is reserved for the running app. When the app stops running (gets killed by OS or removed by the user) or if your device reboots - this memory is lost.
If you save this dictionary onto the device's flash memory drive (and its file system) - it will live past restarts/reboots.
Usually people combine the approaches: when you get the data from the network, you keep it in a global variable, and save it to the file system. After the app restart you try to load the data from the file system. The reason for not using the FS all the time is that it is much slower than RAM access. I guess I'm describing caching.
Note that you can implement manual caching (using plain data or text files, NSUserDefaults, Core Data or other libraries), but also you can utilize a builtin HTTP cache - NSURLCache. If you create a session with NSURLSession.sharedSession it will use the default NSURLCache and respect a caching policy dictated by the server side.
For more control and full offline support I'd recommend to implement caching manually. See this about reading and writing plists and writeToFile:atomically:.

Writing files safely (WinRT)

What approach do you use to write critical app files like settings, configuration files, user files in WinRT, or in general?
To illustrate my concern - in my app I am saving the list of user selected data sources as a JSON file. In case the user updates the list and saves it, I just overwrite the current file with the new JSON serialized list. But if the app were killed from the task manager or the computer lost power in that very moment when the file is being written, it would stay in an inconsistent state and would probably cause the app not to launch or the user would definitely lose data.
I considered writing into a different file and then swap them when finished. Is this solution the best one possible?

(OS X) Determine if file is being written to?

My app is monitoring a "hot" folder somewhere on the local filesystem for newly added files to push to a network location. I'm running into a problem when very large files are being written into the hot folder: the file system event notifying me of changes in the hot folder will fire well before the file completes writing. When my app tries to upload the file, it mis-reads the file size as the current number of copied bytes, not the eventual total number of bytes.
Things I've tried:
NSURL getResourceValue:forKey:error: to read NSURLAllocatedFileSizeKey (same value as NSURLFileSizeKey while the file is being written).
NSFileManager attributesOfItemAtPath:error: to look at NSFileBusy (always NO).
I can't seem to find any mechanism short of repeatedly polling a file for its size to determine if the file is finished copying and can be uploaded.
There aren't great ways to do this.
If you can be certain that the writer is using NSFileCoordinator, then you can also use that to coordinate your access to the file.
Likewise, if you're sure that the writer has opted in to advisory locking, you could try to open the file for shared access by calling open() with the O_SHLOCK and O_NONBLOCK flags. If you succeed, then there are no other descriptors open for exclusive access. You can either use the file descriptor you've got or close it and then use some other API to access the file.
However, if you can't be sure of any of those, then your best bet may be to set a timer to repeatedly check the file's metadata (size, date modified, etc.). Only when you see that it has stopped changing over a reasonable time interval (2 seconds, maybe) would you attempt to access it (and cancel the timer).
You might want to do all three. Wait for the file's metadata to settle down, then use a NSFileCoordinator to read from the file. When it calls your reader block, use open() with O_SHLOCK | O_NONBLOCK to make sure there are no other processes which have exclusive access to it.
You need some form of coordinated file locking.
fcntl() and flock() are common functions for this.
Read up on it first.
Then see what options you have.
If you can control the code base of those other processes, all the better.
The problem with really large files is that what's changed or changing inside them is opaque and isn't always at the end.
Good processes should generally be doing atomic writes. (Write to a temp file then swap it out) but if these files are actually databases then you will want to look at using the db's server app for this sort of thing.
If the files are wrappers containing other files then it gets extra messy as those contents might have dependencies on one another to be in a usable state.

Where can I put temporary files that I don't want show to the user?

in my Mac software I need decrypt a file and, after I do my operations on it, I will remove it. My problem is: Where can I put this file? I don't want show it to the user.
The following API will give you a directory path that is "out of the way":
NSTemporaryDirectory();
Do you mean "decrypt a file in a place the user can't access?" Any place your app can write to, the user can see. And in theory, a user can access any bit or byte on a computer to which they have physical access.
There are obfuscations and such that reduce the odds a user will come across sensitive data, but they are meant for particular situations.
Can you tell us more about your end goal here? Are you trying to implement a DRM/copy protection scheme? Are you trying to prevent cheating in a game? Do you just not trust your user? What?
I think your best bet would be to keep it in memory.
If that's not an option, it depends on what you want to do with it. It's possible you can open a temporary file, and immediately delete it - keeping the valid filehandle open, but not keeping a link to it on the disk.
Another option, perhaps - can you get your secondary program to read from STDIN or a pipe? You could then decrypt the file and pass it's content via a pipe? Clearly, the more complex this process is, the more weak links it might have, but sometimes you just have to get things working.

Best way to maintain an autocomplete/suggest list across sessions

I would like to maintain a list of all of the strings entered into a ComboBox across all uses of an application on a given PC, for use as the AutoCompleteSource for that ComboBox, i.e., I enter in "Fred" in the ComboBox, commit the data, close the application, reopen the application, reopen the ComboBox, type "F", receive the suggestion "Fred".
Assuming I already have in place code to create and maintain such a list in memory and add new elements to it as they are entered into the ComboBox, etc., what do you suggest as the best way to save/load this data in between sessions?
Possible complicating factors: This application will be deployed via OneClick deployment, and will search for updates online every time the application starts. I would prefer that if the application updates, the list still exists after the update has completed.
Thanks for your help.
Depending on your access level, storing them in a flat file seems the easiest choice.
To read the list of options, read the file and return an empty list if it isn't there.
To add an item, create the file if it isn't there and then append the option.
That means you don't send an empty file with your updates, so the data survives.
It appears that you could put the file in the Data Directory or use Isolated Storage.
I would simply use the built in User Settings (My Project > Settings > Pick "User" for the scope of the setting); they are extremely easy to retrieve and update. And although I haven't done extensive testing, I believe they remain through application updates.
Does your application use a database? If so, that would be another option for permanent storage.
Edit
If you decide to go with the User Settings option, here is a new question that addresses an issue with those settings and ClickOnce.