Foundation overloads the pattern matching operator ~=
to enable matching against error codes in catch
clauses.
catch
clauses in Swift support pattern matching, using the same patterns you’d use in a case
clause inside a switch
or in an if case …
statement. For example, to handle a file-not-found error you might write:
import Foundation
do {
let fileURL = URL(filePath: "/abc") // non-existent file
let data = try Data(contentsOf: fileURL)
} catch let error as CocoaError where error.code == .fileReadNoSuchFile {
print("File doesn't exist")
} catch {
print("Other error: \(error)")
}
This binds a value of type CocoaError
to the variable error
and then uses a where
clause to check the specific error code.
However, if you don’t need access to the complete error instance, there’s a shorter way to write this, matching directly against the error code:
let data = try Data(contentsOf: fileURL)
- } catch let error as CocoaError where error.code == .fileReadNoSuchFile {
+ } catch CocoaError.fileReadNoSuchFile {
print("File doesn't exist")
Foundation overloads ~=
I was wondering why this shorter syntax works. Is there some special compiler magic for pattern matching against error codes of NSError
instances? Turns out: no, the answer is much simpler. Foundation includes an overload for the pattern matching operator ~=
that matches error values against error codes.
The implementation looks something like this:
public func ~= (code: CocoaError.Code, error: any Error) -> Bool {
guard let error = error as? CocoaError else { return false }
return error.code == code
}
The actual code in Foundation is a little more complex because it goes through a hidden protocol named _ErrorCodeProtocol
, but that’s not important. You can check out the code in the Foundation repository: Darwin version, swift-corelibs-foundation version.
This matching on error codes is available for CocoaError
, URLError
, POSIXError
, and MachError
(and possibly more types in other Apple frameworks, I haven’t checked).