Ask anyone, and they’ll tell you: WWDC 2014 was one of the most exciting in recent memory. It was, first and foremost, a developer event, with nary a hardware announcement to upstage the latest software & developer tools.

And boy howdy, was there a lot to be excited about.

The announcements from iOS 8 & OS X Yosemite alone would have made 2014 a bellwether year for the Apple platform, with Extensions, Continuity, SpriteKit enhancements, SceneKit for iOS, Metal, Game HealthKit, HomeKit, Local Authentication, and a brand new Photos framework. Not to mention the dramatic improvements to Xcode & Interface Builder, a revamped iTunes Connect, TestFlight, Crash Reports, and CloudKit. And oh yeah—Swift.

The kicker? Apple has graciously relaxed its NDA for new technologies, meaning that we don’t have to wait to talk about all of the shiny new toys we have to play with.

This week, we’ll take a look beneath the headline features, and share some of the more obscure APIs that everyone should know about.

From here on out, NSHipster will primarily write code samples in Swift, with the occasional Objective-C throwback where appropriate. By the end of the summer, we hope to have all of the existing code samples ported to Swift, with the option to toggle between languages.

NSProcessInfo -isOperatingSystemAtLeastVersion

Forget [[UIDevice current Device] system Version] and NSFoundation Version Number , there’s a new way to determine the current operating system in code: NSProcess Info -is Operating System At Least Version

import Foundation let yosemite = NSOperating System Version ( major Version : 10 , minor Version : 10 , patch Version : 0 ) NSProcess Info () . is Operating System At Least Version ( yosemite ) // false

Keep in mind, however, that a test for capability, such as with Some Class.class or responds To Selector: , is preferable to checking the OS version. Compiler macros in C or Swift can be used to conditionally compile source based on the build configuration of the target.

New NSFormatter Subclasses

One of the features most sorely lacking in Foundation was the ability to work with units for quantities like mass or length. In iOS 8 and OS X Yosemite, three new classes were introduced that fills the gap: NSEnergy Formatter , NSMass Formatter , & NSLength Formatter .

This effectively doubles the number of NSFormatter subclasses in Foundation, which was previously limited to NSNumber Formatter , NSDate Formatter , & NSByte Count Formatter .

Although these new formatter classes are part of Foundation, they were added primarily for use in HealthKit.

NSEnergyFormatter

NSEnergy Formatter formats energy in Joules, the raw unit of work for exercises, and Calories, which is used when working with nutrition information.

let energy Formatter = NSEnergy Formatter () energy Formatter . for Food Energy Use = true let joules = 10_000.0 print ( energy Formatter . string From Joules ( joules )) // "2.39 Cal"

NSMassFormatter

Although the fundamental unit of physical existence, mass is pretty much relegated to tracking the weight of users in HealthKit. Yes, mass and weight are different, but this is programming, not science class, so stop being pedantic.

let mass Formatter = NSMass Formatter () let kilograms = 60.0 print ( mass Formatter . string From Kilograms ( kilograms )) // "132 lb"

NSLengthFormatter

Rounding out the new NSFormatter subclasses is NSLength Formatter . Think of it as a more useful version of MKDistance Formatter , with more unit options and formatting options.

let length Formatter = NSLength Formatter () let meters = 5_000.0 print ( length Formatter . string From Meters ( meters )) // "3.107 mi"

CMPedometer

Continuing on iOS 8’s health kick, CMStep Counter is revamped in the latest release. CMPedometer is a strict improvement over its predecessor, with the ability to query from discrete points in time, track both steps and distance, and even calculate how many flights of stairs were climbed.

It’s amazing what that M7 chip is capable of.

import Core Motion let length Formatter = NSLength Formatter () let pedometer = CMPedometer () pedometer . start Pedometer Updates From Date ( NSDate ()) { data , error in if let data = data { print ( "Steps Taken: \( data . number Of Steps ) " ) if let distance = data . distance ? . double Value { print ( "Distance: \( length Formatter . string From Meters ( distance ) ) " ) let time = data . end Date . time Interval Since Date ( data . start Date ) let speed = distance / time print ( "Speed: \( length Formatter . string From Meters ( speed ) ) / s" ) } } }

CMAltimeter

On supported devices, a CMPedometer ’s stats on floors Ascended / floors Descended can be augmented with CMAltimeter to get a more granular look at vertical distance traveled:

import Core Motion let altimeter = CMAltimeter () if CMAltimeter . is Relative Altitude Available () { altimeter . start Relative Altitude Updates To Queue ( NSOperation Queue . main Queue ()) { data , error in if let data = data { print ( "Relative Altitude: \( data . relative Altitude ) " ) } } }

CLFloor

CLFloor is a new API in iOS 8 that ties the new features in CoreMotion with Apple’s ambitious plan to map the interiors of the largest buildings in the world. Look for this information to play a significant role in future hyperlocal mapping applications.

import Core Location class Location Manager Delegate : NSObject , CLLocation Manager Delegate { func location Manager ( manager : CLLocation Manager , did Update Locations locations : [ CLLocation ]) { if let floor = locations . first ? . floor { print ( "Current Floor: \( floor . level ) " ) } } } let manager = CLLocation Manager () manager . delegate = Location Manager Delegate () manager . start Updating Location ()

HKStatistics

As a framework, HealthKit covers a lot of ground, with dozens of new classes and constants. A good place to start, in terms of understanding what’s possible is HKStatistics .

HealthKit manages your biometrics from all of your devices in a single unified API. Statistics on things like heart rate, caloric intake, and aerobic output can be tracked and aggregated in powerful ways.

The following example shows how statistics summed over the duration of the day can be grouped and interpreted individually:

import Health Kit let collection : HKStatistics Collection ? = ... let statistics : HKStatistics ? = collection ? . statistics For Date ( NSDate ()) let sources : [ HKSource ] = statistics ? . sources ?? [] for source in sources { if let quantity = statistics ? . sum Quantity For Source ( source ) { if quantity . is Compatible With Unit ( HKUnit . gram Unit With Metric Prefix ( . Kilo )) { let mass Formatter = NSMass Formatter () let kilograms = quantity . double Value For Unit ( HKUnit . gram Unit With Metric Prefix ( . Kilo )) print ( mass Formatter . string From Kilograms ( kilograms )) } if quantity . is Compatible With Unit ( HKUnit . meter Unit ()) { let length Formatter = NSLength Formatter () let meters = quantity . double Value For Unit ( HKUnit . meter Unit ()) print ( length Formatter . string From Meters ( meters )) } if quantity . is Compatible With Unit ( HKUnit . joule Unit ()) { let energy Formatter = NSEnergy Formatter () let joules = quantity . double Value For Unit ( HKUnit . joule Unit ()) print ( energy Formatter . string From Joules ( joules )) } } }

NSHipster will be covering a lot more about HealthKit in future editions, so stay tuned!

NSStream +getStreamsToHostWithName

In many ways, WWDC 2014 was the year that Apple fixed their shit. Small things, like adding the missing NSStream initializer for creating a bound stream pair (without resorting to awkwardly-bridged CFStream Create Pair With Socket To Host call). Behold: +[NSStream get Streams To Host With Name:port:input Stream:output Stream:]

var input Stream : NSInput Stream ? var output Stream : NSOutput Stream ? NSStream . get Streams To Host With Name ( "nshipster.com" , port : 5432 , input Stream : & input Stream , output Stream : & output Stream )

NSString -localizedCaseInsensitiveContainsString

Also filed under: “small but solid fixes”, is this convenience method for String / NSString :

let string = "Café" let substring = "É" string . localized Case Insensitive Contains String ( substring ) // true

CTRubyAnnotationRef

If you’re a linguistics and typography nerd, this new addition to the CoreText framework may have you standing up on your chair and cheering. “What’s with Jim? Is it me, or has he been acting kind of weird since his trip to San Francisco?”, they’ll say, looking at you atop your desk as you tear your clothes off your body in a frenzy of pure ecstasy. “Yeah, remind me not to stay in the Tenderloin for next year’s conference.”

…oh right. Ruby. No, not Ruby. Ruby. It’s used to display the pronunciation of characters in certain Asian scripts.

@import Core Text ; NSString * kanji = @"猫" ; NSString * hiragana = @"ねこ" ; CFString Ref furigana [ k CTRuby Position Count ] = {( __bridge CFString Ref ) hiragana , NULL , NULL , NULL }; CTRuby Annotation Ref ruby = CTRuby Annotation Create ( k CTRuby Alignment Auto , k CTRuby Overhang Auto , 0 . 5 , furigana );

Admittedly, the documentation isn’t entirely clear on how exactly to incorporate this into the rest of your Core Text drawing calls, but the result would look something like this: