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".string By Applying Transform(NSString Transform Latin To Cyrillic, reverse: false)
// "привет"
"안녕하세요".string By Applying Transform(NSString Transform Latin To Hangul, reverse: true)
// "annyeonghaseyo"
"annyeonghaseyo".string By Applying Transform(NSString Transform Latin To Hangul, reverse: false)
// "안녕하세요"
NSLog(@"%@", [@"privet" string By Applying Transform:NSString Transform Latin To Cyrillic reverse:NO]);
// "привет"
NSLog(@"%@", [@"annyeonghaseyo" string By Applying Transform:NSString Transform Latin To Hangul reverse:NO]);
// "안녕하세요"
NSLog(@"%@", [@"안녕하세요" string By Applying Transform:NSString Transform Latin To Hangul reverse:YES]);
// "annyeonghaseyo"
Unicode Names
"🐷".string By Applying Transform(NSString Transform To Unicode Name, reverse: false)
// "{PIG FACE}"
NSLog(@"%@", [@"🐷" string By Applying Transform:NSString Transform To Unicode Name reverse:NO]);
// "{PIG FACE}"
Normalizing User Input
"Hello! こんにちは! สวัสดี! مرحبا! 您好!"
.string By Applying Transform(NSString Transform To Latin, reverse: false)?
.string By Applying Transform(NSString Transform Strip Diacritics, reverse: false)?
.localized Lowercase String
.components Separated By Characters In Set(NSCharacter Set.whitespace Character Set())
// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]
NSString *input = @"Hello! こんにちは! สวัสดี! مرحبا! 您好!";
NSString *processing = [input string By Applying Transform:NSString Transform To Latin reverse:NO];
processing = [processing string By Applying Transform:NSString Transform Strip Diacritics reverse:NO];
NSArray<NSString *> *output = [processing.localized Lowercase String
components Separated By Characters In Set:[NSCharacter Set whitespace Character Set]];
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 View Controller : UIView Controller, CLLocation Manager Delegate {
let location Manager = CLLocation Manager()
…
override func view Did Load() {
super.view Did Load()
location Manager.delegate = self
location Manager.desired Accuracy = k CLLocation Accuracy Hundred Meters
location Manager.request Location()
}
// MARK: - CLLocation Manager Delegate
func location Manager(manager: CLLocation Manager, did Update Locations locations: [CLLocation]) {
if let location = locations.first {
print("Current location: \(location)")
} else {
…
}
}
func location Manager(manager: CLLocation Manager, did Fail With Error error: NSError) {
print("Error finding location: \(error.localized Description)")
}
}
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:
UIBar Button Item.appearance When Contained In Instances Of Classes([UINavigation Controller.self]).tint Color = UIColor.red Color()
[UIBar Button Item appearance When Contained In Instances Of Classes:@[[UINavigation Controller class]]].tint Color = [UIColor red Color];
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 = NSNumber Formatter()
formatter.number Style = .Ordinal Style
let numbers = [1, 2, 3, 4, 5]
numbers.map { formatter.string From Number($0)! }
// ["1st", "2nd", "3rd", "4th", "5th"]
formatter.locale = NSLocale(locale Identifier: "es")
numbers.map { formatter.string From Number($0)! }
// ["1º", "2º", "3º", "4º", "5º"]
NSNumber Formatter *formatter = [[NSNumber Formatter alloc] init];
formatter.number Style = NSNumber Formatter Ordinal Style;
NSArray<NSNumber *> *numbers = @[@1, @2, @3, @4, @5];
for (NSNumber *number in numbers) {
NSLog(@"%@", [formatter string From Number:number]);
}
// "1st", "2nd", "3rd", "4th", "5th"
formatter.locale = [NSLocale locale With Locale Identifier:@"es"];
for (NSNumber *number in numbers) {
NSLog(@"%@", [formatter string From Number: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: [NSNumber Formatter Style] = [.Currency Style, .Currency Plural Style,
.Currency ISOCode Style, .Currency Accounting Style]
formatter.locale = NSLocale(locale Identifier: "en_US")
styles.map {
formatter.number Style = $0
return formatter.string From Number(-125)!
}
// ["-$125.00", "-125.00 US dollars", "-USD125.00", "($125.00)"]
formatter.locale = NSLocale(locale Identifier: "es_ES")
styles.map {
formatter.number Style = $0
return formatter.string From Number(-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 full Formatter = NSDate Formatter()
full Formatter.set Localized Date Format From Template("yyyy MMMMddhhmm")
// month name and year only
let short Formatter = NSDate Formatter()
short Formatter.set Localized Date Format From Template("yy MMMM")
full Formatter.string From Date(now)
// "June 23, 2015, 4:56 PM"
short Formatter.string From Date(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:
full Formatter.locale = NSLocale(locale Identifier: "de_DE")
full Formatter.set Localized Date Format From Template("yyyy MMMMddhhmm")
short Formatter.locale = NSLocale(locale Identifier: "de_DE")
short Formatter.set Localized Date Format From Template("yy MMMM")
full Formatter.string From Date(now)
// "23. Juni 2015, 4:56 nachm."
short Formatter.string From Date(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!