iOS 9
WWDC 2015 may not have packed quite as many fireworks as its predecessor, but neither was it short on the new and shiny. The introduction of watchOS 2, including a revamped WatchKit, access to the Apple Watch hardware, and support for complications. Major changes to the Swift language and the announcement of open source plans. Brand new frameworks, such as Contacts, ContactsUI, and CoreSpotlight, and new UIKit types like UIStack
and SFSafari
.
We’ll surely delve into these major new components in the weeks and months to come. For now, however, let’s take a look at some of the smaller changes that iOS 9 brings to the APIs we already know and love.
String Transformations
Up first is the news that string transformations, formerly housed in Core Foundation, have made their way to NSString
and the Swift String
type. This is a huge advance in discoverability and ease-of-use for this powerful Cocoa feature, because there is no longer any need to deal with the hassle of bridging to and from CFString
. Here’s how some of our favorite transformations can be done along with the new NSString
constants that enable them:
Transliteration
"privet".stringByApplyingTransform (NSStringTransformLatinToCyrillic , reverse: false)
// "привет"
"안녕하세요".stringByApplyingTransform (NSStringTransformLatinToHangul , reverse: true)
// "annyeonghaseyo"
"annyeonghaseyo".stringByApplyingTransform (NSStringTransformLatinToHangul , reverse: false)
// "안녕하세요"
NSLog(@"%@", [@"privet" stringByApplyingTransform :NSStringTransformLatinToCyrillic reverse:NO]);
// "привет"
NSLog(@"%@", [@"annyeonghaseyo" stringByApplyingTransform :NSStringTransformLatinToHangul reverse:NO]);
// "안녕하세요"
NSLog(@"%@", [@"안녕하세요" stringByApplyingTransform :NSStringTransformLatinToHangul reverse:YES]);
// "annyeonghaseyo"
Unicode Names
"🐷".stringByApplyingTransform (NSStringTransformToUnicodeName , reverse: false)
// "{PIG FACE}"
NSLog(@"%@", [@"🐷" stringByApplyingTransform :NSStringTransformToUnicodeName reverse:NO]);
// "{PIG FACE}"
Normalizing User Input
"Hello! こんにちは! สวัสดี! مرحبا! 您好!"
.stringByApplyingTransform (NSStringTransformToLatin , reverse: false)?
.stringByApplyingTransform (NSStringTransformStripDiacritics , reverse: false)?
.localizedLowercaseString
.componentsSeparatedByCharactersInSet (NSCharacterSet .whitespaceCharacterSet ())
// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]
NSString *input = @"Hello! こんにちは! สวัสดี! مرحبا! 您好!";
NSString *processing = [input stringByApplyingTransform :NSStringTransformToLatin reverse:NO];
processing = [processing stringByApplyingTransform :NSStringTransformStripDiacritics reverse:NO];
NSArray<NSString *> *output = [processing.localizedLowercaseString
componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceCharacterSet ]];
NSLog(@"%@", output);
// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]
For more on string transformations, be sure to check out Mattt’s terrific article on CFString
.
CLLocationManager.requestLocation
Core Location includes a nice new API for apps that simply need to fetch the user’s location without continuous location updates. request
uses the same delegate methods as continuous updates, turning itself off after delivering the location with the desired accuracy:
class ViewController : UIViewController , CLLocationManagerDelegate {
let locationManager = CLLocationManager ()
…
override func viewDidLoad () {
super.viewDidLoad ()
locationManager .delegate = self
locationManager .desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager .requestLocation ()
}
// MARK: - CLLocationManagerDelegate
func locationManager (manager: CLLocationManager , didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print("Current location: \(location)")
} else {
…
}
}
func locationManager (manager: CLLocationManager , didFailWithError error: NSError) {
print("Error finding location: \(error.localizedDescription )")
}
}
Swiftification
The Cocoa APIs in iOS 9 (and OS X 10.11) include thousands of minor changes designed to make Swift interoperability safer and cleaner. These changes start with the familiar nullability annotations, converting implicitly unwrapped parameters and return values to either true Optionals or simple, non-optional data types. But they also go much further.
For example, instead of marking NSArray
return values as nullable, many APIs have been modified to return an empty array—semantically these have the same value (i.e., nothing), but a non-optional array is far simpler to work with. Moreover, many Cocoa APIs are gradually taking advantage of the new Objective-C generic syntax to provide typed arrays. The CLLocation
method location
shown above is one such example: where the locations
parameter used to be imported as an Any
array, it is now an array of CLLocation
, eliminating the need for any casting in the method body.
Finally, some APIs that were completely inaccessible from Swift have been revised, such as methods for retrieving UIAppearance
proxies that limit appearance to a particular containment hierarchy—UIKit’s version of CSS-lite. The old appearance
method is implemented with C variadic parameters, which Swift doesn’t import; the new appearance
method simply takes an array of type objects:
UIBarButtonItem .appearanceWhenContainedInInstancesOfClasses ([UINavigationController .self]).tintColor = UIColor.redColor ()
[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses :@[[UINavigationController class]]].tintColor = [UIColor redColor ];
NSFormatter Additions
The new Contacts framework includes NSFormatter
subclasses for localized formatting of contacts and addresses alongside a new Foundation NSPerson
class. We’ll cover those more in the weeks to come, but here let’s highlight a few additions to two old favorites: NSNumber
and NSDate
.
NSNumberFormatter
First, NSNumber
sees four additional styles in iOS 9, starting with .Ordinal
, used for converting numbers to their ordinal equivalent:
let formatter = NSNumberFormatter ()
formatter.numberStyle = .OrdinalStyle
let numbers = [1, 2, 3, 4, 5]
numbers.map { formatter.stringFromNumber ($0)! }
// ["1st", "2nd", "3rd", "4th", "5th"]
formatter.locale = NSLocale(localeIdentifier : "es")
numbers.map { formatter.stringFromNumber ($0)! }
// ["1º", "2º", "3º", "4º", "5º"]
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterOrdinalStyle ;
NSArray<NSNumber *> *numbers = @[@1, @2, @3, @4, @5];
for (NSNumber *number in numbers) {
NSLog(@"%@", [formatter stringFromNumber :number]);
}
// "1st", "2nd", "3rd", "4th", "5th"
formatter.locale = [NSLocale localeWithLocaleIdentifier :@"es"];
for (NSNumber *number in numbers) {
NSLog(@"%@", [formatter stringFromNumber :number]);
}
// "1º", "2º", "3º", "4º", "5º"
Next, the existing .Currency
gets some company with .Currency
, .Currency
, and .Currency
. When using these new styles, be sure your locale fully specifies both a language and a country, which it makes it possible for the formatter to choose the right currency and presentation:
let styles: [NSNumberFormatterStyle ] = [.CurrencyStyle , .CurrencyPluralStyle ,
.CurrencyISOCodeStyle , .CurrencyAccountingStyle ]
formatter.locale = NSLocale(localeIdentifier : "en_US")
styles.map {
formatter.numberStyle = $0
return formatter.stringFromNumber (-125)!
}
// ["-$125.00", "-125.00 US dollars", "-USD125.00", "($125.00)"]
formatter.locale = NSLocale(localeIdentifier : "es_ES")
styles.map {
formatter.numberStyle = $0
return formatter.stringFromNumber (-125)!
}
// ["-125,00 €", "-125,00 euros", "-125,00 EUR", "-125,00 €"]
NSDateFormatter
Second—and I’ll fess up—this new NSDate
method is a bit of a cheat. set
was introduced in iOS 8 but largely slid under the radar until this year’s sessions on internationalization. This new-_ish_ method makes it supremely easy to define a template for the date and time elements you want; however, it leaves the formatting up to the excellent localization built into NSDate
:
let now = NSDate()
// full date and time
let fullFormatter = NSDateFormatter ()
fullFormatter .setLocalizedDateFormatFromTemplate ("yyyyMMMMddhhmm" )
// month name and year only
let shortFormatter = NSDateFormatter ()
shortFormatter .setLocalizedDateFormatFromTemplate ("yyMMMM" )
fullFormatter .stringFromDate (now)
// "June 23, 2015, 4:56 PM"
shortFormatter .stringFromDate (now)
// "June 15"
If you switch locales, you’ll need to reset the localized date format to get the correct template for the new locale:
fullFormatter .locale = NSLocale(localeIdentifier : "de_DE")
fullFormatter .setLocalizedDateFormatFromTemplate ("yyyyMMMMddhhmm" )
shortFormatter .locale = NSLocale(localeIdentifier : "de_DE")
shortFormatter .setLocalizedDateFormatFromTemplate ("yyMMMM" )
fullFormatter .stringFromDate (now)
// "23. Juni 2015, 4:56 nachm."
shortFormatter .stringFromDate (now)
// "Juni 15"
Well, that’s that for our quick tour through some of iOS 9’s new APIs, though there are many more where those came from. What new features are you most excited about in iOS 9 or OS X 10.11? Check out Apple’s diffs and let us know!