Reader beware: this post is part of the older "Objective-C era" on Cocoa with Love. I don't keep these articles up-to-date and many contain code that no longer works or is superceded by newer APIs. Many others contain out-of-date information or offer advice and opinions I no longer endorse. Read "A new era for Cocoa with Love" for more.
To celebrate the official release of iPhone OS 3.0 this week, I will show you how to verify that an NSString contains a syntactically valid email address using NSPredicate — a class that joins the iPhone SDK 3.0 as part of the Core Data additions. This code will work on Mac OS X too since, as with the rest of Core Data, NSPredicate has been part of Mac OS X since 10.4 (Tiger).
Before I begin...
I gave an interview to Anthony Agius of MacTalk.com.au this week. You can download the MP3 from their website. I'm hesitant to listen to my own voice but I think I talked about what it's like to be an independent Mac/iPhone developer in Melbourne, Australia.
Back to predicates
In programming, a predicate is a condition that returns true or false if the object it processes has the properties that the predicate describes. The key difference between a predicate and a regular boolean expression is that a predicate only considers the properties of one object, where a boolean expression may consider multiple, unrelated objects.
Many programmers are familiar with predicates as used in SQL database queries. For example a query to extract the complete row from the "people" database table for every person named "John Smith" might look like this in SQL:
Everything after the "WHERE" is the predicate — it looks at properties of the row only and is either true (the row will be extracted) or false (the row will be ignored).
Using NSPredicate to evaluate predicates
NSPredicate works in much the same way as the "WHERE" clause of SQL. The main reason that
NSPredicate is being brought to the iPhone is that
NSPredicate fulfils the same role in Core Data that "WHERE" clauses fulfil in SQL — to allow the persistent store to fetch objects that satisfy specific criteria.
Imagine we had an
NSDictionary created using the following method:
we could test if a given row created by this method matched the predicate "
firstname = 'John' AND lastname = 'Smith'" with the following:
The string format used to construct an
NSPredicate in Cocoa is very similar to the syntax of the "WHERE" clause in SQL. You can also construct this
NSPredicate in code by building it from two
NSComparisonPredicates and an
A more common use of
NSPredicate is filtering — extracting rows that match an
NSPredicate from a larger collection:
This is then more like an SQL query where we have selected matching rows from the larger table of data.
NSPredicatehandles filtering only. If you'd like to replicate SQL's "ORDER BY" clause, you can apply
NSSortDescriptoras a separate step.
Verifying an email address
The "LIKE" comparison operator in
NSLikePredicateOperatorType) is commonly used as a convenient means of testing if an
NSString matches a Regular Expression. It's advantage over full libraries with greater options and replacement capability is that it is already in Cocoa — no libraries, no linkage, no hassle.
To test if an
NSString matches a regular expression, we can use the following code:
The only question that remains is: what is a regular expression that can be used to verify that an
NSString contains a syntactically valid email address?
This regular expression is adapted from a version at regular-expressions.info and is a complete verification of RFC 2822.
This adaptation involved escaping all backslashes with another backslash (otherwise the
NSString will try to interpret them before they are used in the Regular Expression) and escaping the ampersands (&) so it isn't interpreted as a Unicode escape sequence and caret (^) characters because — actually I have no idea why except that the expression wouldn't parse without it.
The linked regular-expressions.info page recommends using slightly different regular expressions that force the top-level domain to be a country code or a known top-level domain. With the number of top-level domains due to increase in the near future, I'm not sure this is a good constraint to impose — since this check isn't intended to verify that the provided domain name is valid.
Since this regular expression
NSString is so long, I've split it over 7 lines. This is an underused feature in Standard C languages — if you split a string into pieces but put nothing except whitespace between the pieces, the compiler will treat it as one continuous string. You don't need to write an extremely long string on a single long line.
Just a few lines of code this week but I thought it would be good to draw attention to one of the minor additions making its way to the iPhone in SDK 3.0. I use
NSPredicate all the time on Mac OS to perform searches, extract subarrays and perform quick Regular Expression tests. Without it, the only alternatives on the iPhone were methodical array iterations and manual comparisons in code.