No more stringByAppendingPathComponent in Xcode 7 beta 5?

I may have missed it in the release notes, but the Xcode 7 beta 5 compiler is complaining that stringByAppendingPathComponent is not available on String (only NSString). Is this intentional or a bug I wonder?

Replies

Then that should have been in the release notes. Right now no one knows - oversight or intentional...

What do you need it for?


Isn't simple Swift string concat not doing exactly the same?

"hi there" + "/people"

"hi there" + "/" + "people"


Or simply using a NSString for the path?


var path : NSString = "hi there"
path.stringByAppendingPathComponent("people")

Using the explicit methods for appending path components is better, because it does not make assumptions about what the path separator of the operating system is.

Apple, Can we get a comment on if this change is on purpose and is staying?

Without any mention in the release notes it leaves us in limbo.


Some of us are trying to port apps to swift 2.0 for iOS 9 that use the path apis.

It would be nice to know if we need to add tons of NSString casts everywhere (not pretty) or we can just use the apis like in swift 1.2 and earlier.

In liu of anything from Apple, I would highly suggest NOT changing your code, but adding the previously posted Extensions to NSString instead. That way, if Apple restores the old behavior, all you will need to do is remove the Extensions. In fact, even if Apple does say those methods are gone for good, then one can assume Apple wants you to use URL.... instead, and for me, I will then convert my code to use URLs...

I agree with CharlesS that these breaking API changes are most likely deliberate. But with no comment from Apple (that I'm aware of) or any mention in the release notes, I'm patching things with extensions in the short term to avoid massive code changes in case these change again. Hopefully this gets clarified soon. String APIs have been one of the rougher parts of the evolution of Swift.

The other thing is that even though I suspect that this was a deliberate change, there's no way of knowing that they won't change it back in the next beta, since a lot of people seem to have complained about it. We'll just have to wait and see.

Or whether a path separator exists in the two parts being concatenated.

The problem is that NSFileManager methods, like fileExistsAtPath take a String, not a NSURL. I can understand if they want us to use NSURL in favor of String, but then why didn't they update the NSFileManager to deprecate the String methods and add in NSURL methods?!! (They did actually do this with createDirectoryAtPath:attributes, sort of).

It's generally understood that NSURL-based APIs are now preferred to path-based APIs. In the past couple of years (before Swift) Apple has gradually rounded out the repertoire for NSURL so that you don't ever have to use paths. Paths aren't deprecated, or even obsolescent. They're just — less preferred. It's not a big deal though. If you definitely prefer to continue with paths, you can.


When you see a path-based API method that has no NSURL-based equivalent, you can take that as a pretty strong indication that the method shouldn't really be used at all, for some reason that's got nothing to do with the path vs. URL debate.


One group of such methods are ones that don't return a NSError result when they fail. These are all ancient methods from the times before API design was made consistent. There's no URL-based equivalent because you should always use the new methods with the error result.


Another group of such methods are ones that have an inherent vulnerability to race conditions, such as fileExistsAtPath. This method does you no good, because the file can be deleted just after you've checked for existence. That is, you can't really rely on the result. The correct approach is to go ahead and do whatever you wanted to do with the file (read it, move it, delete it, or whatever), and then see whether that worked. You have to do that even if you checked existence in advance.

Another group of such methods are ones that have an inherent vulnerability to race conditions, such as fileExistsAtPath. This method does you no good, because the file can be deleted just after you've checked for existence. That is, you can't really rely on the result. The correct approach is to go ahead and do whatever you wanted to do with the file (read it, move it, delete it, or whatever), and then see whether that worked. You have to do that even if you checked existence in advance.


There are plenty of times when it makes sense to check if a file exists, though. A prominent example is Xcode's sidebar; if a file doesn't exist, it turns red. It's sometimes nice to know about these things.


Fortunately, there isa URL-based alternative to fileExistsAtPath, and it's -[NSURL checkResourceIsReachable:error:]. The reason the name is different is because fileExistsAtPath can return NO for reasons other than the file not existing; for example, if you don't have read permission to its parent directory, you'd get NO even though the file does indeed exist. Therefore, what you're actually doing is checking if the file is reachablerather than strictly if it exists.

That's not true at all - there are cases where using a file path is imperative.


For example, when loading images. You can create a UIImage using a path, but not an NSURL. That's because it needs to search for retina or device-specific images. An NSURL-equivalent API would be restricted to file URLs -- in which case what's the point? It's better to use a String path and force the API user to make it a string, that would also indicate to them that it's for local resources only.


If an API takes an NSURL, you expect that it can deal with remote resources. Not everything can, and it's awkward and misleading when your code still has to work in terms of URLs.

That's not true at all. There are plenty of APIs in the Cocoa framework that take NSURLs and only work with local files; for example, all of the -writeToURL:etc:etc:etc: methods found on NSString, NSData, NSArray, NSDictionary, etc. only work with local files. Also, NSURL itself has plenty of methods that only work on file-based URLs—the aforementioned checkResourceIsReachable:error: being one of them, the many resources types you can get with getResourceValue:forKey:error: that only apply to local files being another.


Apple's been steadily replacing all their path-based methods with NSURL-based ones since at least Snow Leopard; that UIImage example you gave is the first example I've seen in years that didn't have an NSURL-based equivalent (which, since it's iOS and you're usually not allowed out of your app's sandbox anyway, is probably just because you're not expected to use that method much anyway). NSURLs are what you're supposed to use to refer to files—local and remote—but don't take my word from it. Here it is right from the developer docs:


https://developer.apple.com/library/prerelease/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html#//apple_ref/doc/uid/TP40010672-CH3-SW6


"The preferred way to specify the location of a file or directory is to use the

NSURL
class. Although the
NSString
class has many methods related to path creation, URLs offer a more robust way to locate files and directories."


Doesn't get much clearer than that. The fact that Apple's deliberately making it a PITA to use paths in Swift is also a pretty clear indicator. :-P


BTW, NSURLs do offer features that affect local files that paths don't. A big one is reference NSURLs. If you convert an NSURL to one of these via the -fileReferenceURL, it will gain the ability to track a file even if it's moved around on the disk, like the old FSRef type back in the Classic days. This can be pretty useful on the Mac, where users are used to being able to move things around.

>> There are plenty of times when it makes sense to check if a file exists, though. A prominent example is Xcode's sidebar; if a file doesn't exist, it turns red. It's sometimes nice to know about these things.


But there's no race condition in the way Xcode displays this state. It's merely subject to occasional brief inaccuracy (after an existing file is deleted but before Xcode receiveds a notification of the change).


It's not wrong to know whether a file existed at a certain moment. It's wrong (and a race condition) to assume this necessarily tells you anything about the state at a later moment.


>> there is a URL-based alternative to fileExistsAtPath, and it's -[NSURL checkResourceIsReachable:error:]


Well, any API that accesses the file can tell you whether it existed. You never needed 'fileExistsAtPath' to begin with, so it's not really about finding an alternative.

But there's no race condition in the way Xcode displays this state. It's merely subject to occasional brief inaccuracy (after an existing file is deleted but before Xcode receiveds a notification of the change).


It's not wrong to know whether a file existed at a certain moment. It's wrong (and a race condition) to assume this necessarily tells you anything about the state at a later moment.

In other words: There are occasionally cases where it is acceptable to use -checkResourceIsReachable:error: (or -fileExistsAtPath:).


Well, any API that accesses the file can tell you whether it existed. You never needed 'fileExistsAtPath' to begin with, so it's not really about finding an alternative.

In other words: There is no case where it is acceptable to use -checkResourceIsReachable:error: (or -fileExistsAtPath:).


A little confused at this response.