72

How can I check if a file exists at a URL (instead of a path), in order to set a pre-populated default store into the iPhone Simulator:

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Food.sqlite"];
/*
 Set up the store.
 For the sake of illustration, provide a pre-populated default store.
 */
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
    NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"Food" ofType:@"sqlite"];
    if (defaultStorePath) {
        [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
    }
}

I've read that in recent versions of the templates, the applicationDocumentsDirectory method returns an URL, so I've changed the code to use NSURL objects to represent the file path. But at [fileManager fileExistsAtPath:storePath], I need to change fileExistsAtPath to something like fileExistsAtURL (obviously it doesn't exist).

I've checked the NSFileManager Class Reference and I didn't find any suitable task suited for my purpose.

Any hints please?

Pang
  • 9,564
  • 146
  • 81
  • 122
Pedro Sousa
  • 847
  • 1
  • 8
  • 12

4 Answers4

135
if (![fileManager fileExistsAtPath:[storeURL path]]) 
...

From the documentation:

If this URL object contains a file URL (as determined with isFileURL), the return value of this method is suitable for input into methods of NSFileManager or NSPathUtilities. If the path has a trailing slash it is stripped.

Mundi
  • 79,884
  • 17
  • 117
  • 140
  • 27
    was having an error using `[url absoluteString]`. DO NOT USE absoluteString. Cheers. – codrut Mar 18 '15 at 08:59
  • 6
    Thank you codrut!!!!! absoluteString was messing with my file. Now using .path instead of .absoluteString everything is fine! – Bruno Morais Apr 01 '15 at 20:13
  • It should. The store URL is just different because it is not in the app sandbox but in a shared container folder. This implementation detail is abstracted away, so it should work. – Mundi Nov 03 '15 at 11:17
18

For a file system URL NSURL itself has a method to check the reachability of an URL

NSError *error;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Food.sqlite"];
if ([storeURL checkResourceIsReachableAndReturnError:&error]) {
  // do something
} else {
  NSLog(@"%@", error);
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Most direct solution. There is no need to rely on fileManager. – DawnSong Jan 11 '16 at 07:38
  • This has a problem in Swift land, in that checkResourceIsReachable() throws an exception (!) if the item is not reachable. So you have to go to all the trouble of catching an exception rather than checking a return result... – Kendall Helmstetter Gelner Feb 02 '17 at 22:08
  • @KendallHelmstetterGelner Actually you have not. See my answer [here](http://stackoverflow.com/questions/24181699/how-to-check-if-a-file-exists-in-the-documents-directory-in-swift/36897617#36897617) – vadian Feb 03 '17 at 04:49
  • Thanks, I took a look and commented on it. I like the idea of the try? wrapper but I really don't like calling something I know will mostly be throwing exceptions as I don't want that overhead. – Kendall Helmstetter Gelner Feb 04 '17 at 22:12
  • 1
    @KendallHelmstetterGelner Swift exceptions have no more overhead than 'if / then / else'. That why they must be dealt with in the same scope they are thrown. They are really still just return codes. See the 'Note:' in the [swift book error handling section](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html) -> ... _error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive. As such, the performance characteristics of a throw statement are comparable to those of a return statement._ – Joe Sep 14 '18 at 22:13
5
if (![fileManager fileExistsAtPath:[storeURL path]]) 

is ok, but be carful : for an url like :

..../Documents/1158a3c96ca22c41b8e731b1d1af0e1e?d=mm&s=50

[storeURL path] will give you that path (it apply to [storeURL lastPathComponent] :

..../Documents/1158a3c96ca22c41b8e731b1d1af0e1e

but if you use lastPathComponent on a string like /var/mobile/Applications/DB92F4DC-49E4-4B4A-8271-6A9DAE6963BC/Documents/1158a3c96ca22c41b8e731b1d1af0e1e?d=mm&s=50, it will give you 1158a3c96ca22c41b8e731b1d1af0e1e?d=mm&s=50

And that's good, since in a url, '?' is used for GET parameters, but if you mix with string, you might have troubles.

Mickaël
  • 940
  • 11
  • 9
2

For anyone reading this on Swift 5 days, I achieved it in this way:

func doesFileExist(url: URL) -> Bool {
    let fileManager = FileManager.default
    return fileManager.fileExists(atPath: url.path)
}
Dan
  • 458
  • 1
  • 6
  • 11