#if available does not seam to work for Foundation

Hei I try to use the new api for path on URL. Since url.path seams to be deprecated xcode > 14.0.1 suggest to use #if available(macOS 13, *).

fileprivate init(url: Foundation.URL, fileManager: FileManager) throws {
    if #available(macOS 13.0, *) {
      self.path = url.path(percentEncoded: false)
    } else {
      self.path = url.path
    }
    self.fileManager = fileManager
    try validatePath()
  }

So I write the code in xcode 14.2 and it compiles. Then try to compile it with xcode 14.0.1 and it does not compile. Although it is building for < macos 13 it still goes into the paht for > macos13.

My swift version is:

swift-driver version: 1.62.8 Apple Swift version 5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)
Target: arm64-apple-macosx13.0

And outputing swiftc -emit-sil Sources/Files.swift on the file gives

➜  Files git:(master) ✗ swiftc -emit-sil Sources/Files.swift
Sources/Files.swift:222:23: error: cannot call value of non-function type 'String'
      self.path = url.path(percentEncoded: false)
                      ^
Sources/Files.swift:534:24: warning: 'openFile' was deprecated in macOS 11.0: Use -[NSWorkspace openURL:] instead.
    NSWorkspace.shared.openFile(path)

I'm running xcode 14.0.1 with rosetta on (not sure if this makes a difference??)

This is because path(percentEncoded:) does not exist in Xcode 14 (which does not know about macOS 13 APIs), so it's trying to compile with what it knows: calling getter of the property path, and then attempting to call the returned string as if it was a function, and it fails.

The way to make it compatible with both Xcode 14 and 14.1 (which added the macOS 13 APIs) is to tell the compiler to not try to use the new function when Xcode version is below 14.1. Here's a way:

#if canImport(nameOfANewFrameworkInMacOSVentura)
    // Xcode 14.1 or later
    if #available(macOS 13.0, *) {
      // program is running on macOS 13
      self.path = url.path(percentEncoded: false)
    } else {
      // program is running on macOS earlier than 13
      self.path = url.path
    }
#else
    // earlier than Xcode 14.1
    self.path = url.path
#endif

You need to find a framework that was added in macOS 13 in order for the compiler to figure out which version of Xcode you have, and then only try to use the new function on that version of Xcode. I don't have one on hand right now (there might be none too).

Alternatively, you can wait until you don't need to support compiling with Xcode 14 to make that change. This is what I'd do. It's not like path is going to disappear anytime soon: path is declared as deprecated starting in macOS version 100000.0 (!), which I think means they'd prefer you to use path(percentEncoded:) instead of path but it's probably just a cosmetic change in code to make the intent clearer whether you want the path as written in URL form (percent encoded) or the actual path value (percent decoded). Writing something complex just so you can adopt a cosmetic change makes little sense.

1 Like

Really, the easiest thing to do here is check for swift(>=5.7.1), as that's the version that ships with Xcode 14.1, which includes the new SDKSs. It's dumb, but there's really not a better check until Apple lets us version check their frameworks.

3 Likes

Thanks for the detailed answer. I was really a bit baffled that it is a problem of the version check on the frameworks included.