# iOS 9

By [Nate Cook](https://nshipster.com/authors/nate-cook).
Published 2015-06-22.
Canonical URL: <https://nshipster.com/ios9>

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 `UIStackView` and `SFSafariViewController`.

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 `CFStringRef`. Here’s how some of our favorite transformations can be done along with the new `NSStringTransform*` constants that enable them:

### Transliteration

```swift
"privet".stringByApplyingTransform(NSStringTransformLatinToCyrillic, reverse: false)
// "привет"
"안녕하세요".stringByApplyingTransform(NSStringTransformLatinToHangul, reverse: true)
// "annyeonghaseyo"
"annyeonghaseyo".stringByApplyingTransform(NSStringTransformLatinToHangul, reverse: false)
// "안녕하세요"
```

```objc
NSLog(@"%@", [@"privet" stringByApplyingTransform:NSStringTransformLatinToCyrillic reverse:NO]);
// "привет"
NSLog(@"%@", [@"annyeonghaseyo" stringByApplyingTransform:NSStringTransformLatinToHangul reverse:NO]);
// "안녕하세요"
NSLog(@"%@", [@"안녕하세요" stringByApplyingTransform:NSStringTransformLatinToHangul reverse:YES]);
// "annyeonghaseyo"
```

### Unicode Names

```swift
"🐷".stringByApplyingTransform(NSStringTransformToUnicodeName, reverse: false)
// "{PIG FACE}"
```

```objc
NSLog(@"%@", [@"🐷" stringByApplyingTransform:NSStringTransformToUnicodeName reverse:NO]);
// "{PIG FACE}"
```

### Normalizing User Input

```swift
"Hello! こんにちは! สวัสดี! مرحبا! 您好!"
        .stringByApplyingTransform(NSStringTransformToLatin, reverse: false)?
        .stringByApplyingTransform(NSStringTransformStripDiacritics, reverse: false)?
        .localizedLowercaseString
        .componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]
```

```objc
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 `CFStringTransform`](https://nshipster.com/cfstringtransform/).

## CLLocationManager.requestLocation

Core Location includes a nice new API for apps that simply need to fetch the user’s location without continuous location updates. `requestLocation()` uses the same delegate methods as continuous updates, turning itself off after delivering the location with the desired accuracy:

```swift
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 `CLLocationManagerDelegate` method `locationManager(_:didUpdateLocations:)` shown above is one such example: where the `locations` parameter used to be imported as an `AnyObject` 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 `appearanceWhenContainedIn:` method is implemented with C variadic parameters, which Swift doesn’t import; the new `appearanceWhenContainedInInstancesOfClasses(_:)` method simply takes an array of type objects:

```swift
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UINavigationController.self]).tintColor = UIColor.redColor()
```

```objc
[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationController class]]].tintColor = [UIColor redColor];
```

> Ironically enough, this method crashes at runtime when used from Swift. Betas gonna beta.

## NSFormatter Additions

The new Contacts framework includes `NSFormatter` subclasses for localized formatting of contacts and addresses alongside a new Foundation `NSPersonNameComponentsFormatter` class. We’ll cover those more in the weeks to come, but here let’s highlight a few additions to two old favorites: `NSNumberFormatter` and `NSDateFormatter`.

### NSNumberFormatter

First, `NSNumberFormatter` sees four additional styles in iOS 9, starting with `.OrdinalStyle`, used for converting numbers to their ordinal equivalent:

```swift
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º"]
```

```objc
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 `.CurrencyStyle` gets some company with `.CurrencyPluralStyle`, `.CurrencyISOCodeStyle`, and `.CurrencyAccountingStyle`. When using these new styles, be sure your locale fully specifies both a language _and_ a country, which makes it possible for the formatter to choose the right currency and presentation:

```swift
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 `NSDateFormatter` method is a bit of a cheat. `setLocalizedDateFormatFromTemplate(_:)` 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 `NSDateFormatter`:

```swift
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:

```swift
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](https://developer.apple.com/library/prerelease/ios/releasenotes/General/iOS90APIDiffs/) and [let us know](https://twitter.com/nshipster)!
