Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
We have a lot of very convenient, very powerful methods at our disposal such as [NSData dataWithContentsOfFile:]
. This method goes to the file system, opens the file, reads in all the bytes, closes the file, packs the bytes into an NSData
, and returns it back to us. It replaces a loop and several other lines of code into one convenient package. If it can’t do the work, it returns nil
. That’s pretty simple.
There’s just one problem. It’s great that you’re told that things didn’t work, but you have no idea why it failed. File doesn’t exist? Permissions problem? File too big to fit into memory? You don’t know, so you might write code like this:
NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"];
if (data == nil) {
// uh, tell the user something. Wish we knew more details.
}
An alternative is to write your own open
/ read
-loop / pack / close
code so you know exactly what went wrong, so you can tell the user.
With Mac OS X 10.5 Apple started introducing methods that return error information via a pointer you pass in:
NSError *error;
NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"
options: 0
error: &error;];
if (data == nil) {
// Tell the user exactly what happened by digging
// into the error object.
}
You can think of the error details as out-of-band data, a secondary information stream coming from the method. “Here are the bytes you asked for. Oh, and if something went wrong, you can look over here for more information.” You give a pointer to an NSError
pointer (which ends up being an NSError **
) which gets filled in with an (autoreleased) NSError
if something goes wrong.
I do a lot of code review for friends and coworkers, and I’ve seen a common mistake, even with experienced developers: using the value of the error
pointer, rather than the return value of the method, to decide whether the method succeeded or failed
This is bad code:
NSError *error;
NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"
options: 0
error: &error;];
if (error != nil) {
// Please don't use the error pointer to decide method success or failure.
// Compare |data| to nil first
}
You should always look at the return code of the method. In the case of dataWithContentsOfFile:...
, you should test to see if data
is nil
, and only if it is nil
would you use the error
pointer for more elaboration. On success, the method being called is under no obligation to fill in error with a sane value. It could fill it with an insane value. It could even assign a temporary object to it and not reset it to NULL. So you cannot trust the value of the returned error pointer unless the method told you to.
Apple’s rules are pretty simple: the error pointer is hands-off unless the method indicates a failure. Trying to do otherwise could invite odd behavior, crashes, or at the very least your coworkers saying “neener neener neener” when they see the code.
Interested in error handling? Advanced Mac OS X Programming: The Big Nerd Ranch Guide devotes an entire chapter to the topic.
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...