How to get PFFile from disk? - ios

I want to get a PFFile from disk. I learned here that Parse caches all PFFiles as they are downloaded. I am wondering now how to retrieve that cache. What is the correct path. The path I use is suggested here
NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0] stringByAppendingFormat:#"/Caches/Parse/PFFileCache/%#", pffile.name];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
data is always nil.
Am I using the correct path? /Caches/Parse/PFFileCache/

From your description of the problem you should use a slightly different process. Use the PFFile interface initially, but then store the urls from the files for the case where you need to access the file data later. In this way you have a direct link to the file data and can use a normal connection or session to download.
If you don't actually need the PFFile for anything about the download then using the REST interface may be more suitable for you.
Another alternative is to make the same request which originally returned the file information to you, but instructing the API to use cached information only. This is done by setting cachePolicy to CACHE_ONLY on your PFQuery.

I struggled with this in Swift for some time, and found this thread which aided in the answer here:
let fileURL: String = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0] + ("/Caches/Parse/PFFileCache/\(checkedRemoteAsset.name)")
Thanks to you KKendall!

Related

How to store file with local path in iOS

I am a React native developer and I was trying to integrate a native ios code.
One of the instance takes NSURL * which should be a local path I guess
https://github.com/uber/startup-reason-reporter/blob/master/StartupReasonReporter/StartupReasonReporterPriorRunInfo/UBApplicationStartupReasonReporterPriorRunInfo.h#L21
+ (nonnull instancetype)priorRunAtDirectoryURL:(nullable NSURL *)directoryURL;
I am not sure what does localPath Url looks like in IOS, like what should I pass? for example?
Ps: intentionally including swift tag as well because I think swift developers could also answer it.
Based on description of that function:
Returns the prior run information stored to disk at the given directory URL.
#param directoryURL The directory to use to to store the startup reason data.
#return the previous startup reason data if it was present on disk, or empty startup reason object.
*/
you need to provide a directory they can write into. So Apps's document directory would be the best (as a root) + whatever folder you want (which, based on their code, they will even create for you).
So:
NSURL* docs = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL* myCrashes = [docs URLByAppendingPathComponent:#"myCrashes" isDirectory:TRUE];
it will look something like:
file:///some/path/to/app/sandbox/data/Documents/myCrashes
Try looking up documentation for
- (NSArray<NSURL *> *)URLsForDirectory:(NSSearchPathDirectory)directory
inDomains:(NSSearchPathDomainMask)domainMask;`
That will get you the URL and check out FileManager.SearchPathDirectory enum for all the viable options:
Here's an example for getting the caches directory
NSURL* url = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];`
It could be any one of the 25-26 options in SearchPathDirectory depending on where they put that stuff.

Accessing locally stored (hidden from user, under library) file in iOS

Here is the NSURL object that I am using for creating and accessing important configuration file on iOS with features:
hidden from user
not user generated file (storing configuration related to user)
not temp or cache (not possible to create later with existing data)
must be backed up by iCloud/iTunes
[NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory,
NSUserDomainMask,
YES) objectAtIndex:0]
stringByAppendingString:#"/important.txt"]];
As suggested in FileSystemOverview (developer.apple.com), I am storing this file under Library.
Maybe better way is storing it under Library/Application Support.
Using NSSearchPathForDirectoriesInDomains, which takes:
enum NSSearchPathDirectory
NSApplicationDirectory
NSDeveloperApplicationDirectory
NSLibraryDirectory
NSDeveloperDirectory
NSApplicationSupportDirectory
...
enum NSSearchPathDomainMask
NSUserDomainMask
NSLocalDomainMask
NSSystemDomainMask
...
BOOL expandTilde
Is this the correct way of storing such a file?
There are couple of alternatives for NSSearchPathDirectory and NSSearchPathDomainMask.
Also what about the expandTilde, on iOS is it necessary?
Is there a better way of doing it, instead of creating path as a NSString using objectAtIndex and appending file name then converting it to NSURL?
Thanks.
Using NSApplicationSupportDirectory would be my first choice for this.
But keep a few things in mind:
Unlike the "Documents" folder, the "Library/Application Support" folder doesn't exist in an iOS app sandbox by default so you must create the folder before trying to use it.
"Hidden from the user" only means that the user won't see it under normal circumstances. But the file is easily accessible by anyone with any technical knowledge.
You do want to pass YES for the expandTilde parameter so your app returns a proper path when you run the app in the simulator. On a real iOS device, it makes little difference.
Do not use stringByAppendingString: to create paths. Use stringByAppendingPathComponent:.
NSString *appSupportPath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [appSupportPath stringByAppendingPathComponent:#"important.txt"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
You can get a direct NSURL using NSFileManager:
NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] firstObject];
NSURL *fileURL = [appSupportURL URLByAppendingPathComponent:#"important.txt"];

Obtain the path of app Group from FileManager

I am trying to update my app for WatchKit and I save a NSKeyedArchiver file to the NSDocumentsDirectory normally.
With updating to app groups I need to store it in the app groups folder. The issue I am having is I cant figure out how to just get the path, and not have it referenced as a file I am looking for.
The way it is set up now is to find the file it gives the path as a NSString
/Users/ME/Library/Developer/CoreSimulator/Devices/43F/data/Containers/Data/Application/5E/Documents/fav
but when I store to app groups, no matter which way I access the folder, it is returned
file:///Users/ME/Library/Developer/CoreSimulator/Devices/43F/data/Containers/Data/Application/5E/Documents/fav
What is the best way to just obtain the path to the shared group, rather than have the app looking for the direct file?
So coffee deprived me had forgotten about the .path for filemanager.
NSURL *fileManagerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com"];
NSString *tmpPath = [NSString stringWithFormat:#"%#", fileManagerURL.path];
NSString *finalPath = [NSString stringWithFormat:#"%#",[string stringByAppendingString:#"/Favourites2"]];
I was running into the same problem. I was going through the whole process of building a string to my save location and now I'm switching over to app groups and using the
NSURL *fileManagerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupID];
Well, the problem is, now instead of a string to the location that starts with "/Users/yourname/Library..." you get "file:///Users/yourname/Library..."
Here's what I did. I created the NSURL. Then I called absoluteString on it.
NSURL groupPath = [[fileManager containerURLForSecurityApplicationGroupIdentifier:groupID] absoluteString];
I now have a string that I need to strip off the first 7 characters of, then my old code works just fine, except now instead of being in the Documents directory, it's in a shared app group that can be accessed by both my old code and my new watchkit extension.
Here's the code to strip off the first 7 characters (index 6 since you start with 0), you should be able to use either method...
NSString *newGroupPath = [groupPath substringFromIndex:6];
or
NSString *newGroupPath = [groupPath substringWithRange:NSMakeRange(6, [str length]-6)];
This just removes the "file://" from the absoluteString that was made from the NSURL and gives you back your older string path the starts "/Users/YourName/Library/Developer/yada yada yada"
Hope that helps you, I have spent 4 hours figuring it out.
It seems to work for me on the simulator, I haven't tried it on the Watch yet. But at least my app is now working the way it was before, just saving the data in a shared app group. (I have a singleton that manages all of my data throughout my app, and I want that same singleton to provide data to my watch app).

Can I create an NSURL that refers to in-memory NSData?

The docs for NSURL state that:
An NSURL object represents a URL that can potentially contain the
location of a resource on a remote server, the path of a local file on
disk, or even an arbitrary piece of encoded data.
I have a blob of in-memory data that I'd like to hand to a library that wants to load a resource via an NSURL. Sure, I can first write this NSData to a temp file and then create a file:// NSURL from that, but I'd prefer to have the URL point directly to the buffer that I already have present in memory.
The docs quoted above seem to suggest this is possible, but I can't find any hint of how to accomplish it. Am I missing something?
NSURL supports the data:// URL-Scheme (RFC 2397).
This scheme allows you to build URLs in the form of
data://data:MIME-Type;base64,<data>
A working Cocoa example would be:
NSImage* img = [NSImage imageNamed:#"img"];
NSData* imgData = [img TIFFRepresentation];
NSString* dataFormatString = #"data:image/png;base64,%#";
NSString* dataString = [NSString stringWithFormat:dataFormatString, [imgData base64EncodedStringWithOptions:0]];
NSURL* dataURL = [NSURL URLWithString:dataString];
Passing around large binary blobs with data URLs might be a bit inefficient due to the nature of base64 encoding.
You could also implement a custom NSURLProtocol that specifically deals with your data.
Apple has some sample code that uses a custom protocol to pass around image objects: https://developer.apple.com/library/mac/samplecode/SpecialPictureProtocol/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003816
What you are missing is the NSURLProtocol class. Takes about three dozen lines of code, and any code that handles URLs properly can access your in-memory data. Read the documentation, it's not difficult and there is sample code available.
Unfortunately there are some APIs that take an NSURL as a parameter, but can only handle file URLs.

NSURL for a downloaded or otherwise obtained file to open an iOS app with: What is its filepath?

I have found that so far the path sent to application:openURL:sourceApplication:annotation: is:
file://localhost/private/var/mobile/Applications/<GUID>/Documents/Inbox/file
To check that the filesystem operations that I am about to perform are indeed likely to succeed (and that the url given to me is not a location outside the sandbox), it looks like I have to do this:
NSString* hdurl = [[#"file://localhost/private" stringByAppendingString:NSHomeDirectory()] stringByAppendingString: #"/"];
NSString* path = url.absoluteString;
if ([path hasPrefix:hdurl]) {
// now ready to e.g. call fopen on: [path substringFromIndex:#"file://localhost".length]
Now, I seem to vaguely recall (and this is probably wrong) that in the past I have seen the file:/// style URL being used. That would clearly cause this code to fail.
How am I to know that it will always give me a file://localhost URL prefix?
Apple's documentation on URLs is strangely missing a section on file URLs.
An NSURL that points to a file on the local file system is called a "file URL". To convert the NSURL to an NSString representing the file's path you use:
NSString *filePath = [url path];
To check to see if an NSURL represents a file URL, use:
BOOL isFileURL = [url isFileURL];
Keep in mind that if your app is passed a file URL, you will always have access to the file. There is no need to check if it starts with any prefix. Why would iOS pass you a file that you don't have access to?

Resources