How to access iCloud Documents files in an asynchronous manner - ios

I am trying to add support for iCloud Documents in my existing app, but I am struggling badly with how to do that.
Apple seems to prefer that you use the UIDocument class for that. But UIDocument does not give direct access to the file in the file system, it expects to maintain a copy of the contents of the file in an NSData object instead. That is simply not doable in my case. All my current code and half of the 3rd party libraries that I use, work directly with the file on the file system, not with NSData. Rewriting all that code is simply not doable.
When not using the UIDocument class, Apple expects you to use the NSFileCoordinator to coordinate access to the file's contents. I am trying to do that with my code, but the methods on the NSFileCoordinator seem to expect that all reading and writing will be done in one synchronous sequence. All the methods of NSFileCoordinator take a block as argument, and expect all the reading/writing to be performed inside that block. When the block returns, you are not allowed to make any file access anymore, as far as I understand.
That is not doable in my case as well. Some of my code, and the 3rd party libraries, do the reading / writing in an asynchronous manner on background threads. I can identify the start and end of the period that the code needs access to the file contents, so if there was a separate requireAccess method, and separate relinquishAccess on the NSFileCoordinator, that would enable me to achieve the goal. But that does not seem to be the case.
It is unclear to me what the role of NSFilePresenter in this is. Some of the documentation, especially that of relinquishPresentedItemToReader() in NSFilePresenter seem to indicate that you can actually acquire / relinquish access separately:
If you want to be notified when the reader has completed its task,
pass your own block to the reader and use that block to reacquire the
file or URL for your own uses.
But it does not explain anywhere how to "reacquire" the file.
So the concrete question: I need to do the following steps in an asynchronous manner:
acquire access to the file, and obtain a file: URL for the file on the local file system
do multiple, asynchronous,
read/write operations on the file with normal file system
operations on that file: URL
relinquish access to the file
Does anybody know whether it is possible to do this, and how to do step 1 and step 3 ?

Related

nonatomic append failure outcomes

I've got a file that I want to append to. It's pretty important to me that this is both fast (I'm calling it 50hz on an iPhone 4) and safe.
I've looked at atomic appending. It seems to me like I would have to copy the whole file, append to it, and then use the NSFileManager's replaceItemAtURL to move them over, which sounds rather slow.
On the other hand, I could simply suck up a non-atomic append, assuming that the failure conditions are strictly that some subset of bytes at the end of the data I'm trying to write are not written. My file format writes out the length of each chunk first, so if there's not enough space for the length data or the length data is bigger than the available bytes, I can detect a partial write and discard.
The question is, how feasible would it be to use an atomic append to rapidly atomically append small amounts of data (half a kilobyte or so at a time), and what exactly are the failure outcomes of a non-atomic append?
Edit: I am the only one appending to this file. I am concerned only with external failure conditions, e.g. process termination, device running out of power, disk full, etc. I am currently using a synchronous append.
POSIX gives no guarantees about atomicity of write(2) when writing to a file.
If the platform does not provide any other means of writing that grants additional characteristics (and I'm not aware of any such API in iOS) you basically have to live with the possibility that the write could be partial.
The workaround of many Cocoa APIs (like -[NSData writeToFile:atomically:]) is the mechanism you mentioned: Perform the work on a temporary file and then atomically link(2) the new over the old file. This strategy does not apply well to your use case as it requires a copy of the old contents.
I would suggest the non-atomic approach you already considered. Actually I once used a very similar mechanism in an iOS app where I had to write a transcript of user actions for crash recovery. The recovery code thoroughly tested the transcript for integrity and would bail out on unexpected errors. Yet, I never received a single report of a corrupt file.

Download big file

I have a problem regarding the best approach to make an App that has to download and show pdf's, It is fed by a JSON that has links to 147 pdf files, sized between 1 and 2 MB.
Questions:
What is the best approach to download all the files to an iPad?
Shall I use AFNetworking 2.0?
Is NSFileManager the way to save all the files?
Problems I may encounter:
With an asynchronous download, if lost connection or no more space on the iPad, what are the counter mesures?
Are there tutorials or examples that deal with this situation?
Sorry for all the questions but I'm new to this.
Best Regards.
What is the best approach to download all the files to an iPad?
This is really broad as #rmaddy suggested. Specific questions are more easily answered. There are lots of ways you could download a file via an HTTP request to your device each with pros/cons depending on your situation.
Shall I use AFNetworking 2.0?
Sure. You'll get no argument from me. This is a widely used and solid API to interface with HTTP-based resources.
Is NSFileManager the way to save all the files?
Yes. NSFileManager is the class you use to read/write files from/to your app's sandbox.
With an asynchronous download, if lost connection or no more space on the iPad, what are the counter measures?
I'm not 100% certain so I can't speak to exactly what happens in this case. AFNetworking may provide some help by writing to a temporary file during a download, etc....
Are there tutorials or examples that deal with this situation?
I have a sample project on Github that shows a table of files that you can download. You can watch the progress of your downloads, pause each request, resume and cancel as well. When you're done you can view the file you downloaded. It uses AFNetworking and might be useful to you:
https://github.com/chefnobody/StreamingDownloadTest
When downloading large files, the main counsel would be to avoid trying to load these into memory as you download them. Instead, make sure you stream them directly to persistent storage. In terms of handling space-specific errors, just make sure you check NSError objects that are returned to you in completion handlers or the appropriate delegate methods.
If using AFNetworking, you can specify the outputStream of the AFURLConnectionOperation to reference a NSOutputStream that you create, referencing some path in your persistent storage.
See Memory pressure issue while downloading multiple files using AFNetworking for example.
Alternatively, you can use NSURLSession (whether via AFNetworking or you do it yourself) and instantiate a NSURLSessionDownloadTask, which does the same sort of thing.
Google "NSURLSessionDownloadTask example" and you'll find tons of references. The block-based rendition of downloadTaskWithURL is incredibly simple. To do background downloads is a little more complicated and requires delegate-based implementation (see Downloading Files and Handling iOS Background Activity sections of URL Loading System Programming Guide: Using NSURLSession or watch the WWDC 2013 video, What’s New in Foundation Networking.)
Either way, you avoid some of the memory consumption challenges associated with downloading large files.

Is NSUserDefaults thread safe for sharing data between extensions on IOS?

The documentation for App Extension under "Sharing Data with Your Containing App" uses NSUserDefaults to do so, and write a bit further that
"to avoid data corruption, you must synchronize data accesses. Use Core Data, SQLite, or >Posix locks to help coordinate data access in a shared container."
But when I look documentation for NSUserDefaults says
"The NSUserDefaults class is thread-safe."
So do I need to use some kind of lock when using NSUserDefaults between my extension and container app or not?
Thread safety refers to the ability to change in-memory data structures from one thread in a way that doesn't damage the ability of other threads to also view or change those structures. When you use NSUserDefaults to share data between an app extension and its containing app, you're not sharing in-memory data between multiple threads, you're sharing on-disk data between multiple processes, so discussions of thread safety do not apply.
The documentation for NSUserDefaults synchronize doesn't say for sure, but one can almost certainly assume that it uses an atomic file write — that is, there's no danger of one process reading a file that's been partially written by another process. If you're concerned about race conditions or other timing issues between when your app writes defaults and your extension reads them (or vice versa), just be sure to synchronize immediately after important writes and immediately before important reads.
The comment about data corruption applies to plain file read/write operations — naively reading or writing a file in two processes can result in data corruption, because one process might read a partially written file or partially overwrite file contents. If you're doing your own file I/O directly, you need some sort of coordination mechanism (like NSFileCoordinator, but beware that only works correctly between iOS apps/extensions in iOS 8.2 and newer). Or you can use higher level utilities that do their own coordination, like CFPreferences/NSUserDefaults, SQLite, Core Data, or Posix file locks.
TLDR: Yes, you can safely use NSUserDefaults to share between an extension and its containing app. Just follow the recommendations in Apple's app extensions guide.
The documentation isn't overly clear, as it uses the NSUserDefaults as the main example of one way to share data but also covers other options without much of a pause. You should be safe enough to use NSUserDefaults without attempting to get a lock first, I've been building a Today extension using it and I've had no issues with data corruption. I am calling synchronize after each write though, just to ensure the data is immediately stored.
I am not sure if it is thread safe across extensions because of the following quote from docs:
When you set a default value, it’s changed synchronously within your process, and asynchronously to persistent storage and other processes.
In other words, it seems to indicate that it's thread-safe within your process, but NOT across processes (ex. extensions).
It could be that calling synchronize fixes this, but docs say:
this method is unnecessary and shouldn't be used

iOS file renaming thread safety

Say I have some data in my iOS app that I want to write to a file. I use the writeToFile:atomically: method on NSData, which writes the data to a temp file and then renames the temp file to the location I specified.
Is this operation thread safe? If I do this writing from a background thread and then happen to ask at a very unfortunate moment from another thread if that file exists (or just grab the contents of that file), is it possible to get an invalid result?
By definition atomically is thread safe, if you grab it before the "Atomic" operation is finished, it will not exist. If you access it after the operation is finished, then it will be ok.
It is similar to the atomic property of properties (that we usually set to nonatomic). It makes sets & gets "atomic", which just means that they happen in "one instant" and there is no in-between state.
I think that the worst that can happen is the file won't be found at the given path. Your app needs to handle this situation correctly.
The Apple documentation for [NSFileManager fileExistsAtPath:] has this useful advice (emphasis mine):
Note: Attempting to predicate behavior based on the current state of
the file system or a particular file on the file system is not
recommended. Doing so can cause odd behavior or race conditions. It's
far better to attempt an operation (such as loading a file or creating
a directory), check for errors, and handle those errors gracefully
than it is to try to figure out ahead of time whether the operation
will succeed.

Storing files in Memory

I'm trying to save a file (for security reasons) to memory so I can access it via NSFileManager to pass it to different classes which are opening files with the NSFileManager method contentsAtPath:.
Is it possible to create a file at some special path and use it like a normal file stored in the filesystem (some kind of memory disk?).
I don't want to rely on the NSFileProtection...
Thanks in advance
The short answer is No, there is no ramdisk-like storage under iOS.
It looks, therefore, that you'll need to read the entire file from disk into memory (preferably as an Object) and change whatever methods you've got, that manipulate the file, to do so on the memory/object instead.

Resources