This is the third and final article in my little series covering the new developer APIs in Lion. Check out parts one and two if you haven’t done so already:
-
Part 1: Major new features: Application persistence, automatic document saving, versioning, file coordination, Cocoa autolayout, full-screen apps, popover windows, sandboxing, push notifications.
-
Part 2: New frameworks: AV Foundation, Store Kit (in-app purchasing) and IMServicePlugIn. Changes in AppKit.
-
Part 3: Changes in Core Data, Core Foundation, Core Location, Foundation, QTKit, Quartz and Quartz Core.
Core Data
Formalized Concurrency Model
Working with Core Data on a background thread involves manually managing two separate managed object contexts and their associated object graphs, taking care that you do not pass managed object from one thread/context to another. You should never access a managed object context outside the thread or dispatch queue that created it. This is still true in Lion, and the system now lets you formalize this agreement by creating a context with initWithConcurrenceType:NSConfinementConcurrencyType
.
If you pass NSPrivateQueueConcurrencyType
to initWithConcurrencyType:
, the context creates and manages a private dispatch queue to operate on. To send any message to such a context, you must use the new methods performBlock:
or performBlockAndWait:
. The context will then execute the passed blocks on its own queue.
NSMainQueueConcurrencyType
creates a context that is associated with the main dispatch queue and thus the main thread. Use such a context to link it to controllers and UI objects that are required to run on the main thread.
Support for Nested Managed Object Contexts
In earlier releases of OS X, a managed object context is always directly associated with a “parent” persistent store coordinator. In Lion, rather than having a persistent store coordinator, a context can also have another managed object context as its parent. This nested relationship between managed object contexts has two very useful use cases:
-
Fetches and saves are mediated by the second context, which can execute these time-consuming operations in the background, thereby avoiding blocking the UI.
-
Allow discardable edits that go away by simply throwing away the second context.
Use the -setParentContext:
method to assign a parent context to your managed object context.
Ordered Relationships
To-many relationships in Core Data can now optionally have an order. This much-awaited feature should save many people a lot of tedious manual work that is involved in manually maintaining an order attribute for the items in the relationship. See the isOrdered
attribute of NSRelationshipDescription
.
In code, ordered relationships are represented by the new NSOrderedSet
class (see below). Note that ordered relationships are significantly less efficient than unordered ones. You should only use them if a relationship has an intrinsic order. Do not use ordered relationships just because you want to avoid sorting your fetch results.
External Storage for Attributes
Another area where many developers had to invest manual work was when the Core Data store was designed to hold references to large amounts of binary data that were usually stored in external files (such as images). Developers had to generate unique filenames, create these files, store the filenames in the data store and delete the files when the associated managed object was deleted.
In Lion, managed objects support optional external storage for attribute values. If you specify in the Core Data editor that the value of a certain attribute (typically a BLOB) may be stored externally, Core Data uses internal heuristics (probably based on the size of the BLOB) to decide whether to store the data directly in the database or in a separate external file. As a developer, you don’t have to care either way. See the allowsExternalBinaryDataStorage
attribute of NSAttributeDescription
. I think this is a great feature.
Fetch Requests
NSFetchRequest
got a few new features in Lion:
-
Initializing a fetch request with the new
initWithEntityName:
method saves you a separate call tosetEntity:
and the hassle of first retrieving theNSEntityDescription
. -
If you configure your fetch request to return dictionaries, it supports
GROUP BY
(setPropertiesToGroupBy:
) andHAVING
(setHavingPredicate:
) clauses.
Implement Your Own Incremental Stores
Two new classes, NSIncrementalStore
and NSIncrementalStoreNode
, allow you to add support for other non-atomic persistent stores besides the existing SQLite store. (Writing custom atomic persistent stores was already supported in earlier versions of OS X.)
One cool possibility would be to write a Core Data interface to a web service backend such as CouchDB.
The Core Data Release Notes offer the best overview of the new features in Apple’s documentation.
Core Foundation
With the CFStringGetHyphenationLocationBeforeIndex()
function it is now possible to locate potential hyphenation points within a word. Since hyphenation is language-specific, you have to pass the locale of the string you are inspecting to the function. Do not forget to check whether the system supports hyphenation for that locale beforehand by calling CFStringIsHyphenationAvailableForLocale()
See the Core Foundation Release Notes for details on this and more changes in Core Foundation.
Core Location
The Core Location framework originated on iOS and was introduced to OS X with Snow Leopard. In Lion, Apple brought the header files up to par with iOS. They even added stuff like CLHeading
that doesn’t make a lot of sense on a computer but who knows when the first laptop with a built-in compass will come out? The same goes for the CLDeviceOrientation
enum, which includes values for portrait, landscape, face up and face down orientations.
The most useful addition to the framework seems to be region monitoring. Start the CLLocationManager
by sending it a startMonitoringForRegion:desiredAccuracy:
message to be alerted when the computer enters a specific area, or use startMonitoringSignificantLocationChanges
to be notified whenever the computer’s location changes.
Foundation
As usual, the Foundation Release Notes for Lion give the best explanation of the new features in Apple’s documentation. It is unfortunate that it is so hard to discover these release notes documents for the particular frameworks as they are not linked from either What’s New in Lion or the API Diffs.
Native JSON Support
With NSJSONSerialization
, OS X gets native reading and writing support for JSON, which has become the de facto data format for web APIs.
The +JSONObjectWithData:options:error:
class method takes JSON data that you received from a network request and converts it into a hierarchy of Foundation objects (NSDictionary
, NSArray
, NSString
, NSNumber
, NSNull
). Two useful options, NSJSONReadingMutableContainers
and NSJSONReadingMutableLeaves
let you specify whether the resulting Foundation objects should be immutable (the default) or mutable. The class also offers a variant to read the data directly from an NSInputStream
: +JSONObjectWithStream:options:error:
.
To convert an object graph into UTF-8-encoded JSON, call the +dataWithJSONObject:options:error:
class method. By default, the resulting JSON data will be as compact as possible. Passing NSJSONWritingPrettyPrinted
as an option to the method allows you to generate more readable output.
Key-Value Observing
The new method removeObserver:forKeyPath:context:
allows you to more precisely remove a key-value observer by passing the same context
pointer you used when adding the observer with addObserver:forKeyPath:options:context:
. Using the old removeObserver:forKeyPath:
method could possibly lead to problems.
NSURLConnection
Before Lion, asynchronous NSURLConnection
s always required a running runloop, making the class a bit difficult to use with operation or dispatch queues. Queue support was added in Lion. Setting an NSOperationQueue
with the setDelegateQueue:
method will deliver the delegate messages on the specified queue.
The new sendAsynchronousRequest:queue:completionHandler:
convenience method will start an asynchronous connection and execute the completionHandler
block on the specified queue once the request has finished either successfully or with an error. It frees the developer from implementing any NSURLConnectionDelegate
methods if special handling of the connection, continuous progress reports etc. are not required. Very handy.
Linguistic Tagging
With the new NSLinguisticTagger
class, developers can easily analyze natural-language text and identify different grammatical parts of speech. For example, given the sentence “Steve Jobs just resigned as CEO.” and configured to use the tag scheme NSLinguisticTagSchemeLexicalClass
, NSLinguisticTagger
can identify the words “Steve”, “Jobs” and “CEO” as nouns, the word “just” as an adverb, and the word “as” as a preposition.
Other possible tag schemes include NSLinguisticTagSchemeLemma
to identify stem forms of words, NSLinguisticTagSchemeLanguage
to identify the language and NSLinguisticTagSchemeScript
to identify the script (Latin, Cyrillic, etc.).
The tagging works very well for English. Other languages might not support all tag schemes or might not be supported at all. Call the availableTagSchemesForLanguage:
class method to determine the level of support for a language.
To do the analysis, create an instance of NSLinguisticTagger
and pass it a string with setString:
. Then call enumerateTagsInRange:scheme:options:usingBlock:
to iterate over all tags the linguistic tagger found.
Ordered Sets
NSOrderedSet
and NSMutableOrderedSet
are new collection classes that combine the advantages of NSArray
(ordered collection, fast index-based access) and NSSet
(unique contents, fast access to objects). Note that NSOrderedSet
inherits neither from NSArray
nor from NSSet
.
The class also offers two interesting methods, -(NSArray *)array
and - (NSSet *)set
. These return proxy objects for the underlying ordered set that act like an array or set without actually being one. If the underlying ordered set is mutable, changes to it will directly pass through to the proxy objects and these “immutable” collections will appear to outside code to be changing.
Regular Expressions and Data Detectors
Two new Lion developer features that you may already know from iOS are built-in regular expression support and data detectors.
The NSRegularExpression
class represents a regular expression that you can apply to a string. After creating a regex instance (+regularExpressionWithPattern:options:error:
), you use the Block iterator method enumerateMatchesInString:options:range:usingBlock:
to enumerate all matches in the specified string. Each match is represented by an NSTextCheckingResult
instance.
To use regular expressions for searching and replacing text, use the stringByReplacingMatchesInString:options:range:withTemplate:
method.
The class reference documentation for NSRegularExpression
has a very detailed overview about the supported regex syntax.
NSDataDetector
is a subclass of NSRegularExpression
that offers pre-configured regular expressions to identify patterns such as dates, addresses, phone numbers, or URLs.
Miscellaneous
-
The
NSUserDefaults
) API now has better performance if accessed concurrently from multiple threads. -
NSURLRequest
now supports HTTP pipelining with thesetHTTPShouldUsePipelining:
method. You can also specify a so-called network service type (setNetworkServiceType:
) such as voice or video, supposedly to give the networking subsystem a hint how to prioritize the request. -
NSIndexSet
got three new methods to enumerate the non-contiguous ranges in the set rather than every single index. SeeenumerateRangesUsingBlock:
. -
NSXMLParser
now has a streaming mode where parsing of a document can begin before its entire contents have been downloaded. You had to use libxml2 directly if you wanted to do this before. See theinitWithStream:
method. In addition,NSXMLParser
is now thread-safe. -
NSBundle
has a new methodappStoreReceiptURL
to get the location of the App Store receipt file without hard-coding relative paths in your bundle.
OpenGL 3.2
Lion now supports OpenGL 3.2. Developers should select a specific OpenGL profile to tell the system which version of OpenGL an app is designed for. The current options are either kCGLOGLPVersion_3_2_Core
for Open GL 3.2 or kCGLOGLPVersion_Legacy
, which provides the same functionality found in earlier versions of Mac OS X
.
See the OpenGL Profiles section in Apple’s OpenGL Programming Guide for details.
QTKit
QTKit provides two new classes that make it easier to export movies in different formats. It also features new APIs for reading movie metadata without having to use the QuickTime C API, which is not available in 64-bit apps.
The new QTExportSession
class (not yet documented) represents an export process that produces a transcoded output from a given QTMovie
source. The properties of the exported movie are specified by an instance of QTExportOptions
. The initializer for an export session is the self-explaining initWithMovie:exportOptions:outputURL:error:
method. After you have created the export session instance and set a delegate, send it a run
message to start the export operation asynchronously. The session will inform its delegate about success (exportSessionDidSucceed:
) or failure (exportSession:didFailWithError:
) of the operation. It also informs regularly about progress while the operation is running (exportSession:didReachProgress:
).
The export settings in the QTExportOptions
class are specified by instantiating the class with one of the available format constants: QTExportOptionsAppleM4VCellular
, QTExportOptionsAppleM4V480pSD
, QTExportOptionsAppleM4ViPod
, QTExportOptionsAppleM4VAppleTV
, QTExportOptionsAppleM4VWiFi
, QTExportOptionsAppleM4V720pHD
, QTExportOptionsQuickTimeMovie480p
, QTExportOptionsQuickTimeMovie720p
, QTExportOptionsQuickTimeMovie1080p
, QTExportOptionsAppleM4A
. It does not seem possible to configure the export options in a more granular manner. See the QTExportSession.h
and QTExportOptions.h
header files for details.
The new metadata reading capabilities are based on the QTMetadataItem
class. The QTMovie
and QTTrack
classes have been extended by new methods that return these metadata items. The commonMetadata
method returns an array of QTMetadataItem
objects for each common metadata key for which a value for the current locale is available
.
Metadata can be available in several formats, including QTMetadataFormatQuickTimeUserData
, QTMetadataFormatQuickTimeMetadata
, QTMetadataFormatiTunesMetadata
and QTMetadataFormatID3Metadata
. The availableMetadataFormats
method returns an array of those formats available for the current movie or track while the metadataForFormat:
method lets you retrieve the metadata for a specific format.
Quartz
QLPreviewView
is a new class that allows you to embed a Quick Look preview into your own view hierarchy. Its designated initializer is initWithFrame:style:
, giving you the option of two styles, QLPreviewViewStyleNormal
and QLPreviewViewStyleCompact
. After creating the view, just assign the item to preview (which must implement the QLPreviewItem
protocol) to its previewItem
property and add the view to your view hierarchy just as you would with any other view.
Quartz Core
Core Animation
New Features from iOS
Several Core Animation classes inherited new properties that were introduced before in iOS 4. Examples:
-
CALayer
now hascontentsScale
andrasterizationScale
properties. Setting the newshouldRasterize
property toYES
can improve rendering performance in some cases (especially during animations).shadowPath
allows you to modify the shape of a layer’s drop shadow. -
CAShapeLayer
: Use thestrokeStart
andstrokeEnd
properties to restrict the region where the shape layer’s path is rendered to a subregion of the total. -
CAKeyframeAnimation
has two new calculation modes for intermediate frames of a keyframe animation:kCAAnimationCubic
andkCAAnimationCubicPaced
.Intermediate frames are computed using a Catmull-Rom spline that passes through the keyframes. You can adjust the shape of the spline by specifying an optional set of tension, continuity, and bias values, which modify the spline using the standard Kochanek-Bartels form.
Right. Got that?
Remote Layer Clients and Servers
The new (not yet documented) classes CARemoteLayerServer
and CARemoteLayerClient
seem to allow one process to render a layer tree that is then displayed by another process. I guess this was introduced for the new XPC interprocess communication framework.
Core Image Face Detection
One of the coolest new features of Lion is a public face detection API. Don’t confuse face detection (identifying the position and size of faces in an image) with face recognition (being able to tell if two faces show the same person or not). iPhoto can do the latter while the new API is “only” capable of the former.
Face detection is part of Core Image and therefore works on CIImage
objects. The API is very easy to use, provided that you have converted the image you want to analyze into a CIImage
. The source can either be a still image or a frame of a video.
The central class for face detection is CIDetector
. Although it can only detect faces at the moment, Apple has designed the API in a generalized manner so that other detection algorithms can be easily added in the future. To detect faces, instatiate a CIDetector
with the +detectorOfType:context:options:
class method, passing CIDetectorTypeFace
as the type. The context
argument may be nil
but you can improve performance if you pass in a CIContext
instance that already has uploaded the image to be processed to the GPU. The options
dictionary allows you to opt for higher accuracy or higher speed of the detection, depending on your needs.
Sending the detector instance a featuresInImage:
method starts the detection process and returns an array of CIFaceFeature
objects if any faces were found. Each face is describes by its bounds
as well as the leftEyePosition
, rightEyePosition
and mouthPosition
.