You are on page 1of 879

Cocoa

Programming for OS X: The


Big Nerd Ranch Guide
by Aaron Hillegass, Adam Preble and Nate Chandler
Copyright © 2015 Big Nerd Ranch, LLC.

All rights reserved. Printed in the United States of America. This publication is
protected by copyright, and permission must be obtained from the publisher
prior to any prohibited reproduction, storage in a retrieval system, or
transmission in any form or by any means, electronic, mechanical,
photocopying, recording, or likewise. For information regarding permissions,
contact

Big Nerd Ranch, LLC.


200 Arizona Ave NE
Atlanta, GA 30307
(770) 817-6373
http://www.bignerdranch.com/
book-comments@bignerdranch.com

The 10-gallon hat with propeller logo is a trademark of Big Nerd Ranch, LLC.
Exclusive worldwide distribution of the English edition of this book by

Pearson Technology Group


800 East 96th Street
Indianapolis, IN 46240 USA
http://www.informit.com

The authors and publisher have taken care in writing and printing this book but
The authors and publisher have taken care in writing and printing this book but
make no expressed or implied warranty of any kind and assume no responsibility
for errors or omissions. No liability is assumed for incidental or consequential
damages in connection with or arising out of the use of the information or
programs contained herein.
Many of the designations used by manufacturers and sellers to distinguish their
products are claimed as trademarks. Where those designations appear in this
book, and the publisher was aware of a trademark claim, the designations have
been printed with initial capital letters or in all capitals.

Print ISBN-10 0134076958
Print ISBN-13 978-0134076959

Fifth edition, first printing, April 2015


Release K.5.1.1
Dedication
For Aaron’s sons, Walden and Otto

For Adam’s daughters, Aimee and Leah

For Nate’s nieces and nephews
Acknowledgments
Creating this book required the efforts of many people. We want to thank them
for their help. Their contributions have made this a better book than we could
have ever written alone.
Thanks to the students who took the Cocoa programming course at the Big Nerd
Ranch. They helped us work the kinks out of the exercises and explanations that
appear here. Their curiosity inspired us to make the book more comprehensive,
and their patience made it possible.
Thank you to all the readers of the first four editions who made such great
suggestions on our forums ( http://forums.bignerdranch.com/ ).
Thank you to our technical reviewers, Juan Pablo Claude, Chris Morris, Nick
Teissler, Pouria Almassi, and John Gallagher, who made great additions and
caught many of our most egregious errors.
Finally, a very big thank you to our support team in writing this book: Liz
Holaday, for copy-editing; Chris Loper, whose excellent tool chain made writing
this book that much easier; and most of all Susan Loper, whose collaboration
helped us write the kind of book we believe this technology deserves.
Table of Contents
Introduction

About This Book


Prerequisites
Typographical conventions
Using an eBook
What’s new in the fifth edition?
The Story of Cocoa
NeXTSTEP and OpenStep
From NeXTSTEP to OS X to iOS
OSX, Unix, and Cocoa
Introducing the Swift Language
The Cocoa Frameworks
Tools for Cocoa Programming
Some Advice on Learning
1. Let’s Get Started

Creating an Xcode Project


Getting around in Xcode
Application Design
Model-View-Controller
Creating the MainWindowController class
Creating the User Interface in Interface Builder
Adding view objects
Configuring view objects
XIB files and NIB files
Showing the Window
Making Connections
Creating an outlet
Connecting an outlet
Defining an action method
Connecting actions
Creating the Model Layer
Connecting the Model Layer to the Controller
Improving Controller Design
2. Swift Types

Introducing Swift
Types in Swift
Using Standard Types
Inferring types
Specifying types
Literals and subscripting
Initializers
Properties
Instance methods
Optionals
Subscripting dictionaries
Loops and String Interpolation
Enumerations and the Switch Statement
Enumerations and raw values
Exploring Apple’s Swift Documentation
3. Structures and Classes

Structures
Instance methods
Operator Overloading
Classes
Designated and convenience initializers
Add an instance method
Inheritance
Computed Properties
Reference and Value Types
Implications of reference and value types
Choosing between reference and value types
Making Types Printable
Swift and Objective-C
Working with Foundation Types
Basic bridging
Bridging with collections
Runtime Errors
More Exploring of Apple’s Swift Documentation
Challenge: Safe Landing
Challenge: Vector Angle
4. Memory Management

Automatic Reference Counting


Objects have reference counts
Deallocating objects in a hierarchy
Strong and Weak References
Strong reference cycles
Unowned references
What is ARC?
5. Controls

Setting up RGBWell
Creating the MainWindowController class
Creating an empty XIB file
Creating an instance of MainWindowController
Connecting a window controller and its window
About Controls
Working with Controls
A word about NSCell
Connecting the slider’s target and action
A continuous control
Setting the slider’s range values
Adding two more sliders
NSColorWell and NSColor
Disabling a control
Using the Documentation
Changing the color of the color well
Controls and Outlets
Implicitly unwrapped optionals
For the More Curious: More on NSColor
For the More Curious: Setting the Target Programmatically
Challenge: Busy Board
Debugging Hints
6. Delegation

Setting up SpeakLine
Creating and using an Xcode snippet
Creating the user interface
Synthesizing Speech
Updating Buttons
Delegation
Being a delegate
Implementing another delegate
Common errors in implementing a delegate
Cocoa classes that have delegates
Delegate protocols and notifications
NSApplication and NSApplicationDelegate
The main event loop
For the More Curious: How Optional Delegate Methods Work
Challenge: Enforcing a Window’s Aspect Ratio
7. Working with Table Views

About Table Views


Delegates and data sources
The table view-data source conversation
SpeakLine’s table view and helper objects
Getting Voice Data
Retrieving friendly names
Adding a Table View
Table view and related objects
Tables, Cells, and Views
Table cell views
The NSTableViewDataSource Protocol
Conforming to the protocol
Connecting the dataSource outlet
Implementing data source methods
Binding the text field to the table cell view
The NSTableViewDelegate Protocol
Making a connection with the assistant editor
Implementing a delegate method
Pre-selecting the default voice
Challenge: Make a Data Source
8. KVC, KVO, and Bindings

Bindings
Setting up Thermostat
Using bindings
Key-value observing
Making keys observable
Binding other attributes
KVC and Property Accessors
KVC and nil
Debugging Bindings
Using the Debugger
Using breakpoints
Stepping through code
The LLDB console
Using the debugger to see bindings in action
For the More Curious: Key Paths
For the More Curious: More on Key-Value Observing
For the More Curious: Dependent Keys
Challenge: Convert RGBWell to Use Bindings
9. NSArrayController

RaiseMan’s Model Layer


RaiseMan’s View Layer
Introducing NSArrayController
Adding an Array Controller to the XIB
Binding the Array Controller to the Model
Binding the Table View’s Content to the Array Controller
Connecting the Add Employee Button
Binding the Text Fields to the Table Cell Views
Formatting the Raise Text Field
Connecting the Remove Button
Binding the Table View’s Selection to the Array Controller
Configuring RaiseMan’s Remove Button
Sorting in RaiseMan
How Sorting Works in RaiseMan
For the More Curious: The caseInsensitiveCompare(_:) Method
For the More Curious: Sorting Without NSArrayController
For the More Curious: Filtering
For the More Curious: Using Interface Builder’s View Hierarchy Popover
Challenge: Sorting Names by Length
10. Formatters and Validation

Formatters
Formatters, programmatically
Formatters and a control’s objectValue
Formatters and localization
Validation with Key-Value Coding
Adding Key-Value validation to RaiseMan
For the More Curious: NSValueTransformer
11. NSUndoManager

Message Passing and NSInvocation


How the NSUndoManager Works
Using NSUndoManager
Key-Value Coding and To-Many Relationships
Adding Undo to RaiseMan
Key-Value Observing
Using the Context Pointer Defensively
Undo for Edits
Begin Editing on Insert
For the More Curious: Windows and the Undo Manager
12. Archiving

NSCoder and NSCoding


Encoding
Decoding
The Document Architecture
Info.plist and NSDocumentController
NSDocument
NSWindowController
Saving and NSKeyedArchiver
Loading and NSKeyedUnarchiver
Setting the Extension and Icon for the File Type
Application Data and URLs
For the More Curious: Preventing Infinite Loops
For the More Curious: Creating a Protocol
For the More Curious: Automatic Document Saving
For the More Curious: Document-Based Applications Without Undo
For the More Curious: Universal Type Identifiers
13. Basic Core Data

Defining the Object Model


Configure the Array Controller
Add the Views
Connections and Bindings
How Core Data Works
Fetching Objects from the NSManagedObjectContext
Persistent Store Types
Choosing a Cocoa Persistence Technology
Customizing Objects Created by NSArrayController
Challenge: Begin Editing on Add
Challenge: Implement RaiseMan Using Core Data
14. User Defaults

NSUserDefaults
Adding User Defaults to SpeakLine
Create Names for the Defaults
Register Factory Defaults for the Preferences
Reading the Preferences
Reflecting the Preferences in the UI
Writing the Preferences to User Defaults
Storing the User Defaults
What Can Be Stored in NSUserDefaults?
Precedence of Types of Defaults
What is the User’s Defaults Database?
For the More Curious: Reading/Writing Defaults from the Command Line
For the More Curious: NSUserDefaultsController
Challenge: Reset Preferences
15. Alerts and Closures

NSAlert
Modals and Sheets
Completion Handlers and Closures
Closures and capturing
Make the User Confirm the Deletion
For the More Curious: Functional Methods and Minimizing Closure
Syntax
Challenge: Don’t Fire Them Quite Yet
Challenge: Different Messages for Different Situations
16. Using Notifications
What Notifications Are
What Notifications Are Not
NSNotification
NSNotificationCenter
Starting the Chatter Application
Using Notifications in Chatter
For the More Curious: Delegates and Notifications
Challenge: Beep-beep!
Challenge: Add Usernames
Challenge: Colored Text
Challenge: Disabling the Send Button
17. NSView and Drawing

Setting Up the Dice Application


Creating a view subclass
Views, Rectangles, and Coordinate Systems
frame
bounds
Custom Drawing
drawRect(_:)
When is my view drawn?
Graphics contexts and states
Drawing a die face
Saving and Restoring the Graphics State
Cleaning up with Auto Layout
Drawing Images
Inspectable properties and designable views
Drawing images with finer control
Scroll Views
Creating Views Programmatically
For the More Curious: Core Graphics and Quartz
For the More Curious: Dirty Rects
For the More Curious: Flipped Views
Challenge: Gradients
Challenge: Stroke
Challenge: Make DieView Configurable from Interface Builder
18. Mouse Events

NSResponder
NSEvent
Getting Mouse Events
Click to Roll
Improving Hit Detection
Gesture Recognizers
Challenge: NSBezierPath-based Hit Testing
Challenge: A Drawing App
19. Keyboard Events

NSResponder
NSEvent
Adding Keyboard Input to DieView
Accept first responder
Receive keyboard events
Putting the dice in Dice
Focus Rings
The Key View Loop
For the More Curious: Rollovers
20. Drawing Text with Attributes

NSFont
NSAttributedString
Drawing Strings and Attributed Strings
Drawing Text Die Faces
Extensions
Getting Your View to Generate PDF Data
For the More Curious: NSFontManager
Challenge: Color Text as SpeakLine Speaks It
21. Pasteboards and Nil-Targeted Actions

NSPasteboard
Add Cut, Copy, and Paste to Dice
Nil-Targeted Actions
Looking at the XIB file
Menu Item Validation
For the More Curious: Which Object Sends the Action Message?
For the More Curious: UTIs and the Pasteboard
Custom UTIs
For the More Curious: Lazy Copying
Challenge: Write Multiple Representations
Challenge: Menu Item
22. Drag-and-Drop

Make DieView a Drag Source


Starting a drag
After the drop
Make DieView a Drag Destination
registerForDraggedTypes(_:)
Add highlighting
Implement the dragging destination methods
For the More Curious: Operation Mask
23. NSTimer

NSTimer-based Animation
How Timers Work
NSTimer and Strong/Weak References
For the More Curious: NSRunLoop
24. Sheets

Adding a Sheet
Create the Window Controller
Set Up the Menu Item
Lay Out the Interface
Configuring the Die Views
Present the Sheet
Modal Windows
Encapsulating Presentation APIs
Challenge: Encapsulate Sheet Presentation
Challenge: Add Menu Item Validation
25. Auto Layout

What is Auto Layout?


Adding Constraints to RaiseMan
Constraints from subview to superview

Constraints between siblings

Size constraints
Intrinsic Content Size
Creating Layout Constraints Programmatically
Visual Format Language
Does Not Compute, Part 1: Unsatisfiable Constraints
Does Not Compute, Part 2: Ambiguous Layout
For the More Curious: Autoresizing Masks
Challenge: Add Vertical Constraints
Challenge: Add Constraints Programmatically
26. Localization and Bundles

Different Mechanisms for Localization


Localizing a XIB File
Localizing String Literals
Demystifying NSLocalizedString and genstrings
Explicit Ordering of Tokens in Format Strings
NSBundle
NSBundle’s role in localization
Loading code from bundles
For the More Curious: Localization and Plurality
Challenge: Localizing the Default Name for a Newly Added Employee
Challenge: Localizing the Undo Action Names
27. Printing

Dealing with Pagination


Adding Printing to RaiseMan
For the More Curious: Are You Drawing to the Screen?
Challenge: Add Page Numbers
Challenge: Persist Page Setup
28. Web Services

Web Services APIs


RanchForecast Project
NSURLSession and asynchronous API design
NSURLSession, HTTP status codes, and errors
Add JSON parsing to ScheduleFetcher
Lay out the interface
Opening URLs
Safely Working with Untyped Data Structures
For the More Curious: Parsing XML
Challenge: Improve Error Handling
Challenge: Add a Spinner
Challenge: Parse the XML Courses Feed
29. Unit Testing

Testing in Xcode
Your First Test
A Note on Literals in Testing
Creating a Consistent Testing Environment
Sharing Constants
Refactoring for Testing
For the More Curious: Access Modifiers
For the More Curious: Asynchronous Testing
Challenge: Make Course Implement Equatable
Challenge: Improve Test Coverage of Web Service Responses
Challenge: Test Invalid JSON Dictionary
30. View Controllers

NSViewController
Starting the ViewControl Application
Windows, Controllers, and Memory Management
Container View Controllers
Add a Tab View Controller
View Controllers vs. Window Controllers
Considerations for OS X 10.9 and Earlier
Challenge: SpeakLineViewController
Challenge: Programmatic View Controller
Challenge: Add a Window Controller
31. View Swapping and Custom Container View Controllers

View Swapping
NerdTabViewController
Adding Tab Images
Challenge: Boxless NerdTabViewController
Challenge: NerdSplitViewController
Challenge: Draggable Divider
32. Storyboards

A New UI for RanchForecast


Adding the course list
Adding the web view
Connecting the Course List Selection with the Web View
Creating the CourseListViewControllerDelegate
Creating the parent view controller
For the More Curious: How is the Storyboard Loaded?
33. Core Animation

CALayer
Scattered
Implicit Animation and Actions
More on CALayer
Challenge: Show Filenames
Challenge: Reposition Image Layers
34. Concurrency

Multithreading
A Deep Chasm Opens Before You
Improving Scattered: Time Profiling in Instruments
Introducing Instruments
Analyzing output from Instruments
NSOperationQueue
Multithreaded Scattered
Thread synchronization
For the More Curious: Faster Scattered
Challenge: An Even Better Scattered
35. NSTask

ZIPspector
Asynchronous Reads
iPing
Challenge: .tar and .tgz Files
36. Distributing Your App

Build Configurations
Preprocessor Directives: Using Build Configurations to Change Behavior
Creating a Release Build
A Few Words on Installers
App Sandbox
Entitlements
Containers
Mediated file access and Powerbox
The Mac App Store
Receipt Validation
Local receipt verification
Server-based verification
37. Afterword
Index
Introduction
If you are developing applications for OS X, or are hoping to do so, this book
will be your foundation and will help you understand Cocoa, the set of
frameworks for developing applications for OS X. You, the developer, are going
to love developing for OS X because Cocoa will enable you to write full-
featured applications in a more efficient and elegant manner.
About This Book
This book covers the major design patterns of Cocoa and includes an
introduction to the Swift language. It will also get you started with the most
commonly-used developer tools: Xcode and Instruments. After reading this book,
you will understand these major design patterns which will enable you to
understand and use Apple’s documentation – a critical part of any Cocoa
developer’s toolkit – as well as build your own Cocoa applications from scratch.
This book teaches ideas and provides hands-on exercises that show these ideas in
action. Each chapter will guide you through the process of building or adding
features to an application.
Often, we will ask you to do something and explain the details or theory
afterward. If you are confused, read a little more. Usually, the help you seek will
be only a paragraph or two away.
Because of the hands-on nature of the book, it is essential that you do the
exercises and not just read the words. Doing the exercises will help build the
kind of solid understanding that will enable you to develop on your own when
you are finished with this book. You will also learn a great deal from making
mistakes, reading error messages, and figuring out what went wrong – practical
experience you can’t get from reading alone. At first, you may want to stick with
what we show you, but later in the book when you are more comfortable with
the environment, you should feel free to experiment with the exercises and add
your own ideas.
Most chapters end with one or two challenge exercises. These exercises are
important to do as well. Taking on these challenges gives you the opportunity to
test your skills and problem-solve on your own.
You can get help with this book at bignerdranch.com/books, where you will find
errata and downloadable solutions for the exercises. You can also post questions
and find relevant conversations on the Big Nerd Ranch forums at
forums.bignerdranch.com.

We ask that you not use the downloadable solutions as a shortcut for doing the
exercises. The act of typing in code has far more impact on your learning than
most people realize. By typing the code yourself (and, yes, making mistakes),
you will absorb patterns and develop instincts about Cocoa programming, and
you will absorb patterns and develop instincts about Cocoa programming, and
you will miss out on these benefits if you rely on the solutions or copy and paste
the code instead.
There is a lot of code in this book. Through that code, we will introduce you to
the idioms of the Cocoa community. Our hope is that by presenting exemplary
code, we can help you to become more than a Cocoa developer – a stylish Cocoa
developer.
Most of the time, Cocoa fulfills the following promise: Common things are easy,
and uncommon things are possible. If you find yourself writing many lines of
code to do something rather ordinary, you are probably on the wrong track.
There is a popular adage in the community which you should bear in mind:
Don’t fight the framework. Cocoa is opinionated and you will benefit greatly
from adapting your way of doing things to its way of doing things.

Prerequisites
This book is written for programmers and assumes that you are familiar with
basic programming concepts (like functions, variables, and loops) as well as
object-oriented concepts (like classes, objects, and inheritance). If you do not fit
this profile, you will find this book tough going. You are not expected to have
any experience with Mac programming.
One of the challenges of learning Cocoa programming is learning the Swift
language. If you have a basic foundation in programming and know something
about objects, you will find learning Swift to be easy. This book includes three
chapters to introduce to you to the language. Then you will learn more Swift as
you build Cocoa applications throughout the book. If you would prefer a gentler
introduction, start with Apple’s The Swift Programming Language, available in
the iBooks store or from developer.apple.com/swift, offers a more gentle
introduction. Or, if you can wait until Summer 2015, you can read Swift
Programming: The Big Nerd Ranch Guide first.
This is a hands-on book and assumes that you have access to OS X and the
developer tools. The book requires OS X Yosemite (10.10) or higher. The
exercises are written for Xcode 6.3 and Swift 1.2.
We strongly recommend that you join Apple’s Mac Developer Program at
developer.apple.com/programs. Joining the program gives you access to pre-release
versions of Xcode and OS X. These can be very useful when trying to stay ahead
of Apple’s development curve. In addition, you must be a member of the
developer program to distribute your apps on the App Store.

Typographical conventions
To make the book easier to follow, we have used several typographical
conventions.
In Swift, class names are always capitalized. In this book, we have also made
them appear in a monospaced bold font. In Swift, method names start with a
lowercase letter. Here, method names will also appear in a monospaced bold
font. For example, you might see “The class NSWindowController has the method
showWindow(_:).”

Other literals, including instance variable names that you would see in code, will
appear in a regular monospaced font. Also, filenames will appear in this same
font. Thus, you might see “In MyClass.swift, set the optional favoriteColor to
nil.”
Code samples in this book appear in the regular monospaced font. New portions,
which you will need to type yourself, will appear in bold. Code that you should
delete is struck-through.

Using an eBook
If you are reading this book on a Kindle, KindleFire, Kindle for Android, or
Kindle for iPad, we want to point out that reading the code may be tricky at
times. Longer lines of code may wrap to a second line depending on your
selected font size. Even more problematic, on Kindle for iPad, wrapping code
lines may be hyphenated. If you type these extra hyphens in, they will definitely
break your code.
The longest lines of code in this book are 86 monospace characters, like this one.
let fetchResult: [AnyObject]? =
managedObjectContext.executeFetchRequest(fetchRequest)
You can play with your eReader’s settings to find the best for viewing long code
lines.
When you get to the point where you are actually typing in code, we strongly
suggest opening the book on your PC or Mac in the appropriate Kindle
application. (Kindle for Mac and Kindle for PC are free applications you can
download from Amazon.com.) Make the application window large enough so
that you can see the code with no wrapping lines. You will also be able to see the
figures in full detail.

What’s new in the fifth edition?


This fifth edition includes technologies introduced in OS X 10.8, 10.9, and
10.10. It is updated for Xcode 6.3 and Swift 1.2. It includes coverage of Swift
basics, Auto Layout, unit testing, view controllers and expanded coverage of
view swapping, storyboards, modernized localization and web services APIs,
JSON parsing, Key-Value Validation, and a strong emphasis on demonstrating
best practices for application architecture.
The Story of Cocoa
Once upon a time, two guys named Steve started a company called Apple
Computer in their garage. The company grew rapidly, so they hired an
experienced executive named John Sculley to be its CEO. After a few conflicts,
John Sculley moved Steve Jobs to a position where he had no control over the
company. Steve Jobs left to form another computer company, NeXT Computer.
NeXT hired a small team of brilliant engineers. This small team developed a
computer, an operating system, a printer, a factory, and a set of development
tools. Each piece was years ahead of competing technologies. Unfortunately, the
computer and the printer were commercial failures. In 1993, the factory closed,
and NeXT Computer, Inc. became NeXT Software, Inc. The operating system
and the development tools continued to sell under the name NeXTSTEP.

NeXTSTEP and OpenStep


NeXTSTEP was very popular with scientists, investment banks, and intelligence
agencies. These groups found that NeXTSTEP enabled them to turn their ideas
into applications faster than any other technology. In particular, NeXTSTEP had
three important features:
a Unix-based operating system
NeXT decided to use Unix as the core of NeXTSTEP. It relied on the source
code for BSD Unix from the University of California at Berkeley. Why Unix?
Unix crashed much less frequently than Microsoft Windows or Mac OS and
came with powerful, reliable networking capabilities.
a powerful window server
A window server takes events from the user and forwards them to the
applications. The application then sends drawing commands back to the window
server to update what the user sees. One of the nifty things about the NeXT
window server is that the drawing code that goes to the window server is the
same drawing code that would be sent to the printer. Thus, a programmer has to
write the drawing code only once, and it can then be used for display on the
screen or printing.
If you have used Unix machines before, you are probably familiar with the X
window server. The window server for OS X is completely different but fulfills
the same function as the X window server: It gets events from the user, forwards
them to the applications, and puts data from the applications onto the screen.
an elegant set of libraries and tools
NeXTSTEP came with a set of libraries and tools to enable programmers to deal
with the window server in an elegant manner. The libraries were called
frameworks. In 1993, the frameworks and tools were revised and renamed
OpenStep.
Programmers loved OpenStep because they could experiment more easily with
new ideas. In fact, Tim Berners-Lee developed the first web browser and web
server on NeXTSTEP using the OpenStep libraries and tools. Securities analysts
could code and test new financial models much more quickly. Colleges could
develop the applications that made their research possible. We do not know what
the intelligence community was using it for, but they bought thousands of copies
of OpenStep.

From NeXTSTEP to OS X to iOS


For many years, Apple Computer had been working to develop an operating
system with many of the same features as NeXTSTEP. This effort, known as
Project Copland, gradually spun out of control, and Apple finally decided to pull
the plug and buy the next version of Mac OS instead. After surveying the
existing operating systems, Apple selected NeXTSTEP. Because NeXT was
small, Apple simply bought the whole company in December 1996. In 1997,
Steve Jobs returned to Apple.
NeXTSTEP became Mac OS X, and OpenStep became Cocoa. In 2001, the first
desktop version of Mac OS X was released with several more to follow. In 2012,
Apple dropped the “Mac,” and the operating system became known as OS X.
The mutation of NeXTSTEP didn’t stop with OS X. iOS, the operating system
for iPhones and iPads, is based on OS X, and iOS’s Cocoa Touch is built on the
same foundations as Cocoa. As a developer you will find that your knowledge
transfers well between the two: the design patterns are identical, and many of the
APIs are very similar if not the same.
OSX, Unix, and Cocoa
OS X is Unix underneath, and you can get all the standard Unix programs (such
as the Apache Web server) on OS X. It is extremely stable, and the user interface
is spectacular.
(Apple has made the source code to the Unix part of OS X available under the
name Darwin. A community of developers continues to work to improve
Darwin. You can learn more about Darwin at www.macosforge.org.)
As shown in Figure 1, the window server and your application are Unix
processes. Cocoa is your application’s interface to the window server to receive
events and draw to the screen. At the same time it has access to the Unix layer
where it can make lower level calls.
Figure 1 Where is Cocoa?
Introducing the Swift Language
Programming in Cocoa was initially done in a language called Objective-C.
Objective-C is an extension of the C programming language that adds constructs
for object-oriented programming. In that respect it bears a superficial
resemblance to C++, but the two are extremely different. Unlike C++, Objective-
C is weakly typed and extremely powerful. With power comes responsibility:
Objective-C also allows programmers to make ridiculous errors.
Over the past several years, Apple’s engineers have gone to heroic lengths to
make Objective-C faster and add more modern features, but in order to move
forward, a new language was needed, free of the limitations of the past. Swift,
developed by a small team led by Chris Lattner, was the answer. Apple
introduced Swift in 2014.
Swift maintains the expressiveness of Objective-C while introducing a syntax
that is significantly more rich, succinct, and – in the opinion of some – readable.
It emphasizes type safety and introduces advanced features such as optionals and
generics. Swift is much stricter than Objective-C and will not allow you to make
as many ridiculous errors.
Although we will focus on Swift, you can still write Cocoa code in Objective-C,
even alongside Swift, compiling the two in the same project.
Most importantly, Swift allows the use of these new features while relying on
the same tested, elegant Cocoa frameworks that developers have built upon for
years and years.
The Cocoa Frameworks
A framework is a collection of classes that are intended to be used together. That
is, the classes are compiled together into a reusable library of binary code. Any
related resources are put into a directory with the library. The directory is given
the extension .framework. You can find the built-in frameworks for your
machine in SystemLibrary/Frameworks. Cocoa is made up of three frameworks:
Foundation: Every object-oriented programming language needs the
standard value, collection, and utility classes. Strings, dates, lists, threads,
and timers are in the Foundation framework. All Cocoa apps, from
command-line tools to fully-featured GUI apps, use Foundation.
Foundation is also available on iOS.
AppKit: All things related to the user interface are in the AppKit
framework. These include windows, buttons, text fields, events, and
drawing classes. AppKit is built on top of Foundation and is used in
practically every graphical application on OS X.
Core Data: Core Data makes it easy to save objects to a file and then reload
them into memory. It is a persistence framework.
In addition to the three Cocoa frameworks, over a hundred frameworks ship with
OS X. The frameworks offer a wide variety of features and functionality. For
example, AVFoundation is great for working with audio and video,
AddressBook provides an API to the user’s contacts (with their permission), and
and SpriteKit is a full-featured 2D game engine with physics. You can pick and
choose from these frameworks to suit the needs of your application. You can
also create your own frameworks from the classes that you create. Typically, if a
set of classes is used in several applications, you will want to turn them into a
framework.
This book will focus on the Cocoa frameworks and especially Foundation and
AppKit because they will form the basis of most Cocoa applications that you
will write. Once you have mastered these, other frameworks will be easier to
understand.
Tools for Cocoa Programming
Xcode is the IDE (integrated development environment) used for Cocoa
development. Xcode is available for free on the Mac App Store. Pre-release
versions can be downloaded at developer.apple.com/mac. (You will need to join
Apple’s Mac Developer Program to access these.) We strongly recommend
using Xcode 6.3 with Swift 1.2 or later for the exercises in this book.
Xcode tracks all the resources that go into an application: code, images, sounds,
and so on. You edit your code in Xcode, and Xcode compiles and launches your
application. Xcode can also be used to invoke and control the debugger. Behind
the scenes, swiftc (Apple’s Swift compiler) will be used to compile your code,
and LLDB (Low Level Debugger) will help you find your errors.
Inside Xcode, you will use the Interface Builder editor as a GUI builder to lay out
windows and add UI elements to those windows. But Interface Builder is more than
a simple GUI builder. In Interface Builder, you can create objects and edit their
attributes. Most of those objects are UI elements from the AppKit framework
such as buttons and text fields, but some will be instances of classes that you
create.
You will use Instruments to profile your application’s CPU, memory, and
filesystem usage. Instruments can also be used to debug memory-management
issues. Instruments is built on top of dtrace, which makes it possible to create new
instruments.
Some Advice on Learning
All sorts of people come to our class: the bright and the not so bright, the
motivated and the lazy, the experienced and the novice. Inevitably, the people
who get the most from the class share one characteristic: they remain focused on
the topic at hand.
The first trick to maintaining focus is to get enough sleep: ten hours of sleep
each night while you are studying new ideas. Before dismissing this idea, try it.
You will wake up refreshed and ready to learn. Caffeine is not a substitute for
sleep.
The second trick is to stop thinking about yourself. While learning something
new, many students will think, “Damn, this is hard for me. I wonder if I am
stupid.” Because stupidity is such an unthinkably terrible thing in our culture,
they will then spend hours constructing arguments to explain why they are
intelligent yet having difficulties. The moment you start down this path, you
have lost your focus.
Aaron used to have a boss named Rock. Rock earned a degree in astrophysics
from Cal Tech, but never had a job that used his knowledge of the heavens.
When asked if he regretted getting the degree, he replied, “Actually, my degree
in astrophysics has proved to be very valuable. Some things in this world are just
hard. When I am struggling with something, I sometimes think ‘Damn, this is
hard for me. I wonder if I am stupid,’ and then I remember that I have a degree
in astrophysics from Cal Tech; I must not be stupid.”
Before going any further, assure yourself that you are not stupid and that some
things are just hard. Armed with this affirmation and a well-rested mind, you are
ready to conquer Cocoa.
1
Let’s Get Started
In this chapter, you are going to write a simple Cocoa application. When the user
clicks a button, a random string is generated and displayed in a text field. The
user can then copy the string and use it as a password that cannot be easily
guessed. Figure 1.1 shows the completed application.
Figure 1.1 Completed RandomPassword application

This chapter provides an initial exposure to Cocoa programming. Do not worry


if something does not make perfect sense this first time; you will see it again and
learn more about it in later chapters. For now, the goal is to get started, and the
first step is to create a new Xcode project.
Creating an Xcode Project
Open Xcode. If you are greeted by a welcome screen with a list of options, choose
Create a new Xcode project. If not, then from Xcode’s menu bar, select File → New →
Project... In either case, a panel will appear prompting you to choose a template for
your project (Figure 1.2).
Figure 1.2 Choosing a project template

On the lefthand side of the panel, find the OS X section and from that section,
select Application. From the choices presented on the right, select the Cocoa
Application template and click Next.

The next panel asks you for some project details (Figure 1.3). For Product Name,
enter RandomPassword. For organization name and identifier, you can use Big Nerd
Ranch and com.bignerdranch or you can use your company’s name and
com.yourcompany. From the Language drop-down menu, select Swift. Ensure that the
Use Storyboards, Create Document-Based Application, and Use Core Data boxes are not
checked. Then click Next.
Figure 1.3 Choosing project options
Finally, Xcode asks you where to create the folder that will contain the
RandomPassword project. The default location is the root of your home folder, but
that can get crowded. Instead, find the New Folder button at the bottom left of the
panel and create a new subfolder. Name it something like CocoaProjects and save
your projects there. After you have selected where to save the project, click
Create.

Getting around in Xcode


Xcode will open the new project in the project window (Figure 1.4).

Figure 1.4 Xcode project window


The lefthand side of the project window is the navigator area. This area is home
to a set of navigators – tools that display different perspectives of your project,
such as errors and warnings, stack traces, and testing reports.
The navigator currently open is the project navigator. The project navigator
provides an outline view of the files that make up your project. You can select a
file in the project navigator to open it in the editor area, the center portion of the
project window.
The icons in the project navigator that look like folders represent groups of files.
Putting files in groups keeps your project organized. You can make new groups
and rename existing ones. You can also move files into and out of groups by
dragging them around. Note that groups are for organization within the project
and do not necessarily relate to the location of the files on disk.
The righthand side of the project window is the utilities area. This area is
divided into two parts: the top half is the inspector pane, and the bottom half is
the library pane. You will work with inspectors and libraries later in the chapter.
Finally, at the top of the window is the toolbar. At the far righthand side of the
toolbar, there is a group of three buttons that let you hide and reveal the
navigator and utilities areas. Hiding these areas is helpful when you need more
room for the editor area.
Back in the project navigator, notice that the template created some files for you.
You are going to modify these files and create new ones to build RandomPassword.
But first, let’s go over the design of the application.
Application Design
Cocoa programming is object-oriented, which means that an application consists
of objects doing their jobs and coordinating with each other to get things done.
An object is the instance of a class. A class defines a set of related properties and
methods (which are similar to functions). A class can create one or more
instances of itself, and it is these objects that do the work of the application.
There will be some discussion on classes and objects in Chapter 3, but not a lot.
This book assumes that you are at least familiar with the basic concepts object-
oriented programming, classes, and instances.

Model-View-Controller
In object-oriented programming, one of the principal design patterns is Model-
View-Controller, or MVC. This design pattern says that a well-designed
application has three separate layers: model, view, and controller. Let’s start
with the view layer.

The view layer

The view layer is made up of view objects, or views. You use these objects to
compose the application’s user interface. Buttons and text fields are examples of
views. View classes tend to be standard classes that can be used across many
applications.
In RandomPassword, your view layer will consist of four standard views: a window,
a content view, a button, and a text field. These are shown in Figure 1.5.
Figure 1.5 RandomPassword’s four view objects
The window, button, and text field are familiar concepts. As a user, you have
clicked buttons, closed windows, and typed in text fields. The content view may
be less familiar. It is a transparent rectangle, and its job is to contain the button
and the text field. Typically in Cocoa, whenever you have a window, you also
have a content view that contains other view objects.
Views exist in a hierarchy. The view hierarchy for RandomPassword is shown in
Figure 1.6.
Figure 1.6 RandomPassword’s view hierarchy
Each view object is an instance of an existing Cocoa class. RandomPassword will
have instances of NSWindow, NSView, NSButton, and NSTextField. (The NS prefix
indicates that the class is part of the Cocoa frameworks. NS stands for
NeXTSTEP, the platform for which the frameworks were originally created.)

The model layer

The model layer is an abstraction of the real-world problem that the application
is built to solve. The real-world problem in RandomPassword is the need to create
passwords that are hard for other people to guess.
Many applications solve problems that are complex. Models for these
applications might include various data, data stores, calculations, and web
services.
RandomPassword, on the other hand, solves a simple problem. Its model layer will
consist only of an array of characters and two functions that produce a random
string.

The controller layer

The controller layer manages the flow of the application. It consists of controller
objects, or controllers. Unlike view classes, controller classes are usually custom
classes that you write for a specific application.
RandomPassword will have two controllers: an instance of a class named
AppDelegate and an instance of a class named MainWindowController: The
AppDelegate’s job is to manage the interaction between your application and the
system. The system launches the application and notifies the app delegate. The
app delegate then creates the window controller and puts the window (along with
the rest of the view objects) on screen.
The MainWindowController’s job is to coordinate with the view and model layers
to get the user the requested random password. It receives information from the
view layer, gets data from the model layer, and then updates the view layer with
that data.
Figure 1.7 shows the model, views, and controllers that comprise RandomPassword.
Figure 1.7 Object diagram for RandomPassword
Creating the MainWindowController class
Time to get to work. In the project navigator, select the group named
RandomPassword. (Remember, groups have folder icons.) This is the group that
contains the AppDelegate class file, and it is the group in which you want to
create the MainWindowController class file.
Next, from Xcode’s main menu bar, select File → New → File... (or use the keyboard
shortcut Command-N).
In the panel that appears, find the OS X section on the left and select Source. From
the choices that appear on the right, select Cocoa Class. Then click Next (Figure 1.8).
Figure 1.8 Choosing a template for the new class
In the next panel, name the class MainWindowController. Make it a subclass of
NSWindowController and ensure that the box labeled Also create XIB file for user interface
is checked. For the language, choose Swift. Then click Next (Figure 1.9).
Figure 1.9 Choosing options for the new class

Xcode will ask where to save the new files. Accept the default location and click
Create.

Notice that there are two new files in the project navigator:
MainWindowController.swift and MainWindowController.xib.
MainWindowController.swift is a Swift file. Swift is the language that you will
use to develop Cocoa apps. This file contains a skeleton of the
MainWindowController class that you will flesh out later.

MainWindowController.xib is not part of the MainWindowController class. It


exists because you asked the new class template to Also create XIB file for user interface.
This XIB file (it is pronounced “zib”) will contain the user interface for
RandomPassword. It already contains the window and the content view. In the next
section, you will add the other two view objects – the button and the text field.
Creating the User Interface in Interface
Builder
In the project navigator, select MainWindowController.xib to open it. (If you
double-click the file, it will open in a separate window. Close that window and
single-click the file to see it in the editor area.) Selecting
MainWindowController.xib opens it in the Interface Builder editor, a tool that is part
of Xcode. With Interface Builder, you can create an application’s user interface
visually rather than in code.
Interface Builder has two main sections: the dock and the canvas. The dock, on the
left, lists the objects in the XIB in the document outline view (Figure 1.10).
Figure 1.10 Interface Builder layout

If you see a stack of icons in the dock rather than a list of object names, you are
looking at the dock’s icon view. Click the Show Document Outline button in the
bottom-left corner of the canvas to see the document outline.
In the document outline, select Window at the bottom of the list, and the window
will be highlighted in the canvas. To see the content view, click the disclosure
arrow next to Window and select View.

Adding view objects


In the bottom half of the utilities area, find the icons in the library bar and click
the icon (Figure 1.11). This will reveal the object library.
Figure 1.11 Showing the object library

At the bottom of the library, find the search bar and start typing “text field.” The
library results will update to show the matching object types. Find the Text Field
object type.
A word of caution: Do not press Return when searching the object library.
Pressing Return will put a new object on the canvas, but it will not be on the
window. If you create a an object in the canvas but not on the window, simply
select the object in the canvas and press Delete. Then keep reading to see how to
create an object where you want it.
Drag from the Text Field type in the library onto the window in the canvas
(Figure 1.12). When you release the mouse, a text field object will appear on the
window where you dropped it and be added to the XIB.
Figure 1.12 Dragging a text field onto the window

In the document outline, the window’s View now has a disclosure arrow, and
clicking it will reveal an object named Text Field.
Notice that you are creating a hierarchy of view objects that matches the one
shown in Figure 1.6. The window is the root, and it has a single view as its direct
child. The view has a text field child and soon will have another child – a button.
(Ignore the Text Cell child of the text field for now. The cell of a text field is an
internal detail that you will not be interacting with.) Next, return to the object
library and start typing “button”. The search will yield many button types. These
types are all instances of the NSButton class and differ in their appearance and
behavior. For a basic button, find Push Button and drag it onto the window below
the text field.
Figure 1.13 Dragging a button onto the window
Configuring view objects
The new button needs a better title than Button. To change the button’s title,
double-click the button in the canvas and type Generate Password. In the document
outline, notice that the button object is now labeled Generate Password.
The window needs a better title, too. You cannot change it directly in the canvas;
you have to use the attributes inspector. The attributes inspector lets you view
and edit the attributes of the currently-selected object.
First, select Window in the document outline. Then head to the utilities area on the
right side of the Xcode window. At the top of the utilities area is the inspector bar.
From the inspector bar, select the icon to reveal the attributes inspector
(Figure 1.14).
Figure 1.14 Showing the attributes inspector
Find the Title attribute, which is currently set to Window. Change this attribute to
Password Generator.

Now select the text field in the document outline or on the canvas and return to
the attributes inspector. There are three attributes of the text field that you are
going to change to improve the user interface.
First, find the Alignment attribute (Figure 1.15). Change the alignment to centered.
Second, find the Font attribute. Your text field only has one job, so it might as
well do it loud and proud. Change the font attribute from the default System Regular
to System 26. This will increase the font size of the text.
Third, find the Behavior attribute. This attribute describes how users will be able to
interact with the text field. The default is Editable, which means that users can
select and edit the text. You want users to be able to select the text (so that they
can copy it and paste it elsewhere), but you do not want them to be able to edit
the text. To keep users from editing passwords, change the Behavior attribute to
Selectable.

Figure 1.15 Updating attributes for the text field


You can position your objects by selecting and dragging them around the
canvas. (If you click the x at the top-left corner of the window object in the
canvas, the window will disappear, but you did not delete it – it is only hidden.
To restore it to the canvas, simply select the window in the document outline
again.) You can also change the shapes and sizes of any object (including the
window) by selecting the object and dragging its edges. Do this until the window
on the canvas looks like Figure 1.16.
Figure 1.16 RandomPassword’s user interface
You have adjusted the window and its views to exactly how you want them to
appear to the user. You can prevent the user from changing the size of the
window to maintain the app’s appearance.
In the document outline, select the window and return to the attributes inspector.
In the Controls section, uncheck the box labeled Resize.

XIB files and NIB files


Save MainWindowController.xib. (Xcode auto-saves files, but we are making a
particular point here.) When you save MainWindowController.xib, the objects
you dragged and configured in Interface Builder are encoded as XML and archived
into the XIB file. In fact, “XIB” stands for XML Interface Builder.
When you build RandomPassword, the XIB file will be compiled into a binary file
named MainWindowController.nib. This NIB file is what gets packaged with the
application. A NIB file is smaller than a XIB file and easier for the system to
parse. (Developers, however, tend to use the words XIB and NIB
interchangeably.)
When the application runs and the user interface is needed, the NIB file is loaded
and the view objects are unarchived. At this point, the objects truly exist and can
be shown on screen and interacted with in code.
Showing the Window
At this point, it would be great to run the application and see your objects
unarchived and put on screen. However, if you run RandomPassword, all you will
see is a disappointingly empty window (Figure 1.17).
Give it a try. Use the keyboard shortcut Command-R to build and run the app.
Figure 1.17 Empty window with the wrong title

Not only is the window empty, it also has the wrong title. This is not your
window. It is a window that the template created for the AppDelegate controller
to control. To understand why this is the window you are seeing, let’s look at the
app’s structure, diagrammed in Figure 1.18.
Figure 1.18 window of the AppDelegate is shown
You can see two problems. The system works with the AppDelegate to launch
and manage the application. The AppDelegate only knows about its window.
Thus, the window you created in MainWindowController.xib is not shown to the
user. Figure 1.19 shows the structure that will put your window on screen.
Figure 1.19 window of the MainWindowController is shown

You can make this happen in four steps:


deleting the AppDelegate’s window
giving the AppDelegate a reference to an instance of MainWindowController
creating an instance of MainWindowController and associating it with
MainWindowController.xib when the application is launched

showing the MainWindowController’s window after the controller has been


created
First, return to the project navigator and open MainMenu.xib. In the document
outline, select RandomPassword, which is the window shown in the canvas. Press
Delete to delete this window from the file and the application.
Next, open AppDelegate.swift. The editor area will switch to displaying Xcode’s
code editor. You are going to write some Swift code, but do not worry about
understanding the syntax. You will learn more about Swift in the next chapter.
For now, just type in what we show you.
In AppDelegate.swift, delete the line that references an NSWindow object. (This is
a reference to the window that you just deleted in MainMenu.xib.) Then add a line
that references an instance of MainWindowController.
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!

var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Insert code here to initialize your
application
}
...
In the listing above, the code to be added is in bold and the code to be deleted is
struck-through. Code that is neither bold nor struck-through is existing code for
you to use as a landmark. These are conventions we will follow throughout the
book.
Finally, add code to the applicationDidFinishLaunching(_:) method that
creates an instance of MainWindowController and shows its window.
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Create a window controller with a XIB file
of the same name
let mainWindowController =
MainWindowController(windowNibName:

"MainWindowController")
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}
...
In the first line, you create an instance of MainWindowController and give it the
name of the file that contains the window that you want this window controller
to control.
In the next line, you call the method showWindow(_:). This puts the window
controller’s window on screen and makes it ready for the user to interact with.
The window is the root of a hierarchy of views, and you only have to put the
window on screen to ensure that all of the views in the window’s hierarchy are
put on screen.
In the third line, you store the window controller that you created in the
AppDelegate’s mainWindowController property. The local variable has the same
name as the property and, within the method, you access the property using the
keyword self to distinguish between the property and the local variable.
You may be wondering why you do not immediately save the window controller
to the property. You could do that, but we consider it a best practice in Swift to
create an object and complete its initial work before saving the object to a
property. Showing the window is essential to setting up a window controller, so
you do this with the local variable before setting the property.
You “shadow” the property in this way to make it clear that the instance you
created is intended to be stored in the property of the same name.
Run the app (Command-R), and you will see your view objects. If you click the
button, it will flash blue, but nothing else will happen. Your view objects exist,
but they cannot do much by themselves. You need to connect them to the
window controller to make the magic happen.
Making Connections
Earlier, we described a XIB file as objects encoded as XML. But view objects
alone do not create a user interface. You need connections to the controller layer
so that a controller can execute the appropriate response to user actions. These
connections will also be archived in the XIB file.
A connection lets two objects communicate. There are two kinds of connections
that you can make in Interface Builder: outlets and actions. An outlet is a reference to
a view object. An action is the name of the method that you want executed when
a view object is interacted with.
In RandomPassword, you need two connections between the MainWindowController
and other objects – an outlet that will point to the text field and an action that
names the method to be triggered when the button is clicked.

Creating an outlet
Open MainWindowController.swift and add the following code. (Again, just type
the code in for now; you will learn how it works in the next chapter.)
class MainWindowController: NSWindowController {

@IBOutlet weak var textField: NSTextField!

override func windowDidLoad() {


super.windowDidLoad()

// Implement this method to handle any


initialization after your
// window controller's window has been loaded
from its nib file
}

}
This code creates an outlet named textField in the MainWindowController class.
However, this outlet does not yet reference an object. The @IBOutlet qualifier
on textField tells Xcode that you are going to connect this outlet to an object
using Interface Builder. Let’s do that now.

Connecting an outlet
Reopen MainWindowController.xib. In the document outline, under the Placeholders
heading, find the File's Owner object. In this XIB file, File's Owner stands for an
instance of MainWindowController. Why do you need a placeholder? In
Interface Builder, there is no existing instance of MainWindowController, and there
will not be until the application has finished launching. To make a connection to
the MainWindowController, you connect to File's Owner.
Right-click (or Control-click) on File's Owner to reveal the connections panel.
Within this panel, find the Outlets section (Figure 1.20).
Figure 1.20 Outlets exposed in connections panel

The first outlet is named textField, and it is the outlet that you just added to the
MainWindowController class. The second outlet, named window, is an outlet that
MainWindowController inherits from its superclass, NSWindowController.
Unlike the textField outlet, the window outlet is already connected – it
references the window object in this XIB file. The template connected the window
outlet when it created MainWindowController.xib along with the
MainWindowController class.

To connect the textField outlet, drag from the empty circle next to textField in
the connections panel to the text field object in the canvas (Figure 1.21). When
you release the mouse, the connection will be made.
Figure 1.21 Dragging from connections panel to text field

Now your code can use the textField outlet to access the text field object that
will be unarchived from the XIB file when the application is launched.

Defining an action method


Next you need to connect the button to its action – the name of the method that a
click of the button will trigger. First, let’s write an appropriate method.
Return to MainWindowController.swift and add the following method to the
MainWindowController class. Eventually, this method will generate a random
password, but for now, it will just tell the text field to display the string "button
clicked".
import Cocoa

class MainWindowController: NSWindowController {


@IBOutlet weak var textField: NSTextField!

override func windowDidLoad() {


super.windowDidLoad()

// Implement this method to handle any


initialization after your
// window controller's window has been loaded
from its nib file
}

@IBAction func generatePassword(sender: AnyObject)


{
// Tell the text field what to display
textField.stringValue = "button clicked"
}

}
Adding @IBAction to generatePassword(_:) tells Xcode that this method will be
the action of some object and that you will assign this action in Interface Builder.

Connecting actions
The action of a button has a partner: the target of the button. The action is the
name of the method to be executed, and the target is the object whose class
implements that method.
To connect the action of a button in Interface Builder, you Control-drag from the
button to its target. For your button, you know that the method to be executed is
generatePassword(_:). Thus, the target is the instance of MainWindowController.

Reopen MainWindowController.xib and select the button in the canvas. Control-


drag from the button to File's Owner (standing in for the MainWindowController) in
the document outline, as shown in Figure 1.22. When you release the mouse, a
connections panel will appear showing three possible actions. Select
generatePassword: to make the connection.
Figure 1.22 Control-dragging from button to target

Now when the user clicks the Generate Password button, the generatePassword(_:)
method will be executed.
You may have noticed that the action is generatePassword:, and the name of the
method is generatePassword(_:). This is not sloppy typing on our part. We will
explain the reason for the difference in Chapter 3.
Take another look at the object diagram for RandomPassword (Figure 1.23) to see
what you have accomplished. Your view layer is now correctly connected to the
controller layer, and these connections are archived in
MainWindowController.xib.

Figure 1.23 RandomPassword so far


With your connections made, you can run RandomPassword and see some action.
Click the button, and the text field will report that the button was clicked
(Figure 1.24).
Figure 1.24 Clicking the button triggers text display

If the app does not build or does not run as you expect, check your connections
in MainWindowController.xib. You can do this in the connections inspector.
Select File's Owner in the document outline. Then, in the inspector pane, select the
icon to reveal the connections inspector. This inspector displays the
connections that you created. If you mouse over a connection, it will highlight
the connected object in the canvas. Your connections should look like
Figure 1.25:
Figure 1.25 Confirming connections are correct in the connections
inspector
If you made a mistake, you can delete the bad connection by clicking the x on the
connection and remake it as described earlier.
Now that the connections between the controller and view layers are complete, it
is time to turn to the model layer and write some password-generating code.
Creating the Model Layer
In this section, you are going to create a new file that will contain the code that
creates a random string. The MainWindowController will receive input from the
view layer (a click of the button), call a function in the model layer to do the
real-world work (generate a random string), and then update the view layer
(display the string in the text field).
In the project navigator, Control-click the RandomPassword group and select New
File.... From the Source section under OS X, choose Swift File (Figure 1.26). Save the
file as GeneratePassword in the default location.
Figure 1.26 Creating a new Swift file

Xcode will open the GeneratePassword.swift file. At the top of the file, add an
array named characters. This is the set of characters from which you will
randomly select to create a string.
import Foundation

private let characters =


Array("0123456789abcdefghijklmnopqrstuvwxyz" +

"ABCDEFGHIJKLMNOPQRSTUVWXYZ")
(You may be wondering why you have divided the characters into two strings to
create the array. There is no programming logic here – only publishing logic.
Using two strings lets us show you code that compiles and fits within the
margins of the printed book. Feel free to use a single string instead.) Next, you
are going to write two functions that will work together to create a random
string:
generateRandomCharacter()

returns a randomly-chosen character from the characters array


generateRandomString()

calls generateRandomCharacter() multiple times and concatenates the results to


create a string of the requested length
In GeneratePassword.swift, add the two functions:
private let characters =
Array("0123456789abcdefghijklmnopqrstuvwxyz" +

"ABCDEFGHIJKLMNOPQRSTUVWXYZ")

func generateRandomString(length: Int) -> String {


// Start with an empty string
var string = ""

// Append 'length' number of random characters


for index in 0..<length {
string.append(generateRandomCharacter())
}

return string
}

func generateRandomCharacter() -> Character {


// Create a random index into the characters array
let index =
Int(arc4random_uniform(UInt32(characters.count)))

// Get and return a random character


let character = characters[index]
return character
}
Your model layer is complete. It contains some “data” in the characters array,
and it contains functions that operate on the data. Both parts of the layer work
together to solve the problem of creating a random password.
Connecting the Model Layer to the Controller
In MainWindowController.swift, update generatePassword(_:) to call
generateRandomString() with a constant length.
import Cocoa

class MainWindowController: NSWindowController {

@IBOutlet weak var textField: NSTextField!

override func windowDidLoad() {


super.windowDidLoad()

// Implement this method to handle any


initialization after your
// window controller's window has been loaded
from its nib file
}

@IBAction func generatePassword(sender: AnyObject)


{
// Get a random string of length 8
let length = 8
let password = generateRandomString(length)

// Tell the text field display the string


textField.stringValue = password

textField.stringValue = "button clicked"


}

}
Run the app, click the button, and generate a password. Notice that you can copy
and paste the password. This functionality comes for free in the NSTextField
class and is a good example of how Cocoa classes are well-designed to assist
developers in building apps.
If your application will not build or if it does not run as you expect, check for
typos in both Swift files. See if the compiler is reporting any errors and compare
your code with the code in the book.
Improving Controller Design
Currently, in AppDelegate.swift, you create a window controller and pass in a
string – "MainWindowController". The string is the name of a NIB file. When
the project is launched, the window controller is created and the objects in the
named NIB file are unarchived along with their connections.
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {

// Create a window controller with a XIB file


of the same name
let mainWindowController =
MainWindowController(windowNibName:

"MainWindowController")
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}
...
Right now, an instance of MainWindowController does not know the name of the
NIB file that contains its user interface. Only the AppDelegate has this
information, which is not the best design because the NIB file is really the
business of the MainWindowController. Thus, you are going to move this
information into the MainWindowController class.
First, in AppDelegate.swift, remove the argument from the call that creates the
instance of MainWindowController.
...
class AppDelegate: NSObject, NSApplicationDelegate {

var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {

// Create a window controller with a XIB file


of the same name
let mainWindowController =
MainWindowController(windowNibName:

"MainWindowController")
let mainWindowController =
MainWindowController()
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}
...
Now when the MainWindowController initializer is called without an argument,
the value of the window controller’s windowNibName variable will be used instead.
MainWindowController inherits this variable from NSWindowController, and you
can override it to return the name of the appropriate file.
In MainWindowController.swift, override windowNibName to return the name of
your XIB (NIB) file.
class MainWindowController: NSWindowController {
@IBOutlet weak var textField: NSTextField!

override var windowNibName: String? {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()
}

...

}
Run the app again. It should work the same, but your MainWindowController
class no longer relies on another object to know its business.
Many tutorials that build simple apps would have only used one controller – the
AppDelegate. This may be a simpler way to start, but we find it misleading. The
AppDelegate is not intended to control a user interface or to coordinate an app’s
model and view layers. Soon, you will be creating apps with multiple windows
and windows that can have multiple view controllers. So we believe that it is
best to start with a solid architecture on which you can build.
Your first Cocoa application is now complete! In the next chapter, you will learn
a little more about the Swift language.
2
Swift Types
In the next three chapters, you are going to focus on learning the Swift language.
You will not learn everything you want to know about Swift, but you will learn
enough to get started. Then, as you continue through this book, you will learn
more Swift as you are learning Cocoa development.
If you do not think you will be comfortable picking up Swift at the same time as
Cocoa development, you may want to read Apple’s Swift tutorials first. You can
find them at developer.apple.com/swift. Or you can start with Swift Programming:
The Big Nerd Ranch Guide (available Summer 2015).
But if you have some programming experience and are willing to learn as you
go, then you can start your Swift education here.
Introducing Swift
Swift is a new language that Apple introduced in 2014. It replaces Objective-C
as the recommended development language for Cocoa and iOS.
Swift maintains the expressiveness of Objective-C while introducing a syntax
that is safer as well as succinct and readable. It emphasizes type safety and
introduces advanced features such as optionals, generics, and sophisticated
structures and enumerations.
Most importantly, Swift allows the use of these new features while relying on
the same tested, elegant Cocoa frameworks that developers have built upon for
years.
If you know Objective-C, then the challenge is recasting what you know. It may
seem awkward at first, but we have come to love Swift at Big Nerd Ranch and
believe you will, too.
Types in Swift
Swift types can be arranged into three basic groups: structures, classes, and
enumerations. There are other types that fall outside of these three, such as
closures, but these are the building blocks of Swift. All three can have
properties: values associated with a type
initializers: similar to a function, code that initializes an instance of a type
instance methods: functions specific to a type that can be called on an
instance of that type
class or static methods: functions specific to a type that can be called on
the type itself
Figure 2.1 Swift building blocks

Swift’s structures and enumerations are significantly more powerful than in most
languages. In addition to supporting properties, initializers, and methods, they
can also conform to protocols and can be extended.
Swift’s implementation of typically “primitive” types such as numbers and
boolean values may surprise you: they are all structures, as are the commonly
used string and collection types provided by the Swift standard library.
Numbers: Int, Float, Double

Boolean: Bool

Text: String, Character

Collections: Array<T>, Dictionary<K:Hashable,V>, Set<T:Hashable>


This means that standard types have properties, initializers, and methods of their
own and can conform to protocols and be extended.
own and can conform to protocols and be extended.
Finally, a key feature of Swift is optional types. An optional allows you to store
either a value of a particular type or no value at all. For now, you will focus
solely on non-optionals. Later in the chapter, you will learn more about
optionals.
Using Standard Types
In this section, you are going to experiment with standard types in an Xcode
playground. A playground lets you write code and see the results without the
overhead of manually running an application and checking the output.
From Xcode’s window, select File → New → Playground.... You can accept the default
name for this file; you will only be here briefly. Make sure the platform is OS X.
Figure 2.2 Configuring a playground

Click Next and save this file where you saved the RandomPassword project that you
created in Chapter 1.
When the file opens, notice that the playground is divided into two sections: The
larger white area to the left is the editor where you write code. The gray column
on the right is the sidebar. The playground compiles your code after every line
and shows the results in the sidebar.
Figure 2.3 A playground
In the example code, the var keyword denotes a variable, so the value of str can
be changed from its initial value. Type in the code below to change the value of
str, and you will see the results appear in the sidebar to the right.
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift"
(Notice that we are showing sidebar results to the right of the code for the
benefit of readers who are not actively doing the exercise.)
The let keyword denotes a constant value, which cannot be changed. Add a
constant to the mix:
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift
let constStr = str
"Hello, Swift"
Because constStr is a constant, attempting to change its value will cause an
error.
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift"
let constStr = str
"Hello, Swift"
constStr = "Hello, world"
An error in the playground code will prevent you from seeing any further results
in the sidebar, so you usually want to address it right away. Remove the line that
attempts to change the value of constStr.
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift"
let constStr = str
"Hello, Swift"
constStr = "Hello, world"
In your Swift code, you should use let unless you expect the value will need to
change.

Inferring types
At this point, you may have noticed that neither the constant nor the variable has
a specified type. This does not mean they are untyped! Instead, the compiler
infers their types from the initial values. This is called type inference.
You can find out what type was inferred using Quick Help. Option-click on
constStr to see the Quick Help information for this constant.

Figure 2.4 constStr is of type String

Option-clicking to reveal Quick Help will work for any symbol.

Specifying types
If your constant or variable has an initial value, you can rely on type inference. If
a constant or variable does not have an initial value or if you want to ensure that
it is a certain type, you can specify the type in the declaration.
Add more variables with specified types.
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift"
let constStr = str
"Hello, Swift"

var nextYear: Int


var bodyTemp: Float
var hasPet: Bool
Note that the sidebar does not report any results because these variables do not
yet have values.
Let’s go over these new types and how they are used.

Number and boolean types

The most common type for integers is Int. There are additional integer types
based on word size and signedness, but Apple recommends using Int unless you
really have a reason to use something else.
For floating-point numbers, Swift provides three types with different levels of
precision: Float for 32-bit numbers, Double for 64-bit numbers, and Float80 for
80-bit numbers.
A boolean value is expressed in Swift using the type Bool. A Bool can be either
true or false.

Collection types

The Swift standard library offers three collections: arrays, dictionaries, and sets.
An array is an ordered collection of elements. The array type is written as
Array<T>, where T is the type of element that the array will contain. This type
can be any kind of type: a standard type, a structure, or a class.
Add a variable for an array of integers:
...
var hasPet: Bool
var arrayOfInts: Array<Int>
Arrays are strongly-typed. Once you have declared an array as containing
elements of, say, Int, you cannot add a String to this array.
There is a shorthand syntax for declaring arrays: you can simply use square
brackets around the type that the array will contain. Update the declaration of
arrayOfInts to use the shorthand:
...
var hasPet: Bool
var arrayOfInts: Array<Int>
var arrayOfInts: [Int]
A dictionary is an unordered collection of key-value pairs. The values can be of
any type, including structures and classes. The keys can be of any type as well,
but they must be unique. Specifically, the keys must be hashable, which allows
the dictionary to guarantee that the keys are unique as well as access the value
for a given key more efficiently. Basic Swift types such as Int, Float, Character,
and String are all hashable.
Like Swift arrays, Swift dictionaries are strongly-typed and can only contain
keys and values of the declared type. For example, you might have a dictionary
that stores capital cities by country. The keys for this dictionary would be the
country names and the values would be the city names. Both keys and values
would be strings. Add a variable for such a dictionary:
...
var arrayOfInts: [Int]
var dictionaryOfCapitalsByCountry:
Dictionary<String,String>
There is a shorthand syntax for declaring dictionaries, too. Update
dictionaryOfCapitalsByCountry to use the shorthand:
...
var arrayOfInts: [Int]
var dictionaryOfCapitalsByCountry:
Dictionary<String,String>
var dictionaryOfCapitalsByCountry: [String:String]
A set is similar to an array in that it contains a number of elements of a certain
type. However, sets are unordered and the members must be unique as well as
hashable. The unorderedness of sets makes them faster when you simply need to
determine whether something is a member of a set. Add a variable for a set:
var winningLotteryNumbers: Set<Int>

Literals and subscripting


Standard types can be assigned literal values, or literals. For example, str is
assigned the value of a string literal. A string literal is formed with double
quotes. Contrast the literal value assigned to str with the non-literal value
assigned to constStr.
var str = "Hello, playground"
"Hello, playground"
str = "Hello, Swift"
"Hello, Swift"
let constStr = str
"Hello, Swift"
Add two number literals to your playground:
let number = 42
42
let fmStation = 91.1
91.1
Arrays and dictionaries can be assigned literal values as well. The syntax for
creating literal arrays and dictionaries resembles the shorthand syntax for
specifying these types.
let countingUp = ["one", "two"]
["one", "two"]
let nameByParkingSpace = [13: "Alice", 27: "Bob"]
[13: "Alice", 27: "Bob"]
Swift also provides subscripting as shorthand for accessing arrays. To retrieve an
element in an the array, you provide the element’s index in square brackets after
the array name.
let countingUp = ["one", "two"]
["one", "two"]
let secondElement = countingUp[1]
"two"
...
Notice that index 1 retrieves the second element; an array’s index always starts
at 0.
When subscripting an array, be sure that you are using a valid index. Attempting
to access an out of bounds index results in a trap. A trap is a runtime error that
stops the program before it gets into an unknown state. We will cover runtime
errors in more detail in the next chapter.
Subscripting also works with dictionaries – more on that later in this chapter.

Initializers
So far, you’ve initialized your constants and variables using values. But you can
also create new instances of a specific type. An instance is a particular
embodiment of a type. Historically, this term has been only used with classes,
but in Swift it is used to describe structures, too. For example, the constant
secondElement holds an instance of String.

To create a new instance of a type, you use the type name followed by a pair of
parentheses and, if required, arguments. This signature – the combination of type
and arguments – corresponds to an initializer. Initializers are responsible for
initializing the contents of a new instance of a type. When an initializer is
finished, the instance is ready for action.
Some standard types have initializers that return empty literals when no
arguments are supplied. Add an empty string and an empty array to your
playground.
let emptyString = String()
""
let emptyArrayOfInts = [Int]()
0 elements
let emptySetOfFloats = Set<Float>()
0 elements
Other types have default values:
let defaultNumber = Int()
0
let defaultBool = Bool()
false
Types can have multiple initializers. For example, String has an initializer that
accepts an Int and creates a string based on that value.
let number = 42
42
let meaningOfLife = String(number)
"42"
To create a set, you use the Set initializer that accepts an array literal:
let availableRooms = Set([205, 411, 412])
{412, 205, 411}
Float has several initializers. The parameter-less initializer returns an instance of
Float with the default value. There is also an initializer that accepts a floating-
point literal.
let defaultFloat = Float()
0.0
let floatFromLiteral = Float(3.14)
3.14
If you use type inference for a floating-point literal, the type defaults to Double.
Create the following constant with a floating-point literal.
let easyPi = 3.14
3.14
Use the Float initializer that accepts a Double to create a Float from this Double.
let easyPi = 3.14
3.14
let floatFromDouble = Float(easyPi)
3.14
You can achieve the same result by specifying the type in the declaration.
let easyPi = 3.14
3.14
let floatFromDouble = Float(easyPi)
3.14
let floatingPi: Float = 3.14
3.14

Properties
A property is a value associated with an instance of a type. String has the
property isEmpty, which is a Bool that tells you whether the string is empty.
Array<T> has the property count, which is the number of elements in the array as
an Int. Access these properties in your playground:
let emptyString = ""
emptyString.isEmpty
true

...

var countingUp = ["one", "two"]


["one", "two"]
let secondElement = countingUp[1]
"two"
countingUp.count
2
You will learn much more about properties in the next chapter.

Instance methods
An instance method is a function that is specific to a particular type and can be
called on an instance of that type. Try out the append(_:) and reverse() instance
methods from Array<T>:
countingUp.append("three")
["one", "two", "three"]
let countingDown = countingUp.reverse()
["three, "two", "one"]
The append(_:) method accepts an element of the array’s type and adds it to the
end of the array. The reverse() method returns a new array of the same type but
with the order of the elements reversed.
We will discuss methods, including naming, in the next chapter.
Optionals
Swift has a generic optional type, Optional<T>. In practice, an optional is
indicated by appending ? to a type name:
var anOptionalFloat: Float?
var anOptionalArrayOfStrings: [String]?
var anOptionalArrayOfOptionalStrings: [String?]?
An optional lets you express the possibility that a variable may not store a value
at all. The value of an optional will either be an instance of the specified type or
nil.

Throughout the book, you will have many chances to use optionals. What
follows is an example to get you familiar with the syntax so that you can focus
on the use of the optionals later.
Imagine a group of instrument readings.
var reading1: Float
var reading2: Float
var reading3: Float
Sometimes, an instrument might malfunction and not report a reading. You do
not want this malfunction showing up as, say 0.0; you want it to be something
completely different that tells you to check your instrument or takes some other
action.
You can do this by declaring the readings as optionals:
var reading1: Float?
nil
var reading2: Float?
nil
var reading3: Float?
nil
As an optional float, each reading can contain either a Float or nil. If not given
an initial value, then the value defaults to nil.
You can assign values to an optional just like any other. Assign floating-point
literals to the readings:
reading1 = 9.8
9.8
reading2 = 9.2
9.2
reading3 = 9.7
9.7
However, you cannot use these optional floats like non-optional floats – even if
they have been assigned Float values. Before you can read the value of an
optional variable, you must address the possibility of its value being nil. This is
called unwrapping the optional.
You are going to try out two ways of unwrapping an optional variable: optional
binding and forced unwrapping. You will implement forced unwrapping first.
This is not because it is the better option; in fact, it is the less safe one, but
implementing forced unwrapping first will let you see the dangers and
understand why optional binding is typically better.
To forcibly unwrap an optional, you append a ! to its name. First, try averaging
the readings as if they were non-optional variables:
reading1 = 9.8
9.8
reading2 = 9.2
9.2
reading3 = 9.7
9.7
let avgReading = (reading1 + reading2 + reading3) / 3
This results in an error because optionals require unwrapping. Forcibly unwrap
the readings to make the error go away:
let avgReading = (reading1 + reading2 + reading3) / 3
let avgReading = (reading1! + reading2! + reading3!) /
3 9.56667
Everything looks fine, and you see the correct average in the sidebar. But a
danger lurks in your code. When you forcibly unwrap an optional, you tell the
compiler that you are sure that the optional will not be nil and can be treated as
if it were a normal Float. But what if you are wrong? To find out, comment out
the assignment of reading3, which will return it to its default value, nil.
reading1 = 9.8
9.8
reading2 = 9.2
9.2
reading3 = 9.7
// reading3 = 9.7
You now have an error. To see the console where the error is reported in a
playground, you must open the assistant editor. From Xcode’s View menu, select
Assistant Editor → Show Assistant Editor. Or use the keyboard shortcut Option-
Command-Return. The error reads
fatal error: unexpectedly found nil while unwrapping
an Optional value
If you forcibly unwrap an optional and that optional turns out to be nil, it will
cause a trap, stopping your application.
A safer way to unwrap an optional is optional binding. Optional binding works
within a conditional if-let statement: You assign the optional to a temporary
constant of the corresponding non-optional type. If your optional has a value,
then the assignment is valid and you proceed using the non-optional constant. If
the optional is nil, then you can handle that case with an else clause.
Change your code to use an if-let statement that tests for valid values in all
three readings.
let avgReading = (reading1! + reading2! + reading3!) /
3
if let r1 = reading1,
let r2 = reading2,
let r3 = reading3 {
let avgReading = (r1 + r2 + r3) / 3
} else {
let errorString = "Instrument reported a reading
that was nil."
}
reading3 is currently nil, so its assignment to r3 fails, and the sidebar shows the
error string.
To see the other case in action, restore the line that assigns a value to reading3.
Now that all three readings have values, all three assignments are valid, and the
sidebar updates to show the average of the three readings.
Subscripting dictionaries
Recall that subscripting an array beyond its bounds causes a trap. Dictionaries
are different. The result of subscripting a dictionary is an optional:
let nameByParkingSpace = [13: "Alice", 27: "Bob"]
[13: "Alice", 27: "Bob"]
let spaceAssignee: String? = nameByParkingSpace[13]
"Alice"
If the key is not in the dictionary, the result will be nil. As with other optionals,
it is common to use if-let when subscripting a dictionary:
let spaceAssignee: String? = nameByParkingSpace[13]
if let spaceAssignee = nameByParkingSpace[13] {
println("Key 13 was in the dictionary!")
}
Loops and String Interpolation
Swift has all the familiar control flow statements: if-else, which you saw
above, while, for, for-in, do-while, and switch. While they are familiar, there
may be some differences from what you are accustomed to. The key differences
with C-like languages is that while enclosing parentheses are not necessary on
these statements’ expressions, braces on clauses are necessary. Additionally, the
expressions for if and while-like statements must evaluate to a Bool.
Consider this very traditional C-style loop, written in Swift:
for var i = 0; i < countingUp.count; i++ {
let string = countingUp[i]
// Use 'string'.
}
You could do the same thing a little more cleanly using Swift’s Range type and
the for-in statement:
let range = 0..<countingUp.count
for i in range {
let string = countingUp[i]
// Use 'string'.
}
The most direct route would be to enumerate the items in the array themselves:
for string in countingUp {
// Use 'string'.
}
What if you wanted the index of each item in the array? Swift’s enumerate()
function returns a sequence of integers and values from its argument:
for (i, string) in enumerate(countingUp) {
// (0, "one"), (1, "two")
}
What are those parentheses, you ask? The enumerate() function actually returns
a sequence of tuples. A tuple is an ordered grouping of values, similar to an
array, except each member may have a distinct type. In this example the tuple is
of type (Int, String). We will not spend much time on tuples in this book as
they are not used in Cocoa APIs (Objective-C does not support tuples). They can
be useful in your Swift code, however.
Another application of tuples is in enumerating the contents of a dictionary:
let nameByParkingSpace = [13: "Alice", 27: "Bob"]

for (space, name) in nameByParkingSpace {


let permit = "Space \(space): \(name)"
}
Did you notice that curious markup in the string literal? That is Swift’s string
interpolation. Expressions enclosed within \( and ) are evaluated and inserted
into the string at runtime. In this example you are using local variables, but any
valid Swift expression, such as a method call, can be used.
To see the values of the permit variable in the playground, click the circular Value
History button in the sidebar, then click the All values segment, as shown in
Figure 2.5. This can be very useful for visualizing what is happening in your
playground code’s loops. You will see another application of this in the next
chapter.
Figure 2.5 Using the Value History to see the results of string interpolation
Enumerations and the Switch Statement
An enumeration (“enum” for short) is a type with a discrete set of values. Define
an enum describing pies:
enum PieType {
case Apple
case Cherry
case Pecan
}

let favoritePie = PieType.Apple


When the compiler expects the enum type, you can skip the full type name. For
example:
var piesToBake: [PieType] = []
piesToBake.append(.Apple)
Swift has a powerful switch statement which, among other things, is great for
matching on enum values:
let name: String
switch favoritePie {
case .Apple:
name = "Apple"
case .Cherry:
name = "Cherry"
case .Pecan:
name = "Pecan"
}
The cases for a switch statement must be exhaustive: each possible value of the
switch expression must be accounted for, or a default: case used. Unlike C,
Swift switch cases do not fall through. You must explicitly request this using the
fallthrough keyword.
Switch statements can match on many types, even ranges:
let osxVersion: Int = ...
switch osxVersion {
case 0...8:
case 0...8:
println("A big cat")
case 9:
println("Mavericks")
case 10:
println("Yosemite")
default:
println("Greetings, people of the future! What's
new in 10.\(osxVersion)?")
}
For more on the switch statement and its pattern matching capabilities, see the
Control Flow section in the The Swift Programming Language guide.

Enumerations and raw values


Swift enums can have raw values associated with their cases:
enum PieType: Int {
case Apple = 0
case Cherry
case Pecan
}
With the type specified, you can ask an instance of PieType for its rawValue and
then initialize the enum type with that value. This returns an optional, since the
raw value may not correspond with an actual case of the enum.
let pieRawValue = PieType.Pecan.rawValue
if let pieType = PieType(rawValue: pieRawValue) {
// Got a valid 'pieType'!
}
This technique is demonstrated in Chapter 24.
There is more to enumerations. Each case of an enumeration can have associated
values, similar to a tuple. You will learn more about associated values in
Chapter 28.
Exploring Apple’s Swift Documentation
To explore Apple’s documentation on Swift, start at developer.apple.com/swift. Here
are two particular resources to look for. We suggest bookmarking them and
visiting them when you want to review a particular concept or dig a little deeper.
The Swift Programming Language
This guide describes many features of Swift. It starts with the basics and
includes example code and lots of detail. It also contains the language reference
and formal grammar of Swift.
The Swift Standard Library Reference
The standard library reference lays out the details of Swift types, protocols, and
global, or free, functions.
(Apple also maintains documentation for the Cocoa frameworks, which you will
learn about in Chapter 5.)
Your homework is to browse through the Types section of the standard library
reference and The Basics, Strings and Characters, and Collection Types sections
of the language guide. Solidify what you learned in this chapter and become
familiar with what kind of information these resources offer. If you know where
you can find the details when you need them, then you will feel less pressure to
memorize them instead of focusing on Cocoa development.
3
Structures and Classes
At this point you should be somewhat familiar with using Swift’s standard types:
strings, arrays, enums, etc. It is time to move on to bigger and better things:
defining your own types. In this chapter, you will build a simple 2D physics
simulation. You will create your own structure and a few classes, and you will
learn about the differences between them.
Structures
In Cocoa, structures are typically used to represent groupings of data. For
example, there is NSPoint, which represents a point in 2D space with an X and a
Y value. As your first structure you will create a 2D vector structure.
Create a new playground. From Xcode’s File menu, select New... → Playground. Name
the playground Physics and save it with the rest of your projects.
Start by defining the Vector structure:
import Cocoa

struct Vector {
var x: Double
var y: Double
}
Much like C structures, Swift structures are composite data types. They are
composed of one or more fields, or properties, each of which has a specified
type. A few lines down, create an instance of Vector and access its properties:
let gravity = Vector(x: 0.0, y: -9.8) // {x 0, y
-9.800000000000001}
gravity.x // 0
gravity.y //
-9.800000000000001
You just used Swift’s automatic initializer to create an instance of this structure.
The automatic initializer has a parameter for each property in the structure. If
you were to add a z field, this code would cause a compiler error because it lacks
a z parameter. (Do not worry about the zeros; that is just typical floating point
fun.)
You can provide your own initializers, but when you do, the automatic initializer
is no longer provided. Go back to Vector and add an initializer that takes no
parameters and initializes x and y to 0.
struct Vector {
var x: Double
var y: Double
init() {
x = 0
y = 0
}
}
Initializers in Swift use the init keyword, followed by the parameter list, and
then the body of the initializer. Within the body, the x and y properties are
assigned directly.
An initializer must initialize all of the properties of its structure.
As we warned, defining this initializer has caused the automatic one to vanish,
causing an error in the playground. You can easily define it manually, however:
struct Vector {
var x: Double
var y: Double

init() {
x = 0
y = 0
}

init(x: Double, y: Double) {


self.x = x
self.y = y
}
}
A Swift programmer would say that this initializer takes two parameters, x and y,
both of type Double.
What is self? It represents the instance of the type that is being initialized.
Using self.propertyName is usually unnecessary (you did not use it in init()),
but because the initializer’s parameter names match the names of the properties
you must use self to tell the compiler that you mean the property and not the
parameter.
Before continuing, let’s make an improvement. As the Vector structure stands,
its two initializers have independent code paths. It would be better to have them
use one code path by having the parameterless initializer call the initializer
which takes both x and y.
struct Vector {
var x: Double
var y: Double

init() {
x = 0
y = 0
self.init(x: 0, y: 0)
}

init(x: Double, y: Double) {


self.x = x
self.y = y
}
}
A single code path for initialization is not required for structures, but it is a good
habit to get into as you will use it when working with classes.

Instance methods
Methods allow you to add functionality to your data types. In Swift, you can add
methods to structures as well as classes (and enums!). Instance methods operate
within the context of a single instance of the type. Add an instance method for
multiplying a vector by a scalar:
struct Vector {
...

init(x: Double, y: Double) {


self.x = x
self.y = y
}

func vectorByAddingVector(vector: Vector) ->


Vector {
return Vector(x: self.x + vector.x,
y: self.y + vector.y)
}
}
The func keyword in the context of a structure indicates that this is a method. It
takes a single parameter of type Double and returns an instance of Vector.
Try this new method out:
let gravity = Vector(x: 0.0, y: -9.8) // {x 0, y
-9.800000000000001}
gravity.x
gravity.y
let twoGs = gravity.vectorByAddingVector(gravity) //
{x 0, y -19.6}
What is the name of this method? In conversation you would call it
vectorByAddingVector, but in this text we include parameters, like this:
vectorByAddingVector(_:). By default, the first parameter of a method is not
named – thus the underscore.
Why not name the first parameter? Because the convention – inherited from
Objective-C and Cocoa – is that the base name of the method includes the name
of the first parameter, in this case Vector. Suppose you added another parameter
to that method. What would it look like?
func vectorByAddingVector(vector: Vector,
numberOfTimes: Int) -> Vector {
var result = self
for _ in 0..<numberOfTimes {
...
This method would be called vectorByAddingVector(_:numberOfTimes:). Note
that there is a colon for each parameter.
This can lead to verbose method names, but the code actually becomes very
readable. No guessing or relying on the IDE to tell you what the third parameter
is!
By default, each parameter’s internal name is the same as its external name
(except the first parameter, that is). In
vectorByAddingVector(_:numberOfTimes:), the second parameter is named
numberOfTimes. That is certainly very descriptive, but you might prefer to use a
shorter name (like times) within the method. In that case you would explicitly
set the internal parameter name like this:
func vectorByAddingVector(vector: Vector,
func vectorByAddingVector(vector: Vector,
numberOfTimes times: Int) -> Vector {
var result = self
for _ in 0..<times {
...
The method’s signature has not changed. For those calling it, its name is still
vectorByAddingVector(_:numberOfTimes:), but internally you have the
satisfaction of using the name you want.

Using self in instance methods

As in initializers, self represents the instance that the method is being called on.
As long as there is no conflict with named parameters or local variables,
however, it is entirely optional, so we prefer to leave it off. Make this change to
vectorByAddingVector(_:).
struct Vector {
...

func vectorByAddingVector(vector: Vector) ->


Vector {
return Vector(x: self.x + vector.x,
y: self.y + vector.y)
return Vector(x: x + vector.x,
y: y + vector.y)
}
}
Operator Overloading
By overloading operators you can make your own types work with common (and
even uncommon) operators. This ability falls deep beyond the “with great power
comes great responsibility” line. However, vectors are a natural and respectable
application for this technique.
To define an operator overload you simply add a function that takes the
appropriate types. To start with, instead of calling vectorByAddingVector(_:), it
would be nice to use the + operator. Overload + and * for adding and scaling
vectors, respectively.
struct Vector {
...
}

func +(left: Vector, right: Vector) -> Vector {


return left.vectorByAddingVector(right)
}
func *(left: Vector, right: Double) -> Vector {
return Vector(x: left.x right , y: left.y right)
}
Now you can very succinctly manipulate vectors:
let twoGs = gravity.vectorByAddingVector(gravity)
let twoGs = gravity + gravity
let twoGsAlso = gravity * 2.0
Note that the order of types for binary operators like * and + is important. In
order to write 2.0 * gravity you will need to implement another operator
overload function:
func *(left: Double, right: Vector) -> Vector {
return right * left
}
Classes
Now that you have the beginnings of a robust vector type, let’s put it to work.
Your 2D physics simulation will consist of two classes: Particle, which
represents a single moving object within the simulation, and Simulation, which
contains an array of Particle instances.
Classes are very similar to structures. They have a lot of the same features:
initializers, properties, computed properties, and methods. They have a
significant difference, however, which we will discuss once the simulation is up
and running.
Start by defining the Particle class in your playground. The position is not
important, as long as it is above or below (but not inside!) the Vector structure.
A Particle has three Vector properties: position, velocity, and acceleration.
struct Vector {
...
}

class Particle {

var position: Vector


var velocity: Vector
var acceleration: Vector

}
Classes and structures differ significantly in terms of initializers. Most
noticeably, classes do not have automatic initializers, so you will see a compiler
error: Class 'Particle' has no initializers.
Fix this by adding an initializer to Particle:
class Particle {

var position: Vector


var velocity: Vector
var acceleration: Vector
init(position: Vector) {
self.position = position
self.velocity = Vector()
self.acceleration = Vector()
}
}
You do not need to provide a parameter for every property in a class like you did
in Vector’s init(x:y:). You just need to initialize everything. As with
initializers for structures, a class’s initializer must initialize all of its properties
before returning or performing any other tasks. By requiring this of initializers
the Swift compiler guarantees that every instance is fully initialized before it is
put to work.
Another approach is to give properties default values:
class Particle {

var position: Vector
var velocity: Vector = Vector()
var acceleration: Vector = Vector()

init(position: Vector) {
self.position = position
}
}
In a simple case like this, there is not a clear benefit to either approach.

Designated and convenience initializers


Like structures, classes can have multiple initializers. At least one of them will
be the designated initializer. Remember how you refactored Vector’s init() to
call init(x:y:)? A designated initializer is an initializer which other, non-
designated initializers – convenience initializers – must call. The rule of thumb
with designated initializers is that they are typically the one with the most
parameters. Most classes will only have one designated initializer.
The init(position:) initializer is the Particle class’s designated initializer.
Add a convenience initializer:
class Particle {
...

init(position: Vector) {
self.position = position
self.velocity = Vector()
self.acceleration = Vector()
}

convenience init() {
self.init(position: Vector())
}

}
There is an exception to these designated initializer rules: required initializers,
which you will see in Chapter 12.

Add an instance method


A particle has a position, velocity, and acceleration. It should also know a little
about particle dynamics – specifically, how to update its position and velocity
over time. Add an instance method, tick(_:), to perform these calculations.
class Particle {
...

convenience init() {
self.init(position: Vector())
}

func tick(dt: NSTimeInterval) {


velocity = velocity + acceleration * dt
position = position + velocity * dt
position.y = max(0, position.y)
}

}
The tick(_:) method takes an NSTimeInterval parameter, dt, the number of
seconds to simulate. NSTimeInterval is an alias for Double.
Below the definition of Particle, define the Simulation class, which will have
an array of Particle objects and its own tick(_:) method:
class Particle {
...
}

class Simulation {

var particles: [Particle] = []


var time: NSTimeInterval = 0.0

func addParticle(particle: Particle) {


particles.append(particle)
}

func tick(dt: NSTimeInterval) {


for particle in particles {
particle.acceleration =
particle.acceleration + gravity
particle.tick(dt)
particle.acceleration = Vector()
}
time += dt
}

}
The Simulation class has no initializers defined since all of its properties have
default values. The for-in loop iterates over the contents of the particles
property. The tick(_:) method applies constant acceleration due to gravity to
each of the particles before simulating them for the time interval.
Before you warm up the simulator and add a particle, add a line to evaluate
particle.position.y. You will use this shortly with the playground’s Value
History. Additionally, add some code to remove particles once they drop below y
= 0:
class Simulation {
...

func tick(dt: NSTimeInterval) {


for particle in particles {
particle.acceleration =
particle.acceleration + gravity
particle.tick(dt)
particle.acceleration = Vector()
particle.position.y
}
time += dt
particles = particles.filter { particle in
let live = particle.position.y > 0.0
if !live {
println("Particle terminated at time \
(self.time)")
}
return live
}
}

}
The last chunk of code filters the particles array, removing any particles that
have fallen to the ground. This is a closure, and it is OK if you do not understand
it at this point. You will learn more about closures in Chapter 15.
Now you are ready to run the simulator. Create an instance of the simulator and
a particle, add the particle to the simulation, and see what happens.
class Simulation {
...
}

let simulation = Simulation()

let ball = Particle()


ball.acceleration = Vector(x: 0, y: 100)
simulation.addParticle(ball)

while simulation.particles.count > 0 &&


simulation.time < 500 {
simulation.tick(1.0)
}
You should see the playground tally up (20 times) on a number of lines. If the
playground runs the simulation continuously, you can stop it by commenting out
the while loop. Select the three lines and hit Command-/ to toggle the comment
marks:
// while simulation.particles.count > 0 &&
simulation.time < 500 {
// simulation.tick(1.0)
// }
Double-check your code against the listings above, in particular the lines that
filter the particles array and the line that increments time.
Once you have the simulation running as expected, click the Variables View circle
in the playground sidebar on the line that reads particle.position.y, as shown
in Figure 3.1. A graph will appear, showing the Y values of the particle over
time. The X axis on this graph represents iterations over time and not the X
coordinate of the particle.
Figure 3.1 Graph data history of particle.position.y
Inheritance
Suppose you wanted to simulate a particle that had different behavior than the
Particle class you have already implemented: a rocket that propels itself with
thrust over a certain period of time. Since Particle already knows about physics,
it would be natural to extend and modify its behavior through subclassing.
Define the Rocket class as a subclass of Particle.
class Rocket: Particle {

let thrust: Double


var thrustTimeRemaining: NSTimeInterval
let direction = Vector(x: 0, y: 1)

convenience init(thrust: Double, thrustTime:


NSTimeInterval) {
self.init(position: Vector(), thrust: thrust,
thrustTime: thrustTime)
}

init(position: Vector, thrust: Double, thrustTime:


NSTimeInterval) {
self.thrust = thrust
self.thrustTimeRemaining = thrustTime
super.init(position: position)
}

}
The thrust property represents the magnitude of the rocket’s thrust.
thrustTimeRemaining is the number of seconds that the thrust will be applied for.
direction is the direction that the thrust will be applied in.

Take a minute to go through the initializers you just typed in. Which is the
designated initializer? (Remember the rule of thumb about designated
initializers?)
In order to guarantee that a class’s properties are initialized, initializers are only
inherited if a subclass does not add any properties needing initialization. Thus,
Rocket provides its own initializers and calls the superclass’s designated
initializer.
Next you will override the tick(_:) method, which will do a little math to
calculate the acceleration due to thrust and apply it before calling the
superclass’s – Particle’s – tick(_:) method.
class Rocket: Particle {
...

init(position: Vector, thrust: Double, thrustTime:


NSTimeInterval) {
self.thrust = thrust
self.thrustTimeRemaining = thrustTime
super.init(position: position)
}

override func tick(dt: NSTimeInterval) {


if thrustTimeRemaining > 0.0 {
let thrustTime = min(dt,
thrustTimeRemaining)
let thrustToApply = thrust * thrustTime
let thrustForce = direction *
thrustToApply
acceleration = acceleration + thrustForce
thrustTimeRemaining -= thrustTime
}
super.tick(dt)
}

}
Finally, create an instance of Rocket and add it to the simulation in place of the
ball:
let simulation = Simulation()

let ball = Particle()


ball.acceleration = Vector(x: 0, y: 100)
simulation.addParticle(ball)
// let ball = Particle()
// ball.acceleration = Vector(x: 0, y: 100)
// simulation.addParticle(ball)

let rocket = Rocket(thrust: 10.0, thrustTime: 60.0)


simulation.addParticle(rocket)
The simulation will run for 70 “seconds” with these parameters. The Value History
shows quite a different profile! (Figure 3.2)
Figure 3.2 The rocket’s Y position over time
Note that inheritance is one key differentiator between classes and structures:
structures do not support inheritance.
Computed Properties
It is frequently useful to find a vector’s length or magnitude. You could do this
by adding a function returning a Double:
struct Vector {
...

func length() -> Double {
return sqrt(x*x + y*y)
}
}
However, it is much more natural to think of this as a read-only property. In
Swift the general term for this is computed property, which is in contrast to the
stored properties you have been using so far. A read-only computed property
version of length would look like this:
struct Vector {
...

var length: Double {
get {
return sqrt(x*x + y*y)
}
}
}
This read-only computed property pattern (called a “getter”) is so common, in
fact, that Swift provides a shorthand means of expressing it. Add this to Vector:
struct Vector {
...

var length: Double {


return sqrt(x*x + y*y)
}
}
At other times it is useful to have a getter and setter for a computed property.
This tends to be used to alias other properties or to transform a value before it is
used elsewhere. For example, you could abstract the setting of the textField
from the RandomPassword with a computed property:
class MainWindowController: NSWindowController {

@IBOutlet weak var textField: NSTextField!

var generatedPassword: String {
set {
textField.stringValue = newValue
}
get {
return textField.stringValue
}
}

...

@IBAction func generatePassword(sender: AnyObject)
{
let length = 8
generatedPassword =
generateRandomString(length)
}
}
Computed properties do not have any storage associated with them. If you need
to store a value, you must create a separate stored property for it.
Reference and Value Types
Structures and classes are far more alike in Swift than they are in most
languages. However, there is one major difference in how they operate: classes
are reference types; structures, enums, and tuples are value types.
What does it mean to be a value type? For one thing, a value type is always
treated as a single value, even if it is composed of several individual values via
its properties.
In practical terms, this means that when a value type is assigned or passed as a
parameter, a copy is made. The following code demonstrates the effect with the
Vector structure:
var vector0 = Vector(x: 0, y: 0) vector0 = {x
0, y 0}
var vector1 = vector0 vector0 = {x
0, y 0}, vector1 = {x 0, y 0}
vector0.x = 1 vector0 = {x
1, y 0}, vector1 = {x 0, y 0}
When vector0 is assigned to vector1, the entire value of vector0 is copied into
the memory represented by vector1. When vector0 is changed, vector1 is
unaffected.
Contrast this behavior with classes, which, again, are reference types:
let ball0 = Particle() ball0 ->
Particle: {x 0, y 0} ...
let ball1 = ball0 ball0, ball1 ->
Particle: {x 0, y 0} ...
ball0.particle.x = 1 ball0, ball1 ->
Particle: {x 1, y 0} ...
Even though you assign ball0 to ball1, there is still only one Particle instance
in existence; no copies are made. The ball0 constant is a reference to the
instance, and when ball0 is assigned to ball1, ball1 is then a reference to the
same instance.
(A reference is similar to a pointer in C-based languages. However, a pointer
stores the actual memory address of the object and you can access that address
directly. A Swift reference does not provide direct access to the address of the
object being referenced.)
There is another reference type. Functions are types so that a function can be
passed in to other functions as a defined parameter, or even assigned to a
property. This is the basis of closures, which you saw briefly earlier in this
chapter, and which you will see again in Chapter 15.

Implications of reference and value types


Passing by reference instead of by value has two main implications. The first has
to do with mutability. With a value type the code manipulating that value has
complete control over it.
Reference types, however, are much different: any part of the software that has a
reference to an instance of a reference type can change it. In object oriented
programming this can be desirable, but in complex (and especially
multithreaded) software it is a liability. An object being changed “behind your
back” can cause crashes at best and strange or difficult-to-debug behavior at
worst.
Swift constants help further illustrate this point. A constant value type cannot be
changed once it is defined, period:
let vector: Vector
if someCondition {
vector = Vector(x: 0, y: 1)
}
else {
vector = Vector(x: 0, y: -1)
}
vector.x = 1 // Error: Immutable value 'vector'
may only be initialized once
A constant reference provides no such protection. Only the reference itself is
constant.
let cannonball = Particle()
cannonball.velocity = Vector(x: 100, y: 5) // No
error!
Note that constants within a class or structure are constant. The Rocket class’s
thrust property, defined with let and given an initial value in the initializer,
cannot be changed:
let rocket = Rocket(thrust: 10.0, thrustTime: 60.0)
rocket.thrust = 3 // Error: Cannot
assign 'thrust' in 'rocket'

Choosing between reference and value types


How does a Cocoa programmer decide whether to use a structure or a class for
their new type? In order to answer that you will need to know how the type will
be used.
The vast majority of Cocoa is built on reference types: subclasses of the
Objective-C base class NSObject, which provides a lot of important functionality
for Cocoa. As such, large portions of your app, namely the controller and view
layers, will also need to descend from NSObject.
The model layer is where the answer gets fuzzy. Model-oriented Cocoa
technologies such as KVC, KVO, and Bindings also depend on NSObject, so
many app’s models will also. For other apps whose models are perhaps more
heavy on logic and computation, and less about binding to the UI, you are free to
choose the Swift type that makes the most sense for the problem you are trying
to solve. Do you want the shared mutable state provided by reference types, or
do you prefer the safety of value types? Both have their advantages and costs.
Cocoa, with its deep roots in MVC and Objective-C, will always rely heavily on
reference types. In comparison to Objective-C, however, Swift takes great
strides in making value types powerful. As a Cocoa programmer, both will be
important tools in your belt.
Making Types Printable
If you use a Vector value in string interpolation, you will not get a very pleasing
result:
println("Gravity is \(gravity).") "Gravity
is __lldb_expr_247.Vector."
You can improve this by conforming to the Printable protocol, which looks like
this:
protocol Printable {
var description: String { get }
}
We will cover protocols in more detail in Chapter 6, but the short version is that
a protocol defines a set of properties or methods. In order to conform to a
protocol, your type must implement the required properties and methods.
To conform to Printable, you must implement a read-only computed property
called description to return a String. Start by declaring that Vector conforms to
Printable:
struct Vector {
struct Vector: Printable {
var x: Double
var y: Double
Finally, implement description:
struct Vector: Printable {
...

var description: String {


return "(\(x), \(y))"
}
}
Your Vectors now look great in strings:
println("Gravity is \(gravity).") "Gravity
is (0.0, -9.8)."
Swift and Objective-C
Although you will write your classes in Swift, the classes in the Cocoa
frameworks are written in Objective-C. Swift was designed to work seamlessly
with Objective-C classes. While you can write Cocoa apps in pure Swift, without
a line of Objective-C, it is important to have a basic understanding of how
Objective-C works.
Objective-C methods (which are only available on classes, not structures) are not
called like functions or like Swift methods. Instead of calling a method on an
object, Objective-C sends the object a message.
A message consists of a receiver, selector, and any parameters. The selector is
the name of the method you want executed. The receiver is the object that you
want to execute that method. Here is an example of sending a message in
Objective-C:
NSString *newString;
newString = [originalString
stringByReplacingOccurrencesOfString: @"Mavericks"

withString: @"Yosemite"];
In this example, the receiver is originalString, an instance of NSString, and the
selector is stringByReplacingOccurrencesOfString:withString:. The
parameters are the two NSString literals.
Note that the selector in the message is “the name of the method.” It is not the
method itself or even a reference to it. You can think of a selector as a glorified
string.
Objective-C classes know how to receive a message, match the selector with a
method of the same name, and execute the method. Or they can do something
else with the selector, like forward it in a message to another class. Relying on
selectors and message-passing is relatively unique among languages in modern
use, and its dynamic nature made the powerful design patterns of Cocoa, and
later iOS, possible.
Calling a method is a cut-and-dried process. Either the object implements the
method or it does not, and this can be determined at compile time. Passing a
message, on the other hand, is dynamic. At runtime, the object is asked, “Does
your class implement a method with this name?” If yes, the method with that
name is executed. If no, the message is run up the inheritance hierarchy: The
superclass is asked, “Do you have a method with this name?” If that class does
not have the method, then its superclass is asked, and so on. If the message
reaches NSObject at the top of the hierarchy, and NSObject says, “No, I do not
have a method with that name,” then an exception occurs, and your app will halt.
You are developing in Swift, which means that you are not writing message
sends in code; you are calling methods. These Swift methods have to be named
in such a way that the Swift compiler can turn a method call into a message send
when the receiver is an Objective-C object.
If you were to write the above message send in Swift, it would look like this:
let newString =
originalString.stringByReplacingOccurrencesOfString("Mavericks"


withString: "Yosemite")
Remember, the Swift method has two parameters in the parameter list, but only
one has a name. This is why you see methods named in this text and in Apple’s
documentation with underscores where you expect a parameter name. For
example, this method is listed as
stringByReplacingOccurrencesOfString(_:withString:).
Working with Foundation Types
In Objective-C, a number of familiar types are implemented as classes as part of
the Foundation framework: NSString, NSNumber, NSArray, NSDictionary, and
NSSet. Because Cocoa was built for Objective-C, you will often run into these
classes when working with the Cocoa APIs. The good news is that Apple has
made transitioning between the Swift and Foundation (Objective-C) counterparts
relatively painless. They are toll-free bridged, meaning that there is minimal
computational cost in converting between the two.

Basic bridging
Swift types are automatically bridged to their Foundation counterparts:
let string = "Howdy"
let objcString: NSString = string
The reverse is not true, however:
let swiftString: String = objcString //
Error!
Instead, you must explicitly cast it using as:
let swiftString: String = objcString as String //
Ok!
Another class that you may see is NSNumber. Because Foundation collections can
only store objects, in order to store numbers they must be represented by an
object. NSNumber is the class that Objective-C programmers use for this task.
Swift numbers also bridge easily with NSNumber:
let objcNumber: NSNumber = 3
let swiftNumber = objcNumber as Int

Bridging with collections


Bridging with collections is similar, but a wrinkle emerges when casting from a
Foundation array back to Swift:
let array = [1, 2, 4, 8]
let objcArray: NSArray = array // So
far so good...
let swiftArray: [Int] = objcArray as [Int] //
Error!
You may be surprised to learn that Foundation collections can hold any kind of
object – that is, the collection’s contents do not have to be of the same type! You
will see the Swift type AnyObject used with these collections, like this:
[AnyObject]. (If you are familiar with Objective-C, AnyObject has the same
meaning as id.)
The solutions to this problem are similar to unwrapping optionals: there are safe
and unsafe paths. The unsafe path is to use as!, the forced cast operator:
let swiftArray: [Int] = objcArray as! [Int]
As with forced unwrapping, if the type cannot be cast successfully your app will
crash. If you are certain that the type is correct, such as when the value is coming
from a known API, this is a reasonable assumption to make.
If you are not so certain, you should use the optional type casting operator as?,
which will evaluate to nil if the values cannot be safely cast:
if let swiftArray: [Int] = objcArray as? [Int] {
...
}
This situation is most commonly seen with Cocoa APIs using NSDictionary: it is
typical for the keys to all be NSStrings, but the types of the values commonly
differ depending on the key. We will further discuss how to handle these
untyped collections safely in Chapter 28.
Suppose you were working with an Objective-C class that supplied a dictionary.
When Swift imports the class, it does a basic level of conversion, but it does not
know what type the method actually returns, so it is shown as [NSObject :
AnyObject]!:
class NSProcessInfo: NSObject {
...
var environment: [NSObject : AnyObject]! { get }
...
}
To work with this API you first need to know the actual types contained in the
dictionary, which can usually be found in the documentation. You will then need
to safely cast the result to Swift types:
let processInfo = NSProcessInfo()
if let environment = processInfo.environment as?
[String : String] {
if let path: String = environment["PATH"] {
println("Path is: \(path)")
}
}
It is important to remember that Swift strings and collections are value types and
the Foundation types are all reference types. While Swift’s compiler can enforce
the constant-ness of an array, with NSArray the same array object may be
referenced by many parts of an application.
The Foundation classes we have discussed so far are all immutable, meaning that
they cannot be changed – equivalent to being defined with Swift’s let. Each of
them has a mutable subclass: NSMutableArray, NSMutableString, and so forth.
This has less of an impact on Swift code, but it is important to watch out for if
you are working with a significant body of Objective-C code. Because it is a
reference type, an instance of NSMutableArray could be changed by any code that
has a reference to it.
Runtime Errors
Despite your best efforts, things will sometimes go wrong while your app is
running. In Cocoa, these errors fall into two categories.
Programmer errors are situations that should never happen, which means that
they are the result of, well, a mistake you made. (We say they should never
happen… but we have made plenty of these.) Examples include not meeting the
precondition of a method (the index was not within the array’s bounds),
performing an illegal operation (such as force-casting incorrectly or force-
unwrapping a nil), or sending a message to an Objective-C object that does not
understand it.
Swift alerts you to programmer errors by trapping, which results in stopping the
program. Cocoa APIs use Objective-C exceptions. A trap is typically
accompanied by a fatal error line in the console, while exceptions have much
longer output showing the full stack. Note that Swift does not presently support
exceptions.
Recoverable errors, on the other hand, are errors that your application can check
for, deal with, and move on from. Examples include being unable to contact a
remote server, errors parsing data, or lacking hardware capabilities.
Recoverable errors will be communicated to your code through the return values
of methods (such as a nil return). For more sophisticated APIs, especially those
involving I/O, an NSError object will be used. You will learn about NSError in
Chapter 12.
You can code defensively and check preconditions in your own code using
assert() and fatalError(). For example:
let condition: Bool = ...
assert(condition, "Condition was not met")
fatalError() is useful in methods that are declared to return a value. The Swift
compiler requires that all code paths return a value – unless you call a noreturn
function like fatalError():
func openFortuneCookie() -> String {
if let cookie = cookie {
return cookie.fortune
}
}
else {
fatalError("Must have cookie!")
// No return statement
}
}
More Exploring of Apple’s Swift
Documentation
Your homework for this chapter is to browse through the Classes and Structures,
Properties, Methods, and Initialization sections of Apple's The Swift
Programming Language guide. You should also tackle the challenge exercises
given below.
Challenge: Safe Landing
Your investors have pointed out that rockets are expensive and letting them
plummet to the ground does not enhance reusability. Enhance the Rocket class to
deploy a parachute in order to slow its descent once it is descending (i.e., shows
negative velocity on the Y axis) and reaches a certain altitude.
Challenge: Vector Angle
Your Vector structure should be able to report its angle in radians. Add a read-
only, computed property to it called angle.
The angle of a vector can be expressed in Swift as:
atan2(y, x)
4
Memory Management
All of the objects and values that your app uses take up memory. When those
objects and values are no longer needed, you want the system to reclaim that
memory it so that it can be reused.
Memory management in Cocoa is relatively simple. For instances of value types,
there is nothing to worry about. The memory is immediately reclaimed as soon
as the variable goes out of scope or when the reference type that it is part of
(e.g., a property is a part of an object) is itself reclaimed.
As reference types, objects are much more interesting because their lifetimes are
not tied to scope. Objects frequently live longer than the methods in which they
are created. Cocoa uses reference counting to manage their lifetimes.
Automatic Reference Counting
Reference counting addresses two needs:
Your objects need to stay in memory as long as they are needed…
… and no longer. Objects you no longer need should be deallocated as soon
as possible.
Reference counting works through simple bookkeeping: As long as there is a
reference to an object, the object will be kept in memory. When there are no
more references, the object is deallocated. The good news is that in these modern
times you do not have to do this bookkeeping yourself: you have ARC, or
Automatic Reference Counting, to do it for you. Before you learn how ARC
works, you should understand references and reference counts.

Objects have reference counts


When an object is created, there is always an initial reference to it – the property,
outlet, constant, or variable that stores the result of creating the object. This
gives the brand-new object a reference count of 1. Whenever that reference is
assigned or passed elsewhere, another reference is created and the object’s
reference count is incremented.
When the value of a property, outlet, or variable changes to reference another
object or to be nil, then the previously referenced object has “lost a reference,”
and its reference count is decremented.
An object also loses a reference when the referring property, outlet, constant, or
variable is itself destroyed. For local constants and variables, this happens when
the constant or variable goes out of scope. For properties and outlets, this
happens when the instance that declares the property or outlet is deallocated.
Let’s look at an example from RandomPassword’s AppDelegate.swift. At the
beginning of applicationDidFinishLaunching(_:), you create an instance of
MainWindowController and assign it to the mainWindowController local constant.
This creates a reference to the new MainWindowController, which has a reference
count of 1.
func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Create a window controller
let mainWindowController =
MainWindowController()
...
}
At the end of the same method, AppDelegate’s mainWindowController property is
assigned to reference the same instance of MainWindowController. Setting the
property creates another reference, so the MainWindowController instance now
has a reference count of 2.
func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Create a window controller
let mainWindowController =
MainWindowController()
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)
// Set the property to the window controller
self.mainWindowController =
mainWindowController
}
When applicationDidFinishLaunching(_:) returns, the local constant goes out
of scope, and the MainWindowController loses a reference. However, the
MainWindowController will not be deallocated because another reference – the
property – continues to exist.

Deallocating objects in a hierarchy


Figure 4.1 shows the objects that make up RandomPassword’s interface.
Figure 4.1 Chain of references in view hierarchy
Let’s walk through the references in this diagram. You already know that the
MainWindowController’s reference count is 1 after the application has finished
launching. The MainWindowController has a window property that has been set to
an instance of NSWindow. This gives the window a reference count of 1 as well.
The window has a contentView property set to an NSView, which also has a
reference count of 1.
The NSView has an array named subviews. Here is its property declaration: var
subviews: [AnyObject]
Array<T> is not a reference type, but when an array’s members are instances of a
reference type, then the array contains references, not instances. Thus, the NSView
indirectly has references to the text field and the button.
Now think back to the references created to the MainWindowController in
applicationDidFinishLaunching(_:). If you did not set the
mainWindowController property to reference the MainWindowController, then that
object would be deallocated. What would happen to the rest of the objects in the
view hierarchy?
To find out, open the RandomPassword project and comment out the line in
AppDelegate.swift where the property is set.
func applicationDidFinishLaunching(aNotification:
NSNotification) {
...
// Set the property to the window controller
self.mainWindowController =
mainWindowController
// self.mainWindowController =
mainWindowController
}
Add a couple of println() statements to investigate what is happening. First,
confirm that the window controller’s NIB file was loaded. In
MainWindowController.swift, add a println() statement to the windowDidLoad()
method.
@IBOutlet weak var textField: NSTextField!

override func windowDidLoad() {


super.windowDidLoad()
println("window loaded from NIB named \
(windowNibName)")
}
Then, add an implementation of deinit() to MainWindowController. The
deinitializer is called automatically when an instance is about to be deallocated.
deinit {
println("\(self) will be deallocated")
}
Run the app. The app builds fine, but no window is visible. The console
confirms that the NIB was loaded and then reports the immediate deallocation of
the window controller:
window loaded from NIB named MainWindowController
<RandomPassword.MainWindowController:
0x6000000a1380> will be deallocated
What happened? The only reference to the MainWindowController was lost when
applicationDidFinishLaunching(_:) returned and the mainWindowController
constant went out of scope. Because the property was not set, losing this
reference brought the window controller’s reference count to 0, and it was
deallocated.
But that is not all. When the window controller was deallocated, the reference to
the window was lost. This reference was the only reference to the window, so
the window was deallocated as well. With the window gone, the views in its
hierarchy were deallocated in the same way.
This is a nice piece of clean-up. None of the view objects are left behind after
the window was deallocated. View hierarchies are purposely set up this way to
prevent parts of your interface from leaking after a higher-level view has been
deallocated.
However, you want your window controller and its view hierarchy to stick
around – at least for a while – after the application has launched. Restore the line
that sets the mainWindowController property.
func applicationDidFinishLaunching(aNotification:
NSNotification) {
...
// Set the property to the window controller
// self.mainWindowController =
mainWindowController
self.mainWindowController =
mainWindowController
}
Run the app again to confirm that the window appears and that the console does
not report the deallocation of the MainWindowController.
Strong and Weak References
By default, a reference is a strong reference. All of the references you have seen
so far have been strong references, and an object’s reference count is actually the
count of strong references. As long as there is a strong reference to an object,
that object will remain in memory.
There is another kind of reference: a weak reference. A weak reference allows
you to access the object it references, but it is not included in the object’s
reference count. Thus, a weak reference will not keep an object in memory.
In Swift, weak references are always optional types because they are auto-
zeroing: when the referenced object is deallocated, the weak reference is set to
nil. Auto-zeroing prevents references from dangerously storing an address of an
object that no longer exists.
Try it out. Open MainWindowController.swift and change the
mainWindowController property to a weak reference using the weak keyword:
var windowController: MainWindowController?
weak var windowController: MainWindowController?
Before you run the app: What do you think will happen?
The behavior is indistinguishable from not assigning the mainWindowController
local variable to a property at all. Remove the weak keyword before continuing.
weak var windowController: MainWindowController?
var windowController: MainWindowController?

Strong reference cycles


There is one complicating factor in reference counting: when two objects have
strong references to each other – either directly or indirectly – a strong reference
cycle occurs. In a strong reference cycle, none of the objects can be deallocated
because all of them have a non-zero reference count. The result is that a portion
of the object graph is leaked – the memory will never be freed because all of the
objects are keeping each other alive and there are no references to any of the
objects. They are an island.
Why can’t ARC fix this problem? ARC is not garbage collection. Since ARC is
part of the compiler it cannot analyze the references at runtime, so it is
impossible for ARC to detect a strong reference cycle. Instead, you will have to
spot the potential for a strong reference cycle and design your references to
avoid it.
Figure 4.2 shows an example of a common strong reference cycle. The specific
scenario is a tree, but it is representative of the more general scenario where
there are parent-child relationships.
Figure 4.2 A strong reference cycle

Here is the code for Node, which causes the leak:


class Node {
var children: [Node] = []
var parent: Node?
}
In the diagram, MainWindowController has a strong reference, tree, to the root
node. That node has one child, which has a strong reference back to its parent,
the root. Therefore the root node has a reference count of 2, and its child has a
reference count of 1.
When tree is set to nil, the root node’s reference count is decremented,
bringing it to a reference count of 1. Because none of the objects’ reference
counts have reached 0, nothing is deallocated, and the nodes are leaked.
The solution in this scenario is to make the parent reference a weak reference:
class Node {
var children: [Node] = []
weak var parent: Node?
}
Figure 4.3 shows the result. Because the parent reference is weak, the root node
starts with a reference count of 1.
Figure 4.3 Weak parent reference avoids the cycle

When tree is set to nil, the root node’s reference count is decremented to 0,
which deallocates the root node. Because the children array is a value type on
the node, its strong references to the children are released, which causes the
child’s reference count to reach 0, deallocating it. The entire tree of objects has
been freed.
The key to avoiding strong reference cycles is to think in terms of “what owns
what” in your object graph and make only those references strong. In a tree, the
parent owns the children (strong reference); the child does not own the parent
(weak reference).
Weak references are commonly used in three places in Cocoa:
Outlets to subviews: A window controller has a strong reference to the
window, and a window has strong references (indirectly) to its view
hierarchy. Therefore, a strong reference is unnecessary and could
inadvertently keep a subtree of the view hierarchy alive.
Targets from controls: You will learn more about controls in Chapter 5, but
because a controller typically has indirect strong references to its controls,
they should have weak references back to the controller where the action
methods are implemented.
Delegates: Delegates will be covered in Chapter 6, but the idea is the same
as with controls and their targets.

Unowned references
Rarely, you may see references declared as unowned. An unowned reference is a
non-strong, non-optional reference. Like a weak reference, it does not add to an
object’s reference count. Unlike a weak reference, it is not set to nil when the
referenced object is deallocated.
What are unowned references for? For one thing, they have been used to make
pre-ARC parts of the Cocoa frameworks compatible with ARC. Unowned
references are also sometimes used in closures, which you will learn about in
Chapter 15.
In your code, an unowned reference is best used when you know that the
reference will never be accessed after the referenced object has been deallocated.
For example, you might have an one object whose lifetime is known to be a
subset of the lifetime of the object that it is referencing.
What is ARC?
To understand ARC, you should know a little history. In the old days, Cocoa
programmers managed their objects’ reference counts manually by “retaining”
and “releasing” objects. There were rules, and the rules were fairly simple. This
worked well.
Then garbage collection came to Cocoa in 2007. Garbage collection was fine,
but it had awkward constraints on the contexts in which it could be used – it was
all garbage collection or nothing.
A couple years later, Xcode 3.2 offered an exciting new feature: the Clang Static
Analyzer, which knew the retain counting rules and could alert you to memory
management errors in your code! Garbage collection never came to iOS. Instead,
ARC was announced in 2011, and garbage collection was deprecated shortly
thereafter.
ARC is compiler technology, based on the Clang Static Analyzer. The static
analyzer looks at your code and determines where it should be retaining and
releasing (incrementing and decrementing retain counts). It then inserts
optimized calls, making for faster performance and less error-prone reference
counting than could be achieved before.
5
Controls
Once upon a time, there was a company called Taligent. Taligent was created by
IBM and Apple to develop a set of tools and libraries like Cocoa. About the time
Taligent reached the peak of its mindshare, Aaron met one of its engineers at a
trade show and asked him to create a simple application: A window appears with
a button. When the button is clicked, the words “Hello, World!” appear in a text
field.
The engineer created a project and started subclassing madly, subclassing the
window and the button and the event handler. Then he started generating code:
dozens of lines to get the button and the text field onto the window. After 45
minutes, he was still trying get the app to work. A couple of years later, Taligent
quietly closed its doors forever.
Most C++ and Java tools work on the same principles as the Taligent tools. The
developer subclasses many of the standard classes and generates many lines of
code to get controls to appear on windows. Most of these tools work.
While writing a Cocoa application, you seldom subclass the classes that
represent windows, buttons, or events. Instead, you create objects that work with
the existing classes. Also, you do not create code to get controls on windows.
Instead, the XIB file contains all this information. The resulting application has
significantly fewer lines of code. At first, this outcome may be alarming. In the
long run, most programmers find it delightfully elegant.
In this chapter, you are going to create a simple Cocoa application. The
application is named RGBWell. It displays a color and allows the user to change
the color by moving sliders that represent the different color components.
(Figure 5.1).
Figure 5.1 Each slider adjusts an RGB color component
In addition to creating RGBWell, you will learn more about how NIB files work,
take a closer look at the NSControl class, and explore Apple’s documentation on
the Cocoa frameworks. This list of items makes for a long chapter, but you will
gain a lot of useful Cocoa knowledge and experience.
Setting up RGBWell
Create a new project (Command-Shift-N). From the OS X options, choose Cocoa
Application and configure the project as shown in Figure 5.2.

Figure 5.2 Configuring RGBWell project

As you did in RandomPassword from Chapter 1, you are going to adjust the project
that the template created to use a window controller as the primary controller for
the application. However, in RandomPassword you used an Xcode template to create
the MainWindowController class along with MainWindowController.xib. In this
project, you are going to create the class and the XIB file from scratch to see
how the parts work together.

Creating the MainWindowController class


In the project navigator, Control-click on the RGBWell group and select New File....
In the panel that appears, select Source from the OS X section. From the choices on
the right, choose Swift File and then click Next (Figure 5.3).
Figure 5.3 Creating a Swift file
Name the file MainWindowController. (Xcode will add the .swift extension for
you.) Click Create, and MainWindowController.swift will open in the code editor.
In MainWindowController.swift, replace the import statement and add a class
definition that names the MainWindowController class and makes it a subclass of
NSWindowController.
import Foundation
import Cocoa

class MainWindowController: NSWindowController {

}
Importing Cocoa gives you access to three frameworks: Foundation, AppKit,
and CoreData. AppKit is critical for this file because it provides all of the Cocoa
user interface classes, including NSWindowController.

Creating an empty XIB file


In the project navigator, Control-click the RGBWell project group and select New
File.... From the OS X section, select User Interface. From the choices on the right,
choose Empty and click Next (Figure 5.4).
Figure 5.4 Creating an empty XIB
Name the file MainWindowController. (Xcode will add the .xib extension.) Click
Create, and MainWindowController.xib will open in Interface Builder.

(You have given the XIB file and the window controller class the same name.
This naming convention is not required, but it is widely followed and
understood. Also, note that it is only a naming convention and giving these files
the same name does not have any further consequences.) This empty XIB needs
a window object that will be the root of the hierarchy of objects that comprise
the interface of your application. At the bottom of the utilities area on the
righthand side of the Xcode window, locate the object library ( ). In the search
bar at the bottom of the library, search for a “window” and then drag a Window
from the library onto the canvas (Figure 5.5).
Figure 5.5 Dragging a window onto the canvas
The document outline will update to show two new objects: a Window and a View.
The view is the window’s content view, which is the view that will contain all of
the other views in the interface. When you drag a Window from the object library,
it always comes with a content view.
Select the window in the document outline and, in the inspector area above the
library, reveal the attributes inspector by selecting the icon in the inspector
bar. Set the window’s Title attribute to RGBWell and uncheck the box labeled Visible
At Launch.

Figure 5.6 Configuring the window object


Leaving the Visible At Launch box checked will duplicate some of your code and
mask a relationship between the window controller and the window that we want
you to see. In fact, we recommend unchecking this box as a matter of practice.
However, leaving it checked does not usually cause problems – except when
working with sheets, as you will learn in Chapter 24.
Now that you have a window for your application’s interface, you no longer
need the window that the template created for you.
Open MainMenu.xib and find the window object labeled RGBWell. Select the object
in the document outline and press Delete.

Creating an instance of MainWindowController


An instance of MainWindowController will control the application’s single
window along with the rest of its view hierarchy. Thus, you will need to create
this instance and show its window right after the application has launched.
In AppDelegate.swift, replace the contents of the class implementation with the
following code:
class AppDelegate: NSObject, NSApplicationDelegate {

var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {

// Create a window controller


let mainWindowController =
MainWindowController()
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}

}
In this code, you add a property to AppDelegate whose type is an optional
MainWindowController. The property must be an optional because it is not
initialized until applicationDidFinishLaunching(_:) is called.
In the implementation of applicationDidFinishLaunching(_:), you create an
instance of MainWindowController and then ask it to show its window. Showing
the window puts the window and the rest of its hierarchy on screen and prepares
these objects for user interaction. Finally, you save this instance to the property
so that the AppDelegate can keep a reference to it.
You can now run the app, but no window will appear. You have created a
window controller class and a window object in a XIB file, but you have not yet
connected the MainWindowController’s window outlet to the window object in
MainWindowController.xib.

Connecting a window controller and its window


MainWindowController inherits a window outlet from NSWindowController, which
gives it a reference to its window at runtime so that it can show the window
when asked. This outlet must be connected to a window object in a XIB.
NSWindowController does not supply its own window; the window must be
loaded from the NIB or, less commonly, created programmatically.
But before you can connect the window outlet, the MainWindowController must
know the name of the NIB file in which its window and the rest of its interfaced
is stored so that it can load this file and trigger the unarchiving of the objects
within it. An unarchived object actually exists in the application and can be
interacted with.
In MainWindowController.swift, tell MainWindowController the name of the NIB
file that contains its window by overriding the NSWindowController property
windowNibName.
class MainWindowController: NSWindowController {

override var windowNibName: String? {


return "MainWindowController"
}

}
When the MainWindowController loads this NIB file, the window object is
unarchived. This raises the question of when a window controller loads its NIB.
The NIB is loaded as a side effect of accessing the window property for the first
time. Your MainWindowController attempts to access its window when
showWindow(_:) is called in applicationDidFinishLaunching(_:). Because the
window property is nil, the window controller loads the NIB file.

When the NIB has been loaded, the window object exists, but showWindow(_:)
can only complete its job if the window outlet is connected to this object.
Connecting the outlet and showing the window require the help of the File's
Owner placeholder in MainWindowController.xib.
(If Visible At Launch was still checked, your window would now appear on screen
despite the window outlet not being connected. However, in a real app, you
cannot rely on only this setting to get the window on screen. For example, what
if the window was closed with the intent of reopening it later? You would call
showWindow(_:) to reopen the window, and showWindow(_:) will only work if the
window outlet is connected.)

File's Owner and making connections

As you learned in Chapter 1, a XIB file is an XML representation of objects and


their connections. Representing a connection requires representing the two
objects at either end of the connection.
If the objects at both ends of a connection are included in the XIB, then you can
make the connection directly. However, many times an object in a XIB needs
connecting to an object outside of the XIB. This is where placeholders come in
and, in particular, File's Owner.
File's Owner is a placeholder that stands in for the object that will load the NIB
file at runtime. Typically, this is the controller that will control the hierarchy of
objects represented in the XIB. When you want a connection to this controller,
you make a connection to File's Owner.
For this substitution to work, Interface Builder must know the type of object that
File's Owner is standing in for so that the appropriate outlets and actions can be
exposed and connected. Because you are creating a XIB file from scratch, you
have to provide this information to Interface Builder.
In MainWindowController.xib, select File's Owner in the document outline. Then
select the icon in the inspector bar to reveal the identity inspector. At the top
of this inspector, find the Class attribute and enter MainWindowController
(Figure 5.7).
Figure 5.7 Updating the identity of File's Owner
Interface Builder will now allow you to connect any outlet or action that
MainWindowController declares or inherits – including window.

Finally, to connect the window outlet, Control-click on File's Owner to open the
connections panel. Drag from the window outlet to the window on the canvas
(Figure 5.8).
Figure 5.8 Connecting the window outlet
Now the MainWindowController can find and show its window. Run the app and
confirm that you have an empty RGBWell window as shown in Figure 5.9.
Figure 5.9 Empty RGBWell window confirming correct set-up

Your project is now set up and you are ready to add to the application’s
interface. So let’s take a closer look at an important type of object found in
interfaces – controls.
About Controls
A control is a view that is designed specifically for user interaction. A control is
implemented as a subclass of NSControl, which is a subclass of NSView. Controls
are not part of the controller layer, so keep the terms “control” and “controller”
separate in your mind. It may help to think of controls as “user controls.”
In RandomPassword, you worked with two controls: NSButton and NSTextField. In
RGBWell, you will use two more: NSSlider and NSColorWell.

NSControl inherits from NSView, which inherits from NSResponder, which inherits
from NSObject. Each member of the family tree adds some capabilities
(Figure 5.10).
Figure 5.10 Inheritance diagram for NSControl
NSControl adds the ability to send action messages. The target is a reference to
the object that receives the action message. The action is a selector that is sent
to the target of the action message. (Remember that a selector is essentially the
name of a method in string form.) When the user interacts with a control, the
control sends an action message to the target and the target then executes the
associated action method.
NSControl also has several properties relating to the value of a control. For
numerical values, you can use integerValue, floatValue, or doubleValue. For
text, you can use stringValue. There is also objectValue. This property returns a
model object that is represented by the control. Usually, this is an NSString or an
NSNumber, but it can be an instance of any NSObject subclass. The conversion
from string to model object and back is managed by a formatter. You will learn
more about them in Chapter 10.
Working with Controls
Let’s get back to RGBWell and add a control – an NSSlider – to your interface.
A slider allows a user to select a value from some range. In the object library,
search for a slider and drag a Horizontal Slider onto the top righthand side of the
window (Figure 5.11).
Figure 5.11 Dragging a slider onto the window

Next, find a label in the object library and drag it onto the canvas to the left of
the slider and change its text to R. Figure 5.12 shows what the interface should
look like at this point.
Figure 5.12 RGBWell with one label and one slider
Cocoa controls tend to be very versatile. The same class may have a number of
different visual styles. A Label is an NSTextField, the same class that is used to
accept text input, configured to display static text. Specifically, its border and
background drawing have been disabled, as well as selection and editing.

A word about NSCell


In the document outline shown in Figure 5.13, notice that each control comes
with a partner object: the label has an instance of NSTextFieldCell, and the slider
has an instance of NSSliderCell. These classes share a superclass, NSCell.
Figure 5.13 Controls come with cell partners
What is a cell? A cell handles the implementation details of the control. For the
most part, the cell’s properties and methods are “covered” by identical properties
and methods in the corresponding control. For instance, when you change the
title on a label, the label’s cell is doing the work, but you interact with the label’s
stringValue property.

Cells have a long history in Cocoa. They were originally added to address
performance issues: many of the basic tasks of a control were handed off to the
cell, which could do them more efficiently than the control. Mac computers are
much more powerful than they were in those days, and cells have become an
encumbrance. Apple has stated that it is in the process of deprecating cells, but
you will still see them in your document outline and in older code.

Connecting the slider’s target and action


As a control, your slider knows how to send an action message to its target. Your
slider’s action will be adjustRed: and its target will be the
MainWindowController, as shown in Figure 5.14.

Figure 5.14 The slider’s target and action

In MainWindowController.swift, add an initial implementation for the


adjustRed(_:) method that the slider’s action message will trigger.
class MainWindowController: NSWindowController {

override var windowNibName: String {


return "MainWindowController"
}

@IBAction func adjustRed(sender: NSSlider) {


println("R slider's value is \
(sender.integerValue)")
}
}
This method is an action method and, like every action method, it has a single
sender parameter that references the control that sent the triggering action
message. This allows you to call back to the control to get more information. In
this case, you call back to ask the slider for its integerValue, which is the current
position of the slider’s knob expressed as an Int.
(You may see some action methods where the sender parameter is declared to be
AnyObject instead of a particular NSControl subclass. AnyObject is for situations
where you have multiple types of controls triggering the same action method,
but situations like that are rare. Usually, you want sender to be more specific so
that you can access properties specific to the sender’s type.) Now that you have
an action method defined, you can connect the slider’s action and target.
In MainWindowController.xib, select the slider in the canvas and Control-drag to
File's Owner. From the Received Actions panel, select adjustRed:.

Figure 5.15 Connecting the slider’s action

Dragging to File's Owner connects the slider’s target and selecting the
adjustRed: action connects the action.
In connecting the target outlet, you give the slider a reference to the
MainWindowController. The slider is in the MainWindowController’s view
hierarchy, so the window controller has an indirect strong reference to the slider.
As you learned in Chapter 4, when two objects reference each other, you must
make one of the references weak to prevent a strong reference cycle and allow
the objects to be deallocated when appropriate.
To avoid the possibility of strong reference cycles between controls and their
targets, NSControl defines the target outlet as a weak reference: weak var target:
AnyObject?
Run the app. Move the slider up and down and watch the console at the bottom
of the project window. (Xcode should automatically show the console. If it does
not, press Shift-Command-C to show it yourself.)
Figure 5.16 Console reporting integerValue for slider

Whenever you release the mouse button, the slider sends its action message to its
target, and its target (the MainWindowController) executes the adjustRed(_:)
method.

A continuous control
NSControl has a property named continuous that specifies when the action
message is sent. By default, continuous = false, and the action message is
only sent when the user interaction has ended, like when the mouse button is
released or the Return key is pressed. This is how your slider currently works.
You can drag the slider back and forth, but you will only see a new value
reported when you release the mouse button.
When continuous = true, the action message is sent continuously during the
interaction. For a slider, this means the action message is sent over and over
again while the user is dragging the knob along the slider.
To see the difference, select the slider and reveal the attributes inspector ( ).
Find the Control section and check the checkbox labeled Continuous, as shown in
Figure 5.17.
Figure 5.17 Making slider continuous

Run the app and move the slider. The console will now report values non-stop as
you drag the slider left and right. This is the slider sending its action message to
its target continuously.
its target continuously.
Back in the attributes inspector, notice that the attributes are divided into
sections. The top section is labeled Slider, the next section down is labeled Control,
and the section beneath that is labeled View. These sections reflect the slider’s
inheritance hierarchy. (See Figure 5.10 to review this hierarchy.) The attributes
in each section are properties on a Cocoa class that have been exposed so that
you can set them in Interface Builder.
Note that the labels in the attributes inspector do not exactly match the property
names. NSSlider has the properties minValue and maxValue, which correspond to
the Minimum and Maximum fields in the inspector. NSControl has the properties
continuous and enabled, which are exposed in Interface Builder as the Continuous and
Enabled checkboxes. You will see later how to find all of properties and methods
of a class in Apple’s documentation.

Setting the slider’s range values


Let’s set some slider-specific attributes. At the top of the attributes inspector,
find Value and its Minimum and Maximum fields. Change Maximum to 1 (Figure 5.18).
Figure 5.18 Configuring slider range

Run the app and move the slider. Now that you have changed the slider’s range,
its integerValue is much less interesting. Any value that is not 1 is rounded
down to 0.
Figure 5.19 integerValue for slider after reducing range

In MainWindowController.swift, update the println() statement to use the


slider’s floatValue instead.
class MainWindowController: NSWindowController {

override var windowNibName: String {


return "MainWindowController"
}

@IBAction func adjustRed(sender: NSSlider) {


println("R slider's value is \
(sender.integerValue)")
println("R slider's value is \
(sender.floatValue)")
}

}
Run the app and test out the new values.
Figure 5.20 Console reporting floatValue for slider
Adding two more sliders
RGBWell needs two more sets of sliders and labels. Instead of dragging them from
the object library, you can copy and paste the existing objects.
In MainWindowController.xib, select the label and slider. (Use Command-click
to select multiple objects.) Copy and paste to produce two more label-slider pairs
on the window. Align the pairs and change the new labels to G and B
(Figure 5.21).
Figure 5.21 RGBWell with three label-slider pairs

In MainWindowController.swift, add action methods for the new sliders.


...
@IBAction func adjustRed(sender: NSSlider) {
println("R slider's value is \
(sender.floatValue)")
}

@IBAction func adjustGreen(sender: NSSlider) {


println("G slider's value is \
(sender.floatValue)")
}

@IBAction func adjustBlue(sender: NSSlider) {


println("B slider's value is \
(sender.floatValue)")
}

}
Return to MainWindowController.xib. For each new slider, Control-drag from the
slider to File's Owner and select the appropriate action.
Figure 5.22 New actions need connecting

Run the app and test the sliders. Note that the new sliders report values between
0 and 1 and send their action messages continuously. When you copy and paste
an object in a XIB, all of its attribute values are copied, too.

NSColorWell and NSColor


The last object your interface needs is a color well. Find one in the object library
and drag it onto the window to the left of the sliders. Resize it to match
Figure 5.23.
Figure 5.23 RGBWell with complete set of view objects

Make the necessary connections for this color well. First, in


MainWindowController.swift, add an outlet for the color well.
class MainWindowController: NSWindowController {

@IBOutlet weak var colorWell: NSColorWell!

override var windowNibName: String {


return "MainWindowController"
}

...

}
Second, in MainWindowController.xib, Control-click File's Owner and connect
the colorWell outlet to the color well on the canvas.
Figure 5.24 New colorWell outlet needs connecting
A color well lets the user pick a specific color and save it to use elsewhere in the
application. By default, clicking a color well opens a color picker window that
sets its color. Run the app, click the color well, and see what happens.
Figure 5.25 Clicking color well shows color picker
In this simple app, you only want the color well to display a color. To prevent
the color picker from appearing when the color well is clicked, you are going to
disable the color well.

Disabling a control
Every control has an enabled property. By default, enabled = true. When
enabled = false, the control will not respond to user interaction. For the color
well, this means that clicking the color well will do nothing, which is what you
want. For some controls, setting enabled to false also changes the control’s
appearance. For example, a disabled button is “grayed-out.”
Like continuous, enabled is exposed in Interface Builder, so you can disable the
color well there. With the color well selected, reveal the attributes inspector. In
the Control section, uncheck the box labeled Enabled (Figure 5.26).
Figure 5.26 Disabling the color well

Run the app to confirm that clicking the color well now does nothing.
Now you must tell your color well what color it should display. How can you do
this? To find out, let’s visit the Apple documentation for the NSColorWell class.
Using the Documentation
There are over 300 classes in Cocoa and related frameworks, and every one is
documented in Apple’s API reference. Cocoa programmers spend a lot of time
searching this reference to find answers to development questions. Apple’s
documentation also includes topic-based guides, which are excellent resources
for learning more about a particular subject.
In this section, you are going to use the reference to figure out how to get your
color well to display a color specified by a set of RGB values. From Xcode’s
toolbar, select Window → Documentation and API Reference. In the window that appears,
find the search bar at the top, search for NSColorWell, and select the NSColorWell
Class Reference (Figure 5.27).

Figure 5.27 Reference page for the NSColorWell class

The class reference page begins with an overview and then lists the properties
and methods of the class. The properties and methods are organized into distinct
development tasks for which the class is typically used.
On the lefthand pane of the documentation window, you can see and navigate to
individual tasks, properties, and methods. NSColorWell is not a large class – it
has only three properties and four methods distributed among four tasks. Find
and select the color property to see its details displayed in the center panel of the
page (Figure 5.28).
Figure 5.28 Investigating the color property

This is property you set to change the color well’s color. Its type is NSColor, so
the next question is how to create an NSColor using RGB values. To find the
answer, click on NSColor in the property definition to go to the reference page for
the NSColor class.
On the NSColor reference page, find the task for creating a color from component
values. Within this task, there are several methods that create and return a new
NSColor. Find and select a method with the parameters calibratedRed, green,
blue, and alpha (Figure 5.29).

Figure 5.29 Investigating an initializer


(Notice that the navigation panel in Figure 5.29 lists Objective-C methods and
not Swift methods. You can tell the difference by the syntax: Objective-C
methods never have parentheses. Apple is working to update its documentation
for Swift, but some parts are still rooted in Objective-C. The method selected in
this figure is colorWithCalibratedRed:green:blue:alpha:, which is the
Objective-C version of the Swift initializer that you will use. It is a method that
returns a new, configured instance of NSColor.) The center panel shows details of
an initializer named init(calibratedRed:green:blue:alpha:). This initializer
uses the calibrated color space as opposed to the device color space. Calibrated
color space is recommended because it is device-independent.
The initializer takes four arguments: the three component values plus an alpha
value, which determines the opacity of the color. There are a couple of things to
notice here: First, your code will need to provide an alpha value. Second, the
arguments are of type CGFloat, which is an C type, so you will have to create and
pass values of this type.
Now you have the necessary information to complete your app.

Changing the color of the color well


In MainWindowController.swift, add four new properties to store values from the
sliders and an alpha value that ensures that all of your colors will be 100%
opaque (0% transparent).
class MainWindowController: NSWindowController {

var r = 0.0
var g = 0.0
var b = 0.0
let a = 1.0

@IBOutlet weak var colorWell: NSColorWell!

...

}
Next, update the action methods to store the sliders’ values in these properties.
Note that you use doubleValue because the properties that you just initialized
with floating-point literals have been inferred to be of type Double.
class MainWindowController: NSWindowController {

...

@IBAction func adjustRed(sender: NSSlider) {


println("R slider's value is \
(sender.floatValue)")
r = sender.doubleValue
}

@IBAction func adjustGreen(sender: NSSlider) {


println("G slider's value is \
(sender.floatValue)")
g = sender.doubleValue
}

@IBAction func adjustBlue(sender: NSSlider) {


println("B slider's value is \
(sender.floatValue)")
b = sender.doubleValue
}
}
Then, write an updateColor() method that creates an instance of NSColor using
the stored values and assigns it to the color well’s color property. Recall that this
initializer takes CGFloat values as its arguments, so for each argument, create a
value of type CGFloat from the appropriate Double property.
class MainWindowController: NSWindowController {

...

@IBAction func adjustBlue(sender: NSSlider) {


println("B slider's value is \
(sender.floatValue)")
b = sender.doubleValue
}

func updateColor() {
let newColor = NSColor(calibratedRed:
CGFloat(r),
green:
CGFloat(g),
blue:
CGFloat(b),
alpha:
CGFloat(a))
colorWell.color = newColor
}
}
Finally, in the action methods, call updateColor().
class MainWindowController: NSWindowController {

...

@IBAction func adjustRed(sender: NSSlider) {


println("R slider's value is \
(sender.floatValue)")
r = sender.doubleValue
updateColor()
}

@IBAction func adjustGreen(sender: NSSlider) {


println("G slider's value is \
(sender.floatValue)")
g = sender.doubleValue
updateColor()
}

@IBAction func adjustBlue(sender: NSSlider) {


println("B slider's value is \
(sender.floatValue)")
b = sender.doubleValue
updateColor()
}

...

}
Run the app, move the sliders, and make pretty colors.
Controls and Outlets
So far, you have not needed outlets to the sliders. Everything has been done
within the action methods, so you have been able to use sender to access these
controls. But if you want to access a control outside of its action method, you
need to create and connect an outlet.
One place you might want to access a control is the NSWindowController method
windowDidLoad(). This method is called after the objects and their connections
are loaded from the NIB but before the window is displayed to the user. It is
where you can do extra initialization for your interface.
In this section, you are going to override windowDidLoad() to ensure that when
the app starts, the sliders and the color well correspond to the default values in
MainWindowController.

First, in MainWindowController.swift, add outlets for the three sliders.


class MainWindowController: NSWindowController {

...
let a: Float = 1.0

@IBOutlet weak var rSlider: NSSlider!


@IBOutlet weak var gSlider: NSSlider!
@IBOutlet weak var bSlider: NSSlider!

@IBOutlet weak var colorWell: NSColorWell!

...

}
(You are probably wondering what the ! means at the end of outlet declarations.
It declares the outlets as implicitly unwrapped optionals, which you will learn
about in the next section.)
Second, in MainWindowController.xib, Control-click on File's Owner and use the
connections panel to connect the slider outlets to the corresponding sliders.
Figure 5.30 New outlets need connecting

Finally, override windowDidLoad() to set the sliders to the initialized properties


and to call updateColor().
class MainWindowController: NSWindowController {

...

override var windowNibName: String {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()
rSlider.doubleValue = r
gSlider.doubleValue = g
bSlider.doubleValue = b
updateColor()
}

@IBAction func adjustRed(sender: NSSlider) {


println("R slider's value is \
(sender.floatValue)")
r = sender.doubleValue
updateColor()
}

...

}
The call to super.windowDidLoad() does nothing in this implementation, but it
is a good habit to get into. Some NSWindowController subclasses may inherit
from other NSWindowController subclasses instead of directly from
NSWindowController. When this is true, you must ensure that the superclass has
done its extra initialization before you do yours.
Run the app and notice the change in the starting positions of the sliders and the
color of the color well (Figure 5.31).
Figure 5.31 Sliders now set to initial values

Implicitly unwrapped optionals


An implicitly unwrapped optional is an optional that you never have to unwrap.
You can think of it as an implicitly and forcibly unwrapped optional, and they
come with all the dangers of forcibly unwrapping optionals.
Nevertheless, you will always define your outlets as implicitly unwrapped
optionals. Outlets must be optional because by definition they are loaded from a
NIB file after the initializer has run and never initialized in code. So the Swift
compiler will insist that you make them optional.
Because you trust that the XIB file was set up and connected properly and
because you trust the NIB-loading process, you declare outlets as implicitly
unwrapped optionals.
The NIB-loading process is reliable, but it is easy to forget to make a connection
in Interface Builder. If you fail to connect an outlet and later attempt to access it in
code, the app will crash.
Try it out. In the connections inspector, disconnect the rSlider outlet and run
RGBWell. Immediately after launching, the app will crash. The rSlider outlet that
you promised would have a value did not.
Reconnect the outlet: Control-click File's Owner and drag from the circle next to the
rSlider outlet to the appropriate slider in the canvas.
For the More Curious: More on NSColor
The intricacies of the NSColor class are beyond the scope of this book, but the
basics are sufficient for most Cocoa programming.
NSColor provides a number of class methods returning common colors. Here is a
sampling:
class func blackColor() -> NSColor
class func purpleColor() -> NSColor
class func clearColor() -> NSColor
If you are creating custom controls, you will often want them to blend in with
the system. Since colors used in the system can vary from major releases of
OS X, and since some colors (such as the highlight color) are configurable by
the user, NSColor provides class methods to get these colors, named by their
function:
class func controlTextColor() -> NSColor
class func selectedTextBackgroundColor() -> NSColor
class func windowBackgroundColor() -> NSColor
As you have seen, you can create arbitrary colors:
let bestColor = NSColor(calibratedRed:0.0, green:0.0,
blue:0.0, alpha:1.0)
NSColor is not limited to solid colors. For more on pattern colors, see the
patternImage in the NSColor class reference.

For more information on color, see Apple’s Color Management Overview.


For the More Curious: Setting the Target
Programmatically
NSControl includes the property:
var action: Selector
Notice that the action property is a selector, so to set the action of a control
programmatically, you need to create a selector. For example, suppose you had a
button playButton and a method play:
var playButton: NSButton

func play(sender: NSButton) {
...
}
To set playButton’s action to be play, you would write:
let playSelector = Selector("play:")
playButton.action = playSelector
The first line initializes a Selector with the string "play:". The colon is
necessary because this selector is the name of an action method, and action
methods always have a parameter – sender.
Challenge: Busy Board
A busy board is a toddler’s toy that is a collection of knobs, switches, and sliders
to manipulate. Every action provides a visible response. To get practice working
with controls, create a busy board using some common Cocoa controls.
Create a new Cocoa application like you did in this chapter. Name it BusyApp.
Add the following controls:
a vertical slider and an adjacent text field that reports whether the slider is
going up, going down, or staying the same
two radio buttons that turn the slider’s tick marks on and off
a checkbox whose label reads “Uncheck me” when it is checked and
“Check me” when it is unchecked.
a secure text field and a button that “reveals” the contents of the secure text
field in a regular text field.
a button that resets the other controls to their initial state
Here are two screenshots of BusyApp running:
Figure 5.32 Initial and modified states

Here are some hints:


Use what you have learned in this chapter and in Chapter 1. Use the
documentation and browse the attributes inspector to find the properties you
need for the different controls.
You can double-click a result in the object library to see the object’s class
and some other details.
Search for horizontal and vertical lines in the object library to get the
dividers shown in Figure 5.32.
For the checkbox challenge, look into the NSButton property state.
For the radio button challenge, you get a few hints. First, ignore the Radio
Group object in the object library. That object is based on NSMatrix, which
has been informally deprecated.
Instead, start by dragging push buttons onto the window and then turn them
into radio buttons by adjusting their Style attributes. Give both buttons the
same action and distinguish between them using NSView’s tag property.
If you get stuck while working on this challenge, take a break and come back
later. A fresh perspective often makes it possible to solve a problem that seemed
intractable before. You can also find help on the forum for this book at
forums.bignerdranch.com.
Debugging Hints
Now that you are writing code, not just copying it from the book, you may need
some debugging hints.
Always watch the console.
If a Cocoa object throws an exception, it will be logged to the console and the
event loop will be restarted. If you are not watching the console, you will not
notice the error.
Always use the Debug build configuration during development.
The Release configuration has had its debugging symbols stripped. The debugger
will act a bit strangely when it is dealing with a program with no debugging
symbols.
Here are some common problems and common fixes:
Crash when programmatically using a view set up using Interface Builder.
You probably forgot to make a connection in Interface Builder. All @IBOutlets are
implicitly unwrapped optionals – optional values which are expected to be non-
nil by the time that they are used. If you forget to connect a control in
Interface Builder to its @IBOutlet, when you try to call a method on that outlet, the
app will crash.
Made connection, still nothing happens.
You probably misspelled the name of a method. Swift is case sensitive, so
adjustRed(_:) is not the same as adjustred(_:). If you have done this, when the
NIB is unarchived at runtime, you should see something like Could not
connect the action adjustRed: to target of class
_TtC14CharacterCount11MainWindowController appear in the console.
Application crashes.
If you call a method on an object that has been deallocated, it will crash your
program. (This is difficult but not impossible to do with Swift’s automatic
memory management.) Hunting down these crashers can be difficult – after all,
the problem object has already been deallocated. One way to hunt them down is
to ask the runtime to turn your objects into “zombies” instead of deallocating
them. When you send a message to a zombie, it throws a descriptive exception
that says something like, You tried to send the message count to a freed
instance of the class NSTextField. This will stop the debugger on the line
with the error.
To turn on zombies, open the Product, go to the Scheme submenu, and select Edit
Scheme.... Select the Run (application name).app action and switch to the Diagnostics tab.
Check Enable Zombie Objects.
Interface Builder will not let me make a connection.
A Swift file is messed up. A missing brace? A property declared to be NSTabView
instead of NSTableView? Look carefully.
6
Delegation
In this chapter, you will begin a new application named SpeakLine. This
application will use the NSSpeechSynthesizer class to enable users to type in a
line of text and hear it spoken by the OS X speech synthesizer.
Figure 6.1 SpeakLine in action

Let’s take a moment to talk about the two buttons and how they will function in
the app. The buttons that start and stop the speech synthesizer will be disabled
when appropriate: the Speak button will be disabled when the speech synthesizer
is speaking; the Stop button will be disabled when the speech synthesizer is not
speaking. A disabled button is “grayed-out” and cannot be clicked. In Figure 6.1,
the Speak button is disabled.
Changing the state of the buttons at the right times will require implementing a
delegate method from the NSSpeechSynthesizerDelegate protocol. But before
you get to delegation, you are going to set up the application and get it talking.
Setting up SpeakLine
Create a new Cocoa Application project (Command-Shift-N) named SpeakLine.
Configure it as shown in Figure 6.2.
Figure 6.2 Configuring the project settings for SpeakLine

Change the application to be an application with a single window controller:


1. Delete the window: Open MainMenu.xib. From the document outline, select
and delete the object labeled SpeakLine.
2. Create the window controller class with a XIB file: Create a new Cocoa
class. (Control-click on the SpeakLine group in the project navigator, select
New File..., and then choose the Cocoa class template.) Name it
MainWindowController and make it a subclass of NSWindowController
(Figure 6.3). Check the box labeled Also create XIB file for user interface to let the
class template create the XIB for you.
Figure 6.3 Configuring the project settings for SpeakLine
Override windowNibName in the window controller class: Open
MainWindowController.swift and add the following code: class
MainWindowController: NSWindowController { override var
windowNibName: String { return "MainWindowController" } override func
windowDidLoad() { super.windowDidLoad() } }
Update the code in AppDelegate.swift: In AppDelegate.swift, you are going
to replace the existing code with code that initializes an instance of
MainWindowController. This is the same code you used in RGBWell and
RandomPassword, and you will use it again in later projects. Instead of typing it over
and over again (or copying and pasting), you are going to create a code snippet.

Creating and using an Xcode snippet


A snippet is a named chunk of code. Xcode maintains a code snippet library, and
you can drag snippets from the library onto your files. The snippet library comes
with many standard snippets, and you can create custom snippets, too.
To create your snippet, reopen the RGBWell project from Chapter 5. From the
library bar in the middle of the utilities area, select the icon to reveal the
code snippet library.
Open RGBWell’s AppDelegate.swift file. Select the window controller property
declaration and the implementation of applicationDidFinishLaunching(_:)
inside of the class implementation, as shown in Figure 6.4. Then drag the
selected code onto the library.
Figure 6.4 Dragging selected code onto the snippet library

A pop-up will appear asking you describe the snippet. Name it Single-Window
Application and summarize it as Initializes a main window controller, as shown in
Figure 6.5. Click Done to add the snippet to the library.
Figure 6.5 Naming your snippet
Now you can drag this snippet from the library into your projects. Return to the
new SpeakLine project. Open AppDelegate.swift and delete the contents of the
class implementation so that the file looks like this:
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {


}
Open the snippet library and search for the Single-Window Application snippet. Drag
the snippet from the library onto the code editor and into the empty class
implementation. When you release the mouse, the code in the snippet will be
added to the file.
Figure 6.6 Dragging from the snippet library to the class file

Your AppDelegate.swift file now looks like this:


import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

var mainWindowController: MainWindowController?

func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Create a window controller with a XIB file
of the same name
let mainWindowController =
MainWindowController()
// Put the window of the window controller on
screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}

}
Run the application to confirm that everything is set up correctly. You should
see an empty Window.
If you are adding snippets to the library, you should know how to delete them,
too. To delete a snippet, simply select the snippet in the library and press Delete.

Creating the user interface


Figure 6.7 shows the objects that make up the view and controller layers of
SpeakLine. (Note that we are leaving the AppDelegate out of this diagram to focus
on the core of the application.)
Figure 6.7 Object diagram for SpeakLine’s user interface
There are three view objects, and the window controller has outlets to all three:
textField, speakButton, and stopButton. The window controller needs outlets to
the buttons so that it can enable and disable them as necessary.
Open MainWindowController.swift and add the three outlets and the button
action methods. At this point, the methods will simply print to the console.
class MainWindowController: NSWindowController {

@IBOutlet weak var textField: NSTextField!


@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!

override var windowNibName: String {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()
}

// MARK: - Action methods

@IBAction func speakIt(sender: NSButton) {


// Get typed-in text as a string
let string = textField.stringValue
if string.isEmpty {
println("string from \(textField) is
empty")
} else {
println("string is \"\
(textField.stringValue)\"")
}
}

@IBAction func stopIt(sender: NSButton) {


println("stop button clicked")
}
}
Note the if-else statement here. In Swift, the condition does not require
parentheses, but curly braces around the if and else clauses are always required
– even if the clause consists of a single line.
Also note the use of // MARK: - Action methods. This comment is an
instruction to Xcode to display “Action methods” as one of the selections
available from the jump bar. The jump bar is at the top of any Xcode editing
window and enables you to “jump” to specific locations in a file. Using //
MARK: adds a new location that you can jump to and displays the given label in
the jump bar’s pop-up menu. The dash adds a separator bar before the new label.
Next, open MainWindowController.xib. Drag a text field and two push buttons
onto the window. Arrange, resize, and configure the objects so that they match
the interface shown in Figure 6.8.
Figure 6.8 SpeakLine user interface
The text shown in Figure 6.8 is called placeholder text. It tells the user what the
text field is for and will disappear as soon as the user begins typing. To add
placeholder text, select the text field and open the attributes inspector. Type the
text shown into the Placeholder attribute.
Finally, using Figure 6.7 as a guide, make the necessary connections between the
MainWindowController and the view objects. This process should be familiar
now. For outlets, Control-click File's Owner and drag from each outlet to the
appropriate object on the canvas. For actions, select the object on the canvas and
Control-drag to File's Owner.
Figure 6.9 Connecting three outlets and two actions

Note that this procedure is not the only way to connect outlets and actions, but it
is a good choice when you are learning. It reminds you of what is actually
happening because you are always dragging from the object with the connection
to the object to be connected. In this case, the MainWindowController has outlets
that reference buttons, so you drag from File's Owner to the buttons to connect the
outlets. Each button has a target outlet that should reference the
MainWindowController as well as an action outlet, so you Control-drag from
each button to File's Owner and select the appropriate action.
Run the application. Enter some text, click the buttons, and confirm the results in
the console.
Synthesizing Speech
Cocoa includes the class NSSpeechSynthesizer; instances of this class produce
speech from text.
In MainWindowController.swift, create an instance of NSSpeechSynthesizer.
class MainWindowController: NSWindowController {

@IBOutlet weak var textField: NSTextField!


@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!

let speechSynth = NSSpeechSynthesizer()

override var windowNibName: String {


return "MainWindowController"
}

...
}
The NSSpeechSynthesizer class includes these important methods:
func startSpeakingString(_ text: String!) -> Bool

func stopSpeaking()
Modify your action methods to call these methods.
class MainWindowController: NSWindowController {

...

override func windowDidLoad() {


super.windowDidLoad()
}

// MARK: - Action methods


@IBAction func speakIt(sender: NSButton) {
// Get typed-in text as a string
let string = textField.stringValue
if string.isEmpty {
println("string from \(textField) is
empty")
} else {
println("string is \"\
(textField.stringValue)\"")
speechSynth.startSpeakingString(string)
}

@IBAction func stopIt(sender: NSButton) {


println("stop button clicked")
speechSynth.stopSpeaking()
}
}
Run the application. Type something in and click Speak.
Updating Buttons
In this section, you are going to ensure that the Speak and Stop buttons are enabled
or disabled at the appropriate times. The first step is to create a new property in
MainWindowController that will keep track of whether the speech synthesizer is
currently speaking.
In MainWindowController.swift, add the following code.
class MainWindowController: NSWindowController {

...

let speechSynth = NSSpeechSynthesizer()

var isStarted: Bool = false

override var windowNibName: String {


return "MainWindowController"
}

...
Next, adjust the button’s action methods to set this property.
// MARK: - Action methods

@IBAction func speakIt(sender: AnyObject) {


// Get typed-in text as a string
let string = textField.stringValue
if string.isEmpty {
println("string from \(textField) is
empty")
} else {
speechSynth.startSpeakingString(string)
isStarted = true
}
}
@IBAction func stopIt(sender: AnyObject) {
speechSynth.stopSpeaking()
isStarted = false
}
NSButton inherits the enabled property from NSControl. When enabled=false,
the button is “grayed-out” and cannot be clicked.
In MainWindowController.swift, add a method named updateButtons() that sets
the enabled properties of the buttons based on the value of isStarted.
class MainWindowController: NSWindowController {

...

@IBAction func stopIt(sender: AnyObject) {


speechSynth.stopSpeaking()
isStarted = false
}

func updateButtons() {
if isStarted {
speakButton.enabled = false
stopButton.enabled = true
} else {
stopButton.enabled = false
speakButton.enabled = true
}
}
}
To tie everything together, you are going to call updateButtons() whenever the
value of isStarted changes. You can make this happen using Swift’s didSet
feature.
didSet and willSet are observers that you can declare as part of a stored
property. Implementing observers allows you to respond to the property’s value
being changed.
In the isStarted property, add a didSet observer that updates the buttons.
class MainWindowController: NSWindowController {
...
@IBOutlet weak var stopButton: NSButton!

let speechSynth = NSSpeechSynthesizer()

var isStarted: Bool = false


var isStarted: Bool = false {
didSet {
updateButtons()
}
}

...
Now updateButtons() will be called whenever the value of isStarted is
changed. Run the app to confirm.
Notice that when the application is launched, both buttons are enabled. The
isStarted property is initialized to false, but that does not trigger the observer
to call to updateButtons(). Only changing the property from its initial value does
that.
In MainWindowController.swift, call updateButtons() directly in
windowDidLoad() to set the initial states of the buttons.
class MainWindowController: NSWindowController {

...

override func windowDidLoad() {


super.windowDidLoad()
updateButtons()
}

...
Run the app and confirm that the Stop button is disabled when the window is
loaded.
Your code works. There is, however, an important situation it does not handle:
when the speech synthesizer finishes on its own without interruption, the buttons
do not update to reflect that speaking has stopped. You need to know when the
speech synthesizer has finished, regardless of whether it was stopped
speech synthesizer has finished, regardless of whether it was stopped
prematurely or finished on its own. To address this problem, you are going to
learn about an important design pattern in Cocoa: delegation.
Delegation
Let’s start with a story: Once upon a time, there was a man with no name.
Knight Industries decided that if this man were given guns and wheels and
booster rockets, he would be the perfect crime-fighting tool. First they thought,
“Let’s subclass him and override everything we need to add the guns and wheels
and booster rockets.” The problem was that to subclass Michael Knight, they
needed to wire his insides to the guns, wheels, and booster rockets - a time-
consuming task requiring lots of specialized knowledge. So instead, Knight
Industries created a helper object, the Knight Industries 2000, or “KITT,” a well-
equipped car designed to assist Michael Knight in a variety of crime-fighting
situations.
While approaching the perimeter of an arms dealer’s compound, Michael Knight
would say, “KITT, I need to get to the other side of that wall.” KITT would then
blast a big hole in the wall with a small rocket. After destroying the wall, KITT
would return control to Michael, who would charge through the rubble and
capture the arms dealer.
Note how creating a helper object is different from the RoboCop approach.
RoboCop was a man subclassed and extended. The RoboCop project involved
dozens of surgeons who extended the man into a fighting machine. This is the
approach taken by many object-oriented frameworks.
In the Cocoa framework, many objects are extended in the Knight Industries
way – by supplying them with helper objects. In this section, you are going to
provide the speech synthesizer with a type of helper object called a delegate.

Being a delegate
The first thing to understand about being a delegate is that it is a role – possibly
one of many – that an object may take on. In Cocoa, roles are defined by
protocols. The specific role of “delegate that serves a speech synthesizer” is
defined by the NSSpeechSynthesizerDelegate protocol. To sign up for this role,
an object must be an instance of a class that conforms to the
NSSpeechSynthesizerDelegate protocol.
The beauty of having delegate roles defined by protocols (delegation) instead of
classes (subclassing) is that any object whose type conforms to the protocol can
serve in that capacity. Thus, you can assign critical duties without associating
them with any particular object class. Moreover, an object can take on multiple
roles as needed.
Now let’s get back to the app and your current problem and see how assigning a
delegate to the speech synthesizer can help. The NSSpeechSynthesizerDelegate
protocol contains five methods:
protocol NSSpeechSynthesizerDelegate :
NSObjectProtocol {

optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
didFinishSpeaking: Bool)

optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
willSpeakWord: NSRange,
ofString: String)

optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
willSpeakPhoneme: Int16)

optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
didEncounterErrorAtIndex: Int,
ofString: String,
message: String)

optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
didEncounterSyncMessage: String)
}
The optional keyword tells you that a class conforming to the protocol is not
required to implement the method. The NSSpeechSynthesizerDelegate protocol
does not have any methods that you are required to implement, but many other
protocols do. If a class conforming to a protocol does not implement a required
method, you will get a compile-time error.
Your current problem is that the buttons do not update to reflect that speaking
has stopped when the speech synthesizer finishes speaking on its own. You can
solve this problem with the NSSpeechSynthesizerDelegate method
speechSynthesizer(_:didFinishSpeaking:). Here are the steps you will follow
to implement this delegate method:
declare that the MainWindowController class conforms to the
NSSpeechSynthesizerDelegate protocol

implement speechSynthesizer(_:didFinishSpeaking:) to update the


buttons
set the speech synthesizer’s delegate property to point to the instance of
MainWindowController

Conforming to a protocol

First, to inform the compiler that a class conforms to a protocol, you add the
protocol name to the class definition.
In MainWindowController.swift, change the MainWindowController class
definition to include NSSpeechSynthesizerDelegate:
class MainWindowController: NSWindowController {
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate {

@IBOutlet weak var textField: NSTextField!


@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!

...

Implementing a delegate method

Next, implement speechSynthesizer(_:didFinishSpeaking:) to update the


buttons and print the value of finishedSpeaking. Because the compiler knows
that MainWindowController conforms to NSSpeechSynthesizerDelegate, you can
use auto-complete when writing implementations of protocol methods. To elicit
suggestions from auto-complete, start typing the method name, ignoring the
func keyword. Once you have accepted a suggestion by pressing Return, func
will be added for you.
// MARK: - NSSpeechSynthesizerDelegate

func speechSynthesizer(sender:
NSSpeechSynthesizer,
didFinishSpeaking
finishedSpeaking: Bool) {
isStarted = false
println("finishedSpeaking=\
(finishedSpeaking)")
}

Setting the delegate property

Finally, in MainWindowController’s windowDidLoad(), set the delegate property


of the speech synthesizer to the MainWindowController.
class MainWindowController: NSWindowController {

...

override func windowDidLoad() {


super.windowDidLoad()
updateButtons()
speechSynth.delegate = self
}

...

}
Note that NSSpeechSynthesizer’s delegate is not defined as a strong reference.
This is true of all delegate properties and prevents a strong reference cycle from
occurring between the delegate and the object with the delegate.
Build and run the application. Type something in, start up the speech
synthesizer, and let it finish. The buttons should now update appropriately, and
the console will report that finishedSpeaking = true.
Let’s try the other case. Start the speaking again and this time, click Stop to
interrupt the speaking before it finishes. The console will report that
finishedSpeaking = false. The speechSynthesizer(_:didFinishSpeaking:)
method is called whenever speaking stops – whether the speech synthesizer
finished on its own or whether it was stopped mid-stream. Because this method
is called whenever speaking stops, you no longer need to update the buttons
when the Stop button is clicked.
In stopIt(_:), remove the redundant call to updateButtons().
@IBAction func stopIt(sender: AnyObject) {
speechSynth.stopSpeaking()
isStarted = false
}
Run the app again and confirm that the buttons still update as you expect when
the Stop button is clicked.

Implementing another delegate


Earlier, you learned that each delegate role is defined by a protocol. You have
seen the NSSpeechSynthesizerDelegate protocol. Now let’s look at another.
The NSWindow class also has a delegate property. You can find the methods in
the NSWindowDelegate protocol on the NSWindowDelegate reference page. Just like
classes, protocols have reference pages in Apple’s documentation.
But instead of going to the protocol reference page right now, consider the
following subset of methods from the NSWindowDelegate protocol:
protocol NSWindowDelegate : NSObjectProtocol {
...

optional func windowDidResize(_ notification:
NSNotification)
NSNotification)

optional func windowWillEnterFullScreen(_
notification: NSNotification)

optional func windowDidEnterFullScreen(_
notification: NSNotification)

optional func windowShouldClose(_ sender:
AnyObject) -> Bool

...
}
The first three methods have a similar purpose: the window is informing the
delegate of what has happened or what is about to happen so that the delegate
can coordinate any action to be completed at that time. Methods that inform the
delegate of an impending or completed event are common in delegate protocols.
The fourth method is different. The window is asking its delegate a question: “I
want to close; may I do so?” This is an example of a second type of delegate
method – one where the delegate can directly affect the behavior of the object as
opposed to being passively informed of events.
In SpeakLine, the window currently can be closed at any time. You are going to
implement the NSWindowDelegate method windowShouldClose(_:) to prevent the
window from closing while the speech synthesizer is speaking.
First, in MainWindowController.swift, add the new protocol to the class
definition.
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate {
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate {

@IBOutlet weak var textField: NSTextField!


@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!

...
}
Then, implement windowShouldClose(_:) to return the value of the isStarted
property.
...

// MARK: - NSSpeechSynthesizerDelegate

func speechSynthesizer(sender:
NSSpeechSynthesizer,
didFinishSpeaking
finishedSpeaking: Bool) {
updateButtons()
println("finishedSpeaking=\
(finishedSpeaking)")
}

// MARK: - NSWindowDelegate

func windowShouldClose(sender: AnyObject) -> Bool


{
return !isStarted
}

}
What about setting the delegate property? It has already been done. When you
ask Xcode to create a XIB file along with your NSWindowController subclass, that
XIB file includes a window, and that window’s delegate property is set to File's
Owner.

Run the app. Start up the speech synthesizer and then try to close the window.
The window will not close until the speech synthesizer has stopped speaking.
You did not even have to subclass the window to customize its behavior.

Common errors in implementing a delegate


There are two common errors that developers make when implementing a
delegate. These errors are tricky because they do not result in compiler errors or
even warnings.
If your app runs but the delegate methods that you have implemented are not
being executed, you may have made one of the classic blunders:
Forgetting to set the delegate property: This step is surprisingly easy to
miss in the details of declaring protocols and implementing delegate
methods. The delegate property is optional, so the compiler cannot warn
you about forgetting it.
Misspelling the name of an optional delegate method: Delegate method
names are often long and unwieldy, and if you misspell an optional method
from a protocol, the compiler cannot interpret it as a mistake; it can only
assume that you are implementing a brand-new method. (If you misspell a
required method, you will get an error – not because the compiler noticed
the spelling error, but because it noticed the absence of a required method.)
If you like, try it for yourself. In MainWindowController.swift, make a
spelling error in the definition for
speechSynthesizer(_:didFinishSpeaking:). For example, change the
capital S in speechSynthesizer to a lower-case s. Because Swift is case-
sensitive, this difference in case is a spelling error.
func speechsynthesizer(sender:
NSSpeechSynthesizer,
didFinishSpeaking
finishedSpeaking: Bool) {
updateButtons()
println("finishedSpeaking=\
(finishedSpeaking)")
}

Notice the lack of warnings or errors from Xcode. Run the app and start up
the speech synthesizer. The Stop button can never be enabled. The delegate
method is not being called and there is nothing to tip you off to the problem
– other than the app not working right.
(Be sure to correct the spelling before continuing!)
The best way to avoid misspelling method names is to use auto-complete
when typing them in. If you do not know the method name well enough to
start typing it, you can copy the method declaration from the documentation
or from the protocol’s header file.
To get to the header file for NSSpeechSynthesizerDelegate, select the
protocol name in the class definition and Command-click. The header file
will open in the editor, and you can see all the definitions in the protocol.
(Command-clicking to see the header file also works with other types, like
classes.)
To leave the header file and get back to MainWindowController.swift, click
the back button ( ) at the top left of the editor’s toolbar.
func speechsynthesizer(sender:
NSSpeechSynthesizer,
didFinishSpeaking
finishedSpeaking: Bool) {
updateButtons()
println("finishedSpeaking=\
(finishedSpeaking)")
}

Cocoa classes that have delegates


Delegation is a commonly used design pattern in Cocoa. If you want to find out
if a Cocoa class has an associated delegate protocol, visit the class reference
page and search for a delegate property. If the class has a delegate property,
then the protocol’s reference page will be linked in the property definition.
Here are some of the classes in the AppKit framework that use delegation:
NSAlert NSMatrix NSTabView
NSAnimation NSMenu NSTableView
NSApplication NSPathControl NSText
NSBrowser NSRuleEditor NSTextField
NSDatePicker NSSavePanel NSTextStorage
NSDrawer NSSound NSTextView
NSFontManager NSSpeechRecognizer NSTokenField
NSImage NSSpeechSynthesizer NSToolbar
NSLayoutManager NSSplitView NSWindow
Delegate protocols and notifications
Many methods in delegate protocols, including NSWindowDelegate and
NSApplicationDelegate, have a single parameter of type NSNotification. You
implement these methods just as you would any other delegate method, but they
are forwarded to the delegate by the notification center rather than being called
directly by the object with the delegate property. You will learn how
notifications and the notification center work in Chapter 16.
NSApplication and NSApplicationDelegate
Every Cocoa application has a single instance of NSApplication and a single
instance of a custom class named AppDelegate, which conforms to the
NSApplicationDelegate protocol. When events happen in the lifecycle of the
application, methods on the NSApplicationDelegate protocol are called. You
implement these methods as needed in the “app delegate.”
Here are a few methods from the NSApplicationDelegate protocol:
optional func applicationDidFinishLaunching(_
aNotification: NSNotification)

optional func application(_ application:
NSApplication,
willPresentError error: NSError) ->
NSError


optional func applicationShouldTerminate(_ sender:
NSApplication) ->

NSApplicationTerminateReply

optional func applicationWillTerminate(_
aNotification: NSNotification)
When you create a new project with the Cocoa application template, the
template creates the AppDelegate class. The class is declared as conforming to
the NSApplicationDelegate protocol and has stubs for the two most-commonly
implemented delegate methods:
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!

func applicationDidFinishLaunching(aNotification:
NSNotification) {
NSNotification) {
// Insert code here to initialize your
application
}

func applicationWillTerminate(aNotification:
NSNotification) {
// Insert code here to tear down your
application
}

}
You never call these methods yourself; they are triggered automatically by the
application lifecycle events.
Wondering about @NSApplicationMain? This line triggers the essential steps to
launching a Cocoa application: instantiating the NSApplication and the
AppDelegate and setting the NSApplication’s delegate property to the
AppDelegate.

The main event loop


Cocoa applications are event-driven. When a Cocoa application is launched, the
NSApplication starts and maintains an event loop on the main thread. The main
event loop’s job is to wait around for an event to happen, like a key being
pressed or a mouse being clicked.
When an event occurs, it is forwarded from the window server to the appropriate
NSApplication, which puts it in its event queue. Then, when an event is queued
up, the event loop finds the appropriate method to handle that event and calls it.
This method calls other methods, which call more methods, and so on. Once all
of the methods have returned, control returns to the loop, and the next event is
read in. If the queue is empty, the NSApplication goes back to sitting around and
waiting for something to happen.
Your code is executed as part of the chain of method calls that the event loop
kicks off in response to an event. Thus, your code is at the mercy of the event
loop. Cocoa will call you. A big part of learning Cocoa is learning the right
methods to implement – action methods, delegate methods, and notification
observers (more on those in Chapter 16) – to get your code executed at the right
time.
For the More Curious: How Optional
Delegate Methods Work
At the beginning of this chapter, you saw that the NSSpeechSynthesizerDelegate
protocol defines five optional methods. In your delegate, you implemented only
one of them, didFinishSpeaking. A delegate does not have to implement an
optional method, but if it does, it will get called. In many languages, this sort of
thing would be impossible. How is it achieved in Swift?
NSSpeechSynthesizer is a standard Cocoa class written in Objective-C, but if it
were written in Swift the implementation might contain something like this:
class NSSpeechSynthesizer {
...
weak var delegate: NSSpeechSynthesizerDelegate?
...
func startSpeakingString(text: String) -> Bool {
// Start speaking the string
...
// Finished speaking - if there is a delegate,
let it know (if it cares)
delegate?.didFinishSpeaking?(sender: self,
finishedSpeaking: true)
}
The highlighted line uses optional chaining. Only if all the optionals have values
will the entire line be executed. In SpeakLine, you set up a delegate and
implemented didFinishSpeaking. Thus, both delegate? and
didFinishSpeaking? are non-nil, and your didFinishSpeaking method gets
called.
Imagine the other cases. If you did not set up a delegate for the speech
synthesizer, then delegate? would evaluate to nil, and the line would short-
circuit, which means that the remainder of the line is not executed. If you set up
a delegate but did not implement the optional didFinishSpeaking method, then
didFinishSpeaking?, would evaluate to nil, and the line would short-circuit
there.
Challenge: Enforcing a Window’s Aspect
Ratio
Create a new single-window application with one window. As the user resizes
the window, make sure that the window always remains twice as tall as it is
wide.
Here is the signature of the delegate method you will implement:
optional func windowWillResize(sender: NSWindow,
toSize frameSize: NSSize) ->
NSSize
The first argument is the window that is being resized. The second argument
describes the size that the user has asked for. The return value is the size that the
user will get.
Hint: The second argument and the return value are typealiases for a struct:
struct CGSize {
var width: CGFloat
var height: CGFloat
}
Here is how you create an appropriate NSSize to return that is 200 points wide
and 100 points tall:
let mySize = NSSize(width: 200.0, height: 100.0)
println("mySize is \(mySize.width) wide and \
(mySize.height) tall")
You can set the initial size of the window in the size inspector in Interface Builder.
7
Working with Table Views
So far, the speech synthesizer has been speaking in the system-wide default
voice. In this chapter, you will enable the user to choose what voice the speech
synthesizer speaks in. In particular, you are going to add a table view to SpeakLine
that lists the voices available on the system. The user can then select a row in
this table to change the voice. The completed application is shown in Figure 7.1.
Figure 7.1 Users can change the speech synthesizer’s voice

Before diving into the application, let’s go over some general ideas about table
views.
About Table Views
A table view is used to display data arranged in rows and columns. You can find
an example of a table view in the Sound Effects tab of the Sound preference pane in
System Preferences (Figure 7.2). Each row in the table view corresponds to a
different sound effect. The two columns correspond to attributes of the a sound:
its name and its type.
Figure 7.2 Alert sounds are presented in a table view with two columns

Delegates and data sources


A table view can have two helper objects: a delegate and a data source.
Typically, a single object plays both roles.
One of the roles of the table view’s delegate is to respond to user interaction
with the table view. When the user interacts with a table view – such as by
selecting a row – the table view notifies its delegate. As the delegate of the table
view, an object can react when the user interacts with the table view. For
example, when the user selects a row in the Sound Effects tab, the table view’s
delegate plays the sound effect that corresponds to that row.
You set up the delegate for the table view in the same way that you set up the
delegate for the speech synthesizer in Chapter 6: The class of the object to be the
data source must conform to the NSTableViewDelegate protocol. The class
implements the necessary delegate methods. The table view’s delegate property
is set to an instance of that class.
The table view’s data source supplies the table view with the data it should
display. A table view knows how to display data, but in keeping with Model-
View-Controller, the table view should not know about the data itself. Thus, the
table view requires a data source (a controller) to talk to the model on its behalf.
This indirection allows the table view to display virtually any kind of indexable
data.
Like the delegate, the data source is a role that is defined by a protocol, and you
set up the data source along the same lines: The class of the object to be the data
source must conform to the NSTableViewDataSource protocol. The class
implements the necessary data source methods. The table view’s dataSource
property is set to an instance of that class.

The table view-data source conversation


When first working with NSTableView and its data source, most programmers
expect the data source to be the boss and order the table view around saying,
“Display this list of objects.” It does not work that way. The data source waits on
the table view to ask for data. The data source is the table view’s helper rather
than its boss.
In addition, the table view asks for its data one piece at a time rather than all at
once. The table view asks for a single object to display at a specific row and
column combination. The data source complies by returning the object that holds
that specific data.
The conversation between a table view and its data source consists mainly of the
table view asking two questions. The first question is asked once at the
beginning: “How many rows are there in the data set?” The second question is
asked repeatedly and as necessary: “May I have the data for row X and column
Y?” The table view uses the answer to the first question to ensure that it only
inquires about valid rows in the second.
These two questions are methods from the NSTableViewDataSource protocol:
func numberOfRowsInTableView(tableView: NSTableView) -
> Int

func tableView(tableView: NSTableView,
objectValueForTableColumn
tableColumn: NSTableColumn?,
row: Int) -> AnyObject?

SpeakLine’s table view and helper objects


In SpeakLine, the MainWindowController will be both the data source and the
delegate of the table view. Figure 7.3 shows the objects in SpeakLine and their
relationships. The ones you will add in this chapter are highlighted.
Figure 7.3 SpeakLine and its new table view
Getting Voice Data
The first thing you are going to do is get the data that the data source will
provide to the table view. This is the list of voices that the system has available
for the speech synthesizer.
To find out what voices are available, you call the following
NSSpeechSynthesizer class method:
class func availableVoices() -> [AnyObject]?
This method returns an array that contains voice identifiers. Voice identifiers are
strings written in reverse domain notation. Here is an example of a voice
identifier: com.apple.speech.synthesis.voice.Fred.
Also, notice that availableVoices() is a class method. This means you cannot
call availableVoices() on an instance of NSSpeechSynthesizer – only on the
NSSpeechSynthesizer class itself. Class methods typically retrieve general
information about a class.
In MainWindowController.swift, call availableVoices() on
NSSpeechSynthesizer and store the results in a property.
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate {

...

let speechSynth = NSSpeechSynthesizer()

let voices = NSSpeechSynthesizer.availableVoices()


as! [String]

var isStarted: Bool = false {


didSet {
updateButtons()
}
}
...
}
The availableVoices() method returns an array of NSString objects instead of
an array of Swift strings. You cast the returned array to [String] so that you can
proceed using Swift types instead of Objective-C classes.
Next, in windowDidLoad(), print the array of voice identifiers to the console.
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate {
...

override func windowDidLoad() {


super.windowDidLoad()
updateButtons()
speechSynth.delegate = self
println(voices)
}

...
}
Run the app, check the console, and confirm that your users will have many
voices to choose from.

Retrieving friendly names


Your table view should display friendlier names, like Fred, rather than the voice
identifiers. You can retrieve the name of a voice from the voice’s attributes,
which you can get by calling another NSSpeechSynthesizer class method and
passing in a voice identifier:
class func attributesForVoice(voiceIdentifier: String)
-> [NSObject : AnyObject]?

This method returns an optional dictionary. The dictionary is an optional because


its existence depends on whether the passed-in string is a valid voice identifier.
The key in the voice attributes dictionary that stores the voice’s name is defined
The key in the voice attributes dictionary that stores the voice’s name is defined
as
let NSVoiceName: NSString!
In MainWindowController.swift, add a method that retrieves a voice name for a
given voice identifier.
...

func voiceNameForIdentifier(identifier: String) ->


String? {
if let attributes =
NSSpeechSynthesizer.attributesForVoice(identifier) {
return attributes[NSVoiceName] as? String
}
else {
return nil
}
}

// MARK: - NSSpeechSynthesizerDelegate

...

}
Then, in windowDidLoad(), print out the name of each voice.
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate {
...

override func windowDidLoad() {


super.windowDidLoad()
updateButtons()
speechSynth.delegate = self
println(voices)
for voice in voices {
println(voiceNameForIdentifier(voice)!)
}
}

...
}
Run the app and confirm that you have a list of friendly voice names. Later, you
will use the voiceNameForIdentifier(_:) method to assist the data source in
returning data to the table view. But for now, let’s turn to adding the table view
to the application’s interface.
Adding a Table View
Open MainWindowController.xib. As shown in Figure 7.1, the table view will sit
to the right of the text field and be roughly half its size.
In the canvas, select the window and drag its right edge to make it wide enough
to allow for the table view. Then, in the object library, find Table View and drag
one onto the window next to the text field (Figure 7.4).
Figure 7.4 Dragging a table view

(A yellow Auto Layout warning may appear to the right of the window in the
document outline. Do not worry about this warning. It will not cause any
problems, and you will learn about Auto Layout in Chapter 25.)

Table view and related objects


In the document outline, notice that the table view that you just added is not
simply an instance of NSTableView; it is a collection of several objects that work
together to provide a full-featured table view experience to the user (Figure 7.5).
Figure 7.5 Table view dragged from object library consists of several
objects

You will not be interacting with the objects surrounding the table view in this
exercise, but it is good to know what these objects are and what they do. At the
outermost-level, there is a Bordered Scroll View – an instance of NSScrollView. The
scroll view is necessary because a table can have more data than it can display at
one time, so the user can scroll the data as needed. The scroll view contains a
clip view, two scrollers, and a table header view:
The Clip View is an instance of NSClipView. A scroll view always comes with
clip view, which manages drawing the currently visible portion of the table.
The clip view contains the instance of NSTableView.
The two Scroller objects are instances of NSScroller. These are the controls
that allow the user to scroll the table.
The Table Header View is an instance of NSTableHeaderView. The table header
view displays table column titles and responds to mouse clicks on the titles.

Table columns

The Table View object is the instance of NSTableView, and a table view contains
one or more instances of NSTableColumn.
The default table view in Interface Builder comes with two columns, but your table
view only needs one. In the document outline, select one of the Table Column
objects and delete it.
The remaining column needs a title. In the document outline, select the Table
Column and, in the attributes inspector, change the title attribute to Voices.

Notice that the table header view still appears to have two column headers. In
reality, it has a single column header that has not yet resized itself. Select the
Scroll View in the document outline and then drag its edges in the canvas to resize
it for a single column. This will cause the header to resize itself as well.

Disabling resizing of the window

Earlier you enlarged the window to make room for the table view. When you
change the size of the window of an app that you have already run, OS X
remembers the last window size. This means that the window’s new size may
not be reflected in the next run.
In Chapter 25, you will learn how to add Auto Layout constraints to your layout,
which will ensure that the window and its views size and arrange themselves in a
reasonable manner no matter what changes are made or how the window was
previously sized. In the meantime, to keep OS X from helpfully remembering
the window’s last size, you can disable resizing the window altogether.
In the document outline, select the SpeakLine window and reveal its attributes.
Find the checkbox labeled Resize and uncheck it (Figure 7.6).
Figure 7.6 Disabling window resizing
Now you will not be able to resize the window while the app is running, but you
can still adjust the window’s size in Interface Builder.
Before running, make any necessary adjustments to the width of the window and
reposition the buttons beneath the table view, as shown in Figure 7.7.
Figure 7.7 Layout for the SpeakLine window

Run the app to see the empty table view that is now a part of SpeakLine’s user
interface. That was the easy part. Getting it to display the right data will involve
several steps. It will help to know more about the objects inside the table view.
Tables, Cells, and Views
A table has columns and rows. The smallest unit in a table, formed by the
intersection of a row and a column, is a “cell.” The same term is also used to
refer to an instance of NSCell.
Historically, the difference between these two uses of “cell” was not a problem
because tables in Cocoa were always cell-based tables. In a cell-based table, a
cell is always an instance of NSCell or one of its subclasses.
Cell-based tables can technically still be used, but they have been deprecated in
favor of view-based tables. In a view-based table, a table cell is an instance of
NSView or one of its subclasses – most commonly NSTableCellView.

Table cell views


A table cell view can be as simple or complex as your application needs. Each
column in your table has a table cell view of a particular type. Interface Builder’s
object library offers two types of table cell view:
Text Table Cell View contains a single text field
Image & Text Table Cell View contains a text field and an image view

You can also create custom table cell views, although doing so is an advanced
technique beyond the scope of this book.
Your table view’s single column only needs to display a name, so a Text Table Cell
View is what you want. Fortunately, this is the type of table cell view that is
included by default in a table column. Figure 7.8 shows the objects that make up
the Voices column’s table cell view.
Figure 7.8 Object types within a Text Table Cell View
Within the text table cell view is a text field labeled Table View Cell. As an instance
of NSTextField, this text field comes with an actual NSCell – an NSTextFieldCell
that is also labeled Table View Cell. You can ignore the text field cell; you will only
need to think about the table cell view and the text field for this application.
(If you are unsure about the type of an object in the document outline, use its
icon as a hint. For instance, controls are always represented with slider icons.
You can also open the identity inspector and check the object’s class name.)
Now think back to the table view-data source conversation you learned about
earlier in the chapter. When the table view asks for data to display in a particular
cell, the data source returns an object. The table view responds by updating the
table cell view at the requested row and column. In particular, the table view sets
the objectValue property of the table cell view to the object that the data source
returned.
Setting the objectValue of the table cell view will not affect what the table
actually displays. The table displays the value of the text field of the table cell
view. This means you must do a little more work to ensure that the value of the
text field is updated when the objectValue of the table cell view is set. You will
do this shortly using Cocoa bindings, but first let’s set up the
MainWindowController to be the data source for the table view.
The NSTableViewDataSource Protocol
Setting up a data source for a table view follows the same pattern that you saw in
Chapter 6 when setting up a delegate for the speech synthesizer. There are three
steps: conforming to the protocol, implementing methods from the protocol, and
setting the dataSource property.

Conforming to the protocol


In MainWindowController.swift, add the NSTableViewDataSource protocol to the
MainWindowController class definition.
import Cocoa

class MainWindowController: NSWindowController,


NSSpeechSynthesizerDelegate,
NSWindowDelegate {
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate,
NSTableViewDataSource {
...

Connecting the dataSource outlet


In the last chapter, you set the speech synthesizer’s delegate property in code.
But NSTableView has exposed its dataSource property as an outlet in
Interface Builder, so you can connect it there.

In MainWindowController.xib, Control-click the table view in the document


outline and drag from the dataSource outlet to File's Owner.
Figure 7.9 Connecting the table view’s dataSource
Note that dragging between two objects in the document outline works the same
as dragging between the document outline and the canvas. This is handy when
working with a collection of views because it can be difficult to tell which object
you have selected in the canvas.
Run the app and scroll up to the beginning of the console’s output to find this
error:
*** Illegal NSTableView data source
(<SpeakLine.MainWindowController:
0x6080000e3680>).
Must implement numberOfRowsInTableView: and
tableView:objectValueForTableColumn:row:
The methods named in the error are optional in the protocol but required at
runtime if you are using data source methods to supply data to the table view.
The other option is using bindings to supply data to the table view. Even though
you will use bindings shortly, you will not be using them to supply data to the
table. Thus, these two methods are required.

Implementing data source methods


In MainWindowController.swift, implement the required data source methods
using what you know about voices.
...

// MARK: - NSWindowDelegate

func windowShouldClose(sender: AnyObject) -> Bool


{
return !isStarted
}

// MARK: - NSTableViewDataSource

func numberOfRowsInTableView(tableView:
NSTableView) -> Int {
return voices.count
}

func tableView(tableView: NSTableView,


objectValueForTableColumn
tableColumn: NSTableColumn?,
row: Int) -> AnyObject? {
let voice = voices[row]
let voiceName = voiceNameForIdentifier(voice)
return voiceName
}

}
Run the app. The table view is displaying something, but it is not the names of
the voices (Figure 7.10).
Figure 7.10 Table view not displaying names
(If your table view is not displaying anything at all, remember the classic
protocol blunders: check that the table view’s dataSource property has been
connected and check the spelling of the data source method names.) What is
going on with this table view? First, note that the table view-data source
conversation is working. For each cell, the data source is returning an object, and
the table view is setting that object to the objectValue of the table cell view.
However, remember that updating the table cell view has no automatic effect on
the text field, so the text field in each table cell view defaults to displaying its
label – Table View Cell.
To fix this situation, you are going to use Cocoa bindings to bind the value of
each text field to the objectValue of its table cell view.

Binding the text field to the table cell view


Bindings are an important feature of Cocoa, and you will take a closer look at
them in Chapter 8. For now, you are going to create a single binding in
Interface Builder.

Return to MainWindowController.xib. In the document outline, select the text


field (Table View Cell) and click the icon to switch to the bindings inspector
(Figure 7.11). In the Value section at the top of the inspector, expand the
disclosure arrow to the left of the Value binding. In the pop-up menu to the right
of the Bind to label, make sure that Table Cell View is selected. Then click the
checkbox to the label’s left.
Figure 7.11 Binding the value of the text field
Notice the that Model Key Path is objectValue. This refers to the objectValue
property of the table cell view. You have just bound the value of the text field to
the objectValue of the table cell view. With this binding in place, whenever the
table cell view’s objectValue changes, the text field will be updated with the
same data.
Run the application and confirm that your table view displays the names of the
available voices. Scroll down the list to see more names. As you scroll, the table
view asks its data source for more data, and for each row, the data source returns
an object holding the data that the table view needs. The table view uses this
object to set the objectValue of the appropriate table cell view, and thanks to the
binding you created setting objectValue automatically updates the value of the
table cell view’s text field and the voice name appears in the table.
This is just your first brush with bindings, but they are pervasive in Cocoa
development. You will learn more about them in Chapter 8 and have many more
opportunities to work with them over the course of the book.
The NSTableViewDelegate Protocol
The second (and far simpler) part of this chapter is enabling the user to change
the voice of the speech synthesizer by selecting a voice from the table. The user
can make a selection by clicking the table row or by using the arrow keys to
highlight a voice. User interaction with a table view is handled by the table
view’s delegate. As the delegate of the table view, your object can react when
the user interacts with the table view.
In MainWindowController.swift, add the NSTableViewDelegate protocol to the
MainWindowController class definition.
import Cocoa

class MainWindowController: NSWindowController,


NSSpeechSynthesizerDelegate,
NSWindowDelegate,
NSTableViewDataSource {
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate,
NSTableViewDataSource,
NSTableViewDelegate {
...
In MainWindowController.xib, Control-click the table view in the document
outline and drag from the delegate outlet to File's Owner.
Figure 7.12 Connecting the table view’s delegate
To be able to respond to the user’s selection, the delegate will need to know
which row was selected. This information is available from the NSTableView
property selectedRow.
To access the table view’s selectedRow, the MainWindowController needs an
outlet to the table view. You could make this connection the way you have in the
past: open MainWindowController.swift, add an outlet named tableView, and
then return to Interface Builder to connect the outlet. But this time, you are going to
use the assistant editor.

Making a connection with the assistant editor

With MainWindowController.xib open, click the icon in the Xcode toolbar.


This will open the assistant editor, which is a second editor that allows you to
work with two files at once. The assistant editor will open
MainWindowController.swift to let you see the XIB file and the controller code
at the same time.
(If you need room to see both files, remember that you can hide the navigator
and utilities areas using the group of buttons at the top righthand side of Xcode’s
toolbar.) In MainWindowController.xib, select the table view in the document
outline. Control-drag from the table view to MainWindowController.swift in the
assistant editor. Release the mouse just below the other outlets (Figure 7.13).
Figure 7.13 Dragging to the file in the assistant editor

When you release the mouse, a pop-up will appear asking you to configure the
connection. Make it an outlet, name it tableView, and specify the type as
NSTableView (Figure 7.14). Then click Connect.

Figure 7.14 Configuring the connection

The outlet will immediately appear in MainWindowController.swift. Even better,


this outlet is already connected to the table view object in
MainWindowController.xib, so you do not have to open the connections panel
and connect the outlet separately. When you make a connection by dragging to
the assistant editor, you are creating an outlet in code and connecting it in
Interface Builder in one step.

(You can also connect actions this way. When the pop-up for the new connection
appears, you change Outlet to Action, name the action, and specify the control
type.) Making connections using the assistant editor is faster than switching
between files, but faster is not necessarily better. For one thing, many
programmers are more comfortable writing code than dragging and dropping. Or
you may want to define your outlets and action methods before starting work on
the interface. When you are learning, switching between files can help cement
the idea of what is happening as you switch between your code and interface, but
feel free to use either process as you continue through the book.

To close the assistant editor and return to the standard editor, click the icon
on the Xcode toolbar.

Implementing a delegate method


In MainWindowController.swift, implement the NSTableViewDelegate method
tableViewSelectionDidChange(_:) to take the value of the selected row and use
it to set the speech synthesizer’s voice.
// MARK: - NSTableViewDelegate

func tableViewSelectionDidChange(notification:
NSNotification) {
let row = tableView.selectedRow

// Set the voice back to the default if the


user has deselected all rows
if row == -1 {
speechSynth.setVoice(nil)
return
}
let voice = voices[row]
speechSynth.setVoice(voice)
}

}
Build and run the application. Try out a new voice or two.
Pre-selecting the default voice
There is one nice feature left to add: when the application starts, the default
voice should be pre-selected in the table view so that the reader can see what
voice will be spoken in by default.
Within windowDidLoad(), add code to select the appropriate row and, if
necessary, scroll to it:
func windowDidLoad() {
super.windowDidLoad()
updateButtons()
speechSynth.delegate = self
for voice in voices {
println(voiceNameForIdentifier(voice)!)
}
let defaultVoice =
NSSpeechSynthesizer.defaultVoice()
if let defaultRow = find(voices, defaultVoice)
{
let indices = NSIndexSet(index:
defaultRow)
tableView.selectRowIndexes(indices,
byExtendingSelection: false)
tableView.scrollRowToVisible(defaultRow)
}
}
The find() function takes two parameters: a sequence and an element that is a
potential member of that sequence. It returns an optional index within the
sequence – or nil if the element is not in the sequence. It is most commonly
used to find the index of a value within an array, as you are doing here.
To test this new feature, you will need to change the default voice on your
system. You can do this in the Dictation & Speech preference pane in System Preferences.
In this chapter, you have implemented a simple table view – and it took a lot to
get there. Implementing table views is a complex topic. To learn more, visit
Apple’s Table View Programming Guide for Mac. You can find this or any other
Apple guide by opening the documentation (Window → Document and API Reference)
and searching for the guide’s title. As you will learn in Chapter 9, Cocoa
bindings can be used to avoid the table view data source code altogether.
However, manually implementing a data source provides a much greater degree
of control.
Challenge: Make a Data Source
Make a to-do list application. The user will type a task into the text field. When
the user clicks the Add button, you will add the string to an array and the new task
will appear at the end of the list (Figure 7.15).
Figure 7.15 Objects in to-do list app

How will you get the table view to fetch updated information? You tell the table
view to reload its data using the aptly-named method reloadData(). The table
view will then reload all the cells that the user can see.
You get extra points for making the table view editable.
8
KVC, KVO, and Bindings
Key-value coding (KVC) is a mechanism that allows you to get and set the value
of a property indirectly using a key. A key is the name of a property as a string.
KVC has been part of Cocoa for a long time, and important features of Cocoa,
like Core Data and Cocoa bindings, rely on KVC. To understand and use these
technologies, it helps to understand the basics of KVC.
To experiment with KVC, create a new playground. From Xcode’s File menu,
select New... → Playground. Name the playground KVC and save it with your
previous exercises.
At the top of the file, define a Student class as a subclass of NSObject. Give it
two variables with default values.
import Cocoa

var str = "Hello, playground"

class Student: NSObject {


var name = ""
var gradeLevel = 0
}
(The gradeLevel property is based on the US school system that runs from
kindergarten (0) to 12th grade.)
Student must be a class that inherits from NSObject (directly or indirectly)
because the KVC methods that you will use shortly are defined on NSObject.
Create an instance of Student and then use the KVC method
setValue(_:forKey:) to update its properties.
class Student: NSObject {
var name = ""
var gradeLevel = 0
}

let s = Student()
{__lldb_expr_...
s.setValue("Kelly", forKey:"name")
{__lldb_expr_...
s.setValue(3, forKey:"gradeLevel")
{__lldb_expr_...
To see the property values for the new instance, click the eye icon in the sidebar.
The KVC method for getting the value of a property with a key is
valueForKey(_:). Use this method to access the same properties.
s.name
"Kelly"
s.gradeLevel
3
s.valueForKey("name")
"Kelly"
s.valueForKey("gradeLevel")
3
Why are these abilities interesting? By themselves, they may seem like just an
interesting parlor trick, but many Cocoa features rely on the ability to get and set
values by key. Let’s take a look at one of those features – Cocoa bindings.
Bindings
Bindings use KVC as well as KVO (key-value observing, which you will learn
about shortly). Bindings can simplify and, in some cases, eliminate the code that
links the view layer and the model layer of an application. To see how this
works, you are going to create a new application named Thermostat.

Setting up Thermostat
In Xcode, create a new project (Command-Shift-N) that is a Cocoa Application. Name
the project Thermostat, set the language to Swift, and uncheck Use Storyboards,
Create Document-Based Application, and Use Core Data.

Thermostat will be a single-window application, so do the four-step single-window


application shuffle to tweak what Xcode’s template created:
1. Delete the existing window: Open MainMenu.xib. From the document
outline, select and delete the window object labeled Thermostat.
2. Replace the implementation of AppDelegate with the snippet you created in
Chapter 5 (or type in the code listed below): Open AppDelegate.swift and
delete the code inside the class implementation. In the code snippet library (
), find the Single-Window Application snippet and drag it into the empty class
implementation. This will cause an error for the moment because the
MainWindowController class does not exist yet. (You will create it next.)
Your AppDelegate.swift file should now look like this: class AppDelegate:
NSObject, NSApplicationDelegate { var mainWindowController:
MainWindowController? func
applicationDidFinishLaunching(aNotification: NSNotification) { //
Create a window controller with a XIB file of the same name let
mainWindowController = MainWindowController() // Put the window
of the window controller on screen
mainWindowController.showWindow(self) // Set the property to point
to the window controller self.mainWindowController =
mainWindowController } }
3. Create the MainWindowController class: In the project navigator, right-click
the Thermostat group and select New File.... Create a new Cocoa class named
MainWindowController and make it a subclass of NSWindowController.
Check the Also create XIB file for user interface box.
4. Override windowNibName: Because you used the parameter-less constructor
to create the instance of MainWindowController, you must provide the name
of the NIB file in the MainWindowController class. In
MainWindowController.swift, add the following code: class
MainWindowController: NSWindowController { override var
windowNibName: String { return "MainWindowController" } override
func windowDidLoad() { super.windowDidLoad() } }
Run the application to confirm that everything is set up correctly. You should
see an empty Window.
Figure 8.1 shows what Thermostat will look like.
Figure 8.1 Thermostat application
In MainWindowController.xib, set up this interface. Drag a vertical slider, a label,
and two buttons onto the canvas. Change the button titles to Warmer and Cooler.
Resize and arrange the views and the window to match Figure 8.1.
Next, select the slider and open the attributes inspector. Check the box labeled
Continuous.

Finally, select the window and return to the attributes inspector. Change the
window’s title to Thermostat and uncheck the box labeled Resize to keep the window
from being resized when the application is running.

Using bindings
This application is similar enough to what you have done before that you can
probably imagine how you would write the code and wire up the interface: The
slider and label would display the default temperature on launch. The action
methods of the slider and the buttons would change the temperature and tell the
slider and label to update themselves according to the new value.
This would work, but bindings provide another route. You had a taste of
bindings in Chapter 7. Now you are going to use them to build Thermostat.
In MainWindowController.swift, add a temperature property and give it a
comfortable default value (in Fahrenheit).
class MainWindowController: NSWindowController {

var temperature = 68

override var windowNibName: String {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()
}

}
The plan is to bind attributes of the slider and the label to this property. In
particular, you are going to bind the value attribute of the slider and the value
attribute of the label to the temperature key. Once bound, KVC will keep these
attributes in sync with the value of temperature.
To bind the slider’s value, select the slider and then click the icon to reveal
the bindings inspector. This inspector lists all of the attributes that you can bind.
Find and disclose the Value section. In the pop-up menu labeled Bind to, select File's
Owner. and then check the Bind to checkbox. This tells Interface Builder that you will
be binding the slider to a key found on MainWindowController.
Figure 8.2 Binding slider’s Value attribute to temperature key
In the Model Key Path field, delete the current value. When the field is blank, you
will be offered a drop-down menu of the properties available on File's Owner
(MainWindowController). Choose temperature.
The key path is a “model” key path because you typically bind a view object to a
piece of data in the application’s model. Indeed, the temperature property is the
model for this simple application.
Now bind the label’s value in the same way. Select the label and reveal the
bindings inspector. Bind to File's Owner and enter temperature in the Model Key Path
field.
(If you inadvertently check the Bind to checkbox before changing the value of the
pop-up to File's Owner, then a Shared User Defaults Controller object will be added to the
XIB. This is not a problem. You can simply select the object in the document
outline and delete it.) Run the application. The label and slider launch with the
default value displayed. When you move the slider, the label updates to display
the new value. All of this was built with no code other than declaring the
temperature property. (The buttons do not do anything yet, but you will fix that
soon.)
Figure 8.3 Label reports slider's new value
Behind the scenes, KVC is happening: the slider uses setValue(_:forKey:) to
change the value of temperature, and the label uses valueForKey(_:) to get the
new value.
How does the label know to call valueForKey(_:) to get an updated value? This
is thanks to another Cocoa technology – key-value observing.

Key-value observing
With KVO, an object can sign up to observe changes to the value of a key. When
the key’s value changes, the observer is notified. Bindings are an abstraction
layer on top of KVO and KVC which keep your Cocoa views in sync with the
model objects they are bound to. Here is how it works:
The label has an attribute bound to File's Owner.temperature. When the label is
created at runtime, it informs the MainWindowController (the File's Owner) that it is
observing the temperature key.
As an observer of temperature, the label is notified whenever the value of
temperature is changed by KVC. (What if the property is changed directly?
Hang on; you will see about that shortly.)
The slider is also bound to File's Owner.temperature, so it is also an observer of
temperature. At the moment, only the slider can change the value. Let’s hook up
the Warmer and Cooler buttons so that they can change the value and the slider can
respond to that change.
In MainWindowController.swift, add two action methods that use KVC to
change temperature.
...

override func windowDidLoad() {


super.windowDidLoad()
}

@IBAction func makeWarmer(sender: NSButton) {


let newTemperature = temperature + 1
setValue(newTemperature, forKey:"temperature")
}

@IBAction func makeCooler(sender: NSButton) {


let newTemperature = temperature - 1
setValue(newTemperature, forKey:"temperature")
}

}
Notice that these methods only contain code that updates the temperature; there
is nothing here that notifies the views to update themselves.
In MainWindowController.xib, connect the buttons to these actions. (Control-
drag from each button to File's Owner. Select the appropriate action from the list.)
Figure 8.4 Connecting buttons to actions
Run the application and click the buttons to adjust the temperature. Thanks to
KVO, the label and the slider both respond to changes in temperature.

Making keys observable


KVO may appear to be magical. It might be nice if it were! Sadly, it is not. In
order for KVO to see changes, the changes must be made in a KVO-compliant
manner. KVO and KVC both rely on the Objective-C runtime to work, but Swift
has been designed to accommodate this.
So far, you have only used KVC to update the temperature. Changes made using
KVC are KVO-compliant, and observers are automatically notified of the
change. But you also need to ensure that observers are notified if the property is
changed directly.
By default, Swift properties are not KVO-compliant, but there are two
workarounds: you can explicitly trigger notification of the observers or you can
make the property dynamic.
First, in MainWindowController.swift, rewrite the action methods to change the
property directly.
...

@IBAction func makeWarmer(sender: NSButton) {


let newTemperature = temperature + 1
setValue(newTemperature, forKey:"temperature")
temperature++
}

@IBAction func makeCooler(sender: NSButton) {


let newTemperature = temperature - 1
setValue(newTemperature, forKey:"temperature")
temperature--
}

}
If you like, run the application and confirm that clicking the buttons no longer
updates the slider or the label.
To explicitly trigger the notification of observers, you can use two KVO
methods inherited from NSObject. They are willChangeValueForKey(_:) and
didChangeValueForKey(_:). Try this with the Warmer button.

In MainWindowController.swift, add KVO methods to makeWarmer(_:).


...

@IBAction func makeWarmer(sender: AnyObject) {


willChangeValueForKey("temperature")
temperature++
didChangeValueForKey("temperature")
}
@IBAction func makeCooler(sender: NSButton) {
temperature--
}

}
Now the slider and label will be notified before and after the value is changed.
Run the application and confirm that clicking the Warmer button now updates the
slider and the label.
The second way to ensure KVO compliance is using Swift’s dynamic keyword
on the property’s declaration.
In MainWindowController.swift, make the temperature property dynamic.
class MainWindowController: NSWindowController {

override var windowNibName: String {


return "MainWindowController"
}

var temperature = 68
dynamic var temperature = 68

override func windowDidLoad() {


super.windowDidLoad()
}

...
Now you can remove the KVO method calls from makeWarmer(_:):
...

@IBAction func makeWarmer(sender: AnyObject) {


willChangeValueForKey("temperature")
temperature++
didChangeValueForKey("temperature")
}
@IBAction func makeCooler(sender: NSButton) {
temperature--
}

}
Run the application and confirm that both buttons now work as expected.
Most of the time you will use dynamic to make a property KVO-compliant.
However, remember that KVO is not magical: observers of KVO-compliant
properties are notified immediately after the property is changed. This can affect
performance or result in surprising behavior in multi-threaded apps. In these
cases you will want the control offered by the willChangeValueForKey(_:) and
didChangeValueForKey(_:) methods.

Note that the dynamic keyword is not specific to KVO; it makes the property
behave like an Objective-C property so that it can benefit from the runtime
flexibility that Objective-C offers. Using KVO with bindings is one of those
benefits.
There are benefits to runtime flexibility, and there are also dangers. When you
use KVC, you are abandoning Swift’s type safety, and if not used carefully this
can result in runtime errors that are hard to debug. There is more information on
debugging bindings later in the chapter.
Binding other attributes
To clarify the idea of bindings, let’s add the ability to turn the thermostat on and
off. When the thermostat is off, the Warmer and Cooler buttons and the slider will
be disabled. So instead of binding Value attributes to a key, you will bind Enabled
attributes to a key.
In MainWindowController.swift, add an isOn property that will know how to
work with KVO.
class MainWindowController: NSWindowController {

override var windowNibName: String {


return "MainWindowController"
}

dynamic var temperature = 68


dynamic var isOn = true

override func windowDidLoad() {


super.windowDidLoad()
}

...
Now you have a key to which you can bind your attributes. In
MainWindowController.xib, select the slider and open the bindings inspector.
Find the Enabled attribute and disclose its contents. Bind this attribute to File's Owner
and set the model key path to isOn.
Figure 8.5 Binding slider’s Enabled attribute to temperature key
Repeat this process with the Warmer and Cooler buttons.
Finally, you will need something to trigger the change in isOn. Drag a button on
to the bottom righthand corner of the canvas. Change its title to Power. To give
this button a different look, open the attributes inspector and change the button
style to Recessed. Figure 8.6 shows the results.
Figure 8.6 Adding a power button
A recessed button’s type defaults to Push On Push Off (as opposed to the Momentary
Push In type of the push buttons you have been using). The value attribute of a
button with this type is a boolean value for whether the button is currently
“pushed on” or “pushed off.” You can bind this attribute to isOn to toggle the
value of the property.
Select the power button in the canvas and return to the bindings inspector.
Reveal the Value attribute. Bind this attribute to File's Owner and set the model key
path to isOn.
Run the app and turn the thermostat off.
Figure 8.7 Turning off the thermostat
Your Thermostat is now fully functional. Return to MainWindowController.swift,
and see how little window controller code was required to make this happen.
class MainWindowController: NSWindowController {
override var windowNibName: String? {
return "MainWindowController"
}

dynamic var temperature = 68
dynamic var isOn = true

@IBAction func makeWarmer(sender: NSButton) {
temperature++
}


@IBAction func makeCooler(sender: NSButton) {
temperature--
}
}
Figure 8.8 is an object diagram of what you have done.
Figure 8.8 Object diagram with bindings

You are now likely wondering when to use bindings and when to update the
view layer in your controller code. The answer, of course, is “it depends.” Some
Cocoa APIs require bindings, so you must use them there. If you have a
relatively straightforward UI, bindings make an excellent choice. However, as
you will see in a moment, bindings can be difficult to debug – there is no code to
step through! We suggest that for more complicated user interfaces, you do
things the hard way. As you gain more experience with bindings – and there is a
lot more beyond what this book can cover – try using them in more complicated
settings. One aspect of bindings that is easy to forget is that they are difficult to
understand when you are new to a code base. It is hard to see them all at once
(there are no diagrams like Figure 8.8), so learning what bindings are present can
take a lot of poking around.
KVC and Property Accessors
KVC will call setters and getters for properties, if they are present. To see this in
action, add accessors for temperature:
dynamic var temperature = 68
private var privateTemperature = 68
dynamic var temperature: Int {
set {
println("set temperature to \(newValue)")
privateTemperature = newValue
}
get {
println("get temperature")
return privateTemperature
}
}
Run the application and move the slider to change the temperature. Notice how
the setter is called, followed by the getter, the latter of which corresponds to
bindings updating the label in response to the KVO notification. If you use the
buttons to change the temperature you will see the setter (property access) and
two getter calls (updating the slider and label).
In this example you changed temperature to a computed property. In Swift,
computed properties have no storage, so an additional property was needed to
replicate the existing behavior of this application.
Why is privateTemperature marked private? Usually this pattern of a
computed property with separate backing storage (the privateTemperature
property) is used to control access to a value, so it makes sense to mark it as
private to make sure that external code uses the intended interface for changing
it – in this case the computed property.
KVC and nil
What do you think happens if you use KVC to set a numeric type, such as a
Float or Int, to nil?
setValue(nil, forKey: "temperature")
Instead of making an assumption, such as assigning the value 0, KVC’s
designers decided to let the object decide what to do. When nil is set for
numeric type, KVC calls the method setNilValueForKey(_:). The default
implementation on NSObject throws an exception.
Although it is not needed in this application – none of the controls will try to set
it to nil – you could implement setNilValueForKey(_:) on
MainWindowController to handle this situation:
override func setNilValueForKey(key: String) {
switch key {
case "temperature":
temperature = 68
default:
super.setNilValueForKey(key)
}
}
Under what circumstances could this be an issue? One case is a text field bound
to a numeric property. If the user deletes all of the text and hits Return, the text
field interprets this as nil. You will see another solution to this problem, as well
as learn about a related topic, Key-Value Validation, in Chapter 10.
What about setting nil for other types? Because KVC depends on the Objective-
C runtime, this can be tricky. Optional reference types will work as expected: the
property will be set to nil. Some optional value types will work as well, such as
String?, array, set, and dictionary optionals. This works because all of these
value types have Objective-C object counterparts. Although this may change in
future versions of Swift and OS X, setting a numeric optional, Int? for example,
to nil via KVC presently results in a crash. You can work around this by using
the optional Objective-C counterpart, NSNumber?.
Debugging Bindings
Bindings are a powerful feature of Cocoa, but they are notoriously difficult to
debug. To a new Cocoa developer (and indeed even to the experienced), they
can be quite mysterious. Because they are generally configured in the XIB file it
can be difficult to track down the source of a problematic binding. Additionally,
because they use strings, the compiler cannot help you by checking that the key
actually exists on the object you are attempting to bind to.
The most common problem in using bindings is a key name typo, followed
closely by binding to the wrong object. You will see this error at runtime when
the related NIB file is opened: an exception will be logged to the console. There
will be a couple dozen lines of output, but the most interesting line will be at the
top of the output and look something like this:
[<Thermostat.MainWindowController 0x610000023260>
valueForUndefinedKey:]:
this class is not key value coding-compliant for
the key temperatur.
By pulling this line apart you can learn a few things about the error:
1. A binding was made to a non-existent key (valueForUndefinedKey:).
2. The target of the binding is MainWindowController.
3. The key string is "temperatur".

When you see an exception like this, think through the components of the
exception message. Did you expect to be binding to that key on that particular
object? Is the object or the key wrong? Comparing the answers to those
questions with your intentions will lead you to the source of the problem.
To see a list of all the bindings for a particular object, reveal its connections
panel by Control-clicking it (or its placeholder) in the document outline. The
bindings are listed at the bottom with the key in the left column and the object
binding to it in the right column (Figure 8.9). Think about each of the bindings
and make sure that it is what you expect.
Figure 8.9 Connections panel for MainWindowController
Using the Debugger
When an application is launched from Xcode, the debugger is attached to it. The
debugger monitors the current state of the application, like what method it is
currently executing and the values of the variables that are accessible from that
method. Using the debugger can help you understand what an application is
actually doing, which, in turn, helps you find and fix bugs.

Using breakpoints
One way to use the debugger is to set a breakpoint. Setting a breakpoint on a line
of code pauses the execution of the application at that line (before it executes).
Then you can execute the subsequent code line by line. This is useful when your
application is not doing what you expected and you need to isolate the problem.
Open AppDelegate.swift and find the applicationDidFinishLaunching(_:)
method. Set a breakpoint by clicking in the gutter (the lightly shaded bar on the
left side of the editor area) next to the first line of code in the method
(Figure 8.10). The blue indicator shows where the application will “break” the
next time you run it.
Figure 8.10 A breakpoint

Run the application. The application will stop execution before the line of code
where you put the breakpoint is executed. Notice the light green indicator and
shading that appear on that line to show the current point of execution.
Now your application is temporarily frozen in time, and you can examine it more
closely. In the navigator area, click the tab to open the debug navigator. This
navigator shows a stack trace of where the breakpoint stopped execution
(Figure 8.11). A stack trace shows you the methods and functions whose frames
were in the stack when the application halted.
Figure 8.11 The debug navigator and debug area

The method where execution stopped is at the top of the stack trace. It was called
by the method just below it, which was called by the method just below it, and
so on. Notice that the methods that you have written code for are in black text
while the methods belonging to Apple are in gray.
Select the method at the top of the stack, applicationDidFinishLaunching(_:).
In the debug area below the editor area, check out the variables view to the left
of the console. This area shows the variables within the scope of
MainWindowController’s makeWarmer(_:) method along with their current values.
(Figure 8.12).
Figure 8.12 Debug area with variables view

(If you do not see the variables view, find the button in the bottom-right
corner of the debug area. Click the left icon to show the variables view.) In the
variables view you will see three variables: aNotification, self, and
mainWindowController. Reference types are typically shown with their address –
location in memory – while value types show a representation of their value.
You can click the disclosure triangle to see more about non-scalar types. In Auto
mode, the variables view attempts to show the most relevant variables. In this
case that is the parameter of this method (aNotification), a reference to the
current instance, self, since the debugger is in the context of an instance
method, and the local variable mainWindowController.
You may have noticed that the local variable mainWindowController already has
an address shown, but the line initializing it has not yet executed. This is simply
because the compiler did not clear the memory, as an optimization.
Click the disclosure button next to self. The first item under self is the
superclass, in this case NSObject. The Swift module name, ObjectiveC, is also
shown. Clicking the disclosure button next to a superclass will show the
properties inherited from the superclass.
AppDelegate has one property shown, mainWindowController. Its value appears
as nil because it is an optional that has not been assigned.

Stepping through code


In addition to giving you a snapshot of the application at a given point, the
debugger also allows you to step through your code line by line and see what
your application does as each line executes. The buttons that control the
execution are on the debugger bar that sits between the editor area and the debug
area (Figure 8.13). Keyboard shortcuts for these are shown in the Debug menu.
Figure 8.13 Debugger bar
When the line the debugger is stopped on is executed, a new instance of
MainWindowController will be created and the local variable

mainWindowController will be set. Click the Step Over button on the debugger
bar. The debugger will stop again. Expand mainWindowController. The
internalTemperature is shown, as is isOn.

If you expand its superclass, AppKit.NSWindowController, you will see that


_window is 0x0. You are now looking at the details of an ObjectiveC class. The
_window instance variable (the backing storage for a property) has not yet been
initialized because the window is not loaded from the NIB until the window
property is accessed, which happens in showWindow(_:).

Click Step Over once again to step over the call to showWindow(_:). Note that
_window now has an address.

Back at the top level of the variables view, if you expand self you will see that
the mainWindowController property (which corresponds to
self.mainWindowController) is still nil. Step over the next line, the
assignment of that property, to see it change.
At this point, you could continue stepping through the code to see what happens.
Or you could click the Continue button to let your program run until the next
breakpoint. Or you could step into a method ( ). Stepping into a method takes
you to the method that is called by the line of code that currently has the green
execution indicator. Once you are in the method, you have the chance to step
through its code in the same way.
When you step out of a method, you are taken to the method that called it. To try
this, with Thermostat still stopped in the debugger, open
MainWindowController.swift and add a breakpoint in the setter for the computed

property temperature. Hit Continue . The app window will appear. Change the
temperature using the Warmer or Colder button, and execution will stop again, this
time in the setter. Click Step Out and you will find yourself in the action
method that called the setter.

Deleting breakpoints

To run your application normally again, you will be getting rid of both of the
breakpoints you set. However, since you are going to use the breakpoint in the
temperature setter in the next section, leave it there for now.

To find and delete the other breakpoint, switch to the breakpoint navigator by
clicking . Click on the MainWindowController.swift row in the breakpoint
navigator to jump to that line.
There are two ways to delete a breakpoint. The first is to right-click the blue
breakpoint in the gutter and select Delete Breakpoint. The second – and more fun –
way is to drag the breakpoint out of the gutter. It will change to a puff of smoke,
indicating that it has been removed.
Sometimes, a developer will set a breakpoint and forget about it. Then, when the
application is run, execution stops, and it looks like the application has crashed.
If an application of yours unexpectedly stops, make sure you are not halting on a
forgotten breakpoint.

Setting an exception breakpoint

You can also tell the debugger to break automatically on any line that causes
your application to crash or that causes an exception to be thrown.
Go to the breakpoint navigator, if it is not already shown. At the bottom of this
navigator, click the + icon and select Add Exception Breakpoint....
If your application is throwing exceptions and you are not sure why, adding an
exception breakpoint will help you pinpoint what is going on. Note that by
default this will stop on all exceptions, even normal exceptions thrown by C++
code deep within the frameworks. To only show ObjectiveC exceptions, right-
click on the All Exceptions row, select Edit Breakpoint, and in the popover that appears
change All to ObjectiveC.

The LLDB console


To the right of the variables view is the debugger console. If it is hidden, click
the Show Console button in the lower-right corner of the debug area to show it.
When the debugger is stopped, the console displays a prompt: (lldb). Anything
that can be done in Xcode’s UI can also be done using the LLDB console. If you
are familiar with gdb you will feel right at home. Although LLDB is completely
different from gdb, aliases have been provided for convenience.
The most commonly used LLDB command is expr, which is short for
expression. This command evaluates whatever Swift expression is passed to it.
You can use it to evaluate simple expressions:
(lldb) expr mainWindowController.temperature
get temperature
(Int) $R0 = 68
(lldb) expr mainWindowController.temperature = 70
set temperature to 70
get temperature
get temperature
(lldb) expr mainWindowController.temperature
get temperature
(Int) $R2 = 70
Notice that the output from the println() calls in the setter and getter appears as
well. You can also instantiate objects and call methods:
(lldb) expr
NSSpeechSynthesizer().startSpeakingString("I am
LLDB.")
(Bool) $R3 = true
You may see a lot of debugging tutorials which use p (short for print). Those
are compatibility aliases for gdb users. The print command is equivalent to
expr.
Sometimes it is handy to print an ObjectiveC object. While expr can do this, the
results can be rather verbose:
(lldb) p NSSpeechSynthesizer()
(NSSpeechSynthesizer) $R18 = 0x0000608000002750 {
NSObject = {
isa = 0x0000608000002750
}
_privateNSSpeechSynthesizerVars = 0x00006080000745c0
}
The po command (“print object”) can make this output more succinct:
(lldb) po NSSpeechSynthesizer()
<NSSpeechSynthesizer: 0x600000000a40>
There is much, much more to learn about LLDB. For next steps, see the tutorial
on the LLDB website at lldb.llvm.org. You can also find a lot with the built-in help.
Just type help at the LLDB prompt.

Using the debugger to see bindings in action


If you do not already have a breakpoint in the setter for temperature in
MainWindowController, set one now. Run the application, change the

temperature, and examine the stack shown in the debug navigator ( ). It will
look something like this:
#0 Thermostat.MainWindowController.temperature.setter
: Swift.Int
at MainWindowController.swift:20
#1 @objc
Thermostat.MainWindowController.temperature.setter :
Swift.Int
#2 NSSetLongLongValueAndNotify
#3 -[NSObject(NSKeyValueCoding) setValue:forKey:]
#4 -[NSObject(NSKeyValueCoding)
setValue:forKeyPath:]
#5 -[NSBinder
setValue:forKeyPath:ofObject:mode:validateImmediately:...

#6 -[NSBinder setValue:forBinding:error:]
#7 -[NSValueBinder
applyObjectValue:forBinding:canRecoverFromErrors:...
#8 -[NSValueBinder
applyDisplayedValueHandleErrors:typeOfAlert:canRec...
#9 -[NSValueBinder performAction:]
#10 -[NSBindingAdaptor
_objectDidTriggerAction:bindingAdaptor:]
#11 -[NSControl sendAction:to:] ()
... ...
There is a lot going on in there! You can trace it from frame #11, where the
control sends the action to its target – in this case the slider changing – which is
then captured by the bindings system. Bindings then converts this change into a
KVC call – notice the setValue:forKey: on frame #3, which corresponds to the
KVC methods you used earlier in this chapter. This finally results in a call to the
Swift setter.
All of the frames starting with -[ are ObjectiveC method calls. Methods
beginning with an underscore _ are private methods.
_NSSetLongLongValueAndNotify is a private C function.
Remove the setter breakpoint and place a breakpoint in the getter. When you run
the app it will stop immediately. The stack will show bindings setting the initial
values for the controls as the NIB is being loaded.
#0 Thermostat.MainWindowController.temperature.getter
: Swift.Int
at MainWindowController.swift
#1 @objc
Thermostat.MainWindowController.temperature.getter :
Swift.Int
#2 _NSGetLongLongValueWithMethod ()
#3 -[NSObject(NSKeyValueCoding) valueForKey:] ()
... ...
#7 -[NSValueBinder
observeValueForKeyPath:ofObject:context:] ()
#8 -[NSObject(NSKeyValueBindingCreation)
bind:toObject:withKeyPath:optio...
#9 -[NSIBObjectData
nibInstantiateWithOwner:options:topLevelObjects:]
#10 loadNib ()
#11 +[NSBundle(NSNibLoading)
loadNibFile:nameTable:options:withZone:owne...
#12 +[NSBundle(NSNibLoadingInternal)
_loadNibFile:externalNameTable:optio...
#13 -[NSWindowController loadWindow] ()
#14 -[NSWindowController window] ()
#15 -[NSWindowController showWindow:] ()
#16
Thermostat.AppDelegate.applicationDidFinishLaunching
at AppDelegate.swift
... ...

Hit Continue twice, at which point the window should appear.


Change the temperature and the program will stop in the debugger again, where
you can see the KVO calls in the stack:
#0 Thermostat.MainWindowController.temperature.getter
: Swift.Int
at MainWindowController.swift
#1 @objc
Thermostat.MainWindowController.temperature.getter :
Swift.Int
#2 _NSGetLongLongValueWithMethod
#3 -[NSObject(NSKeyValueCoding) valueForKey:]
... ...
#8 -[NSTextValueBinder
observeValueForKeyPath:ofObject:context:] ()
#9 NSKeyValueNotifyObserver ()
#10 NSKeyValueDidChange ()
#11 -[NSObject(NSKeyValueObserverNotification)
didChangeValueForKey:] ()
... ...
#19 -[NSBindingAdaptor
_objectDidTriggerAction:bindingAdaptor:] ()
#20 -[NSControl sendAction:to:]
... ...
Pretty cool, huh?
Pretty cool, huh?
For the More Curious: Key Paths
Objects are often arranged in a network. For example, a person might have a
spouse who has a scooter that has a model name (Figure 8.14).
Figure 8.14 Directed graph of objects

To get the selected person’s spouse’s scooter’s model name, you can use a key
path:
let modelName =
selectedPerson.valueForKeyPath("spouse.scooter.modelName")
as String
We would say that spouse and scooter are relationships of the Person class and
that modelName is an attribute of the Scooter class.
There are also operators that you can include in key paths. For example, if you
have an array of Person objects, you could get their average expectedRaise by
using key paths:
let average = (employees as
NSArray).valueForKeyPath("@avg.expectedRaise") as!
Float
Here are some commonly used operators:
@avg
@count
@max
@min
@sum
Now that you know about key paths, let’s see how you would create bindings
programmatically. If you had a text field in which you wanted to show the
average expected raise of the arranged objects of an array controller, you could
create a binding like this:
textField.bind(NSValueBinding, // "value"
toObject: employeeController,
withKeyPath: "arrangedObjects.@avg.expectedRaise",
options: nil)
Of course, it is usually easier to create a binding in Interface Builder.
Use the unbind(_:) method to remove the binding:
textField.unbind(NSValueBinding)
For the More Curious: More on KeyValue
Observing
How did the label become an observer of the temperature key in the
MainWindowController object? The code to become an observer of this key might
look something like this:
let mainWindowController = ...
mainWindowController.addObserver(self,
forKeyPath: "temperature",
options:
NSKeyValueObservingOptions.Old,
context: &MyKVOContext)
This method is defined in NSObject. It is how you say, “Hey! Let me know
whenever temperature changes.” The options and context determine what extra
data is sent along when temperature changes. The method that is triggered looks
like this:
override func observeValueForKeyPath(keyPath: String!,
ofObject object:
AnyObject!,
change:
[NSObject : AnyObject]!,
context:
UnsafeMutablePointer<Void>) {
...
}
The keyPath, in this case, would be "temperature"; the object would be the
MainWindowController; context would be the pointer to MyKVOContext that was
supplied as the context when you became an observer; and change is a dictionary
(a collection of key-value pairs) that can hold the old value of temperature
and/or the new value.
You will see these APIs in action in Chapter 11.
For the More Curious: Dependent Keys
In some cases a value may be dependent on another. For example, if you have a
Person class with a computed property fullName that is dependent on the
properties firstName and lastName, wouldn’t it be nice if observers of fullName
could be notified when either firstName or lastName changes? KVO’s designers
thought so, too, which is why they implemented a convention for defining
dependent keys.
To define a key’s dependencies you must implement a specially named class
method which returns a Set of key paths. In the example above you would
implement keyPathsForValuesAffectingFullName():
class Person {
dynamic var firstName = ""
dynamic var lastName = ""

dynamic var fullName {
return "\(firstName) \(lastName)"
}

class func keyPathsForValuesAffectingFullName() ->
NSSet {
return Set(["firstName", "lastName"])
}
}
Note the naming of this method: keyPathsForValuesAffectingKey(). Do not
forget that it is a class method. KVO will not pick up on it if you forget and
make it an instance method.
Challenge: Convert RGBWell to Use Bindings
In Chapter 5 you used action methods to trigger manually updating the r, g, and
b properties. Convert RGBWell to use bindings instead.

Here are some hints:


To be clear about what you are doing, state what bindings you need: “I
want to bind the ‘R’ slider’s value attribute to the r property on
MainWindowController.”

You have avoided creating an initializer for MainWindowController in order


to inherit the initializer from NSWindowController. To continue to inherit
this initializer, you will need to provide a default value for an NSColor
property. You can do this with an NSColor class method. Return to the
NSColor reference page to find one that works for the application.

When your application is finished, your action methods should be simpler


and you should not need any outlets. You can remove outlets by opening
the connections panel and clicking the x next to the appropriate connection.
9
NSArrayController
Over the next few chapters, you will create RaiseMan, a full-featured application
for keeping track of employees and the raise that each of them will receive this
year. As you continue through the book, you will add, among other things, file
saving, undo support, and printing capabilities.
RaiseMan will use a table view to display the employees in two columns, one for
the employees’ names, the other for their raises. By the end of this chapter the
user will be able to add and remove employees, edit their names and raises, and
use the table view to sort by name and raise.
First, you will create a class to represent employees. Second, you will lay out the
UI. Finally, you will use an NSArrayController and bindings to display an array
of objects in the table view. By the end of the chapter, your application will look
like Figure 9.1.
Figure 9.1 RaiseMan at the end of this chapter

Create a new project in Xcode. In the Application section, under the OS X header,
choose Cocoa Application for the type.
Configure the options for your project as shown in Figure 9.2: set Product Name to
RaiseMan and Language to Swift. Uncheck Use Storyboards and check Create
DocumentBased Application. Set the Document Extension to rsmn and uncheck Use Core Data.

Figure 9.2 New document-based project


A document-based application exists to view and edit documents of some sort.
TextEdit, for example, is a document-based application. System Preferences, on the
other hand, is not.
In a document-based application, the majority of the user interface is built
around presenting a document. Typically, the user interface takes the form of a
window which displays a document – the document window. Since a document
window is presenting a single document, it is easy enough to instantiate multiple
document windows, each of which presents a different document. As a result, in
a document-based application, you can have multiple documents open
simultaneously, each with its own window. You will learn more about document
architecture in Chapter 12.
In a document-based application, a subclass of NSDocument is used to control the
window or windows used to display a single document. Often, this means that an
NSDocument acts much as a window controller does: An NSDocument has a
reference to the data that makes up the document, and it manages shuttling that
data to the views in the window it manages. When you created your new
document-based app project, a subclass of NSDocument called Document was
created for you.
RaiseMan’s Model Layer
Start out by creating the class whose instances will be represented in RaiseMan’s
table view: Employee. Select the top level RaiseMan group in the project navigator.
Create a new class by choosing the File → New → File... menu item. In the dialog
box that appears, find and select Swift File in the Source section under the OS X
header. Name the file Employee, as shown in Figure 9.3.
Figure 9.3 Creating Employee.swift

In Employee.swift, create a new class called Employee with two properties:


import Foundation

class Employee : NSObject {


var name: String? = "New Employee"
var raise: Float = 0.05
}
You have declared Employee to be a subclass of NSObject. This is necessary in
order for the class to play nice with the bindings that you will be setting up later.
Note that Employee is a class in the model layer – it has no information about the
user interface. As such, this class does not need to know about all the Cocoa
frameworks. Thus, instead of importing Cocoa, you are importing Foundation.
Either would work, but importing the smaller framework is more stylish: Among
other things, it allows this class to be reused in a command-line tool or an iOS
application.
The Document class will serve as the controller for your document’s window. The
controller connects the view with its model, which in RaiseMan is an array of
Employee objects. Open Document.swift and declare the employees property:
class Document: NSDocument {

var employees: [Employee] = []


While you have Document.swift open, remove the lines which return errors in
dataOfType(_:error:) and readFromData(_:ofType:error:). You will
implement these methods in Chapter 12.
override func dataOfType(typeName: String,
error outError: NSErrorPointer) ->
NSData? {
outError.memory =
NSError.errorWithDomain(NSOSStatusErrorDomain,
code: unimpErr,
userInfo: nil)
return nil
}

override func readFromData(data: NSData,


ofType typeName: String,
error outError: NSErrorPointer) ->
Bool {
outError.memory =
NSError.errorWithDomain(NSOSStatusErrorDomain,
code: unimpErr,
userInfo: nil)
return false
}
RaiseMan’s View Layer
Open Document.xib and delete the text field that says Your document contents here. In
its place, drag a Table View and two Push Buttons onto the window. Relabel the table
view columns and buttons and arrange them as shown in Figure 9.4. You will
need to resize your window to get exactly the arrangement shown in the figure.
Figure 9.4 Document window
Introducing NSArrayController
You have already used a table view in SpeakLine. SpeakLine’s use of NSTableView,
however, was basic, allowing only simple user interaction. RaiseMan’s table view
will be much more dynamic. Rows will be added, edited, removed, and sorted.
One way to implement this would be with a table view data source and delegate
in the Document class. This is such a common task, however, that Cocoa provides
a class for just this purpose: NSArrayController.
NSArrayController, like any controller, serves as the glue between a view and its
model, an array of objects. In SpeakLine, the window controller was the table view
data source and delegate, supplying data to the view and reacting to selection
changes. NSArrayController does all of these things and more. You will use
bindings to connect the table view to the array controller and the array controller
to the model. This means you can skip writing a lot of boilerplate code!
Figure 9.5 shows how the array controller will be used in RaiseMan. We will
discuss the specifics shortly, but until then notice how bindings (the single-sided
arrows) connect the table view to the array controller and the array controller to
the model via the Document object.
Figure 9.5 Binding the table view to the model in RaiseMan
The two most important properties on NSArrayController are content and
arrangedObjects. If content is the input to the array controller, arrangedObjects
is the output. You will almost never use the content property, however. Instead,
you will know it by its binding name, Content Array, and you will bind it to a model
array. In RaiseMan, the Content Array will be bound to employees on the document.
The array controller’s arrangedObjects, on the other hand, is typically on the
receiving end of a binding. For example, a table view’s Content would be bound to
the array controller’s arrangedObjects.
Why is it called “arranged objects”? You can think of an array controller as a
series of camera lenses. You aim the array controller at an array by binding the
Content Array. The array controller then runs that array through its filtering lens,
and then runs the result of that through its sorting lens. What you see through the
array controller are the objects, arranged for presentation. You will learn more
about filtering and sorting later in this chapter.
There is one more aspect of array controllers to mention: selection. Because the
ordering of arrangedObjects may not match the content array, the array
controller’s selectionIndexes property can be used to map the selection to the
correct model objects. Usually this is done by binding the table view’s Selection
Indexes to the array controller’s selectionIndexes in order to keep the two in
sync.
As you will see, the magic of binding table view-to-array controller-to-model is
As you will see, the magic of binding table view-to-array controller-to-model is
that any changes to the model are automatically reflected in the table view, and
any changes in the table view are automatically applied to the model.
Array controllers are a powerful tool for abstraction: The model array can be
ordered according to its needs, while the array controller filters and reorders the
content according to the user’s needs. In other words, the model array is
unaffected by the filtering and reordering.
One more note about array controller and view bindings: although the exercise in
this chapter focuses on table view bindings, you can bind other Cocoa view
classes, such as NSPopUpButton and NSComboBox, to array controllers.
Adding an Array Controller to the XIB
You are going to add an array controller to the XIB. That is, the array controller
will be instantiated when the XIB is loaded. This may seem odd because so far
you have only added views to XIBs. Doing so, however, will make your life
much easier as you will need to make a number of configuration changes which
would be tedious to perform in code. Drag an Array Controller from the object
library and drop it onto Interface Builder’s canvas (Figure 9.6).
Figure 9.6 Adding an array controller to Document.xib

An icon for the Array Controller will appear at the top level in Interface Builder’s Icon
View.
The array controller needs to know what kind of object will be stored in its
content array. Knowing the class allows the array controller to create new
objects. With the Array Controller still selected, switch to the attributes inspector.
Locate the Object Controller section. Set the Class Name to RaiseMan.Employee. Note
that you prefix the class’s name, Employee, with RaiseMan, the name of the
module where Employee lives. Unless otherwise specified, your classes will live
inside your product module, whose name by default matches your product name.
Note that if Employee were an Objective-C class, the prefix would be
unnecessary.
Xcode should look something like Figure 9.7.

Figure 9.7 Configuring NSArrayController’s class name and keys


Binding the Array Controller to the Model
With the array controller still selected, switch to the bindings inspector ( ).
Find the Content Array binding in the list and expand it. Select File's Owner in the pop-
up next to Bind to and check the box. Leave the Controller Key blank and enter
employees for the Model Key Path (Figure 9.8). Cocoa programmers would say that
they are binding the Content Array of the array controller to the employees array of
File's Owner (which is an instance of Document).

Figure 9.8 Binding the array controller’s content array

This binding sets up the input of the array controller to be the employees array of
the Document. After you have finished configuring all the bindings, this input will
be transformed by the array controller and displayed in the table view.
Recall that an array controller’s Content Array is the array that the array controller
manages. The content array is not affected by any filtering or sorting that you
configure on the array controller. When you add or remove objects via the array
controller, however, those changes will be reflected in the content array.
Binding the Table View’s Content to the
Array Controller
In Chapter 7, you implemented a data source for the table view. The table view
asked the data source for the number of rows, then for the data for each cell. As
we mentioned earlier in this chapter, none of that code is necessary with
bindings and array controllers. When a table view has its Content binding set, it
does not ask the data source for cell data.
Select the table view. Remember, selecting the table view can be tricky; it is
easy to select the scroll view or clip view that contains the table view rather than
the table view itself. Use the document outline or the jump bar to make sure you
have selected the table view and not one of its superviews.
With the table view selected, switch to the bindings inspector and find the Table
Content section. Bind the table view’s Content to the Array Controller’s
arrangedObjects. Leave the Model Key Path empty. Do not panic if you see a red
error indicator at the right-hand side of the Model Key Path field; this is just Xcode
trying to be helpful.
Remember, the array controller’s arrangedObjects is the filtered and sorted
representation of its input – in this case the Document’s employees array.
Connecting the Add Employee Button
Now that you have set up bindings from the table view to the array controller,
the next step is to hook up the Add Employee button.
Unlike what you have done in previous exercises, in this exercise you will not
set the buttons’ target to be File's Owner (recall that in this case, File's Owner is
the document). Instead, NSArrayController is again going to come to your aid to
serve as a controller object and augment your Document class: The array
controller will be the target of the buttons, and their actions will be methods on
NSArrayController (Figure 9.9).

Figure 9.9 The wiring between the buttons and the array controller

Control-drag from the Add Employee button to the Array Controller to set the array
controller as the target of the button (Figure 9.10). Set the action to add:.
Figure 9.10 Setting the array controller as the Add Employee button’s
target
Run the app. When you click the Add Employee button, a new row is added to the
table view. This row corresponds to an Employee object in the array controller’s
content array, but you will have to take our word for it, since the cells still say
Table View Cell. You will fix that next.
Binding the Text Fields to the Table Cell
Views
Just like with a data source, when a table view’s Content is bound, each row in the
table view maps to an object in that array. Each cell in the row, an instance of
NSTableCellView, has its objectValue set to that object. Controls within those
cells, such as text fields, will typically display the value of a property on that
object. In RaiseMan, the objectValue of each cell will be the Employee object for
that row.
Therefore, you need to bind the text fields in RaiseMan’s columns to the cell
views’ objectValue. You will follow a similar approach to the one you used
when configuring the binding in SpeakLine.
The employee’s name will be displayed in the first column’s cell. To set this
binding up, select the appropriate text field (inside the first table column’s table
cell view) by expanding the document outline and drilling down into the view
hierarchy until you have gone a couple of levels beyond the Table View, as shown
in Figure 9.11.
Figure 9.11 Selecting the text field in the first column of the table view
Make sure you have selected the NSTextField and not the NSTextFieldCell! This
can be confusing because they have the same label in the document outline.
Remember, the text field is a control and so it has an icon resembling a slider.
In the bindings inspector, find the Value binding and expand it. Check Bind to and
select the Table Cell View in the pop up. Leave the Controller Key empty and set the
Model Key Path to objectValue.name (Figure 9.12). A Cocoa programmer would
say that you are binding the text field’s value to the table cell view’s
objectValue’s name.

In SpeakLine, you set the Model Key Path to be objectValue. Why did you not do that
this time? In SpeakLine, the table cell view’s objectValue was a String; in RaiseMan,
though, it is an Employee. This means that the text fields’ values cannot be bound
directly to the table cell views’ objectValue (a text field does not know how to
display an Employee). Instead, you need to bind each text field’s value to the
appropriate property on objectValue.
Figure 9.12 Binding the text field to the table cell view’s Employee’s name

With the text field still selected, switch to the attributes inspector. Find the pop-
up button labeled Behavior and change it to Editable. This will allow the user to
change the employees’ names. That is, when the user uses this text field to edit
an employee’s name, that change is reflected in the Employee object. Pretty
amazing, isn’t it? The bindings that you have set up allow user interactions in the
view layer to pass back via the array controller and update the model layer.
Try running the app now. You should be able to add employees and edit their
names in the first column of the table view. The second column will remain
fixed. Your next step is to display the employees’ raises there.
Select the text field within the Raise column’s table cell view. (As before, make
sure to select the NSTextField and not the NSTextFieldCell. Check that your
selection matches the selection shown in the document outline in Figure 9.13.) In
the bindings inspector, bind Value to the Table Cell View with a Model Key Path of
objectValue.raise, much as you did with the first column (Figure 9.13).
Figure 9.13 Binding the text field to the table cell view’s Employee’s raise
With the text field still selected, switch to the attributes inspector and change the
Behavior to Editable, as before. This will allow the user to change the employees’
raises.
Try running RaiseMan at this point. You should be able to give employees
raises. Since an Employee’s raise is a Float, raises will be displayed in decimal
format by default. This style of display is not great. It would be much better to
display the raises as percentages.
Formatting the Raise Text Field
To display the raises correctly, as percentages, you will use a number formatter.
Find the Number Formatter in the object library and drag it onto the second column’s
table cell view (Figure 9.14).
Figure 9.14 Add a number formatter

Notice the location of the number formatter in the document outline. It will show
up nested inside the text field’s cell (Figure 9.15). If you need to select it again,
you can do this most easily using the document outline.
Figure 9.15 The number formatter in the document outline
With the number formatter still selected, open the attributes inspector and
configure it to display the number as a percentage by selecting Percent from the
Style pop-up menu. Next, check the Lenient checkbox; this will make the formatter
less picky about input (Figure 9.16).
Figure 9.16 Number formatter attributes
Making the number formatter lenient stops your app from punishing your users
for what they are likely to consider reasonable input. For example, with lenient
unchecked, 5 will be rejected. In a non-lenient percent number formatter, all
entries must end with a percent sign, so your user would have to enter 5% instead
of 5. (Note that if you enter a decimal such as .42, the lenient formatter will
interpret it as 0.42%, round it down, and display it as 0%.)
Run the app. You should be able to add Employee objects. You should be able to
edit the attributes of the Employee objects using the table view. You cannot
remove them yet – that is next. Finally, you should be able to open multiple
untitled documents. (No, you cannot save those documents to a file. Soon,
Grasshopper.)
Connecting the Remove Button
Next, you will connect the Remove button to the remove(_:) method on the array
controller. This will be much the same as connecting the Add Employee button, but
there are some additional considerations, as you will see.
Control-drag from the Remove button to Array Controller and set the action to
remove:.

Run the application. Add two employees and give them pleasant names. Select
the first one and click Remove. Contrary to your notions of all that is good and
right in the world, the second employee has been removed! Now that nothing is
selected, try clicking Remove once more. Even though no rows are selected, a row
will be removed! What is going on here?
The array controller’s remove(_:) method removes selected objects in the
content array. Even though a row is selected in the table view, the array
controller does not know anything about it, which can lead to some surprising
results.
To fix this, you need to set up a binding to make the array controller’s selection
stay in sync with the table view’s selection.
Binding the Table View’s Selection to the
Array Controller
The selection indexes of an array controller are the indexes in arrangedObjects
of the items which are currently selected. Note that the selection indexes are not
indexes in the array controller’s content array: if you use a selection index to get
an object out of the content array, you will not get the selected object. The
selection in an array controller determines which items will be affected when the
array controller performs certain actions. For example, as you just saw, when
you trigger the remove(_:) action on an array controller, the items corresponding
to these indexes in arrangedObjects will be removed.
Bind the table view’s Selection Indexes to the Array Controller’s selectionIndexes.
Leave the Model Key Path empty.
Once you have done this, your bindings inspector should look something like
Figure 9.17.
Figure 9.17 Binding the table view to the array controller
Now the table view’s selection of rows will stay in sync with the array
controller’s selection of employees.
Run the app. Add an employee and then deselect it. Click the Remove button. The
table view will not be affected. Select the employee’s row and click the button
again. The employee will be removed.
Now that the table view’s selection mirrors the array controller’s selection, you
can see the reason for the unexpected behavior from earlier: When the array
controller creates a new object, by default it will select that new object. When
you clicked Remove, the array controller removed what it had selected: the last
employee that you had added.
The Remove button is almost working the way you want it to, but not quite. When
no employees are selected, the user should not be able to click the Remove button.
The button should be disabled whenever no employees are selected. To do this,
you will set up a binding between the button and the array controller.
Configuring RaiseMan’s Remove Button
Now that your array controller knows what the user has selected in the table
view, you can use the array controller to ensure that the Remove button is only
enabled when appropriate – when the array controller has an object selected.
This will prevent your users from clicking Remove when they have not selected an
employee to remove.
You will bind the button’s enabled property to the array controller’s canRemove
property. This property is true only when the array controller can actually do
removal. In other words, this property will be false when the array controller
does not have any objects selected.
To set up this binding, select the Remove button and switch to the bindings
inspector. In the Availability section, bind the button’s Enabled attribute to the
canRemove property of the NSArrayController, as shown in Figure 9.18.

Figure 9.18 Binding the enabled attribute of the Remove button

The user will also want to be able to remove the selected employees by pressing
the Delete key on their keyboard. With the Remove button still selected, switch to
the attributes inspector. Set the Key Equivalent to be the Delete key (Figure 9.19).
To set the key equivalent, click in the text field and then press the key you wish
to associate with this button. Xcode will record the keystroke used and display it
in the text field. To remove a key equivalent, click the x on the right-hand side of
the text field.
Figure 9.19 Making the Delete key trigger the Remove button
Run your application. When you click the Add button, a new row should be added
to the table view. When you select a row in the table view and click the Remove
button, the selected row should be removed from the table view.
Sorting in RaiseMan
You are now ready to add sorting to RaiseMan. When you are done, the user will
be able to reorder the employees table by clicking on the table column headers.
This action translates to setting the criteria by which the rows will be sorted.
When a user clicks on a table column header, the table view responds by
changing its sort descriptors. Sort descriptors, represented by the
NSSortDescriptor class, describe how to sort an array of objects. Each sort
descriptor contains a key which is a KVC key path, whether it should be
ascending (as opposed to descending), and a comparison selector, or method
used to compare the two keys. Because sorting with NSSortDescriptor relies
upon KVC, it can only be used with NSObject-based classes.
In a table view, each column corresponds to a sort descriptor. Clicking on the
column will make that column’s sort descriptor take effect. Subsequent clicks
will toggle whether it is ascending or descending. As you will see later in the
chapter, multiple sort descriptors can be used to resolve ambiguity.
As you have done throughout the chapter, you will take advantage of
NSArrayController, this time to do the sorting for you. You have two steps to
complete: First, you will configure each table column’s sort descriptor
properties. The Name column will sort using Employee’s name key, and the Raise
column will use the raise key. Second, you will use bindings to tie the table’s
sort descriptors to the array controller. All of this will be configured in
Document.xib; you will not have to write any code at all.

Start out by configuring the Raise column. Open Document.xib and use the
document outline or the view hierarchy popover to select the Raise column.
Switch to the attributes inspector. Next to Sort Key, enter raise. This makes the
column sort the objects in the table view based on their values for the raise key.
When you press return to end editing, you should see the Selector change to
compare: (Figure 9.20). This selector specifies that the column will use that
NSNumber’s compare(_:) method to order the values for the raise key.

Figure 9.20 The Raise column configured to sort using the raise key
Next, configure the Name column. In Document.xib, select that column.
You are going to make this column sort by comparing the names of the
employees without concern for capitalization. In the attributes inspector, set the
Sort Key to be name and the Selector to be caseInsensitiveCompare:, as shown in
Figure 9.21. This column will now sort the objects displayed in the table view by
putting the values for their name key in the order determined by String’s
caseInsensitiveCompare(_:) method.

Figure 9.21 The Name column configured to sort using the name key

At this point, you have finished configuring the sort mechanism of the table
view. When the user clicks on the column headers, the table view will update its
sortDescriptors property. However, if you run the app now, nothing will
happen when column headers are clicked. That is because even though the table
view’s sort descriptors are updated, the array controller’s configuration is not
currently changing to match, and the array controller determines the order in
which objects are displayed in the table view.
To fix this, you will need to bind the table view’s sort descriptors to the array
controller’s sortDescriptors property, which determines the order of its
arrangedObjects array.

Select the Table View. Switch to the bindings inspector and expand the Sort
Descriptors binding in the Table Content section. Bind the Sort Descriptors to the Array
Controller. Set the Controller Key to sortDescriptors. Leave the Model Key Path blank.
Your binding should look like Figure 9.22.
Figure 9.22 Binding the table view’s sort descriptors to the array controller

Run your application. Try clicking on the Name and Raise column headers to sort
the data. Notice that if you click the same column twice in a row, you will
reverse the ordering from the first click. This corresponds to changing the sort
descriptor for to that column from ascending to descending.
How Sorting Works in RaiseMan
Often, a single sort descriptor will not completely sort a list of objects. To see
this in action, run RaiseMan and add three employees. Give them all different
names. Give one of them a larger raise (20%, say) and leave the other raises
alone. You should have a setup like Figure 9.23.
Figure 9.23 Three employees added in RaiseMan

Click on the column header of the Raise column. This will add a sort descriptor to
the table view’s sortDescriptors array. Bindings will keep the array controller’s
sortDescriptors in sync, so its arrangedObjects will be rearranged. The order of
the rows in the table view will now list the employees in ascending order of
raises: the two employees with smaller raises should appear first, followed by
the employee with the larger raise (Figure 9.24).
Figure 9.24 Three employees ordered by raise
The array controller has used the sort descriptor (which uses the raise key and
NSNumber’s compare(_:) method) to sort the employee with the larger raise below
the other employees, but it has not sorted the two employees relative to one
another. The two employees with the smaller raise are left in the same relative
order.
In situations like this, it makes sense to apply multiple sort descriptors at once.
Try this out by clicking on the Name column. You will see that the your
employees are ordered alphabetically. The array controller is now sorting first by
employee name and then (if there are two employees with identical names) by
raise. When you click on a table column header, the sort descriptor for that table
column is moved to the front of the array controller’s sortDescriptors array
(Figure 9.25).
Figure 9.25 Three employees sorted first by name, then by raise

In this case, unless you have given two employees the same name, only the name
sort descriptor is having any effect. Both sort descriptors are applied, but the
sort descriptor is having any effect. Both sort descriptors are applied, but the
employees are totally ordered by the name sort descriptor, so the raise sort
descriptor does not alter the order.
To see both two descriptors in action, you should sort first by raise and then by
name. To tell the array controller to do this, click again on the Raise column. This
will move the raise column’s sort descriptor from the back of the
sortDescriptors array to the front. As a result, the array controller will sort first
by raise and then by name. You will see the employee with the larger raise
remain at the bottom. Now, though, the two employees with smaller raises will
be sorted by name (Figure 9.26).
Figure 9.26 Three employees sorted first by raise, then by name
For the More Curious: The
caseInsensitiveCompare(_:) Method
In the previous section, you specified that the Selector used to sort the Name column
should be caseInsensitiveCompare:. The caseInsensitiveCompare: selector
corresponds to the method caseInsensitiveCompare(_:) on String. You can use
that method programmatically as follows:
let motorsickle: String = "Piaggio"
let motorsigh: String = "Italjet"
let order: NSComparisonResult =
motorsickle.caseInsensitiveCompare(motorsigh)

// Would motorsickle come first in the dictionary?
if order == .OrderedAscending {

}
Notice that caseInsensitiveCompare(_:) returns an NSComparisonResult. The
NSComparisonResult describes the how the first string (motorsickle in this
example) is ordered in relation to the second (motorsigh here) – in other words,
which comes first, if either.
An NSComparisonResult can take on one of three values:
OrderedAscending: The first value comes before the second value. For
example, if you compared 5 to 7, you would get this result—5 is less than 7.
OrderedSame: Both values are in the same position under the ordering. For
example, with caseInsensitiveCompare(_:), “PICKLE” and “pickle” are
ordered the same because you are not considering capitalization.
OrderedDescending: The first value comes after the second value. For
example, if you compared 7 to 5— 7 is greater than 5.
For the More Curious: Sorting Without
NSArrayController
In Chapter 7, you populated a table view by implementing the data source
methods. You might have wondered at that time how you could implement this
sorting behavior in SpeakLine.
Recall that clicking on the table column headers updates an array of
NSSortDescriptor objects, which the array controller then applies to reorder its
content. You can sort any array of NSObject-based objects yourself using the
following method on NSArray:
func sortedArrayUsingDescriptors(sortDescriptors:
[AnyObject]) -> [AnyObject]
When is the appropriate time to sort? When the user clicks on the header of a
column, an optional NSTableViewDataSource method will be called:
optional func tableView(tableView: NSTableView,
sortDescriptorsDidChange
oldDescriptors: [AnyObject])
Thus, if you have an array that holds the information for a table view, you can
implement the method like this:
@IBOutlet weak var tableView: NSTableView!

var objects: [SomeObject]

func tableView(tableView: NSTableView,
sortDescriptorsDidChange
oldDescriptors: [AnyObject])
{
let sortDescriptors =
tableView.sortDescriptors
let objectsArray = objects as NSArray
let sortedObjects =
objectsArray.sortedArrayUsingDescriptors(sortDescriptors)

objects = sortedObjects
objects = sortedObjects
tableView.reloadData()
}
And voilà!, sorting in your application.
For the More Curious: Filtering
As discussed earlier, NSArrayController arranges its objects by filtering and
sorting. These two lenses correspond to properties on NSArrayController:
filterPredicate, an NSPredicate, and sortDescriptors, an array of
NSSortDescriptor objects.

NSPredicate is a logical operator that can be evaluated against an object.


Predicates are typically instantiated using a format string. This predicate
evaluates to true when raise is above 5%:
if let raisePredicate = NSPredicate(format: "raise >
0.05") {
let matched: Bool =
raisePredicate.evaluateWithObject(employee)
...
}
You can also create an array by filtering another. This predicate performs a case-
insensitive comparison on the name and requires that the raise is greater than
zero:
let employeesArray = employees as NSArray
let formatString = "name like[c] %@ and raise > 0.0"
if let compoundPredicate = NSPredicate(format:
formatString, someName) {
let matches =

employeesArray.filteredArrayUsingPredicate(compoundPredicate)
as [Employee]
...
}
As you may have surmised, initializing an NSPredicate will fail if the string is
not a valid format string.
NSArrayController can be bound to a string via its Filter Predicate binding, or you
can set it programmatically using the filterPredicate property. You can read
more about predicates and predicate format strings in Apple’s Predicate
Programming Guide.
Because predicates rely on KVC there is some overhead. If you need to filter a
large array of objects, Swift’s Array<T> type’s filter(_:) method will be much
faster. This code is equivalent to the above example:
let matches = employees.filter { employee -> Bool in
if let name = employee.name {
let nameMatches =
name.caseInsensitiveCompare(someName) == .OrderedSame
return nameMatches && employee.raise > 0.0
}
else {
return false
}
}
This syntax may be unfamiliar to you if you have not seen closures. Closures are
discussed further in Chapter 15.
For the More Curious: Using Interface
Builder’s View Hierarchy Popover
Selecting nested views in Interface Builder can be a trying ordeal. You can spend a
great deal of time clicking repeatedly, trying to navigate to just the right view.
Luckily, Interface Builder provides you with three different mechanisms for
selecting just the view you want: the document outline, the jump bar, and the
view hierarchy popover. In Figure 9.27, you can see the selected Name table
column in the context of the entire hierarchy of XIB objects.
Figure 9.27 The Name table column selected in the document outline

The document outline, as you have already seen, is displayed on the left-hand
side of the Interface Builder editor. You have already been using it very effectively
to select objects in the XIB. Its biggest advantage over the other mechanisms is
that it shows you the entire hierarchy of items in the XIB.
You have also had a few opportunities to use the jump bar. It is very nice to be
able to glance up at it to see what is currently selected. In Figure 9.28, the jump
bar shows that the Name table column is selected. You can also click on any of
the items in the jump bar to present a menu which will allow you to drill into
that item’s children.
Figure 9.28 The Name table column displayed in the jump bar

The third tool, which has not been discussed yet, is the view hierarchy popover.
This tool’s greatest strength is the speed with which it allows you to select
deeply nested objects.
Switch over to RaiseMan’s Document.xib to try it out. Hover your mouse
somewhere over the first column of the table view in Interface Builder’s canvas
(Figure 9.29).
Figure 9.29 Positioning the mouse pointer somewhere over the Name table
column

With your mouse still in that position, hold down Control and Shift and then
click. You will see a popover listing all of the objects under your mouse. You
can then select any of the objects from the list to jump to it. Try it out by
selecting the Name table column (Figure 9.30).
Figure 9.30 Selecting the Name table column from the view hierarchy
popover
After selecting the table column, you can double-check that you have the right
object selected by looking at the jump bar.
Challenge: Sorting Names by Length
Make the application sort people based on the number of characters in their
names. You can complete this challenge using only Interface Builder – the trick is to
use a key path. (Hint: Strings have a length method.)
10
Formatters and Validation
Users are complicated. They have strong opinions about how they want to input
and see data. As a developer, you want to support their whims, to get that I
thought it might do this and it does! reaction. With formatters your text fields
can present and accept data textually without cluttering your controller.
Formatters also provide validation at the controller layer, while Key-Value
Validation allows you to validate at the model layer.
Formatters
Formatters convert strings into data and data into strings. In the same way that
an array controller is a lens for an array, changing the model of your application
into a representation for the view layer, a formatter is a lens for one piece of data
that can be represented to the user as a string.

Formatters, programmatically
Each formatter works with a specific type of data. NSNumberFormatter works
with numeric types, while NSDateFormatter works with NSDate objects.
Suppose you had a date in a playground:
let date = NSDate() // "Feb
28, 2015, 8:07 PM"
You could get a string using description:
date.description // "2015-
03-01 01:07:59 +0000"
That is fine for debugging, but it does not provide much in the way of options.
Instead, you can use a date formatter to generate a string with a medium length
date and a short time:
let dateFormatter = NSDateFormatter() //
<NSDateFormatter: 0x7f8a558016c0>
dateFormatter.dateStyle = .MediumStyle
dateFormatter.timeStyle = .ShortStyle
dateFormatter.stringFromDate(date) // "Feb
28, 2015, 8:07:59 PM"
Note that the playground appears to be using the same settings when
representing NSDate objects. You can also generate an NSDate from a string, if the
string matches the expected format:
let destination: NSDate? =
dateFormatter.dateFromString("Oct 26, 1985, 01:21 AM")
If you have a specific format you need, specify a dateFormat string. You will use
this technique for parsing dates from JSON in Chapter 28. Otherwise, use the
preset styles in order to take advantage of the user’s locale settings.
Formatter styles set multiple properties at once. Therefore you will want to rely
on styles for course-grained settings, fine tuning with properties afterward. Here
is an example with NSNumberFormatter:
let ransom = 1_000_000 //
1000000
let numberFormatter = NSNumberFormatter()

numberFormatter.numberStyle = .CurrencyStyle
numberFormatter.stringFromNumber(ransom) //
"$1,000,000.00"

numberFormatter.maximumFractionDigits = 0
numberFormatter.stringFromNumber(ransom) //
"$1,000,000"

Formatters and a control’s objectValue


Formatters are very closely integrated with controls. In fact, NSControl has a
formatter property. In RaiseMan you dropped a number formatter on the Raise cell
text field. That step was instantiating the number formatter and assigning the text
field’s formatter property.
Figure 10.1 shows the connection panel for the text field; the formatter outlet is
connected. Why is the formatter shown as a child of the cell in the document
outline? Well, remember that in Chapter 5 we said that the cells handle most of
the work? This is a case where NSControl is simply passing the property through
to the cell.
Figure 10.1 The text field's formatter outlet connected
When the user edits the contents of a text field with a formatter on it, the
formatter is given the opportunity to validate the input. The formatter asks, “Can
I parse this string to the appropriate object?” If it cannot, an error is shown. If it
can, this value is set as the text field’s objectValue. If a value binding is
configured for the text field, then there will be another opportunity for
validation, which we will discuss later in this chapter.
Conversely, when the objectValue is set on a text field with a formatter, the
formatter will convert the object to a string representation, which will be shown
in the text field. This representation is also available as stringValue.

Formatters and localization


One more note on formatters: Date and number formatters have the added
benefit of knowing a lot more about your users’ tastes – or, at least, how they
like to see their numbers and dates – than you could hope to. The user’s locale
settings, which can be found in the Location & Region preference pane of System
Preferences, govern number, date, and time styles. We will discuss localization in
much more detail in Chapter 26.
Cocoa provides a number of formatters; we covered the two most popular here.
If you find that Cocoa does not provide a formatter that does what you need, you
can write your own by subclassing NSFormatter and take advantage of the strong
integration with controls that formatters enjoy.
Validation with Key-Value Coding
As mentioned earlier, there are two opportunities for validation when working
with Cocoa controls and bindings. The first, formatters, straddles the view and
controller layers of MVC. Once a value is accepted by a formatter, if present,
there is a second opportunity for validation via Key-Value Coding (KVC), or,
more specifically, by Key-Value Validation. Key-Value Validation is done at the
model and/or controller layers. That is, the model objects themselves can opt in
to validating changes.

Adding Key-Value validation to RaiseMan


RaiseMan’s users can enter any name for an employee, even the empty string. But
they cannot enter just anything for the raise. Although you made the number
formatter lenient, it will not accept non-numeric input. For example, you cannot
enter “five”. If you try, you will be presented with an alert:
Figure 10.2 Entering “five” for the raise

The formatter, however, will allow you to enter the empty string. This is a
problem for a couple of reasons. First of all, an “empty” raise is not meaningful.
More pressingly, this is a problem because it causes KVC to throw an exception.
Try this in RaiseMan: add an employee, click to select the raise, delete the text, and
press Return. Apparently, nothing happened: the Raise text field is still being
edited. But if you look in the Xcode console, in the debug area, you will see the
exception:
RaiseMan[13301:1762192] An uncaught exception was
raised
RaiseMan[13301:1762192] [<RaiseMan.Employee
0x60000004def0> setNilValueForKey]:
could not set nil as the value for the key raise.
This is what happens when an exception is thrown while handling an event: the
exception is logged to the console and processing that cycle of the event loop is
aborted. If you perform an action in your Cocoa app and nothing happens, check
the console for an exception.
Here is what is causing this exception:
1. You enter the empty string into the text field.
2. The number formatter transforms the string into an optional NSNumber. In
this case it is nil. This is the text field’s new objectValue.
3. The bindings system checks whether it needs to validate this value.
Validation is not enabled for the binding between the text field and the
employee, so none is done.
4. Bindings uses KVC to set this value for the Employee’s key "raise".
5. While KVC knows how to transform any NSNumber into a Float, it does not
know how to transform nil into a Float. So KVC throws an exception.
To prevent this exception from being thrown – and to prevent the user from
entering non-meaningful input – you will add validation for this binding. In
particular, you are going to make the Employee class provide validation for its
raise key.

Enabling validation for a particular binding from the object that is being bound
to – Employee in this case – requires two steps: First, you must make that binding
opt into validation. Second, you must actually write the method that validates the
input coming from the bindings system.
You will start out by enabling validation for the binding. Remember, the one you
are interested in here is the binding from the Value of the text field in the Raise
column to the objectValue.raise keypath of its Table Cell View.
Open Document.xib and select that text field using the document outline or using
the view hierarchy popover, as described in the section called “For the More
Curious: Using Interface Builder’s View Hierarchy Popover” . Switch to the
bindings inspector, expand the Value binding, and check Validates Immediately
(Figure 10.3).
Figure 10.3 Enabling validation for the Raise text field

With this option checked, the bindings system will try to ask the Employee object
to validate the value that the user has entered before updating Employee with the
new value.
validateKEY(_:error:) When you enter a new value into the text field, the
bindings system will look for a method named according to the following
scheme: validateKEY(_:error:). Since the key in this case is raise, the system
will look for a method called validateRaise(_:error:) on Employee. This
method, if it exists, should have the following signature:
func validateRaise(raiseNumberPointer:
AutoreleasingUnsafeMutablePointer<NSNumber?>,
error outError: NSErrorPointer)
-> Bool
The method takes a pointer to an optional NSNumber and a pointer to an NSError.
It takes pointer parameters because they allow pass-by-writeback. This means
you can use them to pass information back to the code that calls this method:
You can pass an optional NSNumber back to the calling code through the first
parameter and an NSError through the second parameter.
The first parameter also contains the value that bindings is trying to validate.
You access this value using the memory property: raiseNumberPointer.memory.
The method returns a Bool. If you return false, you are saying that the value in
the raiseNumberPointer is invalid. Typically, when the value is invalid, you will
want to pass an NSError describing why back to the calling code through the
error parameter.

If you return true from the method, you are saying that the value contained in
the raiseNumberPointer when the method returns is valid. Notice that you can
return true without saying that the value the method was called with was valid:
You can change the value in raiseNumberPointer’s memory property in your
method’s implementation.
Open Employee.swift and implement validateRaise(_:error:) to validate only
non-nil numbers:
func validateRaise(raiseNumberPointer:

AutoreleasingUnsafeMutablePointer<NSNumber?>,
error outError: NSErrorPointer)
-> Bool {
let raiseNumber = raiseNumberPointer.memory
if raiseNumber == nil {
let domain =
"UserInputValidationErrorDomain"
let code = 0
let userInfo =
[NSLocalizedDescriptionKey : "An
employee's raise must be a number."]
outError.memory = NSError(domain: domain,
code: code,
userInfo:
userInfo)
return false
} else {
return true
}
}
Be careful when typing this method name in. Xcode will not auto-complete its
signature for you.
In this implementation, you check whether raiseNumberPointer contains a nil
value. If it does, you indicate that the value is invalid by generating an error,
writing that error into the outError pointer’s memory, and returning false. If
raiseNumberPointer contains a non-nil value, you indicate that the value is
valid by returning true.
Run the app and add a new employee. Try entering the empty string into the Raise
text field. You should see an error as in Figure 10.4.
Figure 10.4 The error presented when entering the empty string

Note that there is a reasonable alternative implementation of this method. Instead


of indicating that nil is an invalid value for the raise property by returning
false, you could set raiseNumberProperty.memory to a reasonable value such as
0.0 into and return true. Deciding between this approach and the approach
shown above must be done on an app-by-app basis, since the decision involves a
trade-off. If you implement the method to transform nil into 0.0, then the user
will be able to end editing with an empty text field, but they may unintentionally
lose information (the old raise value) if they press Return accidentally after
clearing the field. On the other hand, if you implement the method to reject nil,
you make it more difficult for the user to set the value to 0.0 (or another default
value).
For the More Curious: NSValueTransformer
Bindings read values from objects. Sometimes, a value will need some
massaging before it can be used. To fulfill this purpose, Apple created
NSValueTransformer. One example is NSNegateBoolean, which transforms true
into false, and false into true.
The Hidden binding on views in Interface Builder is very convenient, as long as you
are binding to a Bool that is true when you want the view hidden. If your Bool is
true for shown, you can simply enter NSNegateBoolean in the Value Transformer
field of the bindings inspector. No need to jump through hoops to create a
bindings-compliant Bool for the inverse.
You can create your own NSValueTransformer subclasses and attach them to
bindings in your application. When subclassing NSValueTransformer you must
register your subclass by name at runtime. Unlike formatters, value transformers
are used only by bindings.
For more on value transformers, see Apple’s Value Transformer Programming
Guide.
11
NSUndoManager
Using NSUndoManager, you can add undo capabilities to your applications in a
very elegant manner. As objects are added, deleted, and edited, the undo
manager keeps track of all actions that must be taken to undo these changes. As
you invoke the undo mechanism, the undo manager keeps track of all actions
that must be taken to redo those changes. This mechanism works by maintaining
two stacks: one for undo and another for redo. The objects on the stacks are
instances of NSInvocation, a class that describes a message send: the receiver,
method name, and arguments.
This is a pretty heavy topic to cover so early in a book. (Sometimes when we
think about undo, our heads start to swim a bit.) However, undo interacts with
the document architecture. If we tackle this work now, you will see in the next
chapter the document architecture working as it is intended, complete with those
interactions.
Message Passing and NSInvocation
Before learning more about the undo manager, you need to know a couple things
about message passing. As you learned in Chapter 5, Cocoa is built on
Objective-C, and many of its patterns take advantage of Objective-C message
passing. Message passing is dynamic, meaning that unlike a method call, the
path of a message is not fixed. For example, an Objective-C object can respond
to or forward messages for methods it has not implemented.
When an object is sent a message that it does not understand, before raising an
exception the message-sending system checks whether the object has
implemented the forwardInvocation(_:) method, which takes an NSInvocation
as its argument. If the object has this method, the message sent is packed up as
an NSInvocation and passed to forwardInvocation(_:) to deal with. As you will
soon see, NSUndoManager takes advantage of this.
You may be wondering, how does this all work in the context of Swift? Method
calls to Objective-C objects get compiled into message sends. As a result, even
though the message forwarding machinery that the undo manager relies on
depends on Objective-C, all your Swift method calls will go through that
machinery.
Note, however, before you set off to implement forwardInvocation(_:), that
because NSInvocation is not available in Swift, it can only be implemented in
Objective-C.
How the NSUndoManager Works
Suppose that the user opens a new RaiseMan document and makes three edits:
1. Inserts a new record
2. Changes the name from New Employee to John Locke
3. Changes the raise from 5 to 20
To support undo, as each edit is performed your controller will add an invocation
that would undo that edit to the undo stack. For the sake of simplifying the
prose, let’s say, “The inverse of the edit gets added to the undo stack.”
Figure 11.1 shows what the undo stack would look like after these three edits.
Figure 11.1 The undo stack

If the user now chooses the Undo menu item, the first invocation is taken off the
stack and invoked. This would change the person’s raise back to five percent. If
the user chooses the Undo menu item again, the person’s name would change
back to New Employee.
Each time an item is popped off the undo stack and invoked, the inverse of the
undo operation must be added to the redo stack. Thus, after undoing the two
operations described, the undo and redo stacks should look like Figure 11.2.
Figure 11.2 The revised undo stack
The undo manager is quite clever: When the user is doing edits, the undo
invocations go onto the undo stack. When the user is undoing edits, the undo
invocations go onto the redo stack. When the user is redoing edits, the undo
invocations go onto the undo stack. This stack management is handled
automatically for you; your only job is to give the undo manager the inverse
invocations that need to be added.
Using NSUndoManager
Now that you have a general idea of how your code will interact with the undo
manager, let’s look at a more concrete example. Suppose that you are writing a
thermostat application. It has a property, undoManager, an instance of
NSUndoManager, as well as a method, makeItHotter(), whose inverse is
makeItColder(). Here is how you would implement undo for it:
func makeItHotter() {
temperature += 10

undoManager.prepareWithInvocationTarget(self).makeItColder()

showTheTemperature()
}
Two steps are performed: the prepareWithInvocationTarget(_:) method is
called on the undo manager. It returns an object which you then call
makeItColder() on. The return type of prepareWithInvocationTarget(_:) is
AnyObject. The object it returns does not implement makeItColder(), however,
so forwardInvocation(_:) gets called with makeItColder: wrapped up in an
NSInvocation. This object returned by prepareWithInvocationTarget(_:) has a
clever override for forwardInvocation(_:) such that it adds the invocation for
makeItColder: to the undo stack.

To complete the example, you would implement makeItColder():


func makeItColder() {
temperature -= 10

undoManager.prepareWithInvocationTarget(self).makeItHotter()

showTheTemperature()
}
Note that you have again registered the inverse with the undo manager. If
makeItColder() is invoked as a result of an undo, this inverse will go onto the
redo stack.
The invocations on either stack are grouped. By default, all invocations added to
a stack during a single event (such as a mouse click or a keyboard button press)
are grouped together. Thus, if one user action causes changes in several objects,
all the changes are undone by a single click of the Undo menu item. You can
control this grouping using the methods beginUndoGrouping() and
endUndoGrouping(). If you are following this approach, you will want to use the
groupingLevel property to determine whether the undo manager is already
grouping actions.
The undo manager can also change the label on the Undo and Redo menu items.
For example, “Undo Insert” is more descriptive than just “Undo.” To set the
label, use the following code: undoManager.setActionName("Insert")
Note that a group of undo invocations only gets a single action name, the last
one that is set. As a result, you have to take care when setting the action name if
a single event triggers a number of calls to prepareWithInvocationTarget(_:).
How do you get an undo manager? You can create one explicitly, but this is
seldom necessary. In RaiseMan, you will use the one provided by your Document,
which it has since each instance of NSDocument already has its own undo
manager.
KeyValue Coding and To-Many
Relationships
In RaiseMan the user will want to be able to undo and redo adding and removing
employees. To do this, you will need to execute code which registers the inverse
action with the undo manager when employees are added or removed. Since the
NSArrayController handles adding and removing Employee objects to and from
the employees array, you will not simply be able to execute this code when
Document changes the employees array – Document is not changing employees, the
NSArrayController is!

Instead, you will need to hook into the key-value coding mechanism that the
NSArrayController is leveraging to add and remove employees. KVC defines a
number of optional methods by convention which you can implement to pull off
exactly this sort of task. You will implement a couple of these methods on
Document; KVC will call them when the array controller is changing the
employees array. In your implementations, you will add code to register inverse
operations with the undo manager when employees are added to or removed
from the array.
In RaiseMan, the employees property on Document represents a relationship between
Document and Employee of a particular type: it is an ordered to-many relationship.
There are four different categories of properties which KVC recognizes:
1. Simple attributes. Example: Each student has a first name. Simple attributes
are typically numbers or instances of String, NSDate, or NSData.
2. To-one relationships. Example: Each student has a school. It is like a
simple attribute, but the type is a complex object, not a simple one.
To-one relationships are implemented using references: An instance of
Student has a reference to an instance of School.

3. Ordered to-many relationships. Example: Each playlist has a list of songs.


The songs are in a particular order. This is typically implemented using an
array.
4. Unordered to-many relationships. Example: Each teacher has a bunch of
students. You can display the students in a particular order (such as sorted
by last name), but that ordering is not inherent in the relationship. This is
typically implemented using an NSMutableSet.
You saw in Chapter 8 how KVC could be used to read and write simple
properties and to-one relationships. Let’s take a look at how KVC interacts with
the to-many relationships now.
Suppose, for example, that an instance of Playlist has an array of Song objects.
If you want to use key-value coding to manipulate that array, you will ask the
playlist for its mutableArrayValueForKey(_:). You will get back a proxy object.
That proxy object knows that it represents the array that holds the songs.
let arrayProxy =
playlist.mutableArrayValueForKey("songs")
let countFromProxy = arrayProxy.count
In this example, when asked for the count the proxy object will ask the Playlist
object if it has a countOfSongs property. If Playlist does, the proxy object will
access the Playlist’s property and return its value. If there is no such property,
the proxy object will access the Playlist’s songs property and return its count
(Figure 11.3).
Figure 11.3 Key-value coding for ordered relationships

Note, then, countOfSongs is not just a nice name for a property which describes
the count of a songs property. Instead, this name is determined by convention to
be what the key-value coding mechanism goes looking for.
There are several similar conventions for method names which KVC will look
for when methods are called or properties are accessed on the mutable array
proxy:
let playlist: Playlist = Playlist()
let arrayProxy =
let arrayProxy =
playlist.mutableArrayValueForKey("songs")

let countFromProxy = arrayProxy.count // is the same
as
let countFromPlaylist = playlist.countOfSongs // if
countOfSongs exists

let fifthFromProxy = arrayProxy.objectAtIndex(5) as!
Song // is the same as
let fifthFromPlaylist =
playlist.objectInSongsAtIndex(5) // if the method
exists

let song = Song(name: "Your Favorite Music")
arrayProxy.insertObject(song, atIndex: 4) // is the
same as
playlist.insertObject(song, inSongsAtIndex: 4) // if
the method exists

arrayProxy.removeObjectAtIndex(3) // is the same as
playlist.removeObjectFromSongsAtIndex(3) // if the
method exists
There is a similar set of calls for unordered to-many relationships (Figure 11.4).
Figure 11.4 Key-value coding for unordered relationships

let teacher = Teacher()




let setProxy =
teacher.mutableSetValueForKey("students")

let countFromProxy = setProxy.count // is the same as
let countFromTeacher = teacher.countOfStudents // if
countOfStudents exists

let newStudent = Student(name: "Georgie Prescott")
setProxy.addObject(newStudent) // is the same as
teacher.addStudentsObject(newStudent) // if the method
exists

let expelledStudent = Student(name: "Shin Nohara")
setProxy.removeObject(expelledStudent) // is the same
as
teacher.removeStudentsObject(expelledStudent) // if
the method exists
Adding Undo to RaiseMan
Open the RaiseMan project that you started in Chapter 9. You are going to give
users the ability to undo the effects of clicking the Add Employee and Remove
buttons, as well as the ability to undo the changes they make to Employee objects
in the table. The necessary code will go into your Document class.
Because you have bound the contentArray of the array controller to the
employees array of the Document object, the array controller will use key-value
coding to add and remove Employee objects. You will take advantage of this to
add undo invocations to the undo stack when people are added and removed. To
do this, open Document.swift and add the following section to the Document
implementation:
// MARK: - Accessors

func insertObject(employee: Employee,


inEmployeesAtIndex index: Int) {
println("adding \(employee) to the employees
array")

// Add the inverse of this operation to the


undo stack
let undo: NSUndoManager = undoManager!
undo.prepareWithInvocationTarget(self)

.removeObjectFromEmployeesAtIndex(employees.count)
if !undo.undoing {
undo.setActionName("Add Person")
}

employees.append(employee)
}

func removeObjectFromEmployeesAtIndex(index: Int)


{
let employee: Employee = employees[index]
println("removing \(employee) from the
employees array")

// Add the inverse of this operation to the


undo stack
let undo: NSUndoManager = undoManager!
undo.prepareWithInvocationTarget(self)
.insertObject(employee,
inEmployeesAtIndex: index)
if !undo.undoing {
undo.setActionName("Remove Person")
}

// Remove the Employee from the array


employees.removeAtIndex(index)
}
These methods will be called automatically when the NSArrayController wishes
to insert or remove Employee objects (for example, when the Add Employee and
Remove buttons send it add: and remove: messages).

At this point, you have made it possible to undo deletions and insertions.
Undoing edits will be a little trickier. Before tackling this task, build and run
your application. Test the undo capabilities that you have at this point. Note that
redo also works.
KeyValue Observing
You’ve now seen how key-value coding works, both for simple properties and
to-one relationships on the one hand, and for collection properties and to-many
relationships. Key-value observing (KVO), on the other hand, allows you to be
informed when an object’s properties are changed, whether by KVC or by direct
property access.
You saw in Chapter 8 how bindings rely on KVO: objects bound to a key are
automatically added as observers of that key and are then notified whenever its
value changes. To enable undo capabilities for edits, you will need to add an
observer manually. In particular, you want your document object to be an
observer for the keys raise and name for all the Employee objects in its employees
array.
The following method on NSObject allows you to register to be informed of these
changes:
func addObserver(observer: NSObject,
forKeyPath keyPath: String,
options: NSKeyValueObservingOptions,
context: UnsafeMutablePointer<Void>)
You call this method on the object whose property you want to observe. The first
argument that you supply is the observer, the object which should be informed
when the property changes. You pass the name of the property that you want to
observe as the second argument. (Notice that you specify a key path rather than
just a key. This means that, rather than simply observing an object’s property,
you can observe an object’s property’s property. For example, you could
observe a person’s mother’s age by specifying mother.age as the key path.) The
options argument specifies the information you would like to have handed to the
observer when it is informed about the changes. For example, it can be told
about the old value (before the change) and the new value (after the change).
The context variable is used to indicate what level of the class hierarchy is
observing the notification.
When a change occurs, the following method is called on the observer:
func observeValueForKeyPath(keyPath: String,
ofObject object: AnyObject,
change: [NSObject :
change: [NSObject :
AnyObject],
context:
UnsafeMutablePointer<Void>)
The observer is told which key path changed in which object. Here, change is a
dictionary that (depending on the options you asked for when you registered as
an observer) may contain the old value and/or the new value. The method is
called with the context pointer supplied when the observer started observing.
Using the Context Pointer Defensively
In order for any class to observe key-value changes, it must implement the
observeValueForKeyPath(_:ofObject:change:context:) method; it is not
possible to provide a different method to KVO. Consequently, it is possible to
mistakenly intercept notifications intended for a superclass.
Consider the case of some class Maple, which is a subclass of some other class
Tree. Both classes observe Environment’s key path season independently. Unless
the developer takes precautions, Maple will receive its key-value-observing
messages and those intended for Tree, because Maple has effectively overridden
Tree’s KVO method. To fix this, Maple must correctly distinguish between KVO
notifications intended for it from those intended for its superclass. If the
notification is not intended for Maple, it should pass the message on to its
superclass.
The way to pull this off is to use a class-specific pointer value as the context
argument. You will use this approach in the KVO code that you write to make
Document observe Employee’s name and raise properties. Although it is not
necessary within the conditions of this specific exercise, since Employee is not a
part of a complex class hierarchy with other classes that also do observing, this
should be your default approach.
Undo for Edits
It is time now for you to add undo support for editing of employees’s fields to
RaiseMan. The first step is to register Document to observe changes to the Employee
objects in its employees array. Add the following private variable near the top of
Document.swift:
import Cocoa

private var KVOContext: Int = 0

class Document: NSDocument {


You will use the KVOContext variable to get a unique value to use as the KVO
context pointer.
Next, add this section inside the Document implementation:
// MARK: - Key Value Observing

func startObservingEmployee(employee: Employee) {


employee.addObserver(self,
forKeyPath: "name",
options: .Old,
context: &KVOContext)
employee.addObserver(self,
forKeyPath: "raise",
options: .Old,
context: &KVOContext)
}

func stopObservingEmployee(employee: Employee) {


employee.removeObserver(self,
forKeyPath: "name",
context: &KVOContext)
employee.removeObserver(self,
forKeyPath: "raise",
context: &KVOContext)
}
Now add property observers for the employees property so that when the array is
changed the old employees will not be observed and the new ones will be:
var employees: [Employee] = []
var employees: [Employee] = [] {
willSet {
for employee in employees {
stopObservingEmployee(employee)
}
}
didSet {
for employee in employees {
startObservingEmployee(employee)
}
}
}
Implement the method that will be called whenever an Employee object is edited
by either the user or the setValue(_:forKeyPath:) method. Add this method at
the bottom of the Key Value Observing section:
override func observeValueForKeyPath(keyPath: String,
ofObject
object: AnyObject,
change:
[NSObject : AnyObject],
context:
UnsafeMutablePointer<Void>) {
if context != &KVOContext {
// If the context does not match, this
message
// must be intended for our superclass.
super.observeValueForKeyPath(keyPath,
ofObject:
object,
change:
change,
context:
context)
return
}

var oldValue: AnyObject? =


change[NSKeyValueChangeOldKey]
if oldValue is NSNull {
oldValue = nil
}

let undo: NSUndoManager = undoManager!


println("oldValue=\(oldValue)")

undo.prepareWithInvocationTarget(object).setValue(oldValue,


forKeyPath: keyPath)
}
Note that this method puts a call to setValue(_:forKeyPath:) on the undo stack
with the old value for the changed key.
What would happen if the Document is being deallocated (when the window is
closed, for example) and there are Employees in this array? Since the Document
owns the Employees, they will be deallocated. However, the Document will still be
observing these Employees when they are deallocated. Key-value observing will
raise an exception if this happens. It is your job to remove all observers of an
Employee (or any other object) before the Document gets deallocated.

In order to fix this, you need to make the Document stop observing all of its
Employees before the window closes. To do this, you will make the Document be
the delegate of the window and implement windowWillClose(_:). This will give
you a chance to remove the Document as an observer from each of the Employees.
First, make Document adopt the NSWindowDelegate protocol: class Document:
NSDocument, NSWindowDelegate {
Thanks to Xcode’s template for document-based apps, Document is already the
delegate of the Window in Document.xib. Verify that this is still the case by
opening Document.xib and Control-clicking Window in the document outline to
present the window’s connections panel. You should see that the delegate outlet
is set to File's Owner:
Figure 11.5 The window’s delegate outlet set to File's Owner
You are now ready to implement windowWillClose(_:) on Document. Open
Document.swift and, near the bottom of the class’s definition, add this
implementation for the delegate method:
// MARK: - NSWindowDelegate

func windowWillClose(notification: NSNotification)


{
employees = []
}
By setting the employees property to be the empty array, you trigger the willSet
property observer on it, making the Document stop observing each of the
employees. Since this method gets called after any saving is done, it will not
cause any trouble after you have implemented saving in the next chapter.
That should do it. Once you build and run your application, undo and redo
should work flawlessly.
However, in testing your application you may encounter an error: The document
could not be autosaved. Now that you are interacting with the undo system, AppKit is
noticing that your document has unsaved changes and is trying to autosave the
document. You will learn how to save your document to a file in the next
chapter.
Begin Editing on Insert
Your app is coming along nicely, but your users will complain, “Why do I have
to double-click to start editing after an insert? It is obvious that I am going to
immediately change the name of the new person. Can’t you start the editing as
part of the insert?”
Oddly, this is a little tricky to do. Open Document.swift. First, add two new
outlets:
class Document: NSDocument {
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var arrayController:
NSArrayController!
var employees: [Employee] = [] {
Next, while still in Document.swift, add a stub for a new action,
addEmployee(_:), to the implementation:
// MARK: - Actions

@IBAction func addEmployee(sender: NSButton) {

}
You will write the implementation for this method in just a moment. For now,
save that file and open Document.xib.
Control-drag from the Add Employee button to the File's Owner (which is the instance
of Document). Set its action to addEmployee:.
Control-click on the File's Owner in the Icon View to present its connections panel.
Drag from it to connect the tableView outlet to the table view and the
arrayController outlet to the array controller (Figure 11.6).

Figure 11.6 Setting the outlets


Now, in Document.swift, implement the addEmployee(_:) method:
// MARK: - Actions

@IBAction func addEmployee(sender: NSButton) {


let windowController = windowControllers[0]
as! NSWindowController
let window = windowController.window!

let endedEditing =
window.makeFirstResponder(window)
if !endedEditing {
println("Unable to end editing.")
return
}

let undo: NSUndoManager = undoManager!

// Has an edit occurred already in this event?


if undo.groupingLevel > 0 {
// Close the last group
undo.endUndoGrouping()
// Open a new group
undo.beginUndoGrouping()
}

// Create the object


let employee = arrayController.newObject() as!
Employee

// Add it to the array controller's


contentArray
arrayController.addObject(employee)

// Re-sort (in case the user has sorted a


column)
arrayController.rearrangeObjects()

// Get the sorted array


let sortedEmployees =
arrayController.arrangedObjects as! [Employee]

// Find the object just added


let row = find(sortedEmployees, employee)!

// Begin the edit in the first column


println("starting edit of \(employee) in row \
(row)")
tableView.editColumn(0,
row: row,
withEvent: nil,
select: true)
}
We do not really expect you to understand every line of that code now, but
browse through the method and try to get the gist. Run the application and add a
new employee. You should be able to edit the new employee immediately.
For the More Curious: Windows and the
Undo Manager
A view can add inverse actions for its edits to the undo manager. NSTextView, for
example, can put the undo action for each edit that the user makes to the text
onto the undo manager. How does the text view know which undo manager to
use? First, it asks its delegate. NSTextView delegates can implement this method:
optional func undoManagerForTextView(view:
NSTextView!) -> NSUndoManager?
Next, it asks its window. NSWindow inherits a property for this purpose from
NSResponder:
var undoManager: NSUndoManager? { get }
The window’s delegate can supply an undo manager for the window by
implementing the following method:
optional func windowWillReturnUndoManager(window:
NSWindow) -> NSUndoManager?
The undo/redo menu items reflect the state of the undo manager for the key
window (Figure 11.7). (The key window is what most users call the “active
window.” Cocoa developers call it key because it is the one that will get the
keyboard events if the user types.)
Figure 11.7 NSTextView attributes
12
Archiving
While an object-oriented program is running, a complex graph of objects is
being created. It is often necessary to represent this graph of objects as a stream
of bytes, a process called archiving (Figure 12.1). This stream of bytes can then
be sent across a network connection or written into a file. For example, when
creating a NIB from the XIB file you edited in Interface Builder, the compiler is
archiving objects into a file. (Another commonly used term for this process is
“serialization.”)
Figure 12.1 Archiving

When you need to recreate the graph of objects from the stream of bytes, you
will unarchive it. When your application starts up, it unarchives the objects from
the NIB file.
Although objects have both properties and methods, only the properties and the
name of the class go into the archive. In other words, only data, not code, goes
into the archive. As a result, if one application archives an object and another
application unarchives the same object, both applications must have the code for
the class linked in. In the NIB file, for example, you have used classes like
NSWindow and NSButton from the AppKit framework. If you do not link your
application against the AppKit framework, it will be unable to create the
instances of NSWindow and NSButton that it finds in the NIB file.
There was once a shampoo ad that said, “I told two friends, and they told two
friends, and they told two friends, and so on, and so on, and so on.” The
implication was that as long as you told your friends about the shampoo,
everyone who matters would eventually wind up using the shampoo. Object
everyone who matters would eventually wind up using the shampoo. Object
archiving works in much the same way. You archive a root object, it archives the
objects which it has references to, they archive the objects that they have
references to, and so on, and so on, and so on. Eventually, every object that
matters will be in the archive.
Archiving involves two steps: teaching your objects how to archive themselves
and kicking off the archiving.
NSCoder and NSCoding
Archiving relies on the NSCoding protocol. If your class conforms to NSCoding, it
promises to implement the following methods:
init(coder aDecoder: NSCoder)
func encodeWithCoder(aCoder: NSCoder)
An NSCoder is an abstraction of a stream of bytes. You can write your data to a
coder or read your data from a coder. The init(coder:) method in your object
will read data from the coder and save that data to its properties. The
encodeWithCoder(_:) method in your object will read its properties and write
those values to the coder. In this chapter, you will implement both methods in
your Employee class.
NSCoder is like an abstract class. You will not ever create instances of an abstract
class. Instead, an abstract class has some capabilities that are intended to be
implemented by subclasses. You will create instances of the concrete subclasses.
Namely, you will use NSKeyedUnarchiver to read objects from a stream of data
and you will use NSKeyedArchiver to write objects to the stream of data.

Encoding
NSCoder has many methods, but most programmers find themselves using just a
few of them repeatedly. Here are the methods most commonly used when you
are encoding data onto the coder: func encodeObject(anObject: AnyObject?,
forKey aKey: String)
This method writes anObject to the coder and associates it with the key aKey.
This will cause anObject’s encodeWithCoder(_:) method to be called (and they
told two friends, and they told two friends…).
For each of the common value types (such as Bool and Float), NSCoder has an
encode method:
func encodeBool(boolv: Bool, forKey key: String)
func encodeDouble(realv: Double, forKey key: String)
func encodeFloat(realv: Float, forKey key: String)
func encodeInt(intv: Int32, forKey key: String)
func encodeInt(intv: Int32, forKey key: String)
In RaiseMan, you are going to implement file saving by teaching instances of
Employee how to turn themselves into data. You are going to make Employee
conform to the NSCoding protocol.
Open your RaiseMan project.
In Employee.swift, declare that Employee conforms to the NSCoding protocol:
import Foundation

class Employee: NSObject {


class Employee: NSObject, NSCoding {
With this change, Employee is promising that it implements NSCoding. It is not
fulfilling this promise yet, though, so if you try to build your code now the
compiler will complain that your class does not conform to NSCoding. To fix this,
you need to implement the methods from the protocol (encodeWithCoder(_:) and
init(coder:)) and teach the class how to encode itself to data and decode itself
from data.
To start, add encoding to your Employee class. Implement encodeWithCoder(_:)
near the top of Employee.swift:
// MARK: - NSCoding

func encodeWithCoder(aCoder: NSCoder) {


if let name = name {
aCoder.encodeObject(name, forKey: "name")
}
aCoder.encodeFloat(raise, forKey: "raise")
}
All the commonly used AppKit and Foundation classes implement the NSCoding
protocol, with the notable exception of NSObject. Because it inherits from
NSObject, Employee does not call super.encodeWithCoder(coder). If Employee’s
superclass had implemented the NSCoding protocol, the method would have
looked like this:
override func encodeWithCoder(aCoder: NSCoder) {
super.encodeWithCoder(aCoder)
if let name = name {
aCoder.encodeObject(name, forKey: "name")
}
aCoder.encodeFloat(raise, forKey: "raise")
aCoder.encodeFloat(raise, forKey: "raise")
}
The call to the superclass’s encodeWithCoder(_:) method would give the
superclass a chance to write its variables onto the coder. Thus, each class in the
hierarchy writes only its properties (and not its superclass’s properties) to the
coder.

Decoding
When decoding data from the coder, you will use the analogous decoding
methods:
func decodeObjectForKey(key: String) -> AnyObject?
func decodeBoolForKey(key: String) -> Bool
func decodeDoubleForKey(key: String) -> Double
func decodeFloatForKey(key: String) -> Float
func decodeIntForKey(key: String) -> Int32
If the object did not write out data for a key raise when the stream was first
written during encodeWithCoder(_:), the coder will return 0.0 from
decodeFloatForKey("raise"). Similarly, if the object did not call
encodeObject(name, forKey: "name") when encodeWithCoder(_:) was
executed, the coder will return nil if the coder is asked to decode an object for
the key name.
To add decoding to your Employee class, add the following method near the top
of Employee.swift, next to your implementation of encodeWithCoder(_:):
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObjectForKey("name") as!
String?
raise = aDecoder.decodeFloatForKey("raise")
super.init()
}
Once again, you did not call the superclass’s implementation of init(coder:),
because NSObject does not have one. If Employee’s superclass had implemented
the NSCoding protocol, the method would have looked like this:
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObjectForKey("name") as
String?
String?
raise = aDecoder.decodeFloatForKey("raise")
super.init(coder: aDecoder)
}
You will also need to add an implementation of init:
override init() {
super.init()
}
At this point, you may be saying, “But why do I need to implement init?
Wasn’t it being called before?” You are right. init was being called before
init(coder:) was added. However, now that init(coder:) has been added, init
is no longer available.
In Swift, initializers are not always inherited. They are only inherited in special
circumstances: a class’s designated initializers are only inherited by a subclass if
that subclass does not define any designated initializers of its own.
In this case, before you added init(coder:), Employee did not define any
designated initializers, so it inherited init from NSObject. Now that you have
added init(coder:), though, Employee defines a designated initializer, so in
order for init to be available, it must be defined in Employee.swift.
You have now implemented the methods required by the NSCoding protocol.
Now compile the project. Fix any errors. You could run the application at this
point, if you like. However, although you have taught Employee objects to
encode themselves, you have not asked them to do so. Thus, you will see no
change in the behavior of your application.
The Document Architecture
Applications that deal with multiple documents have a lot in common. Every
such application can create new documents, open existing documents, save or
print open documents, and remind the user to save edited documents when they
try to close a window or quit the application. AppKit supplies three classes that
take care of most of the details for you: NSDocumentController, NSDocument, and
NSWindowController. Together, these three classes constitute the document
architecture.
The purpose of the document architecture relates to the Model-View-Controller
design pattern discussed in Chapter 1. In RaiseMan, your subclass of NSDocument
acts as the window controller. It will have a reference to the model objects, and
will, directly or via the array controller, carry out the following duties:
saving the model data to a file
loading the model data from a file
giving the views the data to display
taking user input from the views and updating the model

Info.plist and NSDocumentController


When it builds an application, Xcode includes a file called Info.plist. (Later in
this chapter, you will change the Info.plist.) When the application is launched,
it reads from the Info.plist, which tells it what type of files it works with. If it
finds that it is a document-based application, it creates an instance of
NSDocumentController (Figure 12.2). You will seldom have to deal with the
document controller; it lurks in the background and takes care of a bunch of
details for you. For example, when you choose the New or Save All menu item, the
document controller handles the request. If you need to send messages to the
document controller, you could get to it like this:
let documentController =
NSDocumentController.sharedDocumentController()
Figure 12.2 Document controller

The document controller manages an array of instances of NSDocument subclasses


– one for each open document. In RaiseMan, the document controller manages an
array of Documents. When you select New from the File menu, the document
controller handles creating a new instance of Document for you.

NSDocument
The document objects are instances of a subclass of NSDocument. In your RaiseMan
application, for example, the document objects are instances of Document. For
many applications, you can simply subclass NSDocument and not interact with the
rest of the document architecture directly. As you will learn later in the chapter,
though, sometimes you will need to use one or more NSWindowController
subclasses as well.

Saving documents

The menu items Save, Save As..., Save All, and Close are all different, but all deal with
the same problem: getting the model into a file or file wrapper. (A file wrapper is
a directory that looks like a file to the user.) To handle these menu items, your
NSDocument subclass must implement one of three methods:
func dataOfType(typeName: String,
error outError: NSErrorPointer) ->
NSData?
func fileWrapperOfType(typeName: String!,
error outError: NSErrorPointer)
-> NSFileWrapper?
func writeToURL(url: NSURL,
func writeToURL(url: NSURL,
ofType typeName: String,
error outError: NSErrorPointer) ->
Bool

Understanding NSErrorPointer

NSErrorPointer can be a bit confusing. The idea is that if the method – whether
dataOfType(_:error:), fileWrapperOfType(_:error:), or
writeToURL(_:ofType:error:) – is unable to do its job, it will create an NSError
and store it in the NSErrorPointer’s memory field. For example, to read an NSData
from a file, you would supply an address where the error would be placed:
let url = NSURL(fileURLWithPath: "pathto/file.ext")

var error: NSError?


let success = data.writeToURL(url,
options:
NSDataWritingOptions.allZeros,
error: &error)
if !success {
if let error = error {
println("Read failed: \
(error.localizedDescription)")
}
}
Thus, writeToURL(_:options:error:) will either return true for success, or
false, passing an NSError object by writeback.
In these save methods, and the load methods you will see in a moment, you will
be responsible for creating an NSError if the methods fail.

Loading documents

The Open..., Open Recent, and Revert To Saved menu items, although different, all deal
with the same basic problem: getting the model from a file or file wrapper. To
handle these menu items, your NSDocument subclass must implement one of three
methods:
func readFromData(data: NSData,
func readFromData(data: NSData,
ofType typeName: String,
error outError: NSErrorPointer) ->
Bool
func readFromFileWrapper(fileWrapper: NSFileWrapper,
ofType typeName: String,
error outError:
NSErrorPointer) -> Bool
func readFromURL(url: NSURL,
ofType typeName: String,
error outError: NSErrorPointer) ->
Bool
After implementing one save method and one load method, your NSDocument
subclass will know how to read from and write to files. When opening a file,
your NSDocument subclass will read the document file before loading a window
from the NIB file. As a consequence, you will not be able to call methods on the
user interface objects immediately after loading the file (because they will not
exist yet). To solve this problem, after the NIB file is read, the following method
is called on your NSDocument subclass:
func windowControllerDidLoadNib(windowController:
NSWindowController!)
In your NSDocument subclass, you will implement this method to update the user
interface objects.
If the user chooses Revert To Saved from the menu, the model is loaded, but
windowControllerDidLoadNib(_:) is not called. You will, therefore, also have to
update the user interface objects in the method that loads the data, just in case it
was a revert operation. One common way to deal with this possibility is to check
one of the outlets set in the NIB file. If it is nil, the NIB file has not been
loaded, and there is no need to update the user interface.

NSWindowController
The final class in the document architecture to discuss is NSWindowController,
though you will not initially need to worry about it. A document will have an
instance of NSWindowController for each window that it is displaying; these
instances are stored in NSDocument’s windowControllers property.
The default behavior, from Xcode’s template, is for the document to create an
instance of NSWindowController to partially control the window that is loaded
from the document’s XIB. As most applications have only one window per
document, the default behavior of the window controller is usually perfect.
Figure 12.3 shows how NSWindowController fits into the document architecture
in the default setup.
Figure 12.3 A document using an NSWindowController

Though the default setup is often exactly what you want, there are times when
you might want to create a custom subclass of NSWindowController:
You need to have more than one window on the same document. For
example, in a CAD program you might have a window of text that
describes the object being drawn and another window that shows a
rendering of it. The document architecture in such an app would look like
Figure 12.5.
Figure 12.4 A document using multiple window controllers

In this case, the NSDocument’s windowControllers would contain two window


controllers.
Figure 12.5 A document using multiple window controllers
You want to put the user interface controller logic and model controller logic
into separate classes. When doing this, the document architecture would look
like Figure 12.6.
Figure 12.6 A document using a single custom window controller
Saving and NSKeyedArchiver
Now that you have taught your Employee object to encode and decode itself, you
are now ready to add saving and loading to your application. When it is time to
save your employees to a file, your Document class will be asked to create an
instance of NSData. Once your object has created and returned an NSData object,
it will be automatically written to a file.
To create an NSData object, you will use the NSKeyedArchiver class.
NSKeyedArchiver has the following class method: class func
archivedDataWithRootObject(rootObject: AnyObject) -> NSData
This method archives the objects into the NSData object’s buffer of bytes.
Once again, we return to the idea of “I told two friends, and they told two
friends.” When you encode an object, it will encode its objects, and they will
encode their objects, and so on, and so on, and so on. What you will encode,
then, is the employees array. It will encode the Employee objects to which it has
references. Each Employee object (because you implemented
encodeWithCoder(_:)) will, in turn, encode its name string and its raise Float.

To add saving capabilities to your application, edit the method


dataOfType(_:error:) in Document.swift so that it looks like this:
override func dataOfType(typeName: String,
error outError:
NSErrorPointer) -> NSData? {
// End editing
tableView.window?.endEditingFor(nil)

// Create an NSData object from the employees


array
return
NSKeyedArchiver.archivedDataWithRootObject(employees)
return nil
}
Note that we just ignored the error argument. There will be no errors.
Loading and NSKeyedUnarchiver
Now you will add the ability to load files to your application. Once again,
NSDocument has taken care of most of the details for you.

To do the unarchiving, you will use NSKeyedUnarchiver, which has the following
handy method:
class func unarchiveObjectWithData(data: NSData) ->
AnyObject?
In Document.swift, edit your readFromData(_:ofType:error:) method to look
like this:
override func readFromData(data: NSData,
ofType typeName: String,
error outError:
NSErrorPointer) -> Bool {
println("About to read data of type \
(typeName).");
employees =
NSKeyedUnarchiver.unarchiveObjectWithData(data) as!
[Employee]
return true
return false
}
You could update the user interface after the XIB file is loaded, but
NSArrayController will handle it for you: the windowControllerDidLoadNib(_:)
method does not need to do anything. Leave it here for now because you will
add to it in Chapter 14:
override func windowControllerDidLoadNib(aController:
NSWindowController) {
super.windowControllerDidLoadNib(aController)
}
Note that your document is asked which XIB file to load when a document is
opened or created. This method also needs no changing:
override var windowNibName: String? {
return "Document"
return "Document"
}
The window is automatically marked as edited when you make an edit, because
you have properly enabled the undo mechanism. When you register your
changes with the undo manager for this document, it will automatically mark the
document as edited.
At this point, your application can read and write to files. Compile your
application and try it out. Everything should work correctly.
When you browse to the saved files, Finder will display them with a generic icon
and list their Kind as Document Type. Let’s look at how to define our
application’s document type more fully.
Setting the Extension and Icon for the File
Type
RaiseMan files already have the extension .rsmn, which we configured when we
created the project. But .rsmn files need an icon.
You will need to find an .icns file and copy it into your project. A fine icon is
found at ApplicationsTextEdit.app/Contents/Resources/txt.icns. To navigate
to this directory in Finder, select Go to Folder... from the Go menu, and enter the path
ApplicationsTextEdit.app/Contents/Resources/.

Notice the path that you just accessed in Finder. The second component of the
path is TextEdit.app, the TextEdit application! How can that be? TextEdit.app is a
package, or more specifically a bundle. In OS X, packages are folders that
appear in Finder to be a single file. Bundles are packages with a specific structure
for the location of the binary code and application resources.
Take a look at TextEdit’s bundle. In Finder, go to the Applications directory. You
can do this by selecting Go to Folder... from the Go menu.
In the applications directory, locate TextEdit, and control-click on it. Select Show
Package Contents from the menu that is presented (Figure 12.7).

Figure 12.7 Showing TextEdit’s contents


This will open the TextEdit bundle as a directory. Inside, there is a single directory
named Contents. Open that directory. It contains three interesting things:
1. The Info.plist file, which includes the information about the application,
its file types, and associated icons. Finder uses this information.
2. The MacOS/ directory, which contains the executable file.
3. The Resources/ directory, which has the images, sounds, and NIB files that
the application uses. You will see localized resources for several different
languages.
You are going to drag the txt.icns file from inside the TextEdit app bundle into
RaiseMan, so that you can use it as your document icon.
Still in Finder, open the Resources folder and locate the txt.icns file. Drag it from
Finder into the Supporting Files group inside the project navigator view of Xcode
(Figure 12.8).
Figure 12.8 Drag icon into project

Xcode will bring up a sheet. Make sure that you check Copy items if needed
(Figure 12.9). This will copy the icon file into your project directory.
Figure 12.9 Make it a copy

To set the document type information, select the RaiseMan project in the project
navigator. With the RaiseMan target selected, under the Info tab, find the Document
Types heading and expand the existing document type. Set the name to be
RaiseMan Doc, the Icon to txt, and the identifier to com.bignerdranch.raiseman-
doc.
Next, configure an exported universal type identifier (UTI) for RaiseMan
documents. A UTI describes the type of data contained in the file; we will cover
UTIs in more detail later in this chapter.
Exported UTIs are found immediately below the Document Types heading in the
target info. If there is not already a blank exported UTI, add one by using the
Add button. Set the Description, Identifier, Icon, and Extensions to be identical to the
settings you made in the DocumentType. For Conforms To, type public.data
(Figure 12.10).
Figure 12.10 Configuring the document type and exported UTI

The changes that you just made are saved in the app’s Info.plist. OS X will
read this file to determine the document types and exported UTIs associated with
your app.
Run your app. You should be able to save data to a file and read it in again. In
Finder, the txt.icns icon will be used as the icon for your .rsmn files.
Application Data and URLs
This chapter has focused on persisting data associated with the user’s
documents, managed by Cocoa’s document architecture, but many applications
will need to store data that is managed directly by the application. Consider a
shoebox app (an app that stores collections of images, documents, notes, and so
forth). Where should it store its data? What about cached data?
Data that is not related to a document has a specific home on OS X: the library.
The library, or more appropriately ~/Library, is a (usually hidden) folder in the
user’s home folder, as the path suggests. The library contains folders for specific
purposes: Caches for cached data that can be deleted, Application Support for
arbitrary data that needs to be saved. The latter is where a shoebox application
would store its data.
You do not need to build these URLs yourself. In fact, you should not. Instead,
use NSFileManager’s URLsForDirectory(_:inDomains:).
let fileManager = NSFileManager()

let urls =
fileManager.URLsForDirectory(.ApplicationSupportDirectory,

inDomains:
.UserDomainMask) as! [NSURL]
if let url = urls.first {
let identifier =
NSBundle.mainBundle().bundleIdentifier!
let appSupport =
url.URLByAppendingPathComponent(identifier)
println(appSupport)
}
// Outputs:
file:///Users/nerd/Library/Application%20Support/com.bignerdran

This API can return URLs for numerous common folders. The first parameter of
URLsForDirectory(_:inDomains:) determines what the purpose of the directory
is. Its type is NSSearchPathDirectory; a few of the most commonly used values
are:
ApplicationSupportDirectory
CachesDirectory
DocumentDirectory
The second parameter, the domain, is of type NSSearchPathDomainMask. The user
domain (UserDomainMask) is by far the most commonly used, as it relates to
directories within the user’s home directory.
Note that the bundle identifier is used to create a unique folder within any of
these shared folders, such as Application Support and Caches, so that your
application’s data is not confused with another’s. (This is not the case with app
sandbox containers, as discussed in Chapter 36.)
Once you have gotten the URL for your application’s data, you will need to
create the directory, at least the first time. For that, use NSFileManager’s
createDirectoryAtURL(_:withIntermediateDirectories:attributes:error:).
For the More Curious: Preventing Infinite
Loops
The astute reader may be wondering, “If object A causes object B to be encoded,
and object B causes object C to be encoded, and then object C causes object A to
be encoded again, couldn’t it just go around and around in an infinite loop?” It
would, but NSKeyedArchiver was designed with this possibility in mind.
When an object is encoded, a unique token is also put onto the stream. Once
archived, the object is added to the table of encoded objects under that token.
When told to encode the same object again NSKeyedArchiver simply puts a token
in the stream.
When it decodes an object from the stream NSKeyedUnarchiver puts both the
object and its token in a table. If it finds a token with no associated data, the
unarchiver knows to look up the object in the table instead of creating a new
instance.
This idea led to the method in NSCoder that often confuses developers when they
read the documentation: func encodeConditionalObject(objv: AnyObject?,
forKey key: String)
This method is used when object A has a reference to object B, but object A
does not really care if B is archived. However, if another object has archived B,
A would like the token for B put into the stream. If no other object has archived
B, it will be treated like nil.
For example, if you were writing an encodeWithCoder(_:) method for an Engine
object (Figure 12.11), it might have a property called car that is a pointer to the
Car object that it is part of. If you are archiving only the Engine, you would not
want the entire Car archived. But if you were archiving the entire Car, you would
want the car pointer set. In this case, you would make the Engine object encode
the car pointer conditionally.
Figure 12.11 Conditional encoding example
For the More Curious: Creating a Protocol
Creating your own protocol is very simple. Here is a protocol with two methods.
protocol Encryptable {
init(encryptedData: NSData)
func encrypt() -> NSData
}
The protocol would typically be defined in a file called Encryptable.swift:
If your protocol is tagged @objc, it is possible to add optional methods.
@objc protocol Encryptable {
init(encryptedData: NSData)
func encrypt() -> NSData

optional func schemeName() -> String
}
In this example, encryptData(_:) and decryptData(_:) are required; schemeName
is optional.
If you wanted to make a class conform to both the Encryptable protocol and the
NSCoding protocol, the class would begin like this:
class SerializableObject: NSCoding, Encryptable {
For the More Curious: Automatic Document
Saving
In Mac OS X Lion (10.7), Apple introduced automatic document-saving support
to Cocoa. With automatic document saving, your users will no longer need to be
concerned with manually saving their documents. By monitoring the change
count (described later), Cocoa will cue your document to save itself at
appropriate times. When the user does manually save the document, a new
version will be created. The user can then browse past versions by using an
interface similar to Time Machine. Untitled documents (documents not explicitly
saved by the user) will even be preserved between runs of your application.
In order to support automatic document saving, your NSDocument subclass must
opt in by overriding the class method autosavesInPlace to return true:
override class func autosavesInPlace() -> Bool {
return true
}
If your document saves its data quickly, opting in is probably an easy choice.
Otherwise, autosaving in place may not be appropriate for your application, as it
will cause the interface to block until the save is completed. Refer to Apple’s
Mac App Programming Guide for a more in-depth discussion.
The Xcode document-based app template includes an NSDocument subclass which
enables this feature. NSDocument itself, however, disables this feature; its
implementation of this method returns false.
For the More Curious: Document-Based
Applications Without Undo
The NSUndoManager for your application knows when unsaved edits have
occurred. Also, the window is automatically marked as edited. But what if you
have written an application and are not registering your changes with the undo
manager?
NSDocument keeps track of how many changes have been made. It has a method
for this purpose: func updateChangeCount(change: NSDocumentChangeType)
The NSDocumentChangeType can be one of the following: ChangeDone,
ChangeUndone, or ChangeCleared. ChangeDone increments the change count,
ChangeUndone decrements the change count, and ChangeCleared sets the change
count to 0. The window is marked as dirty unless the change count is 0.
For the More Curious: Universal Type
Identifiers
One of the enduring problems in working with computers is embodied in the
question: What does this data represent? On the Mac, this question gets asked in
several places: when a file is opened from Finder, when data is copied off the
pasteboard, when a file is indexed by Spotlight, and when a file is viewed through
Quicklook. Thus far, there have been several answers: file extensions, creator
codes, MIME types.
Apple has decided that the long-term solution to the problem is universal type
identifiers (UTIs). A UTI is a string that identifies the type of data. This data
may be in a file or in a memory buffer. UTIs are organized hierarchically. For
example, the UTI public.image conforms to public.data.
Your application tells Mac OS X what UTIs your application can read and write,
including new, custom UTIs, by setting values in its Info.plist. The Info.plist
is an XML file that has a dictionary of key-value pairs. Exported UTIs are stored
under the key UTExportedTypeDeclarations. The steps you followed earlier to
add an exported UTI created this key. The pasteboard, which we will cover in
Chapter 21, also uses UTIs to identify data types.
There is a large set of system-defined UTIs. You can find the entire list in
Apple’s Uniform Type Identifiers Reference document.
13
Basic Core Data
At this point, you have implemented an application that keeps track of an array
of objects, takes care of undo, and handles saving and loading from a file. As
you can imagine, there are an awful lot of applications like the one you just
wrote.
Apple has made this type of application very easy to write. You have already
seen how bindings and NSArrayController allow you to skip writing a lot of
tedious, uninteresting code. With Core Data, you can even skip writing the data
objects, saving and loading, and even undo.
That is right: using Core Data and bindings, the RaiseMan application you have
written can be created with no code at all. In this chapter, you are going to create
a simple Core Data application (not unlike RaiseMan) that has no code.
Defining the Object Model
Core Data is all about managing data objects. However, before it can manage
anything it needs to know what it is managing. One of the first steps with a Core
Data application is defining the managed object model.
The managed object model is composed of entities. Entities are similar to classes
in that they define data-bearing objects. An entity has attributes and
relationships. Both of these are similar to properties on regular objects, except
that they have different types: Attributes are value-type data such as strings and
integers, while relationships are references to other entities.
You will use a graphical editor in Xcode to define the managed object model,
which is stored in a .xcdatamodeld file. This file is processed when your project
is built and included in the app bundle. At runtime, Core Data loads the object
model to create an instance of the NSManagedObjectModel class.
For example, suppose you were writing an app to track employees and
departments within a company. The Employee entity would have attributes such
as name and dateOfBirth. The Department entity would have similar attributes:
name and dateOfEstablishment, perhaps. The Employee entity would also have a
department relationship pointing to the Department entity. This relationship
would reference the department a given employee was a member of. A one-to-
many relationship, employees, on the Department, would form the inverse
relationship.
The RaiseMan application used a subclass of NSDocument. In this application, you
will have a subclass of NSPersistentDocument. NSPersistentDocument, a subclass
of NSDocument, automatically reads in the model and creates an
NSManagedObjectContext. NSPersistentDocument will eliminate the need for
many lines of code.
There are two more important classes in Core Data to mention:
NSManagedObjectContext is the class that manages all of the entities, so this is the
class that you will use to create, fetch, and delete them. NSPersistentDocument
provides the context via its managedObjectContext property. The other class is
NSManagedObject, which is the base class for model objects managed by Core
Data. For example, if you fetched all of the Employee entities from the managed
object context, the return value would be an array of NSManagedObject instances.
You can also subclass NSManagedObject.
In this chapter, you will create an app that might be used by folks who own
several used-car lots. This application will enable your users to keep track of the
cars that they wish to sell. When the application is done, it will look like
Figure 13.1.
Figure 13.1 Completed application

Start Xcode and create a new Cocoa Application project. Name the project CarLot.
This will be a document-based application, so check Create Document-Based
Application. Do not worry about the Document Extension, it will be ignored by the
project template. You will want Xcode to set your app up for Core Data, so check
Use Core Data.

In the new project, open Document.xcdatamodeld. Click the Add Entity button at the
bottom left of the editor to create a new entity. Name the entity Car.
While the Car entity is selected, you can add an attribute to it by clicking the +
button at the bottom of the editor’s Attributes section. Add six attributes and give
them the following names and types:
Name Type
condition Integer 16
datePurchased Date
makeModel String
onSpecial Boolean
photo Transformable
price Decimal
Figure 13.2 shows what Car looks like in the modeler. You could put a lot of
other things in the model, but that is enough for this exercise.
Figure 13.2 Completed model
Configure the Array Controller
Open Document.xib. Delete the text field that says Your document contents here. Drag
an array controller onto Interface Builder’s dock. The array controller will be using
the document object’s NSManagedObjectContext to fetch and store data. Use the
bindings inspector to bind the array controller’s managedObjectContext to File's Owner’s
managedObjectContext (Figure 13.3).

Figure 13.3 Giving the array controller a managed object context

With the array controller still selected, switch to the attributes inspector. Under
the Object Controller heading, set Mode to Entity Name. Then set the Entity Name to be Car.
Finally, turn on the Prepares Content option, so that the array controller will fetch
immediately after it is created (Figure 13.4).
Figure 13.4 Configuring the attributes of the cars array controller
Each object in Interface Builder can have a label which identifies it within
Interface Builder. With the NSArrayController still selected, change to the identity
inspector and set its Label to Cars. When you have several array controllers in a
XIB, the labels help to eliminate a lot of confusion.
Add the Views
Drag out a table view from the object library. Select the table view, switch to the
attributes inspector, and change the number of Columns to 3. Name the columns
Make/Model, Price, and Special.

The first column will display a picture of the car as well as its make and model.
However, currently the Table Cell View only contains a text field. In order to display
an image, you need a Table Cell View which contains both an image view and a text
field. The easiest way to do this is to delete the current Table Cell View and replace
it with one that contains both of these views.
Use the document outline to select the column’s Table Cell View, the first child of
the Make/Model table column (Figure 13.5). Once you have selected it, delete it.
Figure 13.5 Selecting the Make/Model column’s table cell view

Now drag an Image & Text Table Cell View from the object library onto the column
(Figure 13.6).
Figure 13.6 Dropping an Image & Text Table Cell View
Every column should now contain a Table Cell View (NSTableCellView), and within
it a text field. The first column’s cell view contains an image view as well.
The second column will display the price of the car. But currently, when the text
field in that column’s Table Cell View displays a number, it will not format the
number correctly as a dollar amount. Fix that by adding a formatter to the text
field.
Drop a number formatter from the object library onto the NSTextField in the
second column. In the attributes inspector, set the Style to Currency (Figure 13.7).
Figure 13.7 Configuring the price number formatter

The third column will indicate whether a car is currently on special. Your users
should be able to easily toggle this setting. A natural choice, then, is to use a
checkbox in this column. But at the moment, this column’s Table Cell View contains
a text field rather than a checkbox. Fix that by replacing the text field with a
checkbox.
Use the document outline to select the text field inside this column’s Table Cell
View. The text field will be labeled Table View Cell in the document outline
(Figure 13.8). Once you have selected the text field, delete it.
Figure 13.8 Selecting the text field within the Special column’s table cell
view
Now drop a Check Box (not a Check Box Cell!) from the object library onto the Table
Cell View in the Special column. Use the snap guides to vertically center the Check
Box as you drag it in (Figure 13.9). After you have dropped it in, select the Check
Box and erase its title.

Figure 13.9 Dropping a Check Box into the table cell view

Before continuing, use the attributes inspector to set the Behavior of the two text
fields (the one in the Make/Model column and the one in the Price column) to Editable.
At this point, you have set up a table where your users’ cars will be displayed.
However, you still have not given them a way to add or remove cars from the
table. Add two buttons right below the table view to enable this (Figure 13.10).
Figure 13.10 Adding buttons to add and remove cars
With the interface as it is, the user will be able to modify some of the fields of
Car: makeModel in the first column, price in the second column, and onSpecial in
the third column. However, there are other fields which the current interface
does not provide a way of modifying: datePurchased, condition, and photo
(which can be viewed, but not modified). To support editing these fields, add a
few more controls beneath the buttons you already added.
From the object library, drag the following onto the window, beneath the Add and
Remove buttons: an NSDatePicker, an NSImageView (the object library refers to
these as Image Wells), an NSLevelIndicator, and a couple of Labels. Change the
labels to display Date Purchased and Condition. Arrange all of these views on the
window something like Figure 13.11, but do not be too particular about the
arrangement quite yet – you will be moving them around before you are done.
Figure 13.11 Arranging the car detail views
Now you need to configure these controls. First, select the NSImageView and
make it Editable using the attributes inspector. Second, select the
NSLevelIndicator. At the top of the Level Indicator section of the attributes
inspector, change the Style to Rating and toggle the State to be Editable. Then, in the
Value subsection, set the Minimum to 0 and the Maximum to 5 (Figure 13.12).

Figure 13.12 Attributes of the NSLevelIndicator


Now that you have configured the controls at the bottom of the window, select
them all (the date picker, the image view, the two labels, and the level indicator).
Using the Editor → Embed In → Box menu item, wrap them in a box (Figure 13.13).
Once the views are embedded in the box, arrange them more carefully using the
Interface Builder snap guides.

Figure 13.13 Arranged views embedded in a box


Connections and Bindings
Next comes a bunch of bindings. We will walk you through them step by step.
Figure 13.14 is a diagram of the bindings that you are going to create between
your views and the array controller.
Figure 13.14 Summary of bindings

Start by binding the table view’s Content binding to Cars’s arrangedObjects. This
binding makes the array controller’s arranged objects be displayed in the table
view. Leave the Model Key Path empty. Remember, Cars is the NSArrayController.
Next, bind the Selection Indexes to Cars’s selectionIndexes. This binding makes the
user’s selection in the table view be reflected in the array controller, allowing
removal to work correctly. See Figure 13.15.
Figure 13.15 Table view bindings
Now bind the Value of the control(s) in each column’s Table Cell View as described
in the following table.
Controller
Column View Binding Bind to Key Path
Key
Image Table Cell
First View
Value
View
Empty objectValue.photo

Table Cell
First Text Field Value
View
Empty objectValue.makeModel

Table Cell
Second Text Field Value
View
Empty objectValue.price

Table Cell
Third Check Box Value
View
Empty objectValue.onSpecial

Note the pattern in the bindings. Each control’s Value is bound to a particular
property of its Table Cell View’s objectValue. Recall that the table’s Content is bound
to the NSArrayController’s arrangedObjects. Each of those objects is an
instance of your Car entity. When the table view’s contents are bound, the
objectValue property of the table cell view (NSTableCellView) is populated with
the entity instance for that row. Once you understand this, you can configure
very straightforward bindings, such as those in the table.
Now set the two buttons’ targets and actions. First, make the Add button trigger
the add: action of the array controller. Next, make the Remove button trigger the
remove: action of the array controller. Connect the Remove button’s Enabled binding
to the Cars array controller: set the Controller Key to canRemove and leave the Model
Key Path blank.

At this point it is time to set up the bindings for the detail controls at the bottom
of the screen. Bind these controls to the selection of the array controller as
follows:
View Binding Bind to Controller Key Key Path
Textual Date Picker With Stepper Value Cars selection datePurchased
Rating Level Indicator Value Cars selection condition
Image Well Value Cars selection photo

When configuring the binding of the Image Well’s Value, also check the box that
says Conditionally Sets Editable. See Figure 13.16.
Figure 13.16 Image view binding

Next, you will set up the binding for the title of the NSBox. Select the box. In the
bindings inspector, under the Title With Pattern heading, bind Display Pattern Title1 to Cars
(your custom name for the array controller). Set the Controller Key to selection and
the Model Key Path to makeModel. Set the Display Pattern to Details for %{title1}@.
Set the No Selection Placeholder to <no selection>. Set the Null Placeholder to <no Make/Model>.
See Figure 13.17.
Figure 13.17 Box binding
Let’s also make the text of the first two columns appear in bold if the car is on
special. Configure the Font Bold binding of the text fields in each of the first two
columns as follows: Bind the Value to the Table Cell View. Leave the Controller Key
blank. Set the Model Key Path to objectValue.onSpecial (Figure 13.18).
Figure 13.18 Specials appear in bold

You are done. Run the application. Saving and loading should work. Undo
should work. Magic, eh?
How Core Data Works
Although you have written no code, many objects have been created to make this
work. Figure 13.19 is a diagram of some of them.
Figure 13.19 Objects in CarLot created by Core Data

The NSPersistentDocument reads in the model you created and uses it to create
an instance of NSManagedObjectModel. In this case, the managed object model has
one NSEntityDescription (which describes the Car entity). That entity
description has several instances of NSAttributeDescription.
Once it has the model, the persistent document creates an instance of
NSPersistentStoreCoordinator and an instance of NSManagedObjectContext. The
NSManagedObjectContext fetches instances of NSManagedObject from the object
store. While those managed objects are in memory, the managed object context
observes them. Whenever the data inside the managed objects is changed, the
managed object context registers the undo action with the document’s
NSUndoManager. The managed object context also knows which objects have been
changed and need to be saved.
Among the classes in the Core Data framework, you will find yourself
interacting with NSManagedObjectContext the most. You will use it to fetch
objects and to save changes to your object graph.
Fetching Objects from the
NSManagedObjectContext
While this chapter’s exercise demonstrates the power of bindings combined with
Core Data, it is not uncommon to use Core Data programmatically.
In order to fetch objects from the NSManagedObjectContext you will first need to
create an NSFetchRequest. A fetch has a number of similarities to
NSArrayController, except that it is typically only used once, while an array
controller is continuously updated.
At a minimum you will need the name of the entity you want to fetch. Without
anything more, the fetch request will return all of the objects for that entity
name. You will usually want to filter and order the results, however. For that you
will provide an NSPredicate and/or an array of NSSortDescriptor objects, just
like with NSArrayController. The following code demonstrates fetching Car
entities from the managed object context:
// Build the fetch request:
let fetchRequest = NSFetchRequest(entityName: "Car")
fetchRequest.predicate = NSPredicate(format: "price >=
1000")
fetchRequest.sortDescriptors = [NSSortDescriptor(key:
"makeModel", ascending: true)]

// Execute the fetch request:
var error: NSError?
let fetchResult: [AnyObject]? =
managedObjectContext.executeFetchRequest(fetchRequest,

error: &error)
Think back to how you configured the array controller in the CarLot exercise: you
bound it to the document’s managedObjectContext, set its Mode to Entity Name, and
supplied the name. Familiar, no?
The return type of executeFetchRequest(_:error:) is [AnyObject]?. To safely
access the results of the fetch you must use optional binding. The as? cast to
[NSManagedObject] is not necessary, but serves to document your expectations:
if let cars = fetchResult as? [NSManagedObject] {
for car in cars {
if let makeModel =
car.valueForKey("makeModel") as? String {
println("Fetched car: \(makeModel)")
}
}
}
If you configure a custom subclass for this entity (in the Data Model inspector while
editing the .xcdatamodeld file), the returned object instances would be of that
class. Instead, you can use KVC to access the attributes and relationships of the
managed object. You can also use KVC to change these values.
Persistent Store Types
Core Data supports several store types: three for saving and loading data
(SQLite, XML, and Binary), and a fourth for caching applications, In-Memory.
The Xcode Core Data application template allows the user to choose between the
persistence formats in the File Format pop-up button on the save panel.
Which should you use? Each of these types has their strengths, but we
recommend SQLite for most applications: it is fast, thanks to its support for
incremental updates, and moderately readable – you can open the file in the
sqlite3 command-line utility should you need to peek inside the store. The
XML format can be inspected too, but it is significantly slower than SQLite.
Note that Core Data is not a database adapter: you will not be able to use your
own SQLite databases with Core Data, and Apple reserves the right to change
the schema at any time in the future. Here is the schema of a Core Data SQLite
store:
$ sqlite3 ~/Desktop/CarLot.sqlite
SQLite version 3.8.5 2014-08-15 22:37:57
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE ZCAR ( Z_PK INTEGER PRIMARY KEY, Z_ENT
INTEGER, Z_OPT INTEGER,
ZCONDITION INTEGER, ZONSPECIAL INTEGER,
ZDATEPURCHASED TIMESTAMP, ZPRICE DECIMAL,
ZMAKEMODEL VARCHAR, ZPHOTO BLOB );
CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY,
Z_NAME VARCHAR,
Z_SUPER INTEGER, Z_MAX
INTEGER);
CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY
KEY, Z_UUID VARCHAR(255),
Z_PLIST BLOB);
This is a database meant for consumption by computers, not developers.
If you have special requirements you can even implement your own persistent
store. For more on persistent stores, see the Core Data Programming Guide.
Choosing a Cocoa Persistence Technology
Core Data is perhaps the most polarizing of the Cocoa frameworks. It has a long
history and there is a lot we simply do not have time to cover here – entire books
have been written on Core Data! Core Data is very sophisticated about handling
data, using a mechanism called faulting to only load fetched objects when
needed. It handles a great deal of non-trivial work for you: tracking changes,
helping to manage schema versions, and so forth. It would take you a long, long
time to duplicate what it offers.
At the same time, even as the Cocoa community has gathered more
understanding of Core Data over the years, it is still a black box. If you
encounter a performance problem it can be very difficult to determine the cause.
Furthermore, when you are using Core Data it is critical to keep in mind that
NSManagedObject instances are closely tied to the NSManagedObjectContext that
they were fetched from. They are not simply model objects; they are Core Data
model objects, which requires careful consideration of how their references are
passed around in your application.
Although archiving is trivial by comparison to Core Data, they both accomplish
a similar goal for many developers: saving and loading objects. But archiving
has a significant downside: the entire object graph must be saved and loaded at
once. This is where Core Data tends to shine, in dealing with medium-sized data
sets.
Unsatisfying though it may be, the best answer to the question of whether you
should use Core Data is “maybe.” For applications with small data sets with very
simple feature sets, archiving will be fine. If your application resembles RaiseMan
or CarLot, you should seriously consider Core Data.
Applications working with very large data sets would likely benefit from a
custom persistence implementation. Core Data’s reliance on KVC carries with it
a significant overhead. We expect that pure Swift objects would offer the best
performance in terms of low overhead property access.
Customizing Objects Created by
NSArrayController
Given that your users are probably going to add cars to the system when they
purchase them, it would be nice if the datePurchased attribute were set to the
current date. One good way to do this is to subclass NSArrayController and
override its newObject() method.
Create a new file of type Cocoa Class. It will be a subclass of NSArrayController.
Name it CarArrayController. In CarArrayController.swift, override newObject:
override func newObject() -> AnyObject {
let newObj = super.newObject() as! NSObject
let now = NSDate()
newObj.setValue(now, forKey:"datePurchased")
return newObj
}
Open Document.xib in Interface Builder and select the array controller. In the identity
inspector, set the class to be CarArrayController. (Be sure that you are in the
identity inspector, not the attributes inspector; this array controller is still holding
on to an array of instances of the Car entity.) See Figure 13.20.
Figure 13.20 Changing the class of array controller
Run your application. When new cars are added, their datePurchased attribute
should be initialized to the current date.
Challenge: Begin Editing on Add
After studying the code for Document.addEmployee(_:) in the RaiseMan
application, make the CarArrayController begin editing the Make/Model column of
the table view when a new record is created. Hint: You will need to add an outlet
to CarArrayController.
Challenge: Implement RaiseMan Using Core
Data
Follow the steps early in this chapter to create a brand new RaiseMan project, but
this time check Use Core Data. Instead of the Employee class, create an Employee
entity, and so forth.
14
User Defaults
Many applications allow you, as the user, to configure their behavior or
appearance. Cocoa provides a simple means of storing such preferences in the
user’s defaults database.
NSUserDefaults
The NSUserDefaults class is your interface to the user’s defaults database. With
it you can register factory defaults, set the user’s defaults, and read the defaults
back out as needed. You can think of it as a very fancy dictionary.
Your application can register a set of defaults “from the factory.” These are the
defaults that your application uses when users have not made their own
configuration selections. These factory defaults are registered programmatically
every time your application is launched.
When users configure your application, you store their preferences as a default
in the NSUserDefaults object. Note that only the defaults your code sets are
saved in this way. Factory defaults are not persisted between runs.
As your application runs, it will ask the NSUserDefaults object for the value of
various defaults which correspond to behaviors in your application. The value
returned may come from the factory defaults, previously written defaults, or
elsewhere. These sources are checked in a set search order.
The terminology when working with NSUserDefaults can be confusing. At an
application level, the user chooses preferences, but NSUserDefaults deals in
defaults. Remember that you register defaults, you write defaults to reflect the
user’s preferences, and you read defaults to inform your app’s behavior.
As an example, when you first launched Xcode, it had a factory-default code
highlighting preference chosen – it is even called Default. You can see this
preference by navigating to Xcode’s preferences and selecting the Fonts & Colors tab.
The Apple engineers responsible for Xcode registered this highlighting default as
the factory default with NSUserDefaults.
If you change Xcode’s code highlighting color scheme to Basic or Dusk – if you
select any highlighting scheme at all – your preference will be written as a
default in the user’s defaults database. If you never change the preference from
its default, the defaults database will not contain an entry for that default.
Note that if you change the color scheme to Basic and then change it back to
Default, the new default will be persisted, even though the new default is the same
as the factory default.
When Xcode is running, it reads the code highlighting color scheme default from
NSUserDefaults and uses the result to determine how to highlight your code.

Here are some commonly used methods on NSUserDefaults. Many of them take
a defaultName parameter. This parameter is a String that identifies which default
is being read or written, much like a key in a dictionary.
Shared defaults
class func standardUserDefaults() -> NSUserDefaults
Returns the shared defaults object.
Registering defaults
func registerDefaults(registrationDictionary:
[NSObject : AnyObject])
Registers the factory defaults for the application.
Setting defaults
func setBool(value: Bool, forKey defaultName: String)
func setFloat(value: Float, forKey defaultName:
String)
func setInteger(value: Int, forKey defaultName:
String)
func setObject(value: AnyObject?, forKey defaultName:
String)
Set the value for the defaultName default. Use the method that is correct for the
type of value that is being stored.
Removing defaults
func removeObjectForKey(defaultName: String)
Removes the value for the defaultName default. The application will return to
using the value given by the factory defaults or elsewhere.
Reading defaults
func boolForKey(defaultName: String) -> Bool
func floatForKey(defaultName: String) -> Float
func integerForKey(defaultName: String) -> Int
func objectForKey(defaultName: String) -> AnyObject?
Read the value for the defaultName default. Usually this will be the user’s
previously stored default value, or, if it has not been set, the factory default.
Adding User Defaults to SpeakLine
Over the rest of this chapter, you are going to add user defaults to your SpeakLine
app. These changes will allow the user’s configuration of the text to be spoken
and the voice that it will be spoken in to be persisted across runs of the app.
Rather than using NSUserDefaults directly to configure SpeakLine’s user interface
and store the user’s defaults from within AppDelegate, you will write a
PreferenceManager wrapper class. This class will make use of NSUserDefaults to
persist preferences. AppDelegate, in turn, will use this wrapper class to access
and change preferences.
Create a new Swift file, PreferenceManager.swift, in the SpeakLine group in the
project navigator. In PreferenceManager.swift, create a new class
PreferenceManager:
import Foundation

class PreferenceManager {

private let userDefaults =


NSUserDefaults.standardUserDefaults()

}
Right now, the class does not do anything, but it has a lot of potential! You gave
the class a constant property outlet to the standard user defaults, which will make
it more convenient to call methods on the user defaults object later when you add
methods to PreferenceManager.
Create Names for the Defaults
You will be registering factory defaults, writing user preferences for defaults,
and reading values for defaults, all based on a string that names each default.
You will define a constant string for each of the default names.
At the top of PreferenceManager.swift, above the PreferenceManager, add the
following constants:
import Foundation

private let activeVoiceKey = "activeVoice"


private let activeTextKey = "activeText"

class PreferenceManager {
Why define constants that simply contain a fixed string? After all, you could just
remember what the string was and type it in whenever you need it. The danger is
that you might misspell the string. If the string is surrounded by quotes, the
compiler will accept the misspelled string. In contrast, if you misspell the name
of a global variable, the compiler will catch your error.
Register Factory Defaults for the Preferences
When your application is first launched, your PreferenceManager object will
need to register the factory defaults. Add a method which registers the defaults
in PreferenceManager.swift:
class PreferenceManager {

private let userDefaults =


NSUserDefaults.standardUserDefaults()

func registerDefaultPreferences() {
let defaults =
[ activeVoiceKey :
NSSpeechSynthesizer.defaultVoice() ,
activeTextKey : "Able was I ere I saw
Elba." ]
userDefaults.registerDefaults(defaults)
}

}
If you try to run the app at this point, you will get a compiler error: Use of
unresolved identifier 'NSSpeechSynthesizer'. This means that the
compiler does not know what NSSpeechSynthesizer is. This is because
NSSpeechSynthesizer is in the AppKit framework. To fix this, you need to
import not just Foundation but all of Cocoa into PreferenceManager.swift:
import Foundation
import Cocoa
By the time that any code using PreferenceManager has a chance to use the class,
these factory defaults should have been registered. No code should ever be able
to retrieve the value for a default before the factory default value has been
registered. To prevent this from happening, make PreferenceManager implement
init to call this method:
class PreferenceManager {
private let userDefaults =
NSUserDefaults.standardUserDefaults()

init() {
registerDefaultPreferences()
}

func registerDefaultPreferences() {
The factory defaults should be registered when the app launches. Open
MainWindowController.swift, add a property for storing a PreferenceManager,
and default initialize it:
...
@IBOutlet weak var tableView: NSTableView!

let preferenceManager = PreferenceManager()


let speechSynth = NSSpeechSynthesizer()
let voices = NSSpeechSynthesizer.availableVoices()
as! [String]
Now a PreferenceManager will be created when the app launches. As a result, the
factory defaults will be registered at that time.
Reading the Preferences
Next, you will add computed properties to your PreferenceManager class for the
two preferences: activeVoice and activeText. The getter will be used to get the
value for each preference. These properties will abstract away the details of how
the preferences are stored. Implement these computed properties in
PreferenceManager.swift:
...

var activeVoice: String? {


get {
return
userDefaults.objectForKey(activeVoiceKey) as? String
}
}

var activeText: String? {


get {
return
userDefaults.objectForKey(activeTextKey) as? String
}
}

}
Reflecting the Preferences in the UI
Now that values for the activeVoice and activeText properties can be read from
the PreferenceManager, make MainWindowController read from them when its
window loads and update its window’s UI accordingly:
func windowDidLoad() {
super.windowDidLoad()
updateButtons()
speechSynth.delegate = self
for voice in voices {
println(voiceNameForIdentifier(voice)!)
}
let defaultVoice =
NSSpeechSynthesizer.defaultVoice()
let defaultVoice =
preferenceManager.activeVoice!
if let defaultRow = find(voices, defaultVoice)
{
let indices = NSIndexSet(index:
defaultRow)
tableView.selectRowIndexes(indices,
byExtendingSelection: false)
tableView.scrollRowToVisible(defaultRow)
}
textField.stringValue =
preferenceManager.activeText!
}
At this point, the app will read from the user defaults, but not write to them, so it
will behave exactly as before. To improve this, you need to save the text that the
user enters and the voice that the user selects into the preferences manager.
Writing the Preferences to User Defaults
Now, add setters for the activeVoice and activeText properties. You will use
each setter to store the user’s preferred value for that preference.
var activeVoice: String? {
set (newActiveVoice) {
userDefaults.setObject(newActiveVoice,
forKey: activeVoiceKey)
}
get {
return
userDefaults.objectForKey(activeVoiceKey) as? String
}
}

var activeText: String? {


set (newActiveText) {
userDefaults.setObject(newActiveText,
forKey: activeTextKey)
}
get {
return
userDefaults.objectForKey(activeTextKey) as? String
}
}
Storing the User Defaults
Up to this point, you have only used PreferenceManager to read preferences to
update the UI. You are now going to give your users the ability to specify their
preferences based on the changes that they make in the UI. First, save the user’s
selection of voice into the preferences manager:
func tableViewSelectionDidChange(notification:
NSNotification) {
let row = tableView.selectedRow
// Set the voice to the default if the user
has deselected all rows.
var voice: String? = nil
if row == -1 {
voice = nil
} else {
voice = voices[row]
}
speechSynth.setVoice(voice)
preferenceManager.activeVoice = voice
}
Now, when the user selects a voice from the table view, that selection will be
saved. Run the app and try it out: choose a different voice, quit the app, and run
the app again. Your selection of voice will persist across runs.
Next, make the text that the app speaks persist across runs. To do this, you will
need to make the app delegate be the delegate of the text field. First, declare that
AppDelegate conforms to NSTextFieldDelegate:
class MainWindowController: NSWindowController,
NSSpeechSynthesizerDelegate,
NSWindowDelegate,
NSTableViewDataSource,
NSTableViewDelegate,
NSTextFieldDelegate {
Now, open MainMenu.xib. If you cannot see the app’s window in Interface Builder,
select it from the drawer. Control-click on the text field to bring up the
connections panel and drag from the delegate outlet to the app delegate object in
the drawer (Figure 14.1).
Figure 14.1 Setting window controller as text field delegate

Finally, implement controlTextDidChange(_:) in a new section at the bottom of


MainWindowController to make the user’s preference for activeText be the
current stringValue of the text field:
...

// MARK: - NSTextFieldDelegate

override func controlTextDidChange(notification:


NSNotification) {
preferenceManager.activeText =
textField.stringValue
}

}
Run the app and enter some text. Quit and run it again. The text you entered
should persist across runs.
What Can Be Stored in NSUserDefaults?
You may have noticed that, aside from the scalar types, NSUserDefaults provides
APIs for setting and getting objects. What kind of objects? Property list objects.
Property list objects are instances of the Objective-C classes NSDictionary,
NSArray, NSString, and NSNumber. A plist data structure is composed entirely of
these objects. As such you can store any data structure in NSUserDefaults that
you like, as long as you can represent it as a plist. Since the Swift basic types are
bridged to these types, you can store them without any issues. An example:
let userDefaults =
NSUserDefaults.standardUserDefaults()
let plistKey = "plist"

if let plist: AnyObject =
userDefaults.objectForKey(plistKey) {
println("plist = \(plist)")
}

let plist = ["favoriteNumbers": [77, 82]]
userDefaults.setObject(plist, forKey: plistKey)
Precedence of Types of Defaults
We have already mentioned that the user’s defaults take precedence over the
factory defaults. In fact, several more levels of precedence exist. These levels of
defaults are known as domains. Here are the domains used by an application,
from highest to lowest priority:
Arguments
Passed on the command line. Most people start applications by double-clicking
an icon instead of from the command line, so this feature is seldom used in a
production app.
Application
What comes from the user’s defaults database.
Global
Defaults that the user has set for his or her entire system.
Language
Defaults based on the user’s preferred language.
Registered defaults
The factory defaults registered at launch.
What is the User’s Defaults Database?
The NSUserDefaults object relies on the user’s defaults database to persist the
defaults that are written to it. The database itself is persisted as .plist files in
~/Library/Preferences. You can open this folder in Finder by using Go → Go to
Folder... and entering the path. These files are binary plists; you can view their
contents in Xcode.
Each application’s preferences are stored in a file using its bundle identifier as
the base name. SpeakLine’s bundle identifier is com.bignerdranch.SpeakLine,
unless you used a different organization identifier or project name when you
created the application in Chapter 6.
In older versions of OS X you could edit these files directly, but in OS X
Mavericks and later a caching system was added. A daemon – a process that
runs in the background – manages the database and all of the .plist files. If you
edit the file your changes will likely be overwritten. Instead, you must use the
defaults command-line tool, which is described in the next section.

Figure 14.2 NSUserDefaults, the user defaults database, and the filesystem
For the More Curious: Reading/Writing
Defaults from the Command Line
You can use the defaults command-line tool to safely read and write defaults.
For example, to see your defaults for Xcode, you can bring up the Terminal and
enter the following command:
defaults read com.apple.dt.Xcode
You should see all your defaults for Xcode. There are a lot of them! A few lines of
ours look like this:
IDEDisableGitSupportForNewProjects = 1;
IDEEnableLiveIssues = 0;
IDEFileTemplateChooserAssistantSelectedTemplateCategory
= Source;
IDEFileTemplateChooserAssistantSelectedTemplateName =
"Cocoa Class";
IDEFileTemplateChooserAssistantSelectedTemplateSection
= "com.apple.platform.macosx";
IDEHasConvertedXcode3BuildPrefs = 1;
IDEHexEditorByteGrouping = 1;
IDEHexEditorLineNumberFormat = 0;
You can also write to the user preferences. To set Xcode’s default directory in the
NSOpenPanel to the /Users directory, you could enter this:
defaults write com.apple.Xcode
NSNavLastRootDirectoryForOpen /Users
Try this:
defaults read com.bignerdranch.SpeakLine
If you entered text and selected a voice, you will see it in the output.
To see your global defaults, enter this:
defaults read NSGlobalDomain
For the More Curious:
NSUserDefaultsController
Sometimes you will want to bind to a value from the NSUserDefaults object. An
NSUserDefaultsController makes this possible. All the NIBs in your application
will use a single shared instance of NSUserDefaultsController.
For example, if you wanted to use bindings to deal with SpeakLine’s text field, you
would bind it to the shared NSUserDefaultsController with a Controller Key of
values and a Model Key Path of activeTextKey (Figure 14.3).
Figure 14.3 Binding to the NSUserDefaultsController
Challenge: Reset Preferences
Add a button that will remove all the user’s defaults. Label the button Reset
Preferences. Do not forget to update the window to reflect the new defaults.
15
Alerts and Closures
Occasionally, you will want to warn the user about something by presenting an
alert. To do this, you use the NSAlert class. NSAlert saves you the work of
designing a custom user interface for a simple and common user interaction,
while also providing the user with a consistent experience.
NSAlert
Figure 15.1 shows a basic modal alert:
Figure 15.1 A modal alert

Here is the code used to create and present that alert:


let alert = NSAlert()
alert.messageText = "Alert message."
alert.informativeText = "A more detailed description
of the situation."
alert.addButtonWithTitle("Default")
alert.addButtonWithTitle("Alternative")
alert.addButtonWithTitle("Other")
let result = alert.runModal()
To use an alert, you use the default initializer, configure it, and show it. Here are
the properties and methods for configuring an NSAlert:
Text
var messageText: String?
var informativeText: String?
Configure the text that the alert will display.
Images
var icon: NSImage!
var alertStyle: NSAlertStyle
Configure the images that the alert will display. Although you can supply an
icon, by default the alert will use the application’s icon.
Buttons
Buttons
func addButtonWithTitle(title: String) -> NSButton
Add the buttons which the alert will make available.
Once you have initialized and configured an NSAlert, you can present it as a
window using runModal().
You will be able to tell which button the user clicked by examining the result
returned from runModal():
let result = alert.runModal()
switch result {
case NSAlertFirstButtonReturn:
// The user clicked "Default"
break
case NSAlertSecondButtonReturn:
// The user clicked "Alternative"
break
case NSAlertThirdButtonReturn:
// The user clicked "Other"
break
default: break
}
If you just want your app to tell the user something (not ask for input), you do
not need to add any buttons. Cocoa will automatically add an OK button for you.
let alert = NSAlert()
alert.messageText = "Alert message."
alert.informativeText = "A more detailed description
of the situation."
alert.runModal()
This code would result in an alert panel being presented with only an OK button
(Figure 15.2).
Figure 15.2 Simple alert panel
Did you notice that this time you did not store the result of runModal() into a
local variable? You did not need to do this because the user can only click OK, so
the result will always be the same.
Modals and Sheets
So far, you have seen APIs for using NSAlert to show a panel modally. When
runModal() is called, it blocks until the user dismisses the alert. While the modal
alert is being shown it is the only window receiving events; all other windows in
the application will be unusable. As a result, when a panel is run modally, users
will not be able to use the rest of the application until they have responded to the
alert panel. Modal alerts are convenient to show but should be used sparingly for
this reason.
Alerts can also be run as a sheet on top of another window (shown in
Figure 15.5). This indicates to the user that the alert (or content of the sheet) is
related to that window. Similar to running an alert panel modally, the window
that the alert is run on top of will not receive events until the sheet has been
dismissed. However, the other windows in the app will receive events. To
present an alert as a sheet you will use a different API and a new concept: a
completion handler.
Completion Handlers and Closures
Imagine that your career as a Cocoa programmer has been so successful that you
have hired a chauffeur. You tell your chauffeur, “I’d like to go to the Apple
Store. Would you bring the car around? Send me an iMessage when you’re at
the portico.” The chauffeur nods solemnly and disappears. The completion
handler is firmly set: “After I’ve pulled the car around to the portico, send an
iMessage.”
Completion handlers are a powerful mechanism for executing arbitrary code at
the completion of a long-running operation. Back in the context of alerts, the
long-running operation is waiting for the user’s button click. Other examples
include network communication (Chapter 28) and calculations made on a
background thread (Chapter 34).
In Swift, a completion handler is implemented using a closure. Closures are very
similar to functions in that their type is defined by their parameter list and a
return type. The power of closures comes from the ability to define them inline,
capture values, and treat the closure itself as a value: they can be passed as
arguments and stored in properties or data structures, to be executed at a later
time.
To get familiar with the closure syntax, here is a simple example:
let say = { (text: String) -> Void in
println("The closure says: \(text)"")
}
say("Hello")
Closure literals are expressions, denoted by the curly braces { and }. The type of
this closure is preceded by the in keyword. This particular closure takes a single
parameter of type String and has no return value. This closure is assigned to the
local constant say. Because it is a closure type it can then be executed just like a
function.
When an alert is presented as a sheet to the user, the alert must first be
configured, and then the beginSheetModalForWindow(_:completionHandler:)
method is called upon it. This method’s first parameter is the window that the
alert should be associated with, and the completion handler is the closure that
will be run when the user clicks on one of the alert buttons.
let alert = NSAlert()
...
let completionHandler = { (response: NSModalResponse)
-> Void in
println("The user selected \(response).")
}
alert.beginSheetModalForWindow(window,
completionHandler: completionHandler)
The completionHandler constant makes it more clear to see what is happening,
but it is much more common to use completion handler closures inline, like this:
let alert = NSAlert()
...
alert.beginSheetModalForWindow(window,
completionHandler: { (response) -> Void in
println("The user selected \(response).")
})
Note that Swift’s type inference allows you to leave off the type of response.

Closures and capturing


Swift closures can capture constants and variables that are within scope. This
means that you are not restricted by the parameters of the closure type: if a
constant or variable is within scope, you can capture it and access it later when
the closure is called.
This powerful feature has important implications for memory management.
When an object reference is captured by a closure, a strong reference is made by
default. This has the benefit of keeping the object in memory, but the
disadvantage of potentially creating a strong reference cycle if the captured
object reference has a strong reference to the closure, as demonstrated in
Figure 15.3.
Figure 15.3 Captured references in closures can create strong reference
cycles
The solution to resolving a strong reference cycle from a closure is to use a
capture list:
chauffeur.completionHandler = { [unowned chauffeur] in
chauffeur.sendMessage("I have arrived at the
portico.")
}
Capture lists allow you describe how reference types are captured. Two
keywords come into play here: unowned and weak. If the reference being
captured should never be deallocated before the closure, use unowned. This is
similar to treating the capture as an implicitly unwrapped optional: accessing it
will crash your application.
On the other hand, if the reference may be deallocated before the closure, use
weak. This treats the capture as an optional. Figure 15.4 shows how this
hypothetical reference cycle was avoided.
Figure 15.4 Strong reference cycle resolved using the capture list

One last thing: if you reference any properties or methods on self, Swift
requires you to use the self constant to make the fact that it will be captured
clear. You can include self in the capture list if you need to.
Now that you have some background on closures, let’s put that knowledge to
work.
Make the User Confirm the Deletion
In RaiseMan, if the user clicks the Remove button, an alert panel should appear to
confirm the deletion (Figure 15.5). Of course, the user should also be able to
remove multiple employees at once, rather than tediously removing them one by
one.
Figure 15.5 Completed application

To enable this behavior, open Document.xib, select the table view, and open the
attributes inspector. Allow the user to select multiple rows by checking Multiple
next to Selection (Figure 15.6).
Figure 15.6 Enabling multiple selection in the table view’s attributes
inspector
You now want to create an action method in Document that will display the alert
as a sheet when the Remove button is clicked. If the user confirms the deletion,
Document will call the remove(_:) method on the array controller to remove the
selected Employee objects.
In Xcode, open the Document.swift file and add the removeEmployees(_:) method:
@IBAction func removeEmployees(sender: NSButton) {
let selectedPeople: [Employee] =
arrayController.selectedObjects as! [Employee]
let alert = NSAlert()
alert.messageText = "Do you really want to remove
these people?"
alert.informativeText = "\(selectedPeople.count)
people will be removed."
alert.addButtonWithTitle("Remove")
alert.addButtonWithTitle("Cancel")
let window = sender.window!
alert.beginSheetModalForWindow(window,
completionHandler: { (response) -> Void in
// If the user chose "Remove", tell the array
controller to delete the people
switch response {
case NSAlertFirstButtonReturn:
// The array controller will delete the
selected objects
// The argument to remove() is ignored
self.arrayController.remove(nil)
default: break
}
})
}
Note that beginSheetModalForWindow(_:completionHandler:) will return
immediately. This allows the event loop to continue processing events for the
sheet and for the other windows (but not the window the sheet was presented
from). Once the user clicks a button, the completion handler will be called upon
to react. If the user selects “Remove”, the array controller will remove the
selected employees. Otherwise, the employee roster will be unchanged.
However, as it stands the alert sheet is never presented because
removeEmployees(_:) never gets called. Right now, when the user clicks the
Remove button, the selected employees will be immediately deleted. To change
this, you need to change the target and action of the Remove button.
Open Document.xib. Control-drag from the Remove button to the File's Owner icon to
make the File's Owner be the new target (Figure 15.7).
Figure 15.7 Setting File's Owner as the Remove button’s target
Set the action to removeEmployees: (Figure 15.8).
Figure 15.8 Setting removeEmployees: as the Remove button’s action

Run your application and remove some employees.


For the More Curious: Functional Methods
and Minimizing Closure Syntax
Although it is not a functional language, Swift has a number of features to
support functional programming. In particular, the standard library includes the
functions map, reduce, and filter. A full overview of these functions is beyond
the scope of this book, but they can be very helpful in constructing clear code for
common tasks.
Recall that the Employee class has a property, name, whose type is String?.
Here’s how you might use these methods to create an array of all non-nil names:
let names = employees.filter({ employee in
return employee.name != nil
}).map({ employee in
return employee.name!
})
When the closure is the last parameter, however, the parentheses do not need to
surround the closure. If the closure is the only parameter, you can leave the
parentheses off altogether. This is called trailing closure syntax:
let names = employees.filter { employee in
return employee.name != nil
}.map { employee in
return employee.name!
}
If a closure is a single statement, you can leave off the return keyword:
let names = employees.filter { employee in
employee.name != nil
}.map { employee in
employee.name!
}
Finally, for simple closures you can even choose not to name the parameters:
let names = employees.filter { $0.name != nil }.map {
$0.name! }
The reduce function makes calculating a sum very clean:
let sum = employees.reduce(0.0) { total, employee in
total + employee.raise
}
let average = sum / Float(employees.count)
In the interests of clarity we will avoid these tricks for the most part. However,
we encourage you to experiment with these different options for closure syntax
to find what makes your code the most clear.
Challenge: Don’t Fire Them Quite Yet
Add a third option to the alert sheet that says Keep, but no raise. Instead of deleting
the employees, this button will simply set the raises of the selected employees to
zero.
Challenge: Different Messages for Different
Situations
As RaiseMan behaves now, when the user only selects one employee to remove,
the alert sheet will have broken English in it:
Figure 15.9 Bad grammar in RaiseMan’s alert sheet

This is happening because the same string is being used for the alert’s
informativeText regardless of the number of employees that may be removed.
When the user attempts to remove a single employee, make the alert’s
informative text be “1 person will be removed.” While making this change, be
sure to preserve the informative text that is displayed when the user attempts to
remove several employees.
Next, change the messageText of the alert. When the user attempts to remove a
single employee, it should read “Do you really want to remove this person?”
Finally, improve the informativeText of the alert again. When the user tries to
remove one employee named “Jane Smith”, the alert’s informative text should
be “Jane Smith will be removed.”
16
Using Notifications
What Notifications Are
Every running application has an instance of NSNotificationCenter, which
functions much like a bulletin board. Objects register themselves as being
interested in certain notifications (“Please write me if anyone finds a lost dog”).
We call the registered object an observer. Other objects can then post
notifications to the center (“I have found a lost dog”). That notification is
subsequently forwarded to all objects that are registered as interested. We call
the object that posted the notification a poster.
Many standard Cocoa classes post notifications: Windows send notifications that
they have changed size. When the selection of a table view changes, the table
view sends a notification. The notifications sent by standard Cocoa objects are
listed in the online documentation.
What Notifications Are Not
A notification center allows objects in an application to send notifications to
other objects in that same application. When programmers first hear about the
notification center, they sometimes think that it is a form of interprocess
communication – that they can create an observer in one application and post
notifications from an object in another.
Notifications do not travel between applications, at least not via
NSNotificationCenter. (Look into NSDistributedNotificationCenter if you
need to pass notifications between applications.)
NSNotification
Notification objects are very simple. A notification is like an envelope into
which the poster places information for the observers. A notification has two
important properties: name and object.
var name: String
var object: AnyObject?
Nearly always, object is a reference to the object that posted the notification. (It
is analogous to a return address.)
NSNotificationCenter
The NSNotificationCenter is the brains of the operation. It allows you to do
three things: register observer objects, post notifications, and unregister
observers.
Here are some commonly used methods implemented by NSNotificationCenter:
class func defaultCenter() -> NSNotificationCenter
func addObserver(observer: AnyObject,
selector aSelector: Selector,
name aName: String?,
object anObject: AnyObject?)
func postNotification(notification: NSNotification)
func postNotificationName(aName: String, object
anObject: AnyObject?)
func removeObserver(observer: AnyObject)
For example, suppose you have an app with a class DatabaseManager dedicated
to interacting with a database. That class needs to make sure its file handle to the
database is closed before your app terminates. To be informed when the
application will terminate, it registers itself as an observer of
NSApplicationWillTerminateNotification with the selector
receiveWillTerminateNotification: (Figure 16.1).

Figure 16.1 An object registering for a notification

The NSNotificationCenter adds the DatabaseManager to its list of observers


(Figure 16.2).
Figure 16.2 An object registered for a notification
When the shared NSApplication is about to exit, it posts
NSApplicationWillTerminateNotification (Figure 16.3).

Figure 16.3 Posting a notification

NSNotificationCenter looks through its observers to see if any of them are


awaiting that notification. In this case, it sees that DatabaseManager is observing
that notification, and sends the receiveWillTerminateNotification: message to
it (Figure 16.4).
Figure 16.4 Posted a notification
Starting the Chatter Application
In this chapter, you will create a little app that allows “chatting” between
different windows. The user will be able to type into and send a message from
each window. Whenever the user sends a message from one window, it will be
displayed in all the other windows. You will use notifications to pass messages
between windows. (Figure 16.5)
Figure 16.5 Completed application

First, lay the groundwork. Create a new project in Xcode called Chatter. Make sure
that Use Storyboards, Create Document-Based Application, and Use Core Data are all unchecked.
Before you get started, you need to remove the cruft that is provided in Xcode’s
template. First, open MainMenu.xib and remove the window. Then open
AppDelegate.swift and remove the outlet to the window.
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!

func applicationDidFinishLaunching(aNotification:
NSNotification) {
You are now ready to get started on the project. In Xcode create a new Cocoa Class
called ChatWindowController, a subclass of NSWindowController. While creating
it, be sure to check Also create XIB file for user interface. This class will do the lion’s
share of the work.
Open ChatWindowController.xib, select the window, and, in the attributes
inspector, uncheck Visible At Launch. This prevents a window from being put on the
screen until showWindow(_:) is called. If you left this checked, when creating a
large number of windows one after another in your app you would see each
window appear briefly at a default location before being moved to the correct
position.
In ChatWindowController.swift, implement windowNibName so that
ChatWindowController knows the name of its NIB file:
class ChatWindowController: NSWindowController {

// MARK: - Lifecycle

override var windowNibName: String {


return "ChatWindowController"
}

override func windowDidLoad() {


Now you need to make the app put some windows on the screen. Open
AppDelegate.swift. Since the user will be able to open multiple windows, you
will need to do this a bit differently in order to maintain a strong reference to
each of the window controllers.
First, add an array of ChatWindowControllers:
class AppDelegate: NSObject, NSApplicationDelegate {

var windowControllers: [ChatWindowController] = []

func applicationDidFinishLaunching(aNotification:
NSNotification) {
Next, create a function which adds a window controller to the array and shows
its window:
func applicationWillTerminate(aNotification:
NSNotification) {

}
// MARK: - Helpers

func addWindowController() {
let windowController = ChatWindowController()
windowController.showWindow(self)
windowControllers.append(windowController)
}
This method will need to be called from two places: first, when the application
launches, and second, when the user selects New from the File menu. Add a call to
this method in applicationDidFinishLaunching(_:):
func applicationDidFinishLaunching(aNotification:
NSNotification) {
addWindowController()
}
Now, add an action method which will be triggered when the user selects the New
menu item. This action method will just call addWindowController:
func applicationWillTerminate(aNotification:
NSNotification) {

// MARK: - Actions

@IBAction func displayNewWindow(sender: NSMenuItem) {


addWindowController()
}

// MARK: - Helpers
It is time to hook up the New menu item. Open MainMenu.xib and find the menu
item inside the File menu. Control-drag from New to App Delegate to set its target to
be the app delegate; then set its action to be displayNewWindow: (Figure 16.6).
Figure 16.6 Setting the New menu item’s action
If you run your app now, you will be able to put a lot of windows on the screen.
It is time to make those windows do something.
First, open ChatWindowController.swift and declare a few properties.
class ChatWindowController: NSWindowController {

dynamic var log: NSAttributedString =


NSAttributedString(string: "")
dynamic var message: String?

// NSTextView does not support weak references.


@IBOutlet var textView: NSTextView!

// MARK: - Lifecycle
You will use the first two of these properties to bind the views (which you will
set up in a moment) to the window controller, which is why they must be marked
dynamic. The third property you will use as an outlet to the text view you will be
adding. Notice that unlike the other outlets you have seen so far, this outlet is not
marked weak.
While you have ChatWindowController.swift open, declare an empty action
method send(_:).
override func windowDidLoad() {
super.windowDidLoad()

// MARK: - Actions
@IBAction func send(sender: AnyObject) {

}
This method will be triggered when the user sends a chat message to the other
window controllers.
Now, open ChatWindowController.xib. Drag out a text view, a text field, and a
button. Lay out these views as shown in Figure 16.7.
Figure 16.7 Laying out the window’s subviews

Now you have a little configuration to do for each of these views.


First, select the text view (within the scroll view and clip view). In the bindings
inspector, bind the text view’s Attributed String to File's Owner’s log property. In the
attributes inspector, uncheck the Editable option; you want to display the messages
that have been sent here, not allow the user to edit them.
Before moving on to the next view, connect File's Owner’s textView outlet to the
text view.
Second, bind the text field’s Value to File's Owner’s message property.
Third, set the button’s target to be File's Owner and set its action to be send:.
Finally, set the window’s initialFirstResponder outlet to be the text field. Now, when
you run the app, the field where you will be entering text is selected right off the
bat.
Using Notifications in Chatter
You are now ready to add notifications to Chatter. Here is the game plan you will
follow for doing that: Each window controller will post a notification whenever
the user sends a message. All of the window controllers will observe these
notifications. When they observe one, they will update their text views to display
the new message.
To begin, create two constant strings at the top of ChatWindowController.swift.
import Cocoa

private let
ChatWindowControllerDidSendMessageNotification
=
"com.bignerdranch.chatter.ChatWindowControllerDidSendMessageNot

private let ChatWindowControllerMessageKey


=
"com.bignerdranch.chatter.ChatWindowControllerMessageKey"

class ChatWindowController: NSWindowController {


The first string is the name of the notification that you will be posting. The
second string is the key which you will use in the notification’s user info
dictionary to store the message that the user entered.
Notice that values for these strings are in reverse DNS. This helps you avoid
creating a notification with the same name as a notification in a library you
might add to Chatter later on.
Now, make ChatWindowController post a notification when the user has clicked
the Send button. You already set the button up to trigger the send(_:) action on
ChatWindowController.
@IBAction func send(sender: AnyObject) {
sender.window?.endEditingFor(nil)
if let message = message {
let userInfo = [ChatWindowControllerMessageKey
: message]
let notificationCenter =
NSNotificationCenter.defaultCenter()
notificationCenter.postNotificationName(

ChatWindowControllerDidSendMessageNotification,
object: self,
userInfo: userInfo)
}
message = ""
}
Let’s take a moment to walk through this code: First of all, you check whether
message is non-nil. If it is nil, then the user clicked send when they did not
have a message, so there is nothing else to do. But if it is not nil, you put the
message inside a user info dictionary which will be sent along with the
notification. Finally, you grab the default notification center and post the
notification.
In a moment, you will make ChatWindowController observe this notification and
update the text view with the received text.
But first, add a method stub near the bottom of ChatWindowController’s
implementation to be called when the notification is posted:
// MARK: - Notifications

// ChatWindowControllerDidSendMessageNotification
func receiveDidSendMessageNotification(note:
NSNotification) {

}
Now, make the ChatWindowController add itself to the default notification center
as an observer of ChatWindowControllerDidSendMessageNotification whenever
any object posts it:
override func windowDidLoad() {
super.windowDidLoad()

let notificationCenter =
NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self,
selector:
Selector("receiveDidSendMessageNotification:"),
name:
ChatWindowControllerDidSendMessageNotification,
object: nil)
}
Now, when any window controller posts
ChatWindowControllerDidSendMessageNotification, every window controller
will have receiveDidSendMessageNotification(_:) called on it.
When a window controller observes this notification, it should add the newly
received text at the bottom of the text displayed in the text view.
// ChatWindowControllerDidSendMessageNotification
func receiveDidSendMessageNotification(note:
NSNotification) {
let mutableLog = log.mutableCopy() as!
NSMutableAttributedString

if log.length > 0 {

mutableLog.appendAttributedString(NSAttributedString(string:
"\n"))
}

let userInfo = note.userInfo! as! [String :


String]
let message =
userInfo[ChatWindowControllerMessageKey]!

let logLine = NSAttributedString(string: message)


mutableLog.appendAttributedString(logLine)

log = mutableLog.copy() as! NSAttributedString

textView.scrollRangeToVisible(NSRange(location:
log.length, length: 0))
}
Notice the last line of code: it makes the text view display the latest message by
scrolling to ensure that the very last character is visible.
scrolling to ensure that the very last character is visible.
At this point, you are nearly done. When you send a notification from one
window controller, all the window controllers will receive that notification and
update their text views accordingly.
Note that each window controller is observing notifications from its own window
as well as any others. This means that the text view in a given window will
include the messages sent from that same window.
The only thing left to do is a bit of cleanup. You need to make each
ChatWindowController remove itself as an observer when it is deallocated.
override func windowDidLoad() {
...
}

deinit {
let notificationCenter =
NSNotificationCenter.defaultCenter()
notificationCenter.removeObserver(self)
}

// MARK: - Actions
This is necessary because the notification center has an unowned reference to the
window controller. This means that if you forget to remove the window
controller as an observer and it gets deallocated, when the notification is next
posted, the notification center will try to call
receiveDidSendMessageNotification(_:) on the deallocated window controller.
At best, this will lead to a crash. At worst, this will cause unexpected behavior.
For the More Curious: Delegates and
Notifications
An object that has made itself the delegate of a standard Cocoa object is
probably interested in receiving notifications from that object as well. For
example, if you have implemented a delegate to handle the
windowShouldClose(_:) delegate method for a window, that same object is likely
to be interested in the NSWindowDidResizeNotification from that same window.
If a standard Cocoa object has a delegate and posts notifications, the delegate is
automatically registered as an observer for the methods it implements. If you are
implementing such a delegate, how would you know what to call the method?
The naming convention is simple: Start with the name of the notification.
Remove the NS from the beginning and make the first letter lowercase. Remove
the Notification from the end. Add a colon. For example, to be notified that
the window has posted an NSWindowDidResizeNotification, the delegate would
implement the following method:
optional func windowDidResize(notification:
NSNotification)
This method will be called automatically after the window resizes. You can also
find this method listed in the documentation and header files for NSWindow.
Challenge: Beep-beep!
Make your application beep when it gives up its active status. NSApplication
posts an NSApplicationDidResignActiveNotification notification. Your
AppDelegate is a delegate of NSApplication. NSBeep() will cause a system beep.
Challenge: Add Usernames
In your current Chatter app, there is no way to tell who sent what message. Add a
text field to the top of the ChatWindowController where the user can enter a
username. Send the username – or, if none has been entered, a good default
value – with the notification when they send a message. Display the username in
the text view.
Challenge: Colored Text
Now that you have completed the last challenge, your users will be able to tell
by reading who sent which message. However, it is still not immediately
apparent visibly which messages were sent from this window controller. In
receiveDidSendMessageNotification(_:), if the notification was posted by self,
add an attribute to the logLine attributed string to make that line blue. You will
want to use NSForegroundColorAttributeName.
Challenge: Disabling the Send Button
As it stands, the Send button never gets disabled. But it should: when there is no
message text entered, no action is taken when the user clicks the button. Disable
the button at the appropriate time using bindings. In particular, bind the button’s
Enabled value to File's Owner’s message property.

Of course, enabled needs a boolean and message is a string. To solve this


problem, create a subclass IsNotEmptyTransformer of NSValueTransformer which
will transform a string into a boolean.
You will want the button’s state to be updated as the user is typing. To
accomplish this, open the text field’s Value binding in Interface Builder and check the
Continuously Updates Value option. This will ensure that changes to the value will be
reflected in the window controller’s message field immediately.
17
NSView and Drawing
All visible objects in an application are either windows or views. Windows are
instances of the class NSWindow and views are instances of the class NSView. A
window has a collection of views, and each of these views is responsible for a
rectangle of the window. The view draws inside that rectangle and handles
mouse events that occur there. A view may also handle keyboard events.
You have worked with several subclasses of NSView including NSButton,
NSTextField, NSTableView, and NSColorWell. From time to time, you may need to
draw something or respond to a mouse event in a way that a standard view class
cannot help you with. In these cases, the answer is to write your own NSView
subclass.
In this chapter, you are going to create a subclass of NSView named DieView.
Even if you do not plan to do much custom drawing or event handling, you will
learn a lot about how Cocoa works by creating a new view class. An instance of
DieView will draw one face of a die (singular of dice). DieView will be part of an
application named Dice. Figure 17.1 shows Dice at the end of this chapter.
Figure 17.1 Dice application with a DieView
Setting Up the Dice Application
Create a new Cocoa Application project (Command-Shift-N). Name the project
Dice and ensure that it is not document-based (Figure 17.2).

Figure 17.2 Configuring project settings for Dice

Update the project to use the single-window application architecture as with


earlier projects:
1. Delete the window: Open MainMenu.xib. From the dock, select and delete
the object labeled Dice.
2. Use the snippet: Open AppDelegate.swift and delete the code inside the
class implementation. In the code snippet library, find the Single-Window
Application snippet and drag it into the empty class implementation. Your file
should look like this:
class AppDelegate: NSObject, NSApplicationDelegate
{

var mainWindowController:
MainWindowController?
func
applicationDidFinishLaunching(aNotification:
NSNotification) {
// Create a window controller with a XIB
file of the same name
let mainWindowController =
MainWindowController()
// Put the window of the window controller
on screen
mainWindowController.showWindow(self)

// Set the property to point to the window


controller
self.mainWindowController =
mainWindowController
}

}
Create the MainWindowController class: Create a new Cocoa class (Command-
N). Name it MainWindowController and make it a subclass of
NSWindowController. Check the Also create XIB file for user interface box.

Override windowNibName: Open MainWindowController.swift and add the


following code:
class MainWindowController: NSWindowController {

override var windowNibName: String {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()
}

}
Run the application to confirm that everything is set up correctly. You should
see an empty Window.
Creating a view subclass
Create a new Swift File and name it DieView. In DieView.swift, provide a stub
implementation of DieView:
import Foundation
import Cocoa

class DieView: NSView {

}
Open MainWindowController.xib and click the window in the editor dock. Drag
a Custom View from the object library and drop it onto the window (Figure 17.3).
Resize the view to fill most of the window.
Figure 17.3 Dropping a view onto the window

Dragging from the object library to the content view creates a view object and
adds it as a subview of the content view. However, the view is not yet an
instance of DieView. Open the identity inspector and set the view’s class to
DieView (Figure 17.4).

Figure 17.4 Setting the class of the view to DieView


If you run the application now, you will not see the DieView. By default, a view
is a transparent rectangular canvas to be drawn on. Your custom view
determines its appearance.
But before you start writing drawing code, let’s look at views, rectangles, and
coordinate systems.
Views, Rectangles, and Coordinate Systems
When a window appears on screen, Cocoa must position each view in the
window’s hierarchy. To know how to position a view, Cocoa needs two pieces
of information:
the origin, or position of the lower-left corner of the view in relation to the
lower-left corner of its superview
the size of the rectangle occupied by the view

frame
The position and size of the view are specified by its frame property, which is an
NSRect.
struct NSRect {
var origin: NSPoint
var size: NSSize
}
The origin of the frame specifies the position of the lower-left corner as the
offset from the superview’s lower-left corner. It is a coordinate in 2D space,
represented by an NSPoint with x and y components.
struct NSPoint {
var x: CGFloat
var y: CGFloat
}
The size of the frame specifies the size of the view’s rectangle. The size is
represented by an NSSize, which has a width and height.
struct NSSize {
var width: CGFloat
var height: CGFloat
}
Why are NSRect, NSSize, and NSPoint structs and not classes? Historically, the
reason is performance, but structs are also inherently safer from bugs related to
mutability because they are passed by value rather than by reference.
Visually, a view’s frame resembles Figure 17.5.
Figure 17.5 NSRect, NSSize, and NSPoint

The members of NSPoint and NSSize are of the type CGFloat, which is a floating
point type. What, then, are the units for NSPoint and NSSize?
You might expect that NSPoint and NSSize would be measured in pixels, but they
are instead measured in points. These points have nothing to do with the NSPoint
struct; they are borrowed from typography where a point is 1/72 of an inch. The
original Macintosh display had 72 pixels per inch - one point was the width of a
pixel. OS X and iOS drawing commands (and PDF, not coincidentally) use
points as their unit of measurement.
The power of points is that they are resolution independent. The idea is that a
point should represent some fairly consistent size, independent of the screen
resolution. On a standard display points correspond 1:1 to pixels, just like they
did on the original Macintosh. On a Retina display, however, a point is two
pixels wide. Because drawing and view coordinates are in points, supporting
pixels wide. Because drawing and view coordinates are in points, supporting
both display types is remarkably uncomplicated for most applications.
NSRect, NSPoint, and NSSize are actually type aliases for the Core Graphics types
CGRect, CGPoint, and CGSize. For legacy reasons the AppKit APIs still use the
NS-prefixed types, but behind the scenes they are aliased to the Core Graphics
types. We will discuss Core Graphics more at the end of this chapter.

bounds
There is another NSRect you use when drawing in Cocoa – the view’s bounds
property. While its type is the same as frame, they have different uses: You use a
view’s frame rectangle to position subviews of the view. For instance, to center a
button in a view, you would use that view’s frame to determine where the button
should go.
You use a view’s bounds rectangle to draw the view itself. For instance, you
would use bounds to tell a button how to draw its rounded rectangle shape.
Custom Drawing
For your first foray into custom drawing, you will start small by drawing a gray
background for the DieView and a green line that bisects the view diagonally.
(Figure 17.6).
Figure 17.6 Drawing with NSColor and NSBezierPath

Your drawing will be done in three steps:


Set the color. The color is an instance of NSColor. NSColor can specify
colors in the RGB, HSV, CMYK, and grayscale color spaces.
Construct the path. The path specifies the line, curve, or shape that you
want to draw. The path is an instance of NSBezierPath. (If you need to draw
the same path multiple times, you can store an instance of NSBezierPath as
a property.)
Draw the constructed path in the set color. For lines and curves, you call
the NSBezierPath method stroke() on the path. For shapes, you call the
fill() method.

All of this happens in the NSView drawRect(_:) method


drawRect(_:)
To draw in a subclass of NSView, you override the drawRect(_:) method.
Open DieView.swift and implement drawRect(_:) to give DieView a light gray
background:
import Cocoa

class DieView: NSView {

override func drawRect(dirtyRect: NSRect) {


let backgroundColor = NSColor.lightGrayColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)
}

}
First, observe that drawRect(_:) takes a single parameter, dirtyRect, which is
the rectangle that needs to be drawn.
Within the body of drawRect(_:), you set the color for drawing using a class
method on NSColor, lightGrayColor. This method returns an instance of NSColor.
Then you call the class method fillRect(_:) on NSBezierPath to fill in the
bounds rectangle with the current color.

Note that because you are drawing the background, you simply pass the bounds
rectangle to the fillRect(_:) method. This method accepts a rectangle, creates a
path based on that rectangle, and fills it in with the current color.
Run the application. Your view should now be visible and filled with gray.
Figure 17.7 DieView with light gray background
Issue a second set of drawing commands to draw the diagonal line:
override func drawRect(dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGrayColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)

NSColor.greenColor().set()
let path = NSBezierPath()
path.moveToPoint(NSPoint(x: 0, y: 0))
path.lineToPoint(NSPoint(x: bounds.width, y:
bounds.height))
path.stroke()
}
In this code, you use two NSBezierPath methods to construct the path of the line.
moveToPoint() sets the starting point for drawing. lineToPoint() maps out the
line from the starting point to the passed-in point. Finally, calling stroke() on
the path draws the line.
Notice that you are accessing bounds.width and bounds.height, yet the structure
definition for NSRect (CGRect) only has origin and size fields. These are
computed properties provided for convenience by means of a Swift extension on
CGRect. The width computed property is equivalent to size.width, and height is
equivalent to size.height.
Run the application and confirm that you have a green diagonal line as shown in
Figure 17.6.

When is my view drawn?


The drawRect(_:) method is called automatically by the system at certain times,
such as when the view first appears or when its size has changed. You should
never call drawRect(_:) directly. Instead, if you know that a view needs
redrawing, you set the view’s property needsDisplay to true:
myView.needsDisplay = true
This informs myView that it is “dirty.” After the current event has been handled,
the system will call drawRect(_:) on all dirty views in order to redraw them.
You do not have to mark the standard views that Cocoa provides as dirty; they
mark themselves as needed. For instance, NSTextField marks itself dirty
whenever its value changes.
On the other hand, you are responsible for ensuring that your custom views mark
themselves as dirty when something happens that requires a change in the view’s
appearance. At the moment, DieView never changes, so it has no need to mark
itself as dirty. (That will change in the next chapter.)

Graphics contexts and states


Take another look at the last code you added to drawRect(_:). You set the color
to be green. Later, path.stroke() is able to retrieve that color to draw a green
line. So where is the color information stored?
The answer is the graphics context. Before Cocoa calls a view’s drawRect(_:), it
configures a graphics context for that view. This context, known as the current
context, is held in thread-local storage, like a global variable. The graphics
context maintains a graphics state, which tracks several values such as the
current color, font, and transform.
Drawing commands read and write to this state. For example, NSColor.set()
sets the current color. NSBezierPath.moveToPoint() and
NSBezierPath.lineToPoint() set the current path (lines to be drawn). Then
NSBezierPath.stroke() draws – using the current color and path.
You cannot issue drawing commands if there is no graphics context. Thus, to
appear in the view, drawing commands must be executed within the lifetime of
that call to drawRect(_:).

Drawing a die face


Now that you have done some simple drawing, let’s get to work on the real
purpose of DieView. To draw the face of a die, an instance of DieView needs
a light gray background (check!)
a white, rounded rectangle with equal length and width (a square with
rounded corners), which is the profile of the die
between one and six black, filled-in circles for the dots
To keep things cleaner, you are going to put the drawing of the white die profile
and the black dots in a new method drawDieWithSize(_:). The drawing code for
the background will remain in drawRect(_:).
In addition, the paths for the profile and dots require calculations based on the
size of the DieView. You will encapsulate this logic in another method,
metricsForSize(_:).

Drawing the die profile

The profile of the die is a square, and it should take up most of the DieView.
Thus, you need to find the smaller of the DieView’s width and height. With this
information, you can define the largest possible square in the view and then
subtract a little padding to get the size of the die profile (Figure 17.8).
In DieView, implement metricsForSize(_:).
func metricsForSize(size: CGSize) -> (edgeLength:
CGFloat, dieFrame: CGRect) {
let edgeLength = min(size.width, size.height)
let padding = edgeLength/10.0
let drawingBounds = CGRect(x: 0, y: 0, width:
edgeLength, height: edgeLength)
let dieFrame =
drawingBounds.rectByInsetting(dx: padding, dy:
padding)
return (edgeLength, dieFrame)
}
These metrics are diagrammed in Figure 17.8. Notice how the dieFrame
rectangle is created from the drawingBounds rectangle – the
rectByInsetting(dx:dy:) method returns a rectangle similar to the one it is
called upon, but inset by dx points on the left and right and by dy points on the
top and button.
Figure 17.8 Metrics for drawing die face

Next, implement drawDieWithSize(_:) to retrieve the metrics and draw the


rectangle with rounded corners.
func metricsForSize(size: CGSize) -> (edgeLength:
CGFloat, dieFrame: CGRect) {
let edgeLength = min(size.width, size.height)
let padding = edgeLength/10.0
let drawingBounds = CGRect(x: 0, y: 0, width:
edgeLength, height: edgeLength)
let dieFrame =
drawingBounds.rectByInsetting(dx: padding, dy:
padding)
return (edgeLength, dieFrame)
}

func drawDieWithSize(size: CGSize) {


let (edgeLength, dieFrame) =
metricsForSize(size)
let cornerRadius: CGFloat = edgeLength/5.0

// Draw the rounded shape of the die profile:


NSColor.whiteColor().set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius,
yRadius: cornerRadius).fill()
}
Finally, update drawRect(_:) to call drawDieWithSize(_:).
override func drawRect(dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGrayColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)
NSColor.greenColor().set()
var path = NSBezierPath()
path.moveToPoint(NSPoint(x: 0, y: 0))
path.lineToPoint(NSPoint(x: bounds.width, y:
bounds.height))
path.stroke()
drawDieWithSize(bounds.size)
}
Run the application. You should see a white, rounded rectangle on the light gray
background.

Drawing the dots

Drawing the dots is more complicated than drawing the profile. First, the
DieView needs to know how many dots to display. Add an intValue property to
represent the number of dots, but make it an optional because there will be times
when the die should not be drawn at all. And for now, give it a hardcoded value
for easy testing.
class DieView: NSView {

var intValue: Int? = 1 {


didSet {
needsDisplay = true
}
}

override func drawRect(dirtyRect: NSRect) {


Note that you are making use of Swift’s didSet feature to react to the value
being changed in order to request that the view be redrawn.
In drawDieWithSize(_:), wrap the die-drawing code in a test for intValue. (You
can select the existing code and then use the Xcode shortcut Command-] to indent the
selected code to the right.)
func drawDieWithSize(size: CGSize) {

if let intValue = intValue {


let (edgeLength, dieFrame) =
metricsForSize(size)
let cornerRadius:CGFloat = edgeLength/5.0

// Draw the rounded shape of the die


profile:
NSColor.whiteColor().set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius,
yRadius: cornerRadius).fill()

}
}
Now, if intValue does not have a value, then the view will draw only its light
gray background.

Dot positioning and drawing


There are seven potential positions where a dot can be drawn on a die face.
(Think about the dots when you roll a 6, plus the dot in the middle for the odd
values.) Your code will go through each potential dot position. If the intValue
requires a dot at the given position, then you will draw one there.
First, update drawDieWithSize(_:) to create two dot geometry variables from the
retrieved metrics:
func drawDieWithSize(size: CGSize) {
if let intValue = intValue {
let (edgeLength, dieFrame) =
metricsForSize(size)
let cornerRadius:CGFloat = edgeLength/5.0
let dotRadius = edgeLength/12.0
let dotFrame =
dieFrame.rectByInsetting(dx: dotRadius * 2.5,

dy: dotRadius * 2.5)

// Draw the rounded shape of the die


profile:
NSColor.whiteColor().set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius,
yRadius: cornerRadius).fill()

// Ready to draw the dots.


}
}
Next, set the dot color and create a nested function named drawDot(). This
function will accept coordinates to position the dot correctly and create a
rectangle to define a dot-sized circle. Then it will call the NSBezierPath
initializer to create the path for the dot. Finally, it will fill in the dot.
// Ready to draw the dots.
// The dots will be black:
NSColor.blackColor().set()

// Nested function to make drawing dots


cleaner:
func drawDot(u: CGFloat, v: CGFloat) {
let dotOrigin = CGPoint(x:
dotFrame.minX + dotFrame.width * u,
y:
dotFrame.minY + dotFrame.height * v)
let dotRect = CGRect(origin:
dotOrigin, size: CGSizeZero)
.rectByInsetting(dx: -
dotRadius, dy: -dotRadius)
NSBezierPath(ovalInRect:
dotRect).fill()
}
}
}
In creating the point for dotOrigin, you are using two more computed properties
on NSRect: minX and minY, which return the minimum X and Y coordinates of the
rect.
The nested function makes it easy to draw dots using U and V coordinates
(coordinates in the range of 0...1). Note that the nested function captured
dotFrame; you did not have to add it as a parameter.

Also note that because the dotRect rectangle passed into the NSBezierPath
initializer is a square, the oval will be a circle.
Finally, use the generic function find() to determine which positions should
have dots.
// Ready to draw the dots.
// The dots will be black:
NSColor.blackColor().set()

// Nested function to make drawing dots


cleaner:
func drawDot(u: CGFloat, v: CGFloat) {
let dotOrigin = CGPoint(x:
dotFrame.minX + dotFrame.width u,
y:
dotFrame.minY + dotFrame.height v)
let dotRect = CGRect(origin:
dotOrigin, size: CGSizeZero)
.rectByInsetting(dx: -
dotRadius, dy: -dotRadius)
NSBezierPath(ovalInRect:
dotRect).fill()
}

// If intValue is in range...
if find(1...6, intValue) != nil {
// Draw the dots:
if find([1, 3, 5], intValue) != nil {
drawDot(0.5, 0.5) // Center dot
}
if find(2...6, intValue) != nil {
drawDot(0, 1) // Upper left
drawDot(1, 0) // Lower right
}
if find(4...6, intValue) != nil {
drawDot(1, 1) // Upper right
drawDot(0, 0) // Lower left
}
if intValue == 6 {
drawDot(0, 0.5) // Mid left/right
drawDot(1, 0.5)
}
}
}
}
Run the application. You should see a die face with a single dot in the center, as
in Figure 17.9. Change the initial value of intValue to make sure that other
values work as expected. Do not forget to test for the absence of a value.
Figure 17.9 Snake eye
Saving and Restoring the Graphics State
Recall that the graphics context manages the graphics state, which includes the
current color, any transforms such as scaling or rotation, and so on. At times, it
is convenient to treat the graphics state like a stack: You make a copy of the
current state and push it onto the stack. Now the copy is the current state. You
can alter the state, run some drawing commands, and then pop the state to return
to the prior state.
To see this in action, let’s add a shadow to the die shown in DieView. AppKit
shadows are configured using the NSShadow class. Update drawDieWithSize(_:):
let dotRadius = edgeLength/12.0
let dotFrame = dieFrame.rectByInsetting(dx:
dotRadius 2.5,
dy:
dotRadius 2.5)

let shadow = NSShadow()


shadow.shadowOffset = NSSize(width: 0, height: -1)
shadow.shadowBlurRadius = edgeLength/20
shadow.set()

// Draw the rounded shape of the die profile:


NSColor.whiteColor().set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius, yRadius:
cornerRadius).fill()
Once you call shadow.set(), the shadow is part of the graphics state. All
subsequent drawing commands will be executed with the shadow effect.
If you run the application now, you will see that you have a nice shadow around
the rounded rectangle of the die, but there is also a shadow around the dots
themselves. (If you are having trouble seeing this, try commenting out the line
that sets the color to black before drawing the dots.)
Instead of looking for a way to un-set the shadow, the best solution is to use the
graphics state stack. You can do this with two methods on NSGraphicsContext:
saveGraphicsState() and restoreGraphicsState(). Though the names of these
methods do not include push/pop terminology, that is how they operate.
Update drawDieWithSize(_:) to push a copy of the state onto the stack before the
shadow is set. Then pop that state before the dots are drawn.
let dotRadius = edgeLength/12.0
let dotFrame = dieFrame.rectByInsetting(dx:
dotRadius 2.5,
dy:
dotRadius 2.5)

NSGraphicsContext.saveGraphicsState()

let shadow = NSShadow()


shadow.shadowOffset = NSSize(width: 0, height: -1)
shadow.shadowBlurRadius = edgeLength/20
shadow.set()

// Draw the rounded shape of the die profile:


NSColor.whiteColor().set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius, yRadius:
cornerRadius).fill()

NSGraphicsContext.restoreGraphicsState()
// Shadow will not apply to subsequent drawing
commands

Run the application and confirm that the shadow is now being applied to the die
face, but not to the dots.
This technique is very powerful when drawing hierarchies of objects which, like
views, are positioned relative to the objects they are related to.
Cleaning up with Auto Layout
Your application has a usability issue: The user can resize the application
window, but the instance of DieView will stay its original size. What you want is
for the DieView to resize along with its superview. You can fix this with Auto
Layout.
Auto Layout is a sophisticated system for controlling the layout of views in a
window by applying constraints to views. You will learn more about Auto
Layout and constraints in Chapter 25.
In this section, you are going to skip ahead and apply a constraint to make
DieView resize with the window. The constraint will keep all four edges of the
DieView exactly 20 points away from the edges of its superview (the content
view of the window). Thus, when the window is resized, the DieView will be
resized to meet this constraint.
Open MainWindowController.xib and select the DieView in the window. In the
lower-right corner of the canvas, find the four Auto Layout buttons and click the
Pin button, which looks like .
In that popover that appears, find the Add New Constraints section and click each of
the four struts extending from the square. The struts will turn solid red. If
necessary, change the four text fields to all read 20. (20 points is the standard
spacing from the edges of the window, but you could enter another value.)
Finally, click the Add 4 constraints button at the bottom (Figure 17.10).
Figure 17.10 Adding constraints
If you make a mistake, click the Resolve Auto Layout Issues button, which looks like
, and select Clear Constraints. Note that these menu items are also available in
Xcode’s Editor menu.

Now, when you run the application you should see the DieView – and the die
itself – resize with the window.
If you resize the window to be small enough, the DieView is completely hidden.
You could fix this by adding more constraints, but a sensible solution for a
custom view is to change its intrinsicContentSize.
A view’s intrinsic content size is the natural size of its content, and by default
Auto Layout will not squish a view smaller than its intrinsic content size. In
DieView, override this property:
override var intrinsicContentSize: NSSize {
return NSSize(width: 20, height: 20)
}
Drawing Images
Often, as part of a custom view’s drawing, you will want to draw an image on
the screen. Cocoa makes this extremely simple.
To try it out, you are going to make a small Cocoa application which draws an
image. When you are done, the app will have a view that draws a regular grid of
images (Figure 17.11)
Figure 17.11 The finished ImageTiling project

In Xcode, create a new Cocoa Application project called ImageTiling (Figure 17.12).
Figure 17.12 Configuring the new ImageTiling project
As you did with Dice, set up the new project to use the single-window
architecture. Name the new window controller class MainWindowController. If
you are not completely confident about setting your app up like this, follow the
steps in the section called “Setting Up the Dice Application” .
To make sure that your window controller is set up correctly, run the app. You
should see a window appear on screen, and that is it. If that is not happening,
review the setup of your window controller to make sure that you are not
missing anything.
With your app setup completed, it is time to move on to drawing images. Create
a new Cocoa Class called TiledImageView, a subclass of NSView. This view will
draw a given image multiple times in a grid pattern. To begin with, give the class
an image property and a few constants to determine the characteristics of the
grid:
import Cocoa

class TiledImageView: NSView {

var image: NSImage?


let columnCount = 5
let rowCount = 5

override func drawRect(dirtyRect: NSRect) {


super.drawRect(dirtyRect)
}

}
In a moment, you are going to implement drawRect(_:) to draw the image a
number of times in a grid. But first, to help with implementing drawRect(_:),
you will write a helper method called frameForImageAtLogicalX(_:y:). You will
use this method to find out, for example, where to draw the image that is third
from the left and second from the bottom:
// MARK: - Drawing

func frameForImageAtLogicalX(logicalX: Int, y


logicalY: Int) -> CGRect {
let spacing = 10
let width = 100
let height = 100
let x = (spacing + width) * logicalX
let y = (spacing + height) * logicalY
return CGRect(x: x, y: y, width: width,
height: height)
}
This method takes in a pair of Ints that describe which image’s frame is to be
computed. In the implementation, you first define a few constants so that you
have nice names rather than magic numbers in your code: the spacing is the
distances between the images; the height and width are the dimensions of the
images. You then calculate the origin of the image’s frame by adding up the
number of images and image insets to the left and below the image.
With this helper method in place, you are ready to implement drawRect(_:). It
will draw the image a number of times in a grid.
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
if let image = image {
for x in 0..<columnCount {
for y in 0..<rowCount {
let frame =
frameForImageAtLogicalX(x, y: y)
image.drawInRect(frame)
}
}
}
}
The implementation iterates over a number of columns and rows, uses the helper
method to get the frame for the image in that column and row, and then draws
the image using NSImage’s method drawInRect(_:).
Now that you have created your NSView subclass, you will need to add it to the
view hierarchy to display it. Just like you did with DieView, open
MainWindowController.xib, and drag out a Custom View from the object library
onto the window. With the new view selected, switch to the identity inspector
and change the Class to TiledImageView (Figure 17.13).
Figure 17.13 Setting custom view’s class to TiledImageView

Build and run your app. At the moment, even though your window now contains
a TiledImageView, it does not look any different from the way it did before,
because the tiled image view is not drawing anything. This is because you have
not set its image property yet.
There are a couple of ways to set the image view’s image property. You could
add an outlet to the image view from the window controller and set the outlet’s
image property in windowDidLoad(). Instead, you are going to follow a slicker
approach.

Inspectable properties and designable views


Open TiledImageView.swift. Add the @IBInspectable attribute to the image
property:
@IBInspectable var image: NSImage?
let columnCount = 5
let rowCount = 5
This attribute will allow you to edit the TiledImageView’s image property within
Interface Builder.

Open MainWindowController.xib again and select the tiled image view. Switch
to the attributes inspector. You will see a new section entitled Tiled Image View. In
that section, you will see a new field: Image. You can enter the name of any image
that your project or Cocoa provides. Try it out by entering NSComputer.
If you build and run the app now, you will see that image drawn in a grid.
Fantastic!
Wouldn’t it be nice to be able to see what it will look like from Interface Builder?
Xcode allows you to do this. Just open TiledImageView.swift and add the
@IBDesignable attribute to the class:
@IBDesignable class TiledImageView: NSView {
Now switch back to MainWindowController.xib. If you look at the title bar, you
will that Xcode is building your app. Once it has finished successfully,
Interface Builder will make your tiled image view draw itself with images
(Figure 17.14).
Figure 17.14 The @IBDesignable ImageTilingView with @IBInspectable
image in Xcode
Drawing images with finer control
The drawInRect(_:) method you used in this exercise draws the image as you
would expect, using the most common settings: the whole image at 100%
opacity, respecting the alpha channel (if present). If you need more control,
however, consider drawInRect(_:fromRect:operation:fraction:):
func drawInRect(rect: NSRect,
fromRect: NSRect,
operation op: NSCompositingOperation,
fraction delta: CGFloat)
The first parameter of this method is the same as the parameter to
drawInRect(_:): the rect that you want to draw into. The second parameter
allows you to specify a subregion of the image which should be drawn. You use
the third parameter to describe how the image drawing should be composited
with other drawing operations (to simply draw the image on top of everything
else, use NSCompositingOperation.CompositeSourceOver). You use the last
parameter to specify the opacity with which the image should be drawn.
Scroll Views
When displaying information to the user, you will often find that there is simply
not enough space to display all that you need to in your app’s view hierarchy.
For example, imagine that you were writing a web browser. Your app would
render HTML and CSS into graphical web pages to show to your users. Many
web pages have a great deal of content – too much to fit on your users’ screens,
let alone in your app’s views. How would you present the large graphical content
of the web pages to the user in the small views that your application has to work
with?
Cocoa provides the class NSScrollView for exactly this type of situation. You
have already seen a scroll view in RaiseMan. In that app, your table view was
embedded inside a scroll view.
A scroll view acts as a window from one of your views into a larger view,
known as the document view, which cannot fit completely within your view.
Only a portion of the document view is ever displayed within the scroll view.
That portion is known as the visible rect (Figure 17.15).
Figure 17.15 NSScrollView in context
Currently, in ImageTiling, the five rows and five columns of the image are not all
visible. To fix this, you will embed the TiledImageView inside a scroll view.
Open MainWindowController.xib, expand the document outline, and select the
Tiled Image View. Tell Interface Builder to embed the tiled image view inside a scroll
view by selecting Editor → Embed In → Scroll View.
At this point, you are almost done. The document outline should show that the
tiled image view is nested within a scroll view now (Figure 17.16).
Figure 17.16 The tiled image view embedded within a scroll view
Run the app. You will not see any images at all now! The only thing that will be
visible is the border of the scroll view (Figure 17.17). (Incidentally, you can edit
the scroll view’s border using the attributes inspector.)
Figure 17.17 No images appear in the running app!

Earlier in the chapter you dealt with a similar problem with DieView: with the
view pinned to the edges of the window, it could be forced down to nothing. The
same problem is occurring here: Auto Layout does not know how big to make
the scroll view’s document view, so it defaults to a size of zero. The best
solution is to give this view an intrinsic content size, too.
Override intrinsicContentSize in TiledImageView. You can calculate the size of
the entire grid by using the furthest image’s frame:
override func drawRect(dirtyRect: NSRect) {
if let image = image {
for x in 0..<columnCount {
for y in 0..<rowCount {
let frame =
frameForImageAtLogicalX(x, y: y)
image.drawInRect(frame)
}
}
}
}

override var intrinsicContentSize: NSSize {


let furthestFrame =
frameForImageAtLogicalX(columnCount-1, y: rowCount-1)
return NSSize(width: furthestFrame.maxX,
height: furthestFrame.maxY)
}

}
Run your app. The scroll view should now contain your tiled image view. Use
your trackpad or mouse to scroll around inside it. You should be able to scroll to
all five rows and columns of the image, as shown in Figure 17.18.
Figure 17.18 Tiled image view now visible and scrollable
Creating Views Programmatically
You will usually create your views in Interface Builder, but occasionally you will
need to create views programmatically. If, for example, you have a pointer to a
window and want to put a button on it, this code would create a button and put it
on the window’s content view:
let superview = window.contentView
let frame = NSRect(x: 10, y: 10, width: 200, height:
100)
let button = NSButton(frame: frame)
button.title = "Click me!"
superview.addSubview(button)
Some of the Cocoa view classes are capable of a surprising variety of
appearances. For example, you have no doubt seen that there are a lot of buttons
in the object library. But there is only one NSButton class. All of the different
button appearances are created by setting various properties on instances of that
class.
To figure out how to programmatically recreate a style seen in Interface Builder,
drag the desired view onto the canvas, select it, and comb through the properties
in the attributes inspector. If you hover over one of the attributes in the inspector,
(Style, Type, and so on) Xcode will display a tooltip with a hint of the property you
can use to control this property. Two common properties for configuring an
NSButton are bezelStyle and bordered.

Be patient, it may take a number of tries before you find all of the properties that
need to be set. An Xcode playground can help to more rapidly iterate until you get
the appearance you are looking for (Figure 17.19).
Figure 17.19 Using a playground to prototype a button
For the More Curious: Core Graphics and
Quartz
The APIs for drawing used in this chapter are commonly referred to as AppKit
drawing APIs. Behind the scenes, the AppKit drawing APIs are implemented
using a framework called Core Graphics, which is colloquially referred to as
Quartz.
If you are serious about custom drawing you will want to look into Core
Graphics. The concepts are very similar, except it is a C API, and it is much
more explicit about the graphics context (almost every Core Graphics API takes
a CGContextRef, essentially a pointer to the context, as its first parameter). Core
Graphics provides somewhat lower-level access to drawing and, in some cases,
much finer control than is possible in AppKit.
For the More Curious: Dirty Rects
As mentioned previously, the NSRect that is passed as an argument to the view is
the region that is “dirty” and needs redrawing. It may be less than the entire
view. If you are doing very time-consuming drawing, redrawing only the dirty
rectangle may speed up your application considerably.
Note that setting needsDisplay to true will trigger the entire visible region of the
view to be redrawn. If you wanted to be more precise about which part of the
view needs redrawing, you would use setNeedsDisplayInRect(_:) instead:
let dirtyRect = NSRect(x: 0, y: 0, width: 50, height:
50)
myView.setNeedsDisplayInRect(dirtyRect)
For the More Curious: Flipped Views
Both PDF and PostScript use the standard Cartesian coordinate system, in which
y increases as you move up the page. AppKit views follow this model by default.
Thus, the origin is usually at the lower-left corner of the view.
For some types of drawing, the math is easier if the upper-left corner is the
origin and y increases as you move down the page. Such a view is said to be
flipped.
To flip a view, you override the flipped property to return true:
override var flipped: Bool {
return true
}
Challenge: Gradients
NSGradient can be used to draw gradients (ramps between two or more colors).
Use NSGradient and NSBezierPath to draw the die face with a gradient effect.
Hint: you will need to pass an instance of NSBezierPath describing the rounded
rectangle to one of the methods on NSGradient.
Challenge: Stroke
Instead of giving the die a more realistic look with a gradient, try to achieve a
more cell-shaded, cartoonish effect by adding a border to the die. This can be
done by stroke()-ing a NSBezierPath. For bonus points, add glints to the dots on
the die.
Challenge: Make DieView Configurable from
Interface Builder
Currently, it is not at all clear in Interface Builder what a DieView will look like when
it is drawn. Change this using the @IBDesignable attribute.
18
Mouse Events
In the previous chapter, you learned to use drawing commands to make a read-
only view. A more useful view would be able to accept user input, and what
better place to start than handling mouse events?
NSResponder
NSView inherits from NSResponder. All the event-handling methods are declared
in NSResponder. We will discuss keyboard events in the next chapter. For now,
we will focus on mouse events. NSResponder declares these methods:
func mouseDown(theEvent: NSEvent)
func rightMouseDown(theEvent: NSEvent)
func otherMouseDown(theEvent: NSEvent)

func mouseUp(theEvent: NSEvent)
func rightMouseUp(theEvent: NSEvent)
func otherMouseUp(theEvent: NSEvent)

func mouseDragged(theEvent: NSEvent)
func scrollWheel(theEvent: NSEvent)
func rightMouseDragged(theEvent: NSEvent)
func otherMouseDragged(theEvent: NSEvent)
Notice that the argument is always an NSEvent object.
NSEvent
An event object has all the information about what the user did to trigger the
event. When you are dealing with mouse events, you might be interested in the
following properties:
var locationInWindow: NSPoint { get }
var modifierFlags: NSEventModifierFlags { get }
var timestamp: NSTimeInterval { get }
unowned(unsafe) var window: NSWindow! { get }
var clickCount: Int { get }
var pressure: Float { get }
var deltaX: CGFloat { get }
var deltaY: CGFloat { get }
var deltaZ: CGFloat { get }
Getting Mouse Events
To get mouse events, you need to override the mouse event methods. Open
DieView.swift from Chapter 17 and override the methods in DieView:
// MARK: - Mouse Events

override func mouseDown(theEvent: NSEvent) {


println("mouseDown")
}

override func mouseDragged(theEvent: NSEvent) {


println("mouseDragged location: \
(theEvent.locationInWindow)")
}

override func mouseUp(theEvent: NSEvent) {


println("mouseUp clickCount: \
(theEvent.clickCount)")
}
Build and run your application. Try double-clicking, and check the click count.
Note that the first click is sent and then the second click. The first click has a
click count of 1; the second click has a click count of 2.
Click to Roll
Your users have been clamoring for a way to roll the die. The designer thinks
that double-clicking on it would be a sensible way to accomplish this, so let’s
add it. Add a randomize() method to DieView:
func randomize() {
intValue = Int(arc4random_uniform(5)) + 1
}
In mouseUp(_:), use NSEvent’s clickCount to check for the double-click before
calling randomize():
override func mouseUp(theEvent: NSEvent) {
println("mouseUp clickCount: \
(theEvent.clickCount)")
if theEvent.clickCount == 2 {
randomize()
}
}
That was easy. Run the application and roll the die by clicking on it. It is not
bad, but it would be much more satisfying if the die visually reacted to the
mouse down. Specifically, you will shift the die shape vertically downward, as
well as lessen the shadow radius.
To do this you will need a state variable to be set while the mouse is down. Add
a property, pressed, below intValue:
var intValue: Int? = 1 {
didSet {
needsDisplay = true
}
}

var pressed: Bool = false {


didSet {
needsDisplay = true
}
}
Update mouseDown(_:) and mouseUp(_:) to set pressed appropriately:
override func mouseDown(theEvent: NSEvent) {
println("mouseDown")
pressed = true
}

override func mouseDragged(theEvent: NSEvent) {


println("mouseDragged location: \
(theEvent.locationInWindow)")
}

override func mouseUp(theEvent: NSEvent) {


println("mouseUp clickCount: \
(theEvent.clickCount)")
if theEvent.clickCount == 2 {
randomize()
}
pressed = false
}
Now modify metricsForSize(_:) to offset the dieFrame downward when the die
is pressed:
func metricsForSize(size: CGSize) -> (edgeLength:
CGFloat, dieFrame: CGRect) {
let edgeLength = min(size.width, size.height)
let padding = edgeLength/10.0
let drawingBounds = CGRect(x: 0, y: 0, width:
edgeLength, height: edgeLength)
let dieFrame =
drawingBounds.rectByInsetting(dx: padding, dy:
padding)
var dieFrame =
drawingBounds.rectByInsetting(dx: padding, dy:
padding)
if pressed {
dieFrame = dieFrame.rectByOffsetting(dx:
0, dy: -edgeLength/40)
}
return (edgeLength, dieFrame)
}
In drawDieWithSize(_:), lessen the shadow when pressed:
let shadow = NSShadow()
shadow.shadowOffset = NSSize(width: 0, height:
-1)
shadow.shadowBlurRadius = edgeLength/20
shadow.shadowBlurRadius = (pressed ?
edgeLength/100 : edgeLength/20)
shadow.set()
Run the application and try clicking on the die. You should see a satisfying, if
simple, animation.
Improving Hit Detection
The hit testing in DieView is pretty lazy. If the user clicks anywhere in the view,
the die reacts. It would be better if the die only depressed when the user clicked
in the die’s rectangle.
CGRect’s contains(_:) function can check whether a rectangle contains a point,
and you can get the die’s rectangle from metricsForSize(_:).
There is a hurdle, however: NSEvent provides locationInWindow, but what you
need is the point in the view’s coordinate system, which dieFrame is in. How can
you convert locationInWindow to the correct coordinate system?
NSView provides a few methods for this very purpose:
func convertPoint(aPoint: NSPoint, fromView aView:
NSView?) -> NSPoint
func convertPoint(aPoint: NSPoint, toView aView:
NSView?) -> NSPoint
There are also methods for converting sizes and rectangles.
Although these methods take references to views as their second parameter,
passing nil as the view results in converting to/from the view’s window. To
convert locationInWindow to the view you will call convertPoint(_:fromView:)
with nil for the view.
Update mouseDown(_:) to get the die frame, convert the mouse down point, and
check that it is in the rectangle:
override func mouseDown(theEvent: NSEvent) {
println("mouseDown clickCount: \
(theEvent.clickCount)")
pressed = true
let dieFrame =
metricsForSize(bounds.size).dieFrame
let pointInView =
convertPoint(theEvent.locationInWindow, fromView: nil)
pressed = dieFrame.contains(pointInView)
}
Run the application and test the hit testing. It should be constrained to the die
face.
face.
Gesture Recognizers
Gesture recognizers, which were added in OS X 10.10, provide a higher-level
means of handling mouse input. If you have used UIKit’s gesture recognizers on
iOS you will find them very familiar.
Each gesture recognizer is designed to recognize one specific gesture:
NSClickGestureRecognizer

NSPanGestureRecognizer

NSMagnificationGestureRecognizer

NSPressGestureRecognizer

NSRotationGestureRecognizer

Gesture recognizers are added to a specific view and report the gestures using
target/action, just like controls:
func setupPan() {
let pan = NSPanGestureRecognizer(target: self,
action:
Selector("pan:"))
someView.addGestureRecognizer(pan)
}

func pan(pan: NSClickGestureRecognizer) {
let location: NSPoint =
pan.locationInView(someView)
switch pan.state {
case .Began:
println("Began panning at \(location)")
case .Changed:
println("Panning at \(location)")
case .Ended:
println("Ended panning at \(location)")
default:
break
break
}
}
Gesture recognizers are much richer than controls, however. The state property
indicates the phase of the recognition.
Multiple gesture recognizers can be used together, but at times you will need to
mediate between them. For example, you may need to differentiate a click from
a double-click. Mediation is performed by implementing a delegate protocol:
class MainWindowController: NSWindowController,
NSGestureRecognizerDelegate {

@IBOutlet weak var someView: NSView!

var click: NSClickGestureRecognizer?
var doubleClick: NSClickGestureRecognizer?

func setupClickAndDoubleClick() {
let click = NSClickGestureRecognizer(target:
self,
action:
Selector("click:"))
click.delegate = self
someView.addGestureRecognizer(click)

let doubleClick =
NSClickGestureRecognizer(target: self,

action: Selector("doubleClick:"))
doubleClick.numberOfClicksRequired = 2
someView.addGestureRecognizer(doubleClick)

self.click = click
self.doubleClick = doubleClick
}

func gestureRecognizer(gestureRecognizer:
NSGestureRecognizer,
shouldRequireFailureOfGestureRecognizer other:
NSGestureRecognizer) -> Bool {
NSGestureRecognizer) -> Bool {
if gestureRecognizer == click && other ==
doubleClick {
return true
}
return false
}

func click(click: NSClickGestureRecognizer) {
if click.state == .Ended {
println("Clicked!")
}
}

func doubleClick(doubleClick:
NSClickGestureRecognizer) {
if doubleClick.state == .Ended {
println("doubleClicked!")
}
}
}
Note that you will need to check the state of the gesture recognizer in the action
method, even for a simple gesture like a click, as it will first have the state
.Began and then .Ended.

Why would you use gesture recognizers over NSResponder’s mouse event
methods? In an application with rich mouse event handling, the mouse event
methods can become quite complicated. Gesture recognizers make it easier to
segment code for different kinds of gestures. Another strong advantage is that
gesture recognizers can be used in a controller – without subclassing NSView.
Challenge: NSBezierPath-based Hit Testing
The hit testing in this application is rather primitive. The user can click outside
the rounded rect, but if they are still inside the rectangle, it will still register a hit.
Use NSBezierPath’s containsPoint(_:) method to make the hit testing
completely accurate.
Challenge: A Drawing App
Create a new document-based application that allows the user to draw ovals in
arbitrary locations and sizes. NSBezierPath has the following initializer:
init(ovalInRect: NSRect) -> NSBezierPath
If you are feeling ambitious, add the ability to save and read files.
If you are feeling extra ambitious, add undo capabilities.
19
Keyboard Events
When the user types, where are the corresponding events sent? First, the window
manager gets the event and forwards it to the active application. The active
application forwards the keyboard events to the key window. (This window is
what most users call the “active window,” but Cocoa developers call it the key
window because it receives keyboard events.) The key window forwards the
events to the active view.
Which view is the active one? Each window has an outlet called firstResponder
that points to one view of that window. That view is the active view for that
window. For example, when you click on a text field, it becomes the
firstResponder of that window (Figure 19.1).

Figure 19.1 The first responder of the key window is active

When the user tries to change the firstResponder to another view (by tabbing or
clicking the other view), the views go through a certain ritual before the
firstResponder outlet is changed. First, the view that may become the
firstResponder is asked whether it accepts first-responder status. A return of NO
means that the view is not interested in keyboard events. For example, you
cannot type into a slider, so it refuses to accept first-responder status.
If the view does accept first-responder status, the view that is currently the first
responder is asked whether it resigns its role as the first responder. If the editing
is not done, the view can refuse to give up first-responder status. For example, if
the user had not typed in his or her entire phone number, the text field could
refuse to resign this status. Finally, if the current first responder resigns, the view
that has accepted first-responder status is notified. Often, this triggers a change
in its appearance (Figure 19.2).
Figure 19.2 Becoming the first responder

Note that each window has its own first responder. Several windows may be
open, but only the first responder of the key window gets the keyboard events.
NSResponder
Let’s look at the following property and methods that are inherited from
NSResponder:
var acceptsFirstResponder: Bool { get }
func resignFirstResponder() -> Bool
func becomeFirstResponder() -> Bool
func keyDown(theEvent: NSEvent)
func keyUp(theEvent: NSEvent)
func flagsChanged(theEvent: NSEvent)
NSEvent
We discussed NSEvent in terms of mouse events in the previous chapter. Here are
some of the properties commonly used when getting information about a
keyboard event:
var characters: String! { get }
var ARepeat: Bool { get }
var keyCode: UInt16 { get }
var modifierFlags: NSEventModifierFlags { get }
Adding Keyboard Input to DieView
Open the Dice project from Chapter 18. Although it would be cheating, users
would like to be able to set the number on the face of the die by typing a number
from one to six.

Accept first responder


Your first task in adding keyboard support is to accept first responder. Add the
following property and methods to DieView:
// MARK: - First Responder

override var acceptsFirstResponder: Bool { return true


}

override func becomeFirstResponder() -> Bool {


return true
}

override func resignFirstResponder() -> Bool {


return true
}
Remember that without accepting first responder your view cannot receive
keyboard events.

Receive keyboard events


You could implement keyDown(_:) and interpret the characters yourself.
Fortunately NSResponder, from which NSView inherits, has a method called
interpretKeyEvents(_:) which does the interpreting for you. It then calls other
NSResponder methods, if present, in order to handle the higher-level meaning of
the key event. For example, when the user types plain characters,
interpretKeyEvents(_:) will call insertText(_:).
// MARK: Keyboard Events

override func keyDown(theEvent: NSEvent) {


interpretKeyEvents([theEvent])
}

override func insertText(insertString: AnyObject) {


let text = insertString as! String
if let number = text.toInt() {
intValue = number
}
}
Run the application and try typing a few numbers; you should see the die
changing as intValue is changed. Do not feel too guilty about cheating; you have
not added the in-game currency yet.

Putting the dice in Dice


Open MainWindowController.xib and select the window. You are about to
duplicate the DieView in the window, and this would make the Auto Layout
configuration more complex than necessary at this point. Remove the constraints
by clicking the Editor → Resolve Auto Layout Issues → Clear Constraints menu item. For
now the DieView will not respond to changes in the window size.
Select the existing DieView and resize it to be 104x104 in the size inspector.
Stretch the window horizontally to make enough room for two more DieViews.
Select the DieView again and duplicate it twice with Edit → Duplicate (or use the
keyboard shortcut Command-D). Position the DieView instances next to each
other as in Figure 19.3. While you are here, change the title of the window to
Dice using the attributes inspector.
Figure 19.3 Three DieView make Dice
Run the application. Each of the dice should be clickable, and you should be able
to type in each of them to change the face value. Which DieView is your
keyboard input going to, though? (Figure 19.4) Your users want to be able to see
which die is selected. You are going to need a focus ring.
Figure 19.4 Which view has the focus?
Focus Rings
When a view has the focus (which just means that it is the first responder), its
appearance changes. For text input controls this is generally indicated with a
blue ring around the control. For table views, the selection highlight may change
from gray to blue.
Cocoa makes it very easy to draw the focus ring. Simply implement this method
and property. Add them to the // MARK: - First Responder section.
override func drawFocusRingMask() {
NSBezierPath.fillRect(bounds)
}
override var focusRingMaskBounds: NSRect {
return bounds
}
Now your users can see which die they will be changing. Note that
drawFocusRingMask() allows you to draw any path. You could easily make the
focus ring take the shape of the die’s rounded rectangle.
The Key View Loop
The key view loop is a sequence of first responder-accepting views in each
window. When the user cycles focus between views with Tab or Shift-Tab, the
key view loop is behind it. Cocoa automatically computes a key view loop based
on view positioning, but it is possible to change it by setting the nextKeyView
outlet on your views, as well as initialFirstResponder.
Unfortunately your users cannot take advantage of the key view loop in Dice,
because DieView ignores the tab key events. Fixing this is simple enough.
interpretKeyEvents(_:) calls insertTab(_:) and insertBacktab(_:) if they are
present. Add them now in the Keyboard Events section of DieView. They will
simply ask the window to handle selecting the next or previous key view.
override func insertTab(sender: AnyObject?) {
window?.selectNextKeyView(sender)
}
override func insertBacktab(sender: AnyObject?) {
window?.selectPreviousKeyView(sender)
}
Now try Tabbing and Shift-Tabbing between the dice in the window. Nice!
Note that keyboard events go to your view unless they trigger a menu item. Try
pressing Command-W. It should close the window (even if your view is the first
responder for the key window).
For the More Curious: Rollovers
Three mouse event methods were not discussed in Chapter 18: mouseMoved(_:),
mouseEntered(_:), and mouseExited(_:).

To receive mouseMoved(_:), the view’s window needs to accept mouse moved


events. If it does, the mouseMoved(_:) message is sent to the window’s first
responder. To set the window to get mouse moved events, set the
acceptsMouseMovedEvents property to true:
window?.acceptsMouseMovedEvents = true
At this point, the view will be sent the message every time the mouse moves.
This is a lot of events. When people ask about mouse moved events, we ask
them why they want it. They usually say, “Uh, rollovers.”
Rollovers are very popular with hyperlinks. As you roll over a region, its
appearance changes to make it clear that if you clicked now, that region would
accept the click. Hyperlinks in Safari, for example, become highlighted when
you roll over them.
To do rollovers, you do not typically use mouseMoved(_:). Instead, you set up a
tracking area and override mouseEntered(_:) and mouseExited(_:). Tracking
areas are much more efficient than manually tracking in mouseMoved(_:).
When a view is added to a window’s view hierarchy, viewDidMoveToWindow() is
called. This is a pretty good place to create tracking areas. Note the options;
MouseEnteredAndExited simply says that mouseEntered(_:) and
mouseExited(_:) will be called on the owner of the tracking area. ActiveAlways
specifies that the area will be active regardless of first-responder status.
InVisibleRect tells the tracking area to ignore the rect parameter and
automatically match the view’s visible rect as the tracking area bounds.
override func viewDidMoveToWindow() {
window?.acceptsMouseMovedEvents = true

let options: NSTrackingAreaOptions =
.MouseEnteredAndExited | .ActiveAlways |
.InVisibleRect

let trackingArea = NSTrackingArea(rect: NSRect(),
options: options,
options: options,
owner: self,
userInfo: nil)
addTrackingArea(trackingArea)
}
Then, you change the appearance when mouseEntered(_:) and mouseExited(_:)
are called. Assuming that you have a property called highlighted of type Bool,
here is the code:
override func mouseEntered(theEvent: NSEvent) {
highlighted = true
}
override func mouseExited(theEvent: NSEvent) {
{
highlighted = false
}
You would then check the highlighted property in your drawRect(_:) method
and draw the view appropriately. Do not forget to include needsDisplay = true
in the property’s didSet block!
20
Drawing Text with Attributes
In Chapter 17, we covered drawing shapes with NSBezierPath. A natural
progression from there would be to draw text. In this chapter you will add
support for drawing text on the die face to extend the range to [0, 9]. At the end
of this chapter, your application will look like Figure 20.1.
Figure 20.1 Completed application
NSFont
Overall, the class NSFont has only two types of methods:
1. class methods for getting the font you want
2. methods for getting metrics on the font, such as letter height
The following are commonly used methods in NSFont:
class func systemFontOfSize(fontSize: CGFloat) ->
NSFont!
class func boldSystemFontOfSize(fontSize: CGFloat) -
> NSFont!
class func labelFontOfSize(fontSize: CGFloat) ->
NSFont!

class func userFontOfSize(fontSize: CGFloat) ->
NSFont!
class func userFixedPitchFontOfSize(fontSize:
CGFloat) -> NSFont!

class func titleBarFontOfSize(fontSize: CGFloat) ->
NSFont!
class func messageFontOfSize(fontSize: CGFloat) ->
NSFont!
class func toolTipsFontOfSize(fontSize: CGFloat) ->
NSFont!
These methods return a font object for the user’s default font for the
corresponding string types. If you supply a fontSize of 0.0, these methods will
use the default font size.
User fonts are intended to be used in areas representing user input: a text field,
for example. The other methods are useful when implementing custom user
interface controls.
init(name fontName: String!, size fontSize: CGFloat) -
> NSFont
This method initializes a font object by name. fontName is the family face name,
such as “HelveticaBoldOblique” or “Times-Roman”. Again, a fontSize of 0.0
uses the default font size.
Unless your application calls for using a specific font, we recommend using the
prior set of methods in place of this one, in order to maintain consistency with
the system.
NSAttributedString
Sometimes, you want to display a string that has certain attributes for a range of
characters. For example, suppose that you want to display the string “Big Nerd
Ranch” and want the letters 0 through 2 to be bold, the letters 4 through 7 to be
orange, and the letters 9 through 13 to be subscripts.
When dealing with a range of numbers, Cocoa uses the struct NSRange. NSRange
has two members, location and length, which are both integers. The location is
the index of the first item, and the length is the number of items in the range.
You can use the function NSMakeRange() to create an NSRange, but in Swift it is
more expedient to use the range expression initializer: NSRange(0...4).
To create strings with attributes that remain in effect over a range of characters,
Cocoa has NSAttributedString and NSMutableAttributedString. Here is how
you could create the NSAttributedString just described:
let attrStr = NSMutableAttributedString(string: "Big
Nerd Ranch")

attrStr.addAttribute(NSFontAttributeName,
value: NSFont.boldSystemFontOfSize(22),
range: NSRange(0...2))

attrStr.addAttribute(NSForegroundColorAttributeName,
value: NSColor.orangeColor(),
range: NSRange(4...7))

attrStr.addAttribute(NSUnderlineStyleAttributeName,
value: 1,
range: NSRange(9...13))
Once you have an attributed string, you can do lots of stuff with it.
attrStr.drawInRect(bounds)

// Put it in a text field
textField.attributedStringValue = attrStr

// Put it on a button
// Put it on a button
button.attributedTitle = attrStr
Figure 20.2 shows the result of putting the attributed string on a button.
Figure 20.2 Using the attributed string

Here are the names of the global variables for the most commonly used
attributes, the type of object they correspond to, and their default values. (A
complete list can be found in NSAttributedString.h.)
Table 20.1
Global Variable Name Corresponds to Default Value
NSFontAttributeName A font object 12-point Helvetica
NSForegroundColorAttributeName A color Black
An
NSParagraphStyleAttributeName NSParagraphStyle
Standard paragraph
object style
The same as the
NSUnderlineColorAttributeName A color
foreground
0 (which means no
NSUnderlineStyleAttributeName A number
underline)
0 (which means no
NSSuperscriptAttributeName A number superscripting or
subscripting)
An NSShadow
NSShadowAttributeName nil (no shadow)
object
The easiest way to create attributed strings is from a file. NSAttributedString
can read and write the following file formats:
A string: Typically from a plain-text file.
RTF: Rich Text Format is a standard for text with multiple fonts and colors.
In this case, you will read and set the contents of the attributed string with
an instance of NSData.
RTFD: This is RTF with attachments and images.
HTML: The attributed string can do basic HTML layout, but you probably
want to use the WebView for best quality.
Word: The attributed string can read and write simple .doc files.
OpenOffice: The attributed string can read and write simple .odt files.
When you read a document in, you may want to know some things about it, such
as the paper size. If you supply a place where the method can put a pointer to a
dictionary, the dictionary will have all the extra information that it could get
from the data. For example:
let rtfUrl = ... // Obtain an NSURL

var error: NSError?

let rtfData = NSData.dataWithContentsOfURL(rtfUrl,
options:
NSDataReadingOptions.DataReadingMappedIfSafe,
error: &error) // -> NSData!

if rtfData != nil {
var docAttrs: NSDictionary?
let rtfAttrStr = NSAttributedString(data: rtfData,
options: nil,
documentAttributes:
&docAttrs,
error: &error)
button.attributedTitle = rtfAttrStr
}
If you do not care about the document attributes, just supply nil.
Drawing Strings and Attributed Strings
Both NSString and NSAttributedString have methods that cause them to be
drawn onto a view. NSAttributedString has the following methods:
func drawAtPoint(point: NSPoint)
func drawInRect(rect: NSRect)
var size: NSSize { get }
NSString has analogous methods. With NSString, you need to supply a
dictionary of attributes to be applied for the entire string.
func drawAtPoint(point: NSPoint, withAttributes attrs:
[NSObject : AnyObject]!)
func drawInRect(rect: NSRect, withAttributes attrs:
[NSObject : AnyObject]!)
func sizeWithAttributes(attrs: [NSObject :
AnyObject]!) -> NSSize
Note that these are methods on NSString, not Swift’s String. To use them on
String objects you must simply cast to an NSString using as.
Drawing Text Die Faces
Open DieView.swift and jump to drawDieWithSize(_:). Add the following code
below the block that draws the die dots. The new code will run if intValue is not
in the range 1...6.
if intValue == 6 {
drawDot(0, 0.5) // Mid left/right
drawDot(1, 0.5)
}
}
else {
var paraStyle =
NSParagraphStyle.defaultParagraphStyle().mutableCopy()
as!
NSMutableParagraphStyle
paraStyle.alignment = .CenterTextAlignment
let font =
NSFont.systemFontOfSize(edgeLength * 0.5)
let attrs = [
NSForegroundColorAttributeName:
NSColor.blackColor(),
NSFontAttributeName: font,
NSParagraphStyleAttributeName:
paraStyle ]
let string = "\(intValue)" as NSString
string.drawInRect(dieFrame,
withAttributes: attrs)
}
}
}
Run the program and try typing in 7, 8, or 9. It works! But there is a minor issue:
the text is not perfectly centered within the die face. You could fix this by
offsetting dieFrame, but there is a better way: the size APIs on NSString.
Your goal is to find the actual size of the string before it is drawn and change the
positioning so it will be exactly in the center. As a software developer you have
a few different options for implementing this:
a few different options for implementing this:
1. Inline in the method where it is needed.
Effective, but it would clutter up the code in that method.
2. Add a method to the class to find the correct rect.
1 point for encapsulation, but your options for reuse are limited to instances
of this class.
3. Create a new method on NSString that draws the string centered.
Excellent solution! It sets you up perfectly for code reuse down the road.
But how can you add methods to NSString? The answer is extensions.

Extensions
Extensions are a Swift language feature that is based on categories in Objective-
C. Extensions allow you to add methods to any class within your application.
These methods are available to you alone.
Without extensions you might try to add a method to String by subclassing it.
The problem with subclassing is that you can only enjoy that method on
instances that you yourself create of your special subclass. Extensions are
available on all instances of the extended class.
The only significant limitation of extensions is that you cannot add stored
properties with them. Computed properties are fine, but it is not presently
possible to add new storage to a class using them.
Let’s create an extension for drawing text centered in a rectangle. In Xcode, right-
click on the yellow Dice folder and select New File. Choose Swift File from the list
and name it NSString+Drawing. Add an extension to the file as follows:
import Foundation
import Cocoa

extension NSString {

func drawCenteredInRect(rect: NSRect, attributes:


[NSString: AnyObject]!) {
let stringSize =
sizeWithAttributes(attributes)
let point = NSPoint(x: rect.origin.x +
(rect.width - stringSize.width)/2.0,
y: rect.origin.y +
(rect.height - stringSize.height)/2.0)
drawAtPoint(point, withAttributes: attributes)
}

}
Now use the extension in DieView.swift:
let string = "\(intValue)" as NSString
string.drawInRect(dieFrame,
withAttributes: attrs)
string.drawCenteredInRect(dieFrame,
attributes: attrs)
Run the application and press 8. The text should be perfectly centered in the die
face. You now have a handy method that you can reuse anytime you need to
draw centered text. Extensions are a great way to add features to framework
classes that you wish the designers had thought of. You can even use them as a
way to group functionality within your own classes.
Getting Your View to Generate PDF Data
All the drawing commands can be converted into PDF by the AppKit
framework. The PDF data can be sent to a printer or to a file. Note that a PDF
will always look as good as possible on any device, because it is resolution
independent.
You have already created a view that knows how to generate PDF data to
describe how it is supposed to look. Getting the PDF data into a file is really
quite easy. NSView has the following method:
func dataWithPDFInsideRect(rect: NSRect) -> NSData
This method creates a data object and then calls drawRect(_:). The drawing
commands that would usually go to the screen instead go into the data object.
Once you have this data object, you simply save it to a file.
Open DieView.swift and add a method that will create a Save panel as a sheet.
@IBAction func savePDF(sender: AnyObject!) {
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["pdf"]
savePanel.beginSheetModalForWindow(window!,
completionHandler: {
[unowned savePanel] (result) in
if result == NSModalResponseOK {
let data =
self.dataWithPDFInsideRect(self.bounds)
var error: NSError?
let ok = data.writeToURL(savePanel.URL!,
options:
NSDataWritingOptions.DataWritingAtomic,
error: &error)
if !ok {
let alert = NSAlert(error: error!)
alert.runModal()
}
}
})
}
Open MainMenu.xib. Drag a new Menu Item from the Library onto the File menu.
Relabel it Save To PDF.... (You may delete all the other menu items from the menu,
if you wish.) Control-drag from the Save To PDF... menu item to the First Responder
placeholder. In the connection panel that appears, select the savePDF: method
(Figure 20.3).
Figure 20.3 Connect menu item

Run the application. You should be able to generate a PDF file and view it in
Preview (Figure 20.4). Note that the focused DieView is printed. This works
automatically thanks to First Responder and the responder chain, which you will
learn about in Chapter 21.
Figure 20.4 Completed application
For the More Curious: NSFontManager
Sometimes, you will find a font that is good but would be perfect if it were bold
or italicized or condensed. NSFontManager can be used to make this sort of
conversion. You can also use a font manager to change the size of the font.
For example, imagine that you have a font and would like a similar font but
bold. Here is the code:
let fontManager = NSFontManager.sharedFontManager()
let boldFont = fontManager.convertFont(someFont,
toHaveTrait:
NSFontTraitMask.BoldFontMask)
Challenge: Color Text as SpeakLine Speaks It
In Chapter 6 and Chapter 7, you built SpeakLine. Revisit the app and make it give
visual feedback to the user. As the speech synthesizer speaks, colorize the word
that is being spoken.
You will need to implement the following method from
NSSpeechSynthesizerDelegate:
optional func speechSynthesizer(sender:
NSSpeechSynthesizer,
willSpeakWord
characterRange: NSRange,
ofString string:
String!)
The first argument, as it always is, is the object being helped – in this case, the
speech synthesizer. The third argument is the string the speech synthesizer is
speaking. The second argument is the one you will need to use. It is an NSRange
which describes which characters in the string are currently being spoken.
Use this range along with the NSAttributedString API to create an attributed
string in which the words in the string being spoken are changed to a different
color.
Here is how you create an attributed string in which some range of characters
from an existing string is green:
let range: NSRange = ...
let theString: String = ...
let attributedString =
NSMutableAttributedString(string: theString)
attributedString.addAttribute(NSForegroundColorAttributeName,

value:NSColor.greenColor(),
range: characterRange)
You will need to read and write the textField’s attributedStringValue property
(which it inherits from NSControl).
Finally, you will need to enable and disable the text field so that the user cannot
change the text while playback is happening. This allows text coloring to work
change the text while playback is happening. This allows text coloring to work
properly.
func updateTextField() {
if speechSynth.speaking {
textField.enabled = false
} else {
textField.enabled = true
}
}
21
Pasteboards and Nil-Targeted Actions
A process called the pasteboard server (/usr/sbin/pboard) runs on your Mac.
Applications use the NSPasteboard class to write data into that process and to
read data from that process. The pasteboard server makes copying, cutting, and
pasting between applications possible.
An application can copy the same data onto the pasteboard in several formats.
For example, an image can be copied onto the pasteboard as a PDF document
and as a PNG image. Then the application that reads the data can choose the
format that it likes most. The pasteboard uses UTIs to identify the various types
used on the pasteboard.
Multiple items can be written to the pasteboard at once, each with its own set of
representations, or types. For example, multiple URLs can be copied from Finder.
When putting data on the pasteboard, an application typically clears the
pasteboard and then writes one or more objects directly to the pasteboard. Each
of those objects forms an individual item on the pasteboard. The objects must
conform to a pasteboard-writing protocol, which supplies the data. The data for
those items is immediately copied to the pasteboard.
The receiving application will then ask the pasteboard for an array of objects. It
supplies an array of classes along with this request, which enables the pasteboard
to provide the richest representations available for each item.
Data can also be passed via the pasteboard lazily. To do so, a class declares the
data it will provide and then promises to provide that data when asked to do so
by means of a delegate method in the future. We will talk about lazy copying at
the end of the chapter.
Apple also provides APIs to work with the pasteboard on a per type basis, which
may be useful if your application requires very fine control of the pasteboard.
Multiple pasteboards are available. There is a pasteboard called the general
pasteboard, for copy and paste operations, and another for drag-and-drop tasks.
One pasteboard stores the last string that the user searched for, another copies
rulers, and another copies fonts.
In this chapter, you will add cut, copy, and paste capabilities to your DieView.
First, you will implement the methods that will read from and write to the
pasteboard. Then we will discuss how those methods get called.
NSPasteboard
The NSPasteboard class acts as an interface to the pasteboard server. Here are
some of the commonly used methods of NSPasteboard:
class func generalPasteboard() -> NSPasteboard!
init(name: String!) -> NSPasteboard
func clearContents() -> Int
func writeObjects(objects: [AnyObject]!) -> Bool
func readObjectsForClasses(classArray: [AnyObject]!,
options: [NSObject :
AnyObject]!) -> [AnyObject]!
Note that UTIs are not used directly in any of the preceding methods. Instead,
they are class focused.
NSPasteboardItem, which itself conforms to NSPasteboardReading and
NSPasteboardWriting, allows you to work much more closely with the
pasteboard contents using UTIs. Here are some of the more commonly used
methods on NSPasteboardItem:
func setDataProvider(dataProvider:
NSPasteboardItemDataProvider!,
forTypes types: [AnyObject]!) ->
Bool
func setData(data: NSData!, forType type: String!) ->
Bool
func setString(string: String!, forType type: String!)
-> Bool
func setPropertyList(propertyList: AnyObject!, forType
type: String!) -> Bool
var types: [AnyObject]! { get }
func availableTypeFromArray(types: [AnyObject]!) ->
String!
func dataForType(type: String!) -> NSData!
func stringForType(type: String!) -> String!
func propertyListForType(type: String!) -> AnyObject!
Add Cut, Copy, and Paste to Dice
Let’s add cut, copy, and paste capability to Dice. You will create methods named
cut:, copy:, and paste: in the DieView class. To make these methods easier to
write, you will first create methods for putting data onto and reading data off of a
pasteboard. Reopen your Dice project and in DieView.swift, add these methods to
DieView:
// MARK: - Pasteboard

func writeToPasteboard(pasteboard: NSPasteboard) {


if let intValue = intValue {
pasteboard.clearContents()
pasteboard.writeObjects(["\(intValue)"])
}
}

func readFromPasteboard(pasteboard: NSPasteboard)


-> Bool {
let objects =
pasteboard.readObjectsForClasses([NSString.self],
options:
[:]) as! [String]
if let str = objects.first {
intValue = str.toInt()
return true
}
return false
}
Note how you have implemented the write and read methods. When writing to
the pasteboard, you clear its contents and then write objects to it. When reading
from the pasteboard, you supply an array of the classes you are interested in and
then request the matching objects from the pasteboard. By implementing the
NSPasteboardWriting and NSPasteboardReading protocols, the NSString class has
made this task very simple.
Implement the cut:, copy:, and paste: methods in DieView:
@IBAction func cut(sender: AnyObject?) {

writeToPasteboard(NSPasteboard.generalPasteboard())
intValue = nil
}
@IBAction func copy(sender: AnyObject?) {

writeToPasteboard(NSPasteboard.generalPasteboard())
}
@IBAction func paste(sender: AnyObject?) {

readFromPasteboard(NSPasteboard.generalPasteboard())
}
Run the application. Note that the Edit menu’s Cut, Copy, and Paste menu items now
work when each DieView is selected. The keyboard equivalents also work. You
can copy numeric strings into the DieView.
NilTargeted Actions
How is the cut:, copy:, or paste: message sent to the correct view? After all,
there are many, many views. If you select a text field, it should get the message.
When you select another view and then choose the Copy or Paste menu item, the
message should go to the newly selected view.
To solve this problem, the clever engineers at NeXT came up with niltargeted
actions. If you set the target of a control to nil, the application will try to send
the action message to several objects until one of them responds. The
application first tries to send the message to the first responder of the key
window. This is exactly the behavior that you want for Cut and Paste. You can
have several windows, each of which can have several views. The active view
on the active window gets sent the cut-and-paste messages.
The beauty of nil-targeted actions does not end there. NSView, NSApplication,
and NSWindow all inherit from NSResponder, which has an instance variable called
nextResponder. If an object does not respond to a nil-targeted action, its
nextResponder gets a chance. The nextResponder for a view is usually its
superview. The nextResponder of the content view of the window is the window.
Thus, the responders are linked together in what is called the responder chain.
Note that nextResponder has nothing to do with nextKeyView.
For example, one menu item closes the key window. It has a target of nil. The
action is performClose:. None of the standard objects respond to performClose:
except NSWindow. Thus, the selected text field, for example, refuses to respond to
performClose:. Then the superview of the text field refuses, and on up the view
hierarchy. Ultimately, the window (the key window) accepts the performClose:
method. So, to the user, the “active” window is closed.
A panel can become the key window but not the main window. If the key
window and the main window are different, both windows get a chance to
respond to the nil-targeted action.
Your question at this point should be: In what order will the objects be tested
before a nil-targeted action is discarded?
1. The firstResponder of the keyWindow and its responder chain. The
responder chain would typically include the superviews and, finally, the key
window.
2. The delegate of the key window.
3. If it is a document-based application, the NSWindowController and then the
NSDocument object for the key window.

4. If the main window is different from the key window, it then goes through
the same series for the main window: the firstResponder of the main
window and its responder chain (including the main window itself), the
main window’s delegate, and the NSWindowController and the NSDocument
object for the main window.
5. The instance of NSApplication.
6. The delegate of the NSApplication.
7. The NSDocumentController.
In OS X 10.10 and up, NSViewController also participates in the responder
chain. It is its view’s next responder.
This series of objects represents the responder chain, and Figure 21.1 presents an
example. The numbers indicate the order in which the objects would be asked
whether they respond to the nil-targeted action.
Figure 21.1 An example of the order in which responders get a chance to
respond
Note that in document-based applications (such as RaiseMan), the NSDocument
object gets a chance to respond to the nil-targeted action. The object receives
the messages from the following menu items: Save, Save As..., Revert To Saved, Print...,
and Page Setup....

Looking at the XIB file


Back in Dice, open MainMenu.xib. Note that the cut, copy, and paste items are
connected to the First Responder placeholder. First Responder represents nil. It gives
you something to drag to when you want an object to have a nil target
(Figure 21.2).
Figure 21.2 First Responder stands in for nil
Menu Item Validation
Cocoa automatically disables menu items when it cannot find that menu item’s
action in the responder chain. At times you will want to disable a menu item
depending on the state of the application. Consider the Cut, Copy, and Paste items:
they are automatically disabled whenever there is nothing to be copied or cut or
if there is nothing on the pasteboard.
This validation is performed by overriding the validateMenuItem(_:) method in
the same class as the actions you want to validate. For example, you could
disable copying when no number is displayed by implementing this method in
DieView:
override func validateMenuItem(menuItem: NSMenuItem) -
> Bool {
switch menuItem.action {
case Selector("copy:"):
return intValue == nil
default:
return super.validateMenuItem(menuItem)
}
}
The return value of validateMenuItem(_:) controls whether the menu item is
disabled, but you can also configure menuItem itself during this call. You might
set the state property to NSOnState or NSOffState to check or uncheck the
menu item, respectively.
For the More Curious: Which Object Sends
the Action Message?
The target on the Cut, Copy, and Paste menu items is nil. Sending a message to
nil will not do anything. All target-action messages are handled by
NSApplication. It has the following method: func sendAction(_: Selector, to:
AnyObject!, from: AnyObject!) -> Bool
When the target is nil, NSApplication knows to try to send messages to the
objects in the responder chain.
For the More Curious: UTIs and the
Pasteboard
In Chapter 12, we discussed how UTIs are used by OS X to identify file types.
The pasteboard also uses UTIs for this purpose. Although you did not use UTIs
directly in this exercise, NSString uses NSPasteboardTypeString as its type when
it reads and writes itself from the pasteboard. If you were to log the value of
NSPasteboardTypeString, you would find that its value is public.utf8-plain-
text.
The hierarchical nature of UTIs enables an application to be broad
(public.image) or specific (public.png) when requesting objects from the
pasteboard. The system will work out whether a type is permissible based on
what types it conforms to.

Custom UTIs
At some point, you will want to use the pasteboard for custom, application-
specific data. In such cases, you can simply use your own UTI. Custom UTIs
should take the form of a reverse DNS name, such as
com.bignerdranch.raiseman.person. You would then implement
NSPasteboardWriting and NSPasteboardReading on your custom object or use
NSPasteboardItem as an abstraction layer.

Note that custom UTIs do not need to be exported (using Info.plist) unless
they are to be used by other applications. If they are exported, they must
conform to public.data.
For the More Curious: Lazy Copying
An application can implement copying to a pasteboard in a lazy manner. For
example, imagine a graphics application that copies large images to the
pasteboard in several formats: PNG, TIFF, PDF, and so on. You can imagine
that copying all these formats onto the pasteboard would be hard on the
application and the pasteboard server. Instead, such an application might do a
lazy copy. That is, the application will declare all the types that it could put on
the pasteboard but will put off copying the data until another application asks for
it.
Essentially, the application puts an “IOU” (instead of the data) on the pasteboard
and gives an object that will provide the data when it is needed. When another
application actually asks for the data, the pasteboard server calls back for the
data.
You use an NSPasteboardItem to create this IOU object:
let pboard = NSPasteboard.generalPasteboard()
pboard.clearContents()
let item = NSPasteboardItem()
item.setDataProvider(self, forTypes:...)
pboard.writeObjects( [item] )
Then implement pasteboard(_:item:provideDataForType:):
func pasteboard(_ pasteboard: NSPasteboard!, item
item: NSPasteboardItem!,
provideDataForType type: String!)
{
item.setData(..., forType:type)
}
When another application needs the data, this method is called. At that point, the
application must copy the data it promised to the supplied pasteboard item.
As you can imagine, a problem will arise if the pasteboard server asks for the
data after the application has terminated. When the application is terminating, if
it has an IOU currently on the pasteboard, it will be asked to supply all the data
promised before terminating. Thus, it is not uncommon for an IOU data provider
to be sent pasteboard(_:item:provideDataForType:) several times while the
application is in the process of terminating.
The trickiest part of lazy copies is that when users copy data to the pasteboard
and later paste it into another application, they do not want the most recent state
of the data. Rather, users wants the data the way it was when they copied it.
When implementing a lazy copy, most developers will take some sort of a
snapshot of the information when declaring the types. When providing the data,
the developer will copy the snapshot, instead of the current state, onto the
pasteboard.
Of course, when the user does a copy somewhere else, your object will no longer
be responsible for keeping the snapshot.
optional func pasteboardFinishedWithDataProvider(_
pasteboard: NSPasteboard!)
If you implement this method, it will be called when you are no longer
responsible for keeping the snapshot.
Challenge: Write Multiple Representations
You are putting the string onto the pasteboard. Create the PDF for the view and
put that on the pasteboard, too. Now you will be able to copy the image of the
die into graphics programs. Test it using Preview’s New from Clipboard menu item.
(Do not break the string’s copy and paste: Put both the string and the PDF onto
the pasteboard.) Hint: You will need to create an NSPasteboardItem.
Challenge: Menu Item
In the RaiseMan project, add a menu item that triggers the removeEmployee(_:)
method in Document.
22
Drag-and-Drop
Drag-and-drop is little more than a flashy copy-and-paste operation. When the
drag starts, some data is copied onto the dragging pasteboard. When the drop
occurs, the data is read off the dragging pasteboard. The only thing that makes
this technique trickier than copy-and-paste is that users need feedback: an image
that appears as they drag, a view that becomes highlighted when they drag into
it, and maybe a big gulping sound when they drop the image.
The user can perform several different operations by dragging from one
application to another, or even between views in the same application. These
operations are represented by the NSDragOperation type:
struct NSDragOperation : RawOptionSetType {
init(_ value: UInt)
static var None: NSDragOperation { get }
static var Copy: NSDragOperation { get }
static var Link: NSDragOperation { get }
static var Generic: NSDragOperation { get }
static var Private: NSDragOperation { get }
static var Move: NSDragOperation { get }
static var Delete: NSDragOperation { get }
static var Every: NSDragOperation { get }
}
For example, nothing may happen (.None), a copy of the data may be created
(.Copy), or the data may be moved (.Move).
Both the source and the destination must agree on the operation that will occur
when the user drops the image.
When you add drag-and-drop to a view, there are two distinct parts of the
change:
1. Make it a drag source.
2. Make it a drag destination.
Let’s take these steps separately. First, you will make your view a drag source.
When that is working, you will make it a drag destination.
Make DieView a Drag Source
When you finish this section, you will be able to drag the die face off the
DieView and drop it into another DieView, or even into a text editor. It will look
like Figure 22.1.
Figure 22.1 Completed application

To be a drag source, your view must conform to the NSDraggingSource protocol.


Only one method is required,
draggingSession(_:sourceOperationMaskForDraggingContext:). This method
declares what operations the view is willing to participate in as a source. In
DieView.swift, mark DieView as conforming to the protocol:
class DieView: NSView {
class DieView: NSView, NSDraggingSource {
Toward the bottom of the class, add a new section mark and implement the
method:
// MARK: - Drag Source

func draggingSession(session: NSDraggingSession,


sourceOperationMaskForDraggingContext context:
NSDraggingContext)

-> NSDragOperation {
return .Copy
}
When a drag is started, this method is automatically called twice: once with
context as .WithinApplication, which determines what operations it is willing
to participate in for destinations within your application; and a second time, with
context as .OutsideApplication, which determines what operations it is
willing to participate in for destinations in other applications. In DieView there is
no distinction between the two, so the return value is the same in both cases.

Starting a drag
To start a drag operation, you will use a method on NSView:
func beginDraggingSessionWithItems(items: [AnyObject],
event: NSEvent,
source:
NSDraggingSource) -> NSDraggingSession
This method takes an array of NSDraggingItem objects as its first argument. The
dragging item knows how to write itself to the pasteboard, the location it is being
dragged from, as well as how to provide an image for the item being dragged.
The event argument will be the event that was supplied to mouseDown(_:). The
source will generally be the view itself.

Add a property to DieView to hold the mouseDown event.


var mouseDownEvent: NSEvent?
Locate mouseDown(_:) and store the event in mouseDownEvent:
override func mouseDown(theEvent: NSEvent) {
println("mouseDown")
mouseDownEvent = theEvent

let dieFrame =
metricsForSize(bounds.size).dieFrame
let pointInView =
convertPoint(theEvent.locationInWindow, fromView: nil)
pressed = dieFrame.contains(pointInView)
}
You will also need to create an image to drag. You can draw on an image just as
you can on a view. Recall from Chapter 17 that drawing in Cocoa requires
configuring the graphics context for the drawing destination. Therefore, in order
to draw into an image you will need an API that configures the drawing context
so that drawing appears on the image and not the screen.
NSImage provides an initializer for this purpose, which takes a closure as an
argument. When the image is asked to draw itself, the closure will be evaluated.
This means that the image can be appropriately sized for the particular drawing
context. As a simple example, here is how to create a solid green image:
let imageSize = ...
let greenImage = NSImage(size: imageSize, flipped:
false) { (imageBounds) in
NSColor.greenColor.set()
NSBezierPath.fillRect(imageBounds)
}
The most appropriate place to begin the dragging session is in mouseDragged(_:)
in DieView. Add the following code:
override func mouseDragged(theEvent: NSEvent) {
println("mouseDragged location: \
(theEvent.locationInWindow)")
let downPoint = mouseDownEvent!.locationInWindow
let dragPoint = theEvent.locationInWindow

let distanceDragged = hypot(downPoint.x -


dragPoint.x, downPoint.y - dragPoint.y)
if distanceDragged < 3 {
return
}

pressed = false

if let intValue = intValue {


let imageSize = bounds.size
let image = NSImage(size: imageSize, flipped:
false) { (imageBounds) in
self.drawDieWithSize(imageBounds.size)
return true
}

let draggingFrameOrigin =
convertPoint(downPoint, fromView: nil)
let draggingFrame = NSRect(origin:
draggingFrameOrigin, size: imageSize)
.rectByOffsetting(dx: -
imageSize.width/2, dy: -imageSize.height/2)

let item = NSDraggingItem(pasteboardWriter: "\


(intValue)")
item.draggingFrame = draggingFrame
item.imageComponentsProvider = {
let component =
NSDraggingImageComponent(key:

NSDraggingImageComponentIconKey)
component.contents = image
component.frame = NSRect(origin:
NSPoint(), size: imageSize)
return [component]
}

beginDraggingSessionWithItems([item], event:
mouseDownEvent!, source: self)
}
}
First the magnitude of the drag is checked using hypot. This helps to prevent
accidental drags. Next, if the die has a non-nil intValue, the NSImage is created,
the dragging frame calculated, and the NSDraggingItem created as well. Note that
the NSDraggingItem has a closure property, imageComponentsProvider. This API
allows you to provide both image and text (label) components that will be drawn
by the system to represent the item as it is being dragged.
That is it. Run the application. You should be able to drag a die face off the view
and into any text editor, resulting in a number. (Try dragging it into Xcode.)
After the drop
When a drop occurs, the drag source will be notified if you implement the
following method:
func draggingSession(session: NSDraggingSession,
endedAtPoint screenPoint: NSPoint,
operation: NSDragOperation)
For example, to make it possible to clear the DieView by dragging the die face to
the trash can in the dock, update your implementation of this method:
func draggingSession(session: NSDraggingSession,
sourceOperationMaskForDraggingContext context:
NSDraggingContext)

-> NSDragOperation {
return .Copy
return .Copy | .Delete
}
Then implement draggedImage(_:endedAt:operation:)
func draggingSession(session: NSDraggingSession,
endedAtPoint screenPoint: NSPoint,
operation: NSDragOperation) {
if operation == .Delete {
intValue = nil
}
}
Run the application. Drag a die face into the trash. It should disappear from the
view.
Make DieView a Drag Destination
There are several parts to being a drag destination. First, you need to declare
your view a destination for the dragging of certain types. NSView has a method
for this purpose:
func registerForDraggedTypes(newTypes: [AnyObject])
You typically call this method in your initWithFrame: method.
Then you need to implement six methods. (Yes, six!) All six methods have the
same argument: an object that conforms to the NSDraggingInfo protocol. That
object provides the dragging pasteboard. The six methods are invoked as
follows:
1. As the image is dragged into the destination, the destination is sent a
draggingEntered(_:) message. Often, the destination view updates its
appearance. For example, it might highlight itself.
2. While the image remains within the destination, a series of
draggingUpdated(_:) messages are sent. Implementing
draggingUpdated(_:) is optional.

3. If the image is dragged outside the destination, draggingExited(_:) is sent.


4. If the image is released on the destination, either it slides back to its source
(and breaks the sequence) or a prepareForDragOperation(_:) message is
sent to the destination, depending on the value returned by the most recent
invocation of draggingEntered(_:) (or draggingUpdated(_:) if the view
implemented it).
5. If the prepareForDragOperation(_:) message returns true, then a
performDragOperation(_:) message is sent. This is typically where the
application reads data off the pasteboard.
6. Finally, if performDragOperation(_:) returned true,
concludeDragOperation(_:) is sent. The appearance may change. This is
where you might generate the big gulping sound that implies a successful
drop.
registerForDraggedTypes(_:)
Next, you will add a call to registerForDraggedTypes(_:) in DieView’s
initializer. However, with NSView you cannot simply override one initializer; you
must override both init(frame:) and init(coder:). In order to avoid duplicating
code you will add a third method, commonInit(). Toward the top of DieView, but
below the properties, add the following:
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}

func commonInit() {

self.registerForDraggedTypes([NSPasteboardTypeString])
}

Add highlighting
To signal the user that the drop is acceptable, your view will highlight itself. Add
a highlightForDragging property to DieView:
var highlightForDragging: Bool = false {
didSet {
needsDisplay = true
}
}
Now you are going to add highlighting to drawRect(_:). The class NSGradient
makes it easy to draw with gradients. In this case, you are going to draw a radial
gradient: white in the center and fading into the backgroundColor.
override func drawRect(dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGrayColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)

drawDieWithSize(bounds.size)
if highlightForDragging {
let gradient = NSGradient(startingColor:
NSColor.whiteColor(),
endingColor:
backgroundColor)
gradient.drawInRect(bounds,
relativeCenterPosition: NSZeroPoint)
}
else {
drawDieWithSize(bounds.size)
}
}

Implement the dragging destination methods


Add the following methods to DieView:
// MARK: - Drag Destination

override func draggingEntered(sender:


NSDraggingInfo) -> NSDragOperation {
if sender.draggingSource() === self {
return .None
}
highlightForDragging = true
return sender.draggingSourceOperationMask()
}

override func draggingExited(sender:


NSDraggingInfo?) {
highlightForDragging = false
}

override func prepareForDragOperation(sender:


NSDraggingInfo) -> Bool {
return true
}

override func performDragOperation(sender:


NSDraggingInfo) -> Bool {
let ok =
readFromPasteboard(sender.draggingPasteboard())
return ok
}

override func concludeDragOperation(sender:


NSDraggingInfo?) {
highlightForDragging = false
}
draggingEntered(_:) is written to disallow the drag if the dragging source is the
same as the destination (by returning .None).
Run the application. Note that you can drag die faces between the views and to
other applications. You can also drag strings containing numbers onto the
DieViews.
For the More Curious: Operation Mask
For some apps, the negotiation of what operation will occur when the user drops
can be quite complicated. After all, the source advertises its willingness to
participate in some kinds of operations through
draggingSession(_:sourceOperationMaskForDraggingContext:). The user may
also indicate preferences by holding down the Control, Option, or Command
key. It is the job of the destination to determine what happens.
The dragging info object will do most of the work for you. It will get the
source’s advertised operation mask and filter it, depending on what modifier
keys the user holds down. To see this, implement draggingUpdated(_:) and log
out the dragging info’s operation mask:
override func draggingUpdated(sender: NSDraggingInfo)
-> NSDragOperation {
println("operation mask = \
(sender.draggingSourceOperationMask().rawValue)")
if sender.draggingSource() === self {
return .None
}
return .Copy | .Delete
}
Run the application. Try dragging text from different sources and holding down
different modifier keys. Note what happens to the mask and the cursor.
23
NSTimer
As you learned in Chapter 5, controls have a target and an action. The target is a
reference to another object; the action is a selector (essentially the name of the
method to be called on the target). When a button is clicked, the action method is
called on the target.
Timers work in a similar way. A timer is an object that has a target, a selector,
and an interval, which is given in seconds (Figure 23.1). After the interval has
elapsed, the action is called on the target. The sender in this case is the timer
itself. The timer can also be set to send the message repeatedly.
Figure 23.1 NSTimer

To play with timers, you will modify the Dice application to “roll” in an animated
fashion, showing the various faces of the die before settling on one.
Note that this is a rather primitive form of animation; you will learn more
sophisticated animation APIs when we cover Core Animation in Chapter 33.
NSTimer-based Animation
Presently, when the user double-clicks on a DieView, the class’s implementation
of mouseUp() calls randomize() to randomly change its intValue. You will
change mouseUp() to call a new function, roll(), which will start an animation
by creating an instance of NSTimer. The timer will be repeating, randomizing the
die value each time it fires. Once a suitable number of randomizations have
occurred the timer will be stopped.
Here is the API to create a scheduled timer:
class func scheduledTimerWithTimeInterval(ti:
NSTimeInterval,
target aTarget: AnyObject,
selector aSelector: Selector,
userInfo: AnyObject?,
repeats yesOrNo: Bool) -> NSTimer
Notice that this is a class method on NSTimer. The initializers for NSTimer create a
timer that is not scheduled – and a timer does not fire unless it is scheduled.
Thus, scheduledTimerWithTimeInterval is easier to use an initializer, because
you do not need to separately schedule the timer.
Start by adding a property to DieView:
var rollsRemaining: Int = 0
This property will help determine when to stop animating the die. In order to
stop the timer, you will call invalidate() on it.
Next, implement the roll() method, which starts the timer, and rollTick(_:),
the timer’s action method.
func roll() {
rollsRemaining = 10
NSTimer.scheduledTimerWithTimeInterval(0.15,
target: self,
selector:
Selector("rollTick:"),
userInfo: nil,
repeats: true)
window?.makeFirstResponder(nil)
}

func rollTick(sender: NSTimer) {


let lastIntValue = intValue
while intValue == lastIntValue {
randomize()
}
rollsRemaining--
if rollsRemaining == 0 {
sender.invalidate()
window?.makeFirstResponder(self)
}
}
In roll() you are clearing the window’s first responder in order to remove the
blue first responder glow while the animation takes place. Once the animation is
complete the first responder is restored. Note the while loop in rollTick(_:);
this makes the animation look cooler by ensuring that the die changes
consistently as it rolls.
Finally, in mouseUp(_:), call roll() to get the new animated behavior:
override func mouseUp(theEvent: NSEvent) {
println("mouseUp clickCount: \
(theEvent.clickCount)")
if theEvent.clickCount == 2 {
randomize()
roll()
}
pressed = false
}
Run your application and double-click on the DieViews to roll ’em.
How Timers Work
Timers rely on the run loop to fire. When a timer is scheduled it calculates the
next time that it should fire and asks the run loop to inform it when that time has
arrived. NSTimer is fine for general purpose delays but is not suitable for cases
where events must be fired with high resolution. It is documented to have a
resolution on the order of 50-100ms.
For cases where timer resolution is not critical, NSTimer supports timer
coalescing. By setting the tolerance property you can allow the system to
massage the firing of all of the timers to happen at once, resulting in significant
improvements in energy usage.
NSTimer and Strong/Weak References
When a timer is scheduled, the run loop keeps a strong reference to the timer.
We would say that the run loop “owns” the timer. Thus it is unnecessary to keep
a strong reference to it yourself.
In some cases, such as your Dice application, it is unnecessary to keep any sort of
reference at all. Generally speaking NSTimer references are only needed in order
to invalidate the timer or to determine whether it is still running. In this
application, since the timer is invalidated when it is fired for the last time, and
because the action method’s sender is the timer, no timer property is needed.
However, at times you will need to invalidate a timer before it fires, and in that
case you will need a reference. We recommend an optional weak reference:
weak var timer: NSTimer?
Weak because the run loop owns the timer, and optional because weak
references in Swift must be optionals.
Finally, it is important to note that while NSTimer is quite a bit like controls, in
that it has a target and action, it is unlike them in that it keeps a strong reference
to its target. This means that while a timer is scheduled it will keep its target in
memory. This is a common memory leak pitfall when using timers; you will
need to take care to invalidate your timers at the appropriate times.
For the More Curious: NSRunLoop
NSRunLoop is an object that waits. It waits for events to arrive and then forwards
them to NSApplication. It waits for timer events to arrive and then forwards them
to NSTimer. You can even attach a network socket to the run loop, and it will wait
for data to arrive on that socket.
24
Sheets
Sheets are a common interface pattern in OS X for situations where you need to
collect input from the user and you want to associate that input with a particular
window. A common example of a sheet is the document save panel. Sheets are
windows that are presented from another window. The sheet comes down over
the window, blocking user input to the presenting window until it is dismissed.
Typically, you will create a new window controller for each sheet.
NSWindow has several methods used to present and dismiss sheets. You call these
methods on the window that is presenting the sheet. The sheet window itself is
specified as a parameter.
func beginSheet(sheetWindow: NSWindow,
completionHandler: ((NSModalResponse) ->
Void)? )
func beginCriticalSheet(sheetWindow: NSWindow,
completionHandler:
((NSModalResponse) -> Void)? )
func endSheet(sheetWindow: NSWindow,
returnCode: NSModalResponse)
Adding a Sheet
In this chapter you will learn about sheets by adding one to the Dice application.
Users will click on a menu item to call up the sheet, shown in Figure 24.1, which
will allow them to configure one of the dice in the window. Clicking OK or Cancel
will dismiss the sheet.
Figure 24.1 Completed application

In order to get data in and out of the window being presented as a sheet, you will
create an NSWindowController subclass, along with a XIB to provide the window
itself. Bindings will make the task of tying the sheet’s contents to the window
controller fairly simple, and you will use target/action to react to the button
clicks.
Figure 24.2 Object diagram
Create the Window Controller
Create a new NSWindowController subclass called
ConfigurationWindowController along with a corresponding XIB. Navigate to
File → New → File.... In the presented sheet, select Source from the OS X section and
select Cocoa Class from the resulting options. Click Next. Enter
ConfigurationWindowController in the Class field, and enter
NSWindowController in the Subclass of field. Ensure that the checkbox labeled Also
create XIB file for user interface is checked, and that Swift is the selected Language.

In ConfigurationWindowController.swift, add properties that you will bind the


UI to, override windowNibName to load the proper NIB, and stub out the button
action methods:
import Cocoa

class ConfigurationWindowController:
NSWindowController {
private dynamic var color: NSColor =
NSColor.whiteColor()
private dynamic var rolls: Int = 10

override var windowNibName: String {


return "ConfigurationWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()

// Implement this method to handle post-nib-


load initialization.
}

@IBAction func okayButtonClicked(button: NSButton)


{
println("OK clicked")
}
@IBAction func cancelButtonClicked(button:
NSButton) {
println("Cancel clicked")
}
}
Why are color and rolls marked private? As you will see, they are
implementation details of ConfigurationWindowController. You declared them
dynamic so that they will work properly with bindings.
When you present this window as a sheet you will need to provide the current
color and number of rolls to populate the sheet. You will also want to read those
values out if the user clicks OK. Instead of making color and rolls accessible,
you will create a structure to encapsulate the state shown and work with it when
populating the sheet. For such a simple sheet it may seem like over-engineering,
but it is a handy pattern to practice now and use later when you have a much
more sophisticated sheet to present.
Declare the DieConfiguration structure in
ConfigurationWindowController.swift and add a computed property to serve as
the interface for the data in the sheet.
import Cocoa

struct DieConfiguration {
let color: NSColor
let rolls: Int

init(color: NSColor, rolls: Int) {


self.color = color
self.rolls = max(rolls, 1)
}
}

class ConfigurationWindowController:
NSWindowController {

var configuration: DieConfiguration {


set {
color = newValue.color
rolls = newValue.rolls
}
get {
return DieConfiguration(color: color,
rolls: rolls)
}
}

private dynamic var color: NSColor =


NSColor.whiteColor()
Notice that a custom initializer has been provided for DieConfiguration. In
general, when defining a struct, as you saw in Chapter 3, you do not need to
provide a custom initializer – Swift will generate one for you. In this case,
however, the custom initializer does more than simply initializing the struct’s
fields: any DieConfiguration created using this initializer will have a rolls
property which is greater than zero.
Other classes which use ConfigurationWindowController will now be able to
read and write the dice attributes displayed in the configuration sheet using the
configuration property.
Set Up the Menu Item
Open MainWindowController.swift and, near the bottom of the definition of
MainWindowController, add a new section Actions and a stub for an @IBAction.
// MARK: - Actions

@IBAction func showDieConfiguration(sender:


AnyObject?) {
println("Configuration menu item clicked")
}
Open MainMenu.xib and add a menu item to the main menu for your application
by dragging it out of the object library (Figure 24.3).
Figure 24.3 Adding a menu item

Change the title of the menu item to “Configure Die”. Control-drag from the
menu item to the First Responder. Set the action to be showDieConfiguration:
(Figure 24.4).
Figure 24.4 Connecting the menu item
Lay Out the Interface
Open ConfigurationWindowController.xib, select the window, and switch to
attributes inspector. Uncheck Resize to disable resizing for the window. Also
uncheck Visible At launch.
That last step is an important step to remember when working with sheets: if you
do not uncheck Visible At Launch, the window will appear before it is presented as a
sheet, which will make your application look buggy and broken.
Figure 24.5 Inspecting new window

Drag out onto the window two labels, two buttons, a color well, a text field, and
a stepper. Arrange everything as in Figure 24.6.
Figure 24.6 ConfigurationWindowController controls arranged

Bind the Value of the color well to the File's Owner’s color as shown in Figure 24.7.
Figure 24.7 Binding the color well’s Value to File's Owner’s color

Next bind the text field’s Value to File's Owner’s rolls (Figure 24.8).
Figure 24.8 Binding the text field’s Value to File's Owner’s rolls

Similarly, bind the stepper’s Value to File's Owner’s rolls.


When the user clicks the OK button, a message should be called on
ConfigurationWindowController so the window controller can dismiss the sheet.
Control-drag from the button to the File's Owner and choose okayButtonClicked: as
the action (Figure 24.9).
Figure 24.9 Setting the target of the OK button
Similarly, set the Cancel button’s target to be the File's Owner and choose
cancelButtonClicked: as the action.
Configuring the Die Views
Your goal here is to have the configuration settings that the user specifies in the
sheet be reflected in the die view. To do that, DieView needs to expose properties
which can be configured.
Open DieView.swift. First, add the properties which users of the class can
configure:
var highlightForDragging: Bool = false {
didSet {
needsDisplay = true
}
}

var color: NSColor = NSColor.whiteColor() {


didSet {
needsDisplay = true
}
}

var numberOfTimesToRoll: Int = 10

var mouseDownEvent: NSEvent?


Now MainWindowController can set the color and numberOfTimesToRoll on each
DieView. But setting these properties does not do anything yet.

Besides giving color a default value, there are two other uses of
NSColor.whiteColor(). You need to replace those uses with uses of the color
property.
First replace the use in drawRect(_:):
override func drawRect(dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGrayColor()
backgroundColor.set()
NSBezierPath.fillRect(bounds)

if highlightForDragging {
let gradient = NSGradient(startingColor:
NSColor.whiteColor(),
endingColor:
backgroundColor)
let gradient = NSGradient(startingColor:
color,
endingColor:
backgroundColor)
gradient.drawInRect(bounds,
relativeCenterPosition: NSZeroPoint)
}
else {
drawDieWithSize(bounds.size)
}
}
Next, replace the use in drawDieWithSize(_:):
shadow.set()

NSColor.whiteColor().set()
color.set()
NSBezierPath(roundedRect: dieFrame,
xRadius: cornerRadius, yRadius:
cornerRadius).fill()
Finally, change the implementation of roll() to use numberOfTimesToRoll:
func roll() {
rollsRemaining = 10
rollsRemaining = numberOfTimesToRoll
NSTimer.scheduledTimerWithTimeInterval(0.15,
target: self,
selector:
Selector("rollTick:"),
userInfo: nil,
repeats: true)
window?.makeFirstResponder(nil)
}
Present the Sheet
When the user clicks the Configure Die menu item, the sheet should be presented in
order to configure the currently selected DieView.
How will you determine which DieView to change? Because DieView accepts first
responder, you can easily determine which, if any die view has the focus (and
blue highlight) by checking the window’s firstResponder.
Open MainWindowController.swift and fill out the stub for
showDieConfiguration(_:).
// MARK - Actions

var configurationWindowController:
ConfigurationWindowController?

@IBAction func showDieConfiguration(sender:


AnyObject?) {
println("Configuration menu item clicked")
if let window = window, let dieView =
window.firstResponder as? DieView {

// Create and configure the window controller


to present as a sheet:
let windowController =
ConfigurationWindowController()
windowController.configuration =
DieConfiguration(color: dieView.color,

rolls: dieView.numberOfTimesToRoll)

window.beginSheet(windowController.window!,
completionHandler: { response in
// The sheet has finished. Did the user
click 'OK'?
if response == NSModalResponseOK {
let configuration =
self.configurationWindowController!.configuration

dieView.color = configuration.color
dieView.numberOfTimesToRoll =
configuration.rolls
}
// All done with the window controller.
self.configurationWindowController = nil
})
configurationWindowController =
windowController
}
}
Note that you are presenting the ConfigurationWindowController’s window on
top of the MainWindowController’s window.
The sheet should end when the user clicks on either button in the configuration
window. Fill out the stubs for okayButtonClicked(_:) and
cancelButtonClicked(_:) that you added to ConfigurationWindowController:
@IBAction func okayButtonClicked(button: NSButton) {
println("OK clicked")
window?.endEditingFor(nil)
dismissWithModalResponse(NSModalResponseOK)
}

@IBAction func cancelButtonClicked(button:


NSButton) {
println("Cancel clicked")

dismissWithModalResponse(NSModalResponseCancel)
}

func dismissWithModalResponse(response:
NSModalResponse) {
window!.sheetParent!.endSheet(window!,
returnCode: response)
}
When either button is clicked, dismissWithModalResponse(_:) will be called,
which will trigger MainWindowController to dismiss the sheet. Note, the
ConfigurationWindowController’s window’s sheetParent is the
MainWindowController’s window. Remember, the value for returnCode that is
passed to endSheet(_:returnCode:) is the value that the completion handler
(which you provided to beginSheet(_:completionHandler:)) will be called with.
Notice that okayButtonClicked(_:) calls endEditingFor(_:) on the window. By
default, Cocoa bindings do not update until editing ends for the control. In the
case of a text field, this is when it is no longer first responder, such as when the
user clicks on a different text field. In order to make sure that the user’s changes
are applied (and carried through to the properties on the window controller, in
this case rolls), you must call endEditingFor(_:).
Run your application and click on one of the dice. Bring up the sheet, adjust the
die, and dismiss the sheet. If none of the dice are first responder, the sheet will
not be presented because the optional binding (if let) and conditional casting
(as? DieView) will fail.
Modal Windows
When a sheet is active, the user is prevented from interacting with the window
from which it is presented. Modal windows are similar, except that they are
presented independently of the other windows and they block user input for the
rest of the application.
To present a window modally, use the runModalForWindow(_:) method on
NSApplication:
func showModalWindow() {
let windowController = CriticalWindowController()
let app = NSApplication.sharedApplication()
let returnCode =
app.runModalForWindow(windowController.window!)
if returnCode ==
CriticalWindowController.returnAccept {
...
}
}
This method will block, and only events destined for the modal window will be
processed; clicking on the menu and other windows will do nothing. When you
are ready to dismiss the modal window, call stopModalWithCode(_:), also on
NSApplication object, with a return code indicating the result of the modal
window:
class CriticalWindowController: NSWindowController {

static let returnAccept = 1
...

@IBAction func dismiss(sender: NSButton) {
let app = NSApplication.sharedApplication()

app.stopModalWithCode(CriticalWindowController.returnAccept)

}
}
}
At that point, runModalForWindow(_:) will return and the calling code will
continue execution. The value returned will be the return code passed to
stopModalWithCode(_:).
Encapsulating Presentation APIs
Cocoa’s sheet and modal window presentation APIs are very flexible: you can
supply your own Int as the modal response or return code. Unfortunately this
leaves room for error and confusion since it can be unclear how to map an Int
back to something meaningful. (Did the user click Accept or Cancel?) By
tucking the sheet or modal window API usage into your window controller you
can add some safety and grace to your use of them.
Consider the following code for the fictional CriticalWindowController:
class CriticalWindowController: NSWindowController {

enum ModalResult: Int {
case Accept
case Cancel
}

func runModal() -> ModalResult {
let app = NSApplication.sharedApplication()
let returnCode =
app.runModalForWindow(window!)
if let result = ModalResult(rawValue:
returnCode) {
return result
}
else {
fatalError("Failed to map \(returnCode) to
ModalResult")
}
}

@IBAction func dismiss(sender: NSButton) {
let app = NSApplication.sharedApplication()

app.stopModalWithCode(ModalResult.Accept.rawValue)
}
}
}
Now, presenting a CriticalWindowController modally is much more clear, with
no guesswork about the meaning of the Int return code:
func showModalWindow() {
let windowController = CriticalWindowController()
switch windowController.runModal() {
case .Accept:
...
case .Cancel:
break
}
}
You can use the same technique when presenting sheets.
Challenge: Encapsulate Sheet Presentation
In Dice, add a method to ConfigurationWindowController:
presentAsSheetOnWindow(_:completionHandler:). The method’s parameter
should be the window the configuration sheet should be displayed on, and the
completion handler will be a closure of type (DieConfiguration?)->(Void). If
the user clicks OK, the DieConfiguration will be passed to the completion
handler; otherwise it will be nil to signal that nothing changed.
Here is how your method will look when it is called:
windowController.presentAsSheetOnWindow(window,
completionHandler: {
configuration in
if let configuration = configuration {
dieView.color = configuration.color
dieView.numberOfTimesToRoll =
configuration.rolls
}
})
Challenge: Add Menu Item Validation
Add menu item validation to disable the Configure Die menu item if no DieView is
presently first responder. If you need to, refer back to the section in Chapter 21.
One hint: the is operator returns true if the left operand is of the same type as
the right operand.
25
Auto Layout
Have you tried resizing the RaiseMan window? The table view and buttons are
steadfast in the face of your pointer. While their confidence is inspiring, as a user
you want the window and its contents to match your needs. As a developer you
want to build an app whose interface gracefully responds to resizing,
accommodating the smallest and the largest of layouts without looking, well,
broken.
As you learned in Chapter 17, views are positioned by their frame, which sets the
view’s position within its superview. By default, when you place a view in Xcode,
the view’s frame is static.
You could change your application’s layout dynamically by observing window
resizing notifications and programmatically reposition your views by setting
their frames, but that would be very tedious. Instead, you will use Auto Layout.
What is Auto Layout?
Auto Layout uses constraints to determine where views within a window should
be positioned. Each view will have several constraints; those constraints describe
how the view should be positioned relative to other views or the window itself,
as well as the width and height of the view. For example:
The left edge of the scroll view should be the standard distance (20 points)
from the left edge of the window.
The Add Employee and Remove buttons should always be the same width.
The left edge of the Remove button should always match the left edge of the
Add Employee button.

The width of the scroll view should always be at least 200 points.

In fact, constraints are nothing more than mathematical equations. When it


comes time to lay out the window, the Auto Layout system will solve all of the
equations and the result is the frame of each view in the window.
As you will see later in the chapter, constraints can be added programmatically,
but it is often easier to create them in Interface Builder so that they will be stored in
the XIB file alongside the view objects.
Adding Constraints to RaiseMan
In this chapter you will use Auto Layout in RaiseMan to allow the table view to
resize with the window while the buttons stay fixed to the right side of the
window.
In your RaiseMan project, open Document.xib. Right now, there are no constraints
in this XIB and it looks like Figure 25.1.
Figure 25.1 RaiseMan unconstrained

Constraints from subview to superview


First you will add constraints for each of the three views – the scroll view which
contains the table view, the Add Employee button, and the Remove button – to the
content view of the window. First, select the scroll view and click the icon
in the Auto Layout control at the bottom right of the canvas to reveal the Add New
Constraints popover (Figure 25.2).

Start by adding a constraint from the bottom of the scroll view to the bottom of
the window: In the top section of the popover, select the disclosure arrow for the
bottom position constraint and select Use Standard Value as in Figure 25.2.
Figure 25.2 Adding the first constraint
Once you have told Interface Builder to use the Standard Value for the constraint from
the bottom of the scroll view to the bottom of the window, it toggles the I-beam
corresponding to the field enabled as in Figure 25.3.
Figure 25.3 The first constraint is ready to be added

Repeat this process for the left and top constraints of the scroll view, but not the
right. Your popover should look like Figure 25.4.
Figure 25.4 The three constraints are ready to be added
Click the Add 3 Constraints button at the bottom of the popover. The three
constraints that you added are now shown in the window on the canvas I-beams
extending from the scroll view to the edges of the window (Figure 25.5). A blue
I-beam indicates that the constraint is fine, while orange indicates that the
constraint is ambiguous. In Figure 25.5 the left I-beam is orange because Auto
Layout does not know how wide the scroll view should be. You will fix this
shortly.
Figure 25.5 Superview constraints added to the scroll view

In the canvas, select the vertical I-beam below the scroll view and open the
attributes inspector (Figure 25.6). The type of this constraint is shown at the top
of the inspector: Vertical Space Constraint. Below it are the two items that are being
constrained and information about their relationship. This tells you that the
vertical spacing between the bottom of the scroll view and its superview are
constrained to be equal to the standard spacing. In other words, it is pinned to the
bottom of the view. A priority of 1,000 indicates that this constraint is required.
Figure 25.6 Vertical space constraint attributes

If you planned to add constraints at runtime you might add a placeholder, design
time constraints, and check Remove at build time.
Try entering a value into the Constant text field (you will have to press Return to
end editing). Notice that the scroll view moves accordingly. Drag the scroll view
around and observe how Interface Builder changes how the constraints are displayed:
it is demonstrating how the layout of the scroll view in the XIB fails to match up
with the constraints, and how the layout will be altered when the app runs!
Before we go on, put the scroll view back in its place on the left hand side of the
window: Select the vertical I-beam below the scroll view again, switch to the
attributes inspector, and set the Constant back to Standard, using the disclosure
arrow.
Let’s finish by setting up constraints between the Add Employee button and the
content view of the window. Select the Add Employee button, click to summon
the Add New Constraints popover, and add two standard constraints: one on the top
and one on the right (Figure 25.7).
Figure 25.7 Adding constraints between the Add Employee button and its
superview
While you have not yet finished adding constraints to RaiseMan, you are done
adding constraints between the top-level controls and their superview, the
window’s content view (Figure 25.8). You might be wondering about the Remove
button. In the next section, you will add constraints between it and other controls
in the window. As you will see there, it is not necessary to constrain it to its
superview.
Figure 25.8 All superview constraints added

Next, you will constrain the text fields embedded in the table view to be
appropriately hugging their superviews, the table cell views. Select the text field
within the Name column using either the document outline or the view hierarchy
popover.
Click . In the Add New Constraints popover, click on each of the four of the I-
beams in turn to constrain the text field to its superview on all four sides. Make
sure that the top and bottom distances are both 0 and that the left and right
distances are both 2. Click Add 4 Constraints (Figure 25.9).
Figure 25.9 Adding superview constraints to Name text field
Repeat the same process for the text field inside the Raise column.
These constraints keep the edges of the text fields close to the bounds of their
containing table cell views. Later, when your table view is resizing, the columns
will resize, and the table cell views will resize accordingly. These constraints
make the text fields grow and shrink with their table cell views.
Run the app. Try resizing the window. When you resize the window, the views
adapt to the changes… somewhat. Let’s take stock of what is working:
The scroll view’s height increases and decreases with the window’s so that
its top and bottom edges are always the standard distance from the top and
bottom of the window, respectively.
The Add Employee button stays in the top right of the window so its top and
right edges are always the standard distance from the top and right of the
window.
A few things are not working as you would probably like, however:
The scroll view’s width does not change.
The Add Employee button slides beneath the scroll view when the window gets
too skinny.
The Remove button does not move.
It is as if each of the views does not know that the others are there. Actually, that
is exactly the problem: there are no constraints between the sibling subviews.
Adding constraints between the siblings will enable them to respect each other’s
space.

Constraints between siblings


You will start by constraining the Remove button to its siblings using the now-
familiar Add New Constraints popover.
Select the Remove button, click to open the popover, and add standard top
and left constraints, as shown in Figure 25.10.
Figure 25.10 Adding neighbor constraints to the Remove button

You can also add constraints between views by Control-dragging. This can be
used to constrain horizontal and vertical spacing, depending on the direction of
used to constrain horizontal and vertical spacing, depending on the direction of
the drag. It can also be used to set views to have equal widths or heights.
To constrain the buttons to have the same width, Control-drag from the Remove
button to the Add Employee button and release the mouse. You will be presented
with a constraint type panel. Select Equal Widths (Figure 25.11).
Figure 25.11 Constraining the Remove button to be as wide as the Add
Employee button

Note: If you do not want to Control-drag to make this kind of constraint, you can
also use the Add New Constraints popover. To accomplish that, you would select the
two views you wanted to have equal widths, click , check the Equal Widths
checkbox, and click Add 1 Constraint.
Next, add the standard left constraint to the Add Employee button. Select the button,
click to present the Add New Constraints popover, select the I-bar to the left of
the view, and make the constraint’s constant be Standard by selecting Use Standard
Value from the button’s menu. Finally, add the constraint by clicking Add 1
Constraint. This will add a horizontal constraint between the Add Employee button and
the scroll view.
Up until this point, the document outline has been displaying an Auto Layout
warning indicator. The warning associated with this indicator is that the scroll
view’s clip view is misplaced. After pinning the left side of the Add Employee
button to the right side of the scroll view, you should see the Auto Layout
warning indicator in the document outline disappear. Interface Builder is recognizing
that the clip view is not misplaced. This is good news! Unfortunately it may
return: this is a long-running Xcode bug related to IB’s interaction with Auto
Layout. As long as you have the scroll view constrained, you will not need to
worry about a warning on the clip view.
Run the app and resize the window. You should now see the scroll view’s width
change as the window grows and shrinks. You should also see the buttons
behave in the way you would expect.

Size constraints
You have most of the resizing behavior now. However, the scroll view will
shrink almost to vanishing. To stop that from happening, you will give it a
minimum size using Auto Layout. This is a two-step process. You will start by
adding fixed width and height constraints. Then you will change the relation of
each from equal to greater than or equal.
Select the scroll view and open the Add New Constraints popover. Check the boxes
for Width and Height. The specific values are not important, but the height should
be greater than 100 and the width greater than 200. Click Add 2 Constraints
(Figure 25.12).
Figure 25.12 Adding height and width constraints to the scroll view
Currently, these constraints give the scroll view a fixed size. Instead, you want to
prevent the scroll view from getting smaller than it is shown in Interface Builder. To
do this, you need to make these width and height constraints require that the
view’s dimensions be greater than or equal to the constant, rather than strictly
equal.
Select the width constraint. One way to do this is to first select the scroll view
and then select the constraint that is drawn across its bottom. Alternatively, you
can select it from the document outline as shown in Figure 25.13.
Figure 25.13 Selecting the width constraint of the scroll view

With the width constraint selected, switch to the attributes inspector. Change the
Relation from Equal to Greater Than or Equal.

Repeat the same process for the scroll view’s height constraint.
Once you are done, Interface Builder will show you that the constraints now use the
greater-than-or-equal relation in two places. First, the greater-than-or-equal
symbol (≥) is displayed in the constraint’s listing in the document outline.
Second, when the constraints are visible in the canvas, the greater-than-or-equal
symbol will be drawn over their tops (Figure 25.14).
Figure 25.14 The two greater-than-or-equal size constraints on the scroll
view
Run the app and resize the window. You should now see the scroll view grow
and shrink appropriately. It will never get too small. And the text fields inside
the table view resize to fit the width of the column they are in. Pretty neat, right?
And you did all of this without writing any code.
Intrinsic Content Size
In the constraints that you have added, you at no point specified a minimum
width for the Add Employee button. However, when you try to shrink the width of
the window, the scroll view reaches its minimum width, but the Add Employee
button’s width stays the same! This is happening because Auto Layout is taking
the button’s intrinsic content size into consideration. In the case of a button, its
intrinsic content size is determined by its title text.
Auto Layout handles changes to intrinsic content size as well. If you changed the
Add Employee button’s title to something longer, the window would be laid out
again so that the button’s new title could be displayed fully. It would first shrink
the scroll view, and, if necessary, expand the window.
This is a pretty powerful feature. It makes the job of localizing your application
much easier, too. If you were localizing your English application for German,
you might have to make a number of text fields much wider, or do this sort of
size calculation yourself. Auto Layout handles the task for you.
Creating Layout Constraints
Programmatically
You have seen how constraints can be created in Interface Builder, but they can also
be added programmatically using the NSLayoutConstraint class. Above, you
created the constraint pinning the left side of the scroll view the standard
distance from the left side of its superview, the window’s content view, using the
Add New Constraints popover in IB. You would create an equivalent constraint
programmatically like this:
let scrollView: NSScrollView = ...
let superview = scrollView.superview!
let constraint = NSLayoutConstraint(item: scrollView,
attribute: .Leading,
relatedBy: .Equal,
toItem: superview,
attribute: .Leading,
multiplier: 1.0,
constant: 20.0)
superview.addConstraint(constraint)
This constraint specifies that the leading edge of the scroll view be 20 points
from the leading edge of the superview.
This raises another interesting aspect of Auto Layout: it allows you to express
your layout in terms of leading and trailing edges so that your layout works in
languages that do not read left-to-right like English. In English, the above
constraint would tie the left-hand side of the scroll view to the left-hand side of
its superview. In a right-to-left language, such as Arabic, however, the constraint
would tie the right-hand side of the scroll view to the right-hand side of its
superview.
The initializer
init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:) is
very precise, but it certainly does not help with keeping your code concise.
Fortunately, the Cocoa engineers came up with something more succinct: the
visual format language.
Auto Layout constraints can be added and removed as needed at runtime, but
you can also animate layout changes to achieve a smooth transition. See Apple’s
Auto Layout Guide; for more information.
Visual Format Language
Start a new Cocoa Application project and call it AutoLabelOut. Leave Document Based
Application unchecked.

Open AppDelegate.swift and add the following code to


applicationDidFinishLaunching(_:):
func applicationDidFinishLaunching(aNotification:
NSNotification) {
// Insert code here to initialize your
application

// Create the controls for the window:

// Create a label:
let label = NSTextField(frame:
NSRect.zeroRect)

label.translatesAutoresizingMaskIntoConstraints =
false
label.stringValue = "Label"
//Give this NSTextField the styling of a
label:
label.backgroundColor = NSColor.clearColor()
label.editable = false
label.selectable = false
label.bezeled = false

// Create a text field:


let textField = NSTextField(frame:
NSRect.zeroRect)

textField.translatesAutoresizingMaskIntoConstraints =
false

// Make the text field update the label's text


// when the text field's text changes:
textField.action =
Selector("takeStringValueFrom:")
textField.target = label

let superview = window.contentView as! NSView


superview.addSubview(label)
superview.addSubview(textField)

// Create the constraints between the


controls:
let horizontalConstraints =

NSLayoutConstraint.constraintsWithVisualFormat(
"|-[label]-
[textField(>=100)]-|",

options:.AlignAllBaseline,
metrics:nil,
views: ["label" :
label, "textField" : textField])

NSLayoutConstraint.activateConstraints(horizontalConstraints)

let verticalConstraints =

NSLayoutConstraint.constraintsWithVisualFormat("V:|-
[textField]-|",
options:.allZeros,
metrics:nil,
views: ["textField" : textField])

NSLayoutConstraint.activateConstraints(verticalConstraints)

}
Whew. Let’s discuss the code above. You start by programmatically creating a
label and text field and adding them to the window.
We will skip over translatesAutoresizingMaskIntoConstraints for now and
discuss it later in the chapter.
After that, the fun part: you create the constraints themselves using a class
method on NSLayoutConstraint,
constraintsWithVisualFormat(_:options:metrics:views:). The most
interesting parameters are the first and last: the visual format and the views
dictionary.
Run your new app. Enter text into the text field and press Enter. The label should
resize to be large enough to display all the text you entered.
The visual format language allows you to describe the layout as ASCII art. A
visual format string creates an array of constraints for the views along one axis,
horizontal or vertical. Let’s parse through the visual format string:
|-[label]-[textField(>=100)]-|
The pipes represent the edges of the superview, in this case the window’s
content view. The square brackets represent views, referenced by name
(remember the dictionary?). So you can surmise that this set of constraints will
keep the label and textField next to each other, while pinning their edges to the
window.
How far will they be from the window, and from each other? When a dash is
used, Auto Layout uses whatever the standard spacing is for that relationship,
which is different for view-window and view-view. If you wanted a specific
number of points between the two views, say 9, you could specify that:
[label]-9-[textField].
The final portion of this string ([textfield(>=100)]) means that the textField’s
width should be greater than or equal to 100 points. Rather than providing a
concrete width for the text field, you could also specify that it be at least as wide
as the label: |-[label]-[textField(>=label)]-|.
Let’s look quickly at the second visual format string, V:|-[textField]-|. The
V: prefix simply indicates that this format string positions objects vertically,
from top to bottom. The default axis is horizontal. So textField will be pinned
at the standard distance from the top and bottom of the window.
There is more to the visual format language. While it is a very powerful tool,
there are some relationships which cannot be expressed with it, such as aspect
ratios between views (e.g., specifying that one view be twice the width of
another).
init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:) is
necessary to create such constraints.
Does Not Compute, Part 1: Unsatisfiable
Constraints
Because constraints are expressed as mathematical expressions and Auto
Layout’s job is to solve the entire system together, you may sometimes run into
situations where Auto Layout cannot solve the system. Generally this will occur
if there are too many or too few constraints.
If there are too many constraints, or if the constraints conflict with each other,
we say that the constraints are unsatisfiable. If this happens, the Auto Layout
system will recognize it at runtime and print debug information to the console,
providing you with a list of the constraints which are in conflict. (If you added
your unsatisfiable constraints in Interface Builder, it would inform you of this
immediately.) Auto Layout will then adjust the priority of conflicting constraints
in order to lay out the view, which will probably result in your user interface
looking off-kilter.
To debug unsatisfiable constraints, review the console output and determine
which constraints are truly in conflict. You may find that two constraints cannot
coexist, or that by reducing a constraint’s priority you can resolve the conflict.
Let’s see an example of unsatisfiable constraints. In the AutoLabelOut project, set
translatesAutoresizingMaskIntoConstraints to true (this is the default):
// Create a label:
let label = NSTextField(frame:
NSRect.zeroRect)

label.translatesAutoresizingMaskIntoConstraints =
false

label.translatesAutoresizingMaskIntoConstraints = true
label.stringValue = "Label"
// Give this NSTextField the styling of a
label:
label.backgroundColor = NSColor.clearColor()
label.editable = false
label.selectable = false
label.bezeled = false

// Create a text field:


let textField = NSTextField(frame:
NSRect.zeroRect)

textField.translatesAutoresizingMaskIntoConstraints =
false

textField.translatesAutoresizingMaskIntoConstraints =
true
Run the program. The window will appear, but it will not be pretty. You should
see quite a bit of complaining in the console output:
Unable to simultaneously satisfy constraints:
(
"
<NSAutoresizingMaskLayoutConstraint:0x600000082710
h=--& v=--& H:|-(0)-
[NSTextField:0x608000182490]
(Names: '|':NSView:0x6000001201e0 )>",
"<NSLayoutConstraint:0x6080000803c0
H:|-(NSSpace(20))-[NSTextField:0x608000182490]
(Names: '|':NSView:0x6000001201e0 )>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6080000803c0
H:|-(NSSpace(20))-[NSTextField:0x608000182490]
(Names: '|':NSView:0x6000001201e0 )>
And so on.
By setting translatesAutoresizingMaskIntoConstraints to true you caused
additional constraints to be added (which we will discuss later in the chapter),
which conflicted with the constraints you defined later using the visual format
language. In this case you knowingly caused this problem, but because
translatesAutoresizingMaskIntoConstraints defaults to true, this can be an
easy mistake to make.
If this were a legitimate problem, reading the console log would be your best
tool for resolving the unsatisfiable constraints.
Before moving on, set translatesAutoresizingMaskIntoConstraints back to
false:
//Create a text field:
let textField = NSTextField(frame:
NSRect.zeroRect)

textField.translatesAutoresizingMaskIntoConstraints =
true

textField.translatesAutoresizingMaskIntoConstraints =
false
Does Not Compute, Part 2: Ambiguous
Layout
A layout is ambiguous when there are not enough constraints for Auto Layout to
determine the positions of all of the views. It is computationally expensive to
determine whether a layout is ambiguous, so you will not see console output as
with unsatisfiable constraints. However, Cocoa provides several tools for
addressing ambiguous layout in the form of debugging APIs.
The most conspicuous tool is NSWindow’s visualizeConstraints(_:). When you
call this method, Cocoa will decorate the window with visual representations of
the constraints. This is shown in Figure 25.15.
Figure 25.15 Visualizing constraints on a window

If the layout is ambiguous, you will also see an Exercise Ambiguity button. Clicking
this button will demonstrate how the constraints are ambiguous, which can help
with debugging.
To display the constraints for your window, add the following to the bottom of
applicationDidFinishLaunching(_:).
superview.addConstraints(verticalConstraints)


window.visualizeConstraints(superview.constraints)
A more subtle approach is to query a view directly:
superview.addConstraints(verticalConstraints)

window.visualizeConstraints(superview.constraints)

superview.updateConstraintsForSubtreeIfNeeded()
if superview.hasAmbiguousLayout {
superview.exerciseAmbiguityInLayout()
}
You have to call updateConstraintsForSubtreeIfNeeded because constraints do
not get set up immediately. This way, the constraints will be set up before
checking whether the view has an ambiguous layout.
For the More Curious: Autoresizing Masks
Before Auto Layout, Cocoa developers used autoresizing masks to handle
dynamic layout. An autoresizing mask defines how a view resizes in response to
its superview resizing. Unlike Auto Layout, autoresizing masks do not provide
any means of defining a view’s relationship with its sibling views, changing
content as with the intrinsic content size, or animating changes.
An autoresizing mask is made up of springs and struts. Springs allow the view to
resize in the horizontal and/or vertical directions. Struts preserve the distance
between the sides of the view and its superview. It is called a mask because it is
expressed as a bit field. Figure 25.16 and Figure 25.17 demonstrate two typical
configurations of springs and struts.
Figure 25.16 View is anchored to the upper left of its superview

Figure 25.17 View is anchored on all sides and will resize with its superview

When Auto Layout was introduced, autoresizing masks were reimplemented in


terms of Auto Layout constraints. In order to maintain compatibility for legacy
code, when a view is added to a hierarchy programmatically, by default
constraints for its autoresizing mask value are added as well. However, when
you are using Auto Layout you do not want those constraints, so you must turn
them off by setting translatesAutoresizingMaskIntoConstraints to false. It is
easy to forget to do this, in which case you will see unsatisfiable constraints
errors.
To use autoresizing masks in Interface Builder you must turn off Auto Layout on the
file inspector; this setting is made on a per-XIB file basis. This will remove all
constraints from the file.
So, should you use autoresizing masks or Auto Layout? We strongly suggest that
you use Auto Layout. Although autoresizing masks are much easier to learn and
suitable for simple layouts, we believe you and your users will be better served
by Auto Layout. There is a learning curve to Auto Layout but in the long term it
will be worth the effort.
Challenge: Add Vertical Constraints
RaiseMan may not handle vertical resizing perfectly if the minimum height you
imposed on the scroll view is too short. Add a constraint to make sure that the
bottom of the Remove button is always at least the standard distance from the
bottom of the window.
To make it easier to see that your solution works, try moving your Remove button
farther down from the Add Employee button.
Challenge: Add Constraints
Programmatically
In this chapter, you added constraints to RaiseMan using Interface Builder. Using the
NSLayoutConstraint class, add the same constraints, except for those inside the
table view. First, do this using the
init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:)
API. Second, do it again using the
constraintsWithVisualFormat(_:options:metrics:views:) API.

You will need to make sure that the constraints you are adding programmatically
do not just duplicate the constraints you already added in Interface Builder.
However, removing all constraints from IB will not work, because Interface Builder
will detect an ambiguous layout and add constraints to prevent it. Instead, select
each of the constraints in turn and, in the attributes inspector, check the Remove at
build time checkbox at the bottom, in the Placeholder section.
26
Localization and Bundles
If the application you create is useful, you will want to share it with all the
people of the world. Unfortunately, we do not all speak the same language.
Suppose that you wish to make your RaiseMan application available to French
speakers. We would say, “You need to localize RaiseMan for French speakers.”
If you are creating an application for the world, you should plan on localizing it
for at least the following languages: English, French, Spanish, German, Dutch,
Italian, Mandarin, and Japanese. Clearly, you do not want to have to rewrite the
entire app for each language. In fact, your goal should be to ensure that your
code will remain unchanged as you add support for new languages. That way, all
the nations of the world can use a single executable in peace and harmony.
Instead of creating multiple executables, you will localize resources and create
strings files. Inside the RaiseMan project directory, a Base.lproj directory holds all
the components of your app that are participating in localization: currently, it
holds XIB files and your document icon, but it can hold other resources as well.
When you localize your app for French speakers, Xcode will create a fr.lproj
directory. The XIBs, images, and sounds that Xcode puts in this directory will be
appropriate for French speakers. At runtime, the app will automatically use the
version of the resource appropriate to the user’s language preference.
Different Mechanisms for Localization
For each type of resource, there is one or more particular way that localization is
done.
User-facing string literals
These are strings that appear in your source files that will be presented to the
user. For example, the string that you set on a label, or the messageText you set
on an NSAlert.
Such strings are always localized using a strings file. Strings files are like
dictionaries. They allow you to associate a name or key for a string (for
example, “remove alert messageText”) with a string that should be used for a
particular language (for English, “Are you sure you want to remove these
employees?”).
You will take a closer look at a strings file later in the chapter.
General resources
This category includes all files that are bundled up as-is with your app: images,
sounds, video, even text documents that you display to the user such as a license.
Note, though, that this does not include XIBs.
A resources file is always localized by providing a language-specific version of
the file.
For example, suppose you have an image that embeds some text. To localize this
resource, you would need to generate an image with translated text for each of
the languages that you are localizing the app for. You would then put each of
those images in the .lproj folder corresponding to the language for which that
version of the image was localized.
XIB Files
XIB files can be localized in one of two ways, similar to the two localization
techniques above:
Localized XIB File
This approach is similar to what is done for general resource files.
Following this route, when you localize a XIB file, you create a copy of the
existing XIB file and change the strings in the file for the new localization.
During compilation, Xcode will create a NIB file for each localization and
copy that localized NIB into the app’s bundle. At runtime, your app will
use the NIB file localized for the language that the user has configured their
system with.
Strings File
This approach is similar to what is done for string literals.
When you follow this approach to localize a XIB into a new language, Xcode
generates a strings file for you which associates controls in the XIB with
the text that should appear in those controls. It is your task to edit those
generated strings files to contain the correct text for that language.
During compilation, Xcode will copy these strings files into your app bundle.
At runtime, when your app unpacks a NIB, it will query the corresponding
strings file for the user’s preferred language and update the controls in the
NIB to display the text from the strings file.
Localized XIB File
This approach is similar to what is done for general resource files.
Following this route, when you localize a XIB file, you create a copy of the
existing XIB file and change the strings in the file for the new localization.
During compilation, Xcode will create a NIB file for each localization and
copy that localized NIB into the app’s bundle. At runtime, your app will
use the NIB file localized for the language that the user has configured their
system with.
Strings File
This approach is similar to what is done for string literals.
When you follow this approach to localize a XIB into a new language, Xcode
generates a strings file for you which associates controls in the XIB with
the text that should appear in those controls. It is your task to edit those
generated strings files to contain the correct text for that language.
During compilation, Xcode will copy these strings files into your app bundle.
At runtime, when your app unpacks a NIB, it will query the corresponding
strings file for the user’s preferred language and update the controls in the
NIB to display the text from the strings file.
Each of the approaches to localizing XIB files has advantages and
disadvantages:
Creating a new XIB file for a language allows greater per-language tailoring.
You are free to rearrange controls to your heart’s content because you are using
an entirely different XIB. This can be useful with languages which tend to have
more characters, when you need to shuffle controls around to make space for the
longer text. (This tends to be unnecessary if you are using Auto Layout,
longer text. (This tends to be unnecessary if you are using Auto Layout,
however, thanks to intrinsic content size.) On the other hand, there is much more
maintenance involved. When you add a new button, for example, you will need
to manually add that button to each localized XIB.
In contrast, creating a strings file makes maintenance easier: you only need to
add a new entry to the strings file when adding a button. On the other hand, there
is much less room for customization, since you do not have a separate XIB to
arrange controls in.
When localizing a XIB, you can mix and match these approaches. For example,
you could use a strings file for localizing for French and use a localized XIB file
to localize for German.
In this chapter, you will use a couple of these techniques to localize RaiseMan for
French speakers. When you are done, the app will look something like
Figure 26.1:
Figure 26.1 Completed application
Localizing a XIB File
Before localizing any of RaiseMan’s resources to French, you need to tell Xcode that
you want the app to be localized for French.
In Xcode, click on the project in the project navigator and select the RaiseMan
project itself (not the target). On the Info tab, find the Localizations section and click
the + button. Choose French from the pop-up list (Figure 26.2).
Figure 26.2 Adding the French language to the RaiseMan project

You have told Xcode that you are going to localize your app to French. Now Xcode
wants to know what files you are going to localize.
You will start off the process of localizing RaiseMan’s resources to French by
localizing Document.xib.
In the sheet that has appeared (Figure 26.3), make sure that only Document.xib is
checked, since you are only ready to localize the document window at this point.
Xcode will create a file which you will need to modify in order to localize
Document.xib for the French language.

As you already saw, there are a couple of options when localizing a XIB file:
you can localize using a strings file or a whole new XIB. Your options for this
choice are displayed in the File Types pop-up menu. By default, Localizable Strings is
selected. When using this option, Xcode will create a strings file, a text file which
allows you to specify the French text to be displayed in the various controls in
the reference version of Document.xib. Using the other option, Interface Builder Cocoa
XIB, Xcode creates an entirely new XIB for French. In this exercise, you are going
to create a strings file.
Figure 26.3 Localizing Document.xib

When the localized file (either a strings file or a XIB) is created, it is pre-
populated with the data of another, already existing localization, which needs to
be overwritten. The localization that is used is called the Reference Language. At this
point, the only option for a Reference Language for Document.xib is Base – no other
localizations exist! If you were to add a German localization after you finished
the French localization, however, you would be able to base it on the French
localization.
Click Finish to create the localized Document.xib.
If you look in Finder, you will see that a new file, Document.strings, has been
created in fr.lproj. Let’s take a look at the file in Xcode. In the project navigator,
expand the disclosure arrow next to Document.xib, and select Document.strings
as in Figure 26.4.
Figure 26.4 Locating Document.strings
When you are done, this file will contain instructions used by Cocoa to change
the text on the various views in Document.xib to the appropriate French words
and phrases. Currently, though, since you used the base localization as the
reference language for this file, it contains instructions for English rather than
French.
You will “francophize” this file. To do this, you will translate a few of the
English phrases that appear in this file into French. First, find the line in the file
where “Raise” is specified to be the title of a NSTableColumn’s headerCell; the
line should look something like this:
/* Class = "NSTableColumn"; headerCell.title =
"Raise"; ObjectID = "hQP-gu-5md"; */
"hQP-gu-5md.headerCell.title" = "Raise";
In your file, the ObjectID will be different. This is to be expected, the ID is
generated randomly and is only used as a unique identifier for views in the XIB.
Change “Raise” to “Augmentation”:
/* Class = "NSTableColumn"; headerCell.title =
"Raise"; ObjectID = "hQP-gu-5md"; */
"hQP-gu-5md.headerCell.title" = "Raise";
"hQP-gu-5md.headerCell.title" = "Augmentation";
Repeat this same process, making the following changes:
Change “Name” to “Nom”.
Change “Add Employee” to “Ajouter Employé”.
Change “Remove” to “Supprimer”.
With those changes made, your file should look a little something like this,
though as before, the ObjectIDs and the order in which the objects appear may be
different:
/* Class = "NSTableColumn"; headerCell.title = "Name";
ObjectID = "7Sc-gL-qrn";
"7Sc-gL-qrn.headerCell.title" = "Nom";

Class = "NSButtonCell"; title = "Remove"; ObjectID =


"8iK-hy-zDX";
"8iK-hy-zDX.title" = "Supprimer";

Class = "NSTextFieldCell"; title = "Text Cell";


ObjectID = "9Go-OH-0Ey";
"9Go-OH-0Ey.title" = "Text Cell";

Class = "NSTextFieldCell"; title = "Text Cell";


ObjectID = "Jna-gD-clS";
"Jna-gD-clS.title" = "Text Cell";

Class = "NSTextFieldCell"; title = "Table View Cell";


ObjectID = "Ra8-xZ-kGh";
"Ra8-xZ-kGh.title" = "Table View Cell";

Class = "NSTableColumn"; headerCell.title = "Raise";


ObjectID = "hQP-gu-5md";
"hQP-gu-5md.headerCell.title" = "Augmentation";

Class = "NSButtonCell"; title = "Add Employee";


ObjectID = "mT3-ua-sE2";
"mT3-ua-sE2.title" = "Ajouter Employé";

Class = "NSTextFieldCell"; title = "Table View Cell";


ObjectID = "n4s-9T-9m5";
"n4s-9T-9m5.title" = "Table View Cell";

Class = "NSWindow"; title = "Window"; ObjectID =


"xOd-HO-29H"; */
"xOd-HO-29H.title" = "Window";
To type in characters with accents, you will need to hold down a key for a
second to see a pop-up with its diacritical marks.
At this point, you have created a localized resource. Note that if you make a lot
of changes to your program, you may need to update
fr.lproj/Document.strings in addition to Document.xib. For example, if you
added another button to Document.xib, you would need to localize the text for it
by adding a couple of lines like
/* Class = "NSButtonCell"; title = "Halve"; ObjectID =
"4SD-xo-a2N"; */
"4SD-xo-a2N.title" = "Réduire de Moitié";
to the French strings file. The Object ID you would use here can be found in the
Document section of the identity inspector. For this reason, it is a good idea to wait
until the application is completed and tested before localizing it.
Before running your app, bring up the Language & Region pane of the System Preferences
application. Drag Français to the top of the list to set it as your preferred language.
Now run your application. Notice that the text in the Document window is all in
French. More exciting still, notice that although the titles of the buttons are
different in length from their English counterparts, all the views are laid out
correctly. This is thanks to Auto Layout’s consideration of intrinsic content size.
Also note that the document architecture takes care of some localization for you.
For example, if you try to close an unsaved document, you will be asked in
French whether you want to save the changes.
Before proceeding, change your preferred language back to English in System
Preferences.
Localizing String Literals
What about the places in your application where you programmatically
determine what text is displayed to the user, such as with a string literal or a
Swift string interpolation? In Document.swift, you have the following line of
code:
let alert = NSAlert()
alert.messageText = "Do you really want to remove
these people?"
alert.informativeText = "\(selectedPeople.count)
people will be removed."
alert.addButtonWithTitle("Remove")
alert.addButtonWithTitle("Cancel")
That alert sheet is not going to bring about world peace.
You have already created a strings file to localize Document.xib. In addition to
any strings files you may have for XIB files, you will want to create a strings file
for strings that are used in your app’s code – one for each language you want to
localize your application for. By default, this file is named Localizable.strings.
The first step of localizing your programmatic strings is to replace usages of raw
string literals with usages of NSLocalizedString.
func NSLocalizedString(key: String, #comment: String)
-> String
This function looks up the localized string corresponding to the key in the
Localizable.strings file for the language that the user’s system is currently
using. The comment argument is not used by your app. Instead, it is a hint to the
translator, used by the genstrings command-line utility, as you will see in a
moment. If NSLocalizedString cannot find the given key in the strings file, it
will return the key itself.
Open Document.swift and replace the string literals that are used to configure the
NSAlert with calls to NSLocalizedString:
let alert = NSAlert()

alert.messageText = "Do you really want to remove


these people?"
alert.messageText
= NSLocalizedString("REMOVE_MESSAGE",
comment: "The remove alert's
messageText")

alert.informativeText = "\(selectedPeople.count)
people will be removed."
let informativeFormat
= NSLocalizedString("REMOVE_INFORMATIVE %d",
comment: "The remove alert's
informativeText")
alert.informativeText
= String(format: informativeFormat,
selectedPeople.count)

alert.addButtonWithTitle("Remove")
let removeButtonTitle = NSLocalizedString("REMOVE_DO",
comment: "The remove
alert's remove button")
alert.addButtonWithTitle(removeButtonTitle)

alert.addButtonWithTitle("Cancel")
let removeCancelTitle =
NSLocalizedString("REMOVE_CANCEL",
comment: "The remove
alert's cancel button")
alert.addButtonWithTitle(removeCancelTitle)

let window = sender.window!


If you run your app now, you will see that the localization keys are displayed in
the UI. Do not worry, you will be putting those strings in the English localization
of your application in a moment.
Open Terminal and cd to the directory where your Document.swift file is. There
are a number of ways to do this. Here is one: In Xcode, select the RaiseMan group in
the project navigator. Expand the utility area and select the file inspector. At the
bottom of the Identity and Type section, you should see the Full Path field. Select the
path (Figure 26.5) and copy it to the clipboard.
Figure 26.5 Finding RaiseMan’s source directory in Xcode

In Terminal, type in cd followed by a space and then paste the path you just copied
from Xcode. You should have something like this at the command line in Terminal:
$ cd Usersnate/Desktop/RaiseMan/RaiseMan
If your path has spaces in it, you must surround it with double quotes.
$ cd "path with spacespath with
spaces/to/RaiseMan/RaiseMan"
Run that command. The working directory will now contain your
Document.swift file. (You can verify this by running ls in Terminal.) At this point,
you are going to use genstrings.
The command-line utility genstrings will generate a strings file for one or more
Swift source files. It does this by searching through each of the files that you
specified for occurrences of NSLocalizedString. It then creates a strings file with
one key-value pair for each occurrence of NSLocalizedString.
In this case, you only need genstrings to search through Document.swift for
occurrences of NSLocalizedString. To do that, type the following into Terminal: $
genstrings Document.swift -o Base.lproj
Run this command.
This command generated a file called Localizable.strings containing all the
localized strings from Document.swift in the Base.lproj directory of your
project.
You generated it into the base localization directory because the generated file is
suitable as a template for other localizations, but is not itself a localization. You
are going to create localizations from this file for English and French.
Use the cat command to make sure that the file was generated: $ cat
Base.lproj/Localizable.strings
The output should look something like this:
/* The remove alert's cancel button
"REMOVE_CANCEL" = "REMOVE_CANCEL";

The remove alert's remove button


"REMOVE_DO" = "REMOVE_DO";

The remove alert's informativeText


"REMOVE_INFORMATIVE %d" = "REMOVE_INFORMATIVE %d";

The remove alert's messageText */


"REMOVE_MESSAGE" = "REMOVE_MESSAGE";
Notice that the file consists of four entries, one for each of your uses of
NSLocalizedString in Document.swift. Each entry consists of a comment, a key
on the left hand side of the equals sign, and a value on the right hand side:
/* The comment that you passed in your call to
NSLocalizedString */
"LOCALIZATION_KEY" = "The value, a localized string";
The value is what will be displayed to the user.
Now that you have created the strings file, it is time to add it to your project. In
Terminal, run the following command to open the Base.lproj folder in Finder: $
open Base.lproj
Locate the Localizable.strings file and drag it into Xcode (Figure 26.6).
Figure 26.6 Adding Localizable.strings to the project
In the project navigator, select the Localizable.strings file you just dropped in,
and show the file inspector in the utility area. In the Localization section, you
should see that Base is checked, but both English and French are unchecked. You will
create both English and French localizations of this file. Check both English and
French.

Try building the app. You may get a warning: “The specified input encoding is
Unicode (UTF-8), but file contents appear to be Unicode (UTF-16); treating as
Unicode (UTF-16)”. If you are seeing this, you are experiencing an Xcode bug. To
fix it, you will need to select each of the localizations of Localizable.strings in
turn from the project navigator, and do the following: Open the file inspector.
You should see that the Text Encoding is set to Default - Unicode (UTF-16). Change the
encoding to Unicode (UTF-16). Xcode will present a sheet asking whether you want to
convert the text to UTF-16. Click the Convert button. After you have done this for
all three of the localizations, the warning should go away.
Expand the disclosure arrow for Localizable.strings in the project navigator
and select the English localization. Update the file to have English localized
strings:
/* The remove alert's cancel button */
"REMOVE_CANCEL" = "REMOVE_CANCEL";
"REMOVE_CANCEL" = "Cancel";

/* The remove alert's remove button */


"REMOVE_DO" = "REMOVE_DO";
"REMOVE_DO" = "Remove";

/* The remove alert's informativeText */


"REMOVE_INFORMATIVE %d" = "REMOVE_INFORMATIVE %d";
"REMOVE_INFORMATIVE %d" = "%d people will be
removed.";

/* The remove alert's messageText */


"REMOVE_MESSAGE" = "REMOVE_MESSAGE";
"REMOVE_MESSAGE" = "Do you really want to remove these
people?";
Run the application. Try removing an employee. The alert panel that is presented
will have the strings that you just added to the English localization of the
Localizable.strings file.

One thing to note here is that you have created a separate file for the English
localization. In the case of Document.xib, you used the Base localization XIB for
English, the development language of the project. Either of these approaches is
fine. Both the English localization (if English is the development language) and
the Base localization get used when the user’s preferred localization is not
present. You can read more about this in the section called “NSBundle’s role in
localization” .
Now edit the French localization of Localizable.strings:
/* The remove alert's cancel button */
"REMOVE_CANCEL" = "REMOVE_CANCEL";
"REMOVE_CANCEL" = "Annuler";

/* The remove alert's remove button */


"REMOVE_DO" = "REMOVE_DO";
"REMOVE_DO" = "Supprimer";

/* The remove alert's informativeText */


"REMOVE_INFORMATIVE %d" = "REMOVE_INFORMATIVE %d";
"REMOVE_INFORMATIVE %d" = "%d personnes seront
supprimées.";

/* The remove alert's messageText */


"REMOVE_MESSAGE" = "REMOVE_MESSAGE";
"REMOVE_MESSAGE" = "Voulez-vous supprimer ces
personnes?";
(Remember, to create the e with an acute accent, hold down the e key until the
pop-up appears, at which point you can type 2 to select the accented e.) Before
running the app, configure your system’s preferred language to be French: Go to
System Preferences, navigate to Language & Text, and drag Français to the top of the list of
Preferred languages.

Now run your app. Try adding and removing some employees. The alert panel
will have text in French.
Demystifying NSLocalizedString and
genstrings
NSLocalizedString and genstrings make it very easy to generate a
Localizable.strings file for your app. However, the steps that they follow to
generate the file are easy and worthwhile to understand. You can do all of this
yourself.
A strings file is just a collection of key-value pairs. The key and the value are
strings surrounded by quotes, and the pair is terminated with a semicolon:
"Key1" = "Value1";
"Key2" = "Value2";
You can create a strings file yourself and add it to your project just by dragging
it into Xcode. Once you have added the file, you can create localizations of it for
other languages using the file inspector.
Once you have added the file to your project and created localizations, you can
use NSBundle to get the localized strings when your app is running:
let mainBundle: NSBundle = NSBundle.mainBundle()
let string: String =
mainBundle.localizedStringForKey("NO_RESULTS",
value:
"No results found",
table:
"Find")
When your app is running, this code would search in the localized versions of
the “Find.strings” file for the value corresponding to the "NO_RESULTS" key. If
no value is found "No results found" is returned. If you do not supply the
name of the table (file), Localizable.strings is used instead. Most simple
applications just have one strings file for each language: Localizable.strings.
Internally, NSLocalizedString just calls this method on NSBundle.mainBundle().
Explicit Ordering of Tokens in Format
Strings
As text is moved from language to language, both the words and the order of the
words change. For example, the words in one language may be laid out like this:
“Ted needs a scooter.” In another, the order might be “A scooter is what Ted
needs.” Suppose that you try to localize the format string to be used like this:
let format = NSLocalizedString("NEEDS", comment: "%@
needs a %@")
let string = String(format: format, "I", "Hero")
The following will work fine for the first language:
"NEEDS" = "%@ needs a %@";
For the second language, you would need to explicitly indicate the index of the
token you want to insert. This is done with a number and the dollar sign:
"NEEDS" = "A %2$@ is what %1$@ needs";
NSBundle
A bundle is a directory of resources that may be used by an application. A
resource can be any kind of file: images, sounds, and NIB files. Bundles can also
contain compiled code. The class NSBundle is a very elegant way of dealing with
bundles.
Your application is a bundle. In Finder, an application looks to the user like any
other file, but it is really a directory filled with NIB files, compiled code, and
other resources. We call this directory the main bundle of the application.
To get the main bundle of an application, use the following code:
let bundle = NSBundle.mainBundle()
This is the most commonly used bundle. If you need to access resources in
another bundle, however, you could ask for the bundle at a certain path:
let bundleURL: NSURL = ...
if let bundle = NSBundle(URL: bundleURL) {
...
}
Once you have an NSBundle object, you can ask it for its resources:
let url: NSURL? = bundle.URLForResource("Chapter",
withExtension: "xml")
This works well for general resources, but for images you should use NSImage’s
init(named:):
if let appIconImage = NSImage(named:
NSImageNameApplicationIcon),
let badgeImage = NSImage(named: "GreenBadge") {
...
}
It will load images from your application bundle by name, and even special
images such as shared AppKit images. See the class reference documentation for
this initializer for more.
NSBundle’s role in localization
NSBundle is the class that makes localization in Cocoa so seamless. For example,
when loading a file resource from a bundle you will first want the URL (or path)
for that resource:
let url: NSURL? = bundle.URLForResource("Tutorial",
withExtension: "mov")
URLForResource(_:withExtension:) transparently handles finding the most
appropriate localized resource given the user’s language preferences and returns
the URL. Once a resource is localized in Xcode there is no further need for special
handling at runtime. NSBundle does it for you.
Remember that these resources can be any file: NIBs, text files, JSON, audio,
video. If a resource (not source code) has the target checked in the file inspector,
it will be packaged up with the app bundle and can be located with this API.
NSBundle plays a more involved role when working with strings files. For
example, in the section called “Demystifying NSLocalizedString and genstrings”
, you saw that Cocoa would locate the string for the key "NO_RESULTS" in a
localized Find.strings file. In fact, it will search through a number of these
files.
Here is the order that the searching is done in:
Global resource
This is a resource that you have not localized. By default, it will be in the top
level of your project’s source code directory.
If this resource is present, no language/region specific version will ever be
returned. In particular, imagine you had a global Localizable.strings file and
also localized versions of Localizable.strings. If you ask NSBundle for a
localized string in the Localizable file, it will only search in the global file,
even if the string is not there.
Region-specific localized resource
In this chapter, you saw language-specific localized resources. You can also
provide finer-grained localization specific to the version of a language in a
particular region.
The user can specify their region in the Language & Region preference pane.
When NSBundle finds a localization which (1) is in the current language and (2)
matches the region the user has specified, that localization is preferred over the
generic (not region-specific) localization in that language.
Language-specific localized resource
These are the resources that you have worked with over the course of this
chapter.
The user can specify a number of preferred languages in the Language & Region
preference pane
If a localized resource is not found in the most preferred language’s lproj
directory, the second preferred language’s directory will be searched, and so on.
Development language resource
This is the localization corresponding to the development language. The
development language is specified in the Info.plist under the
CFBundleDevelopmentRegion key.
If no localization can be found matching any of the user’s preferred languages,
the localization for the language in which the app was developed will be used.
Base localization resource
This is the localization that your other localizations are based on.
If the resource cannot be found in any localization, the base localization will be
used.
NSBundle will search through each of these localization’s lproj directories in this
order, and return the first resource that it finds.

Loading code from bundles


Bundles can be used as plugins in your application. You typically load the
principal class from the bundle:
if let principalClass = pluginBundle.principalClass
as? NSObject.Type {
if let rover = principalClass() as? Rover {
rover.fetch()
}
}
The principal class is configured in the bundle’s Info.plist. For more on
loading code from bundles, see the section entitled Creating Loadable Bundles in
Apple’s Code Loading Programming Topics.
For the More Curious: Localization and
Plurality
Sometimes, you will want to use different strings depending on whether one of
the terms in the string is singular or plural.
For example, when you remove a single employee in the English localization of
RaiseMan, the app will present an alert with broken English (Figure 26.7).

Figure 26.7 Incorrect message in RaiseMan’s remove alert

To address this, you could programmatically check whether the number of


employees being removed is equal to one or greater than one. This would work
just fine for English and French – but, as it turns out, this is not a robust solution.
In English and in French, there are only two forms for nouns: singular (when
there is only one) and plural (when there is any other number). But many
languages have more complex rules around pluralization. For example, in
Arabic, there are different noun forms for one, two, few, and many, in addition
to a form for other numbers which do not fall into those categories.
To ensure that you can present the correct pluralization across all localizations of
your app, Apple introduced the stringsdict file format. This file format allows
you to specify, for each language into which you are localizing your app,
translations of your string for each of the plural cases that occur in that language.
Imagine you were to use a stringsdict file to correctly form the informativeText
displayed in the remove alert. You would create a file named
Localizable.stringsdict alongside the existing Localizable.strings. The
English and French localizations of the stringsdict file would each specify two
translations: a translation for one employee being removed, and a translation for
more than one employee being removed. The English localization of
Localizable.stringsdict would look a bit like this:
<plist version="1.0">
<dict>
<key>REMOVE_INFORMATIVE %d</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@d_number_of_people@ will be
removed.</string>
<key>d_number_of_people</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>

<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d person</string>
<key>other</key>
<string>%d people</string>
</dict>
</dict>
</dict>
</plist>
Unlike the English localization of Localizable.strings, which only has a single
string value for the key "REMOVE_INFORMATIVE %d", Localizable.stringsdict
has an entire dictionary.
The dictionary corresponding to "REMOVE_INFORMATIVE %d" has two fields: a
localized format string "%#@d_number_of_people@ will be removed." and a
dictionary describing how to expand d_number_of_people.
Two ways of expanding d_number_of_people are described: The first ("%d
person") is used when only one person is removed. The second ("%d people")
is used when more than one person is removed.
This stringsdict file is for English, so this file only covers two possibilities:
singular and plural. The Arabic localization of Localizable.stringsdict would
specify five translations to cover all of the plural forms in Arabic.
For more information on this advanced localization technique, take a look at the
Stringsdict File Format section of Apple’s Internationalization and Localization
Guide.
Challenge: Localizing the Default Name for a
Newly Added Employee
Currently, when running RaiseMan in French, when you add a new employee, its
name will be New Employee. Replace the hard-coded string with a call to
NSLocalizedString. Update all three of your Localizable.strings files
accordingly.
For French, you will want to use the string Nouvel Employé.
Challenge: Localizing the Undo Action Names
When you run RaiseMan in French, the undo action names that appear in the Edit
menu (“Add Person”, “Remove Person”) are not localized.
Localize these strings.
27
Printing
Code to handle printing is always relatively hard to write. Many factors are at
play, like pagination, margins, and page orientation (landscape versus portrait).
This chapter is designed to get you started on your journey toward the perfect
printout.
Compared to most operating systems, OS X makes writing print routines
considerably easier. After all, your views already know how to generate PDF,
and OS X knows how to print PDF. If you have a document-based application
and a view that knows how to draw itself, you simply implement
printOperationWithSettings(_:error:). In this method, you create an
NSPrintOperation object, using a view, and return it. The code, in your
NSDocument subclass, would look like this:
override func
printOperationWithSettings(printSettings: [NSObject :
AnyObject],
error
outError: NSErrorPointer)
->
NSPrintOperation? {
let printableView: NSView = ... // View that draws
document's data for printing.
let printInfo: NSPrintInfo = self.printInfo
let printOperation = NSPrintOperation(view:
printableView, printInfo: printInfo)
return printOperation
}
In this code, you create a view, printableView, which draws the contents of the
document the way that you want it to be printed. This is an instance of a view
that you will probably never put onscreen – it exists only to be printed. You then
pack this view up inside an NSPrintOperation, which describes how the
document should be printed.
Along with the implementation of your NSView, which knows how to draw your
document’s data in a way that is nice for printing, this code is all you need to let
your document be printed. If your document’s data can be drawn on a single
page, this is all you need!
Dealing with Pagination
What about multiple pages? A view, after all, has only a single page. How will
you get a view to print multiple-page documents? Off-screen, you will make a
huge view that can display all the pages of the document simultaneously
(Figure 27.1). The print system will ask the view how many pages it is
displaying and will ask the view where each page can be found in the view.
Figure 27.1 Each page is a rectangle on the view

Your view, then, must override two methods:


// How many pages?
func knowsPageRange(aRange: NSRangePointer) -> Bool

// Where is each page?
func rectForPage(pageNumber: Int) -> NSRect
Instead of creating a huge view and returning a different rectangle for each page,
you can take note of the page that the system is preparing to print when
rectForPage(_:) gets called, and always return the same rectangle. Then, in
drawRect(_:), you can use this page number and draw the page that the system is
printing. This is the technique you will be using in the exercise.
Adding Printing to RaiseMan
As an example, you will add printing to your RaiseMan app. You will print the
name and expected raise for as many people as will fit on the paper size that the
user selects from the Print panel (Figure 27.2).
Figure 27.2 Completed application

To do so, you will create a view that does the printing. Instead of making the
view big enough to display all the people simultaneously, you will simply note
which page the system is printing and draw only the names on that page in
drawRect(_:).

Open the RaiseMan project from Chapter 26.


Create a new Cocoa Class file containing a class called EmployeesPrintingView that
is a subclass of NSView. Make the following additions to
EmployeesPrintingView.swift:
import Cocoa

private let font: NSFont =


NSFont.userFixedPitchFontOfSize(12.0)!
private let textAttributes: [String : AnyObject] =
[NSFontAttributeName : font]
private let lineHeight: CGFloat = font.capHeight * 2.0

class EmployeesPrintingView: NSView {


let employees: [Employee]

var pageRect = NSRect()


var linesPerPage: Int = 0
var currentPage: Int = 0

override func drawRect(dirtyRect: NSRect) {


If you try to build now, the compiler will remind you that you have not
initialized employees yet. Add an initializer init(employees:) to do this. This
initializer will call NSView’s init(frame:) initializer. You will also need to
implement init?(coder:).
var currentPage: Int = 0

// MARK: - Lifecycle

init(employees: [Employee]) {
self.employees = employees
super.init(frame: NSRect())
}

required init?(coder: NSCoder) {


assertionFailure("EmployeesPrintingView cannot be
instantiated from a nib.")
}
Now, implement the drawing and pagination methods for
EmployeesPrintingView: // MARK: - Pagination override func
knowsPageRange(range: NSRangePointer) -> Bool { let printOperation =
NSPrintOperation.currentOperation()! let printInfo: NSPrintInfo =
printOperation.printInfo // Where can I draw? pageRect =
printInfo.imageablePageBounds let newFrame = NSRect(origin: CGPoint(),
size: printInfo.paperSize) frame = newFrame // How many lines per page?
linesPerPage = Int(pageRect.height / lineHeight) // Construct the range to
return var rangeOut = NSRange(location: 0, length: 0) // Pages are 1-based.
That is, the first page is page 1. rangeOut.location = 1 // How many pages
will it take? rangeOut.length = employees.count / linesPerPage if
employees.count % linesPerPage > 0 { rangeOut.length += 1 } // Return the
newly constructed range, rangeOut, via the range pointer range.memory =
rangeOut return true } override func rectForPage(page: Int) -> NSRect { //
Note the current page // Although Cocoa uses 1-based indexing for the page
number // it's easier not to do that here. currentPage = page - 1 // Return the
same page rect every time return pageRect } // MARK: - Drawing // The
origin of the view is at the upper-left corner override var flipped: Bool {
return true } override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect) var nameRect = NSRect(x: pageRect.minX, y: 0,
width: 200.0, height: lineHeight) var raiseRect = NSRect(x:
nameRect.maxX, y: 0, width: 100.0, height: lineHeight) for indexOnPage in
0..<linesPerPage { let indexInEmployees = currentPage * linesPerPage +
indexOnPage if indexInEmployees >= employees.count { break } let
employee = employees[indexInEmployees] // Draw index and name
nameRect.origin.y = pageRect.minY + CGFloat(indexOnPage) * lineHeight
let employeeName = (employee.name ?? "") let indexAndName = "\
(indexInEmployees) \(employeeName)"
indexAndName.drawInRect(nameRect, withAttributes: textAttributes) //
Draw raise raiseRect.origin.y = nameRect.minY let raise = String(format:
"%4.1f%%", employee.raise * 100) let raiseString = raise
raiseString.drawInRect(raiseRect, withAttributes: textAttributes) } }
Your custom printing view is now complete. You just need to let your document
know how to use it to print. Open Document.swift and implement
printOperationWithSettings(_:error:):
override func
printOperationWithSettings(printSettings: [NSObject :
AnyObject],
error
outError: NSErrorPointer)
->
NSPrintOperation? {
let employeesPrintingView =
EmployeesPrintingView(employees: employees)
let printInfo: NSPrintInfo = self.printInfo
let printOperation = NSPrintOperation(view:
employeesPrintingView,
printInfo:
printInfo)
return printOperation
}
In the MainMenu.xib file, note that the Print... menu item is nil-targeted and that
its action is printDocument:, which will trigger
printOperationWithSettings(_:error:).

Run the application. To have multiple pages of employees to print, you will need
a whole lot of them. You can simply click Add Employee repeatedly until you have
an adequate number. Once you have gotten a large number of employees into
your document, prepare to print by selecting the File → Print menu item.
When the print sheet appears, click the Show Details button. The sheet will expand
to show additional configuration options. Locate the pop-up button halfway
down the right hand side of the sheet which allows you to select which
configuration details are shown (Figure 27.3).
Figure 27.3 Expanded printing sheet

From the pop-up menu highlighted in Figure 27.3, select Layout and then change
the Pages per Sheet setting to 2. Note that this works just fine.
You can also change the paper size and more or fewer people subsequently
appear on each page. To change the paper size, cancel out of the print sheet,
selecting the File → Page Setup... menu item. In the resulting sheet, change the Paper
Size from US Letter to something else, such as A3.
For the More Curious: Are You Drawing to
the Screen?
In an application, you will often want to draw things differently on screen than
on the printer. For example, in a drawing program, the on-screen view might
show a grid on-screen but not when printed on paper.
In your drawRect(_:) method, you can ask the current graphics context if it is
currently drawing to the screen:
if NSGraphicsContext.currentContextDrawingToScreen() {
// ... draw the grid ...
}
Challenge: Add Page Numbers
Add page numbers to the printout.
Recall that drawRect(_:) is only printing a single page at a time. Use this
knowledge, and the fact that you are tracking the current page number in a
property, to complete the challenge.
Challenge: Persist Page Setup
The configuration changes made in the Page Setup sheet are document-specific.
Cocoa does not provide any automatic support for persisting these settings,
however. It is your responsibility to save and load them. Fortunately,
NSPrintInfo (stored in the document’s printInfo property) conforms to
NSCoding. Modify the Document class to archive the printInfo along with the
employees array when saving, and vice-versa when loading.

This is not quite as straightforward as it sounds. For one thing, the file format of
rsmn files is simply an archived array. It makes no allowances for additional
information. You will need to change the format. A popular pattern for this is to
use a dictionary as the top level object with keys for the high level components
of the file. As an example, consider the following dictionary pseudo-code:
[
"employees": [Employee],
"printInfo": NSPrintInfo
]
When loading, be sure to access the members safely using optional binding and
optional downcasting. This technique is also demonstrated in Chapter 28.
if let employees = topLevelDictionary["employees"] as?
[Employee] {
...
}
Reworking the layout of the RaiseMan document format will break loading of the
files your tens of users have already created with this app. While it would
complicate your deserialization code, it is possible to serialize a file format
version number along with your document data to support loading both the new
and old formats! If no version number is found, then you know the file is in the
old format. If the version number is found, then you know exactly what version
of the format the file is in.
When unarchiving the NSPrintInfo object, you will assign it to the printInfo
property. This property’s setter has a side effect: it adds a frame to the undo
stack, which is undesirable in that it would cause a freshly loaded document to
appear to be modified. To fix this, disable undo registration while setting the
property:
override func readFromData(data: NSData,
ofType typeName: String,
error outError: NSErrorPointer) -
> Bool {
...
...
if let unarchivedPrintInfo =
dictionary["printInfo"] as? NSPrintInfo {

undoManager.disableUndoRegistration()
printInfo = unarchivedPrintInfo

undoManager.enableUndoRegistration()
}
28
Web Services
It is common for an application to need information from somewhere beyond the
user’s computer. The easiest way to make that information available is to put it
on a web server and publish the URL that provides the desired information. Web
servers communicate using HTTP or HTTPS.
HTTP (Hypertext Transfer Protocol) was originally designed for serving HTML
documents for rendering in a web browser. As the web’s popularity soared, so
did creative uses of HTTP. Today, HTTP is commonly used not only to serve
content for web browsers, but also structured data for any software that speaks
HTTP. In this chapter you will see the latter, but the concepts will be applicable
to all web services.
Using a web service is simple:
Format a request as required by the web service
Send the request over HTTP/HTTPS
Receive the response
Parse the response and use that data in your application

Figure 28.1 Your typical web service in action


Web Services APIs
HTTP requests and responses are handled by a small army of classes. The key
players are NSURL, NSURLRequest, NSURLSession, NSURLSessionTask, and
NSURLSessionConfiguration (Figure 28.2).

Figure 28.2 Classes for making HTTP requests

In order to make a web service request you will need to describe the endpoint of
the request. That is, the scheme (usually HTTP or HTTPS), the host (the server’s
address), and the resource path. The NSURL class serves this purpose. You will
recall this versatile class from earlier in the book; it is frequently used in Cocoa
to describe the location of files as well as web services.
Once you have an NSURL you will wrap it in an additional class, NSURLRequest,
which offers additional configuration opportunities in the form of the HTTP
method (GET, PUT, POST, etc.), HTTP headers, cache policy, timeout, and
more. (To configure these you will want to create an instance of
NSMutableURLRequest, a subclass of NSURLRequest.) Once you have a configured
NSURLRequest object, you will ask an NSURLSession to create a connection from
it. An NSURLSession manages a number of individual HTTP connections with
common configurations. For example, all of the requests made with a particular
session will have the same timeout interval and cache policy. When you give
your NSURLSession the NSURLRequest, it will hand you back an NSURLSessionTask,
which wraps up the connection corresponding to the request. You can use the
task to start and stop the connection as well as to get information about it.
The NSURLSessionTask completes when you get the server’s response. At this
point, you will need to parse the response into structured data that your
application can work with. If you implement your own web service, you can
format the request and response data any way you like, but to use an existing
web service you have to work with its format. XML and JSON are commonly
used, and Cocoa provides classes for working with both of them.
RanchForecast Project
Big Nerd Ranch has a web service that delivers a JSON document of its
upcoming classes. The URL of this web service is
http://bookapi.bignerdranch.com/courses.json. In this exercise, you are going to write an
application that requests this data from the web service, parses it, and displays
the upcoming classes in a table view. The app will look like Figure 28.3. Before
you get started, try opening the URL above in your web browser to get a feel for
what your application will be dealing with.
Figure 28.3 Completed application

Create a new project of type Cocoa Application. Name the project RanchForecast. Set
the Language to Swift. Uncheck Create Document-Based Application, Use Core Data, and Use
Storyboards.

Make this project a single-window application:


1. In MainMenu.xib, delete the window.
2. In AppDelegate.swift, replace the class implementation with your Single-
Window Application code snippet from Chapter 6. The file should look like
this: import Cocoa @NSApplicationMain class AppDelegate: NSObject,
NSApplicationDelegate { var mainWindowController:
MainWindowController? func
applicationDidFinishLaunching(aNotification: NSNotification) { let
mainWindowController = MainWindowController()
mainWindowController.showWindow(self) self.mainWindowController =
mainWindowController } }
3. Create a new Cocoa Class named MainWindowController, subclass of
NSWindowController. Check Also create XIB file for user interface, and set the
Language to Swift.

4. In MainWindowController.swift, override the windowNibName property:


override var windowNibName: String! { return
"MainWindowController" }

Run the application to ensure that the empty window is shown.


Now that you have gotten the basic window controller setup out of the way, you
can turn your attention to the model. The model layer of this application will
consist of two classes: The first, ScheduleFetcher, handles communicating with
the web service. The second, Course, describes the properties of a course in the
schedule: a title, a start date, and a URL for the course’s web page. Figure 28.4
shows the MVC architecture for this application.
Figure 28.4 RanchForecast MVC architecture
Create a new Swift File in the RanchForecast group. Name it Course.swift. Implement
the class:
import Foundation

class Course: NSObject {


let title: String
let url: NSURL
let nextStartDate: NSDate

init(title: String, url: NSURL, nextStartDate:


NSDate) {
self.title = title
self.url = url
self.nextStartDate = nextStartDate
super.init()
}
}
Why subclass NSObject? Because you are going to bind the table’s contents to
the Course objects, and bindings only work with Objective-C objects. This
means you must use the Objective-C base class, NSObject.
NSURLSession and asynchronous API design
With the Course class defined, let’s focus on fetching course data from the web
service. In Cocoa, web service requests are issued to the server by way of the
NSURLSession class. NSURLSession initiates and manages a few different kinds of
connections, each represented by an NSURLSessionTask: a connection can
download to a file on disk, upload from a file on disk, or download to memory.
In this case, you do not need to save the data to a file, so you will use this
method, which yields an NSData object:
func dataTaskWithRequest(request: NSURLRequest,
completionHandler: ((NSData!,
NSURLResponse!, NSError!) -> Void)?)
->
NSURLSessionDataTask
NSURLSession’s dataTaskWithRequest(_:completionHandler:) method takes an
NSURLRequest and a completion block and returns a NSURLSessionDataTask
object. You will use the NSURLSessionDataTask to control the connection and to
get information about its progress. We will discuss how to use this class in more
detail shortly.
This is an example of an asynchronous API: once the data task has started, your
app does not have to sit idly by while waiting for a response from the server.
Your app can do any work that you would like it to – such as displaying a
progress indicator. Once a response is received from the server, your completion
handler is called.
This is in contrast to a synchronous API, which would block program execution
on the current thread until the response had been received. You will learn more
about the consequences of blocking a thread, especially the main thread, in
Chapter 34, but until then it is enough to know that it has the extremely
undesirable effect of making your application stop responding to the user. For
now, let’s get back to web services.
You could work with NSURLSession directly in the window controller, but that
would mean putting too much model layer code in the controller. The core
functionality of the app is pulling data from a web server: the code that does this
work belongs in the model layer. It is much better design to put the web service
code in its own class. That class can encapsulate all the work of communicating
with the web service and parsing the result into model objects. By abstracting
away this functionality, you can keep your user interface controller classes
simple and uncluttered, which will make it easier to change the design in the
future. And your new web service class can be reused in other projects.
Create another Swift File in the RanchForecast and name it ScheduleFetcher.swift. In
ScheduleFetcher.swift, define the ScheduleFetcher class, add a property for the
NSURLSession, and initialize it using the default configuration:
import Foundation

class ScheduleFetcher {

let session: NSURLSession

init() {
let config =
NSURLSessionConfiguration.defaultSessionConfiguration()

session = NSURLSession(configuration: config)


}
}
Before continuing, let’s consider the API design decision before you: how
should the programmer who is using ScheduleFetcher initiate a fetch and receive
the results of the fetch (the Course objects or error)? Keep in mind that this will
be an asynchronous API: the fetch request will be initiated, program execution
will continue, and at some point your code will be called again with the result.
Here are a few options:
Delegate pattern: The delegate protocol might look like this: protocol
ScheduleFetcherDelegate { func scheduleFetcher(fetcher: ScheduleFetcher,
didFetchCourses: [Course]) func scheduleFetcher(fetcher: ScheduleFetcher,
didFailWithError: NSError) }
This is a reasonable approach. It would require adding a delegate property
to the ScheduleFetcher and a method to start the fetch.
NSNotificationCenter: While this would work, it would not be very
graceful. Notifications are best for broadcasting events to many listeners
over a period of time, but in this case you only want to ask the web service
for its courses and be done. Notifications also do not provide any type
safety for objects passed through the userInfo dictionary.
Completion handler: Define a method with a completion handler to start
the fetch: func fetchCoursesUsingCompletionHandler(handler: (???) ->
(Void))
Call it like this: fetcher.fetchCoursesUsingCompletionHandler(handler: {
(???) in // React to courses being passed in or error })
This is a very strong option. Completion handlers are best for one-shot
callbacks. They are also used with alerts (Chapter 15) and sheets
(Chapter 24).
In this chapter you will use the completion handler pattern. As you will see, a
strong benefit of this pattern is that it keeps the code that handles the result of the
method call close to where it was called.
You have one more question to answer before proceeding, and that is what the
parameters of the completion handler will be. As the result of the fetch will be
either an array of Course objects or an error, it should somehow convey that to
the completion handler. You might model it on NSURLSession’s API, which you
will use inside ScheduleFetcher. Here is how you will use it:
let task = session.dataTaskWithRequest(req,
completionHandler: { (data, response,
error) -> Void in
if data == nil {
// Handle the error
}
else {
// Do something with data and response
}
})
task.resume()
This completion handler takes three parameters! The NSData returned in the body
of the request, the NSURLResponse object, and an NSError. It works like many
completion handlers in Cocoa: test to see if data is nil, and if it is, then an error
occurred and the error argument may be checked.
If you apply that pattern to ScheduleFetcher you might imagine that its
completion block should take an array of Course objects and an NSError. It might
look like this:
// Method signature:
func fetchCoursesUsingCompletionHandler(
completionHandler:
completionHandler:
([Course]?, NSError?) -> Void)

// In use:
fetcher.fetchCoursesUsingCompletionHandler { (courses,
error) in
if let courses = courses {
// Show the courses
}
else {
// React to the error
}
}
This pattern works great… as long as you know and follow the convention
(some programmers incorrectly test error, for example). Wouldn’t it be great if
you could use Swift to communicate and enforce the proper handling of the
result? Fortunately, Swift enums with their associated values provide a simple
means to clearly indicate success or failure, and you can lean on the compiler
and its type checking to ensure that the meaning is clear. Inside the
ScheduleFetcher class definition, define an enum for the return value:
class ScheduleFetcher {

enum FetchCoursesResult {
case Success([Course])
case Failure(NSError)
}

let session: NSURLSession


Because this enum is defined inside the ScheduleFetcher class, it is referred to as
a nested type. Internal to ScheduleFetcher, the type can be referred to simply as
FetchCoursesResult. Externally, the class name must be included:
ScheduleFetcher.FetchCoursesResult. Nested types help strongly associate a
type with a specific data structure which contains it.
Using your new enum type as the result of the fetch will clearly indicate to the
caller whether the operation was successful or not. Moreover, the enum’s type
encodes that with Success comes an array of Course objects but with Failure
comes just an NSError.
Now implement the fetch method in ScheduleFetcher: Notice that this web
service does not require any parameters, so you do not have to format the
request. The bare URL is sufficient.
func
fetchCoursesUsingCompletionHandler(completionHandler:

(FetchCoursesResult) -> (Void)) {
let url = NSURL(string:
"http://bookapi.bignerdranch.com/courses.json")!
let request = NSURLRequest(URL: url)

let task =
session.dataTaskWithRequest(request,
completionHandler: {
(data, response, error) -> Void in
var result: FetchCoursesResult

if data == nil {
result = .Failure(error)
}
else {
println("Received \(data.length)
bytes.")
result = .Success([]) // Empty array
until parsing is added
}

NSOperationQueue.mainQueue().addOperationWithBlock({
completionHandler(result)
})
})
task.resume()
}
You will learn more about operation queues in Chapter 34. Until then, it is
enough to know that NSURLSessionDataTask calls its completion handler on a
background thread – that is, a thread other than the main thread, where all UI
updates should be performed. When implementing a class like ScheduleFetcher
it is a good idea to call completion handlers on the main thread so that the caller
can directly update the UI with the results. The call to NSOperationQueue you
used here runs the closure parameter on the main thread.
Let’s pause here to test fetchCoursesUsingCompletionHandler(_:). Update
MainWindowController.swift to call it from windowDidLoad() and store the
resulting courses array in a property. Later in this chapter you will bind an array
controller to this property, so the dynamic keyword is necessary to make the
property compatible with bindings.
import Cocoa

class MainWindowController: NSWindowController {

let fetcher = ScheduleFetcher()


dynamic var courses: [Course] = []

override var windowNibName: String! {


return "MainWindowController"
}

override func windowDidLoad() {


super.windowDidLoad()

fetcher.fetchCoursesUsingCompletionHandler {
(result) in
switch result {
case .Success(let courses):
println("Got courses: \(courses)")
self.courses = courses
case .Failure(let error):
println("Got error: \(error)")
NSAlert(error: error).runModal()
self.courses = []
}
}
}
}
Build and run your application. You should see a log message indicating that
data was received. Even though data is received, you have not parsed it yet, so
the window controller’s courses array will remain empty. Resolve any problems
before continuing.
NSURLSession, HTTP status codes, and errors
Part of the response to an HTTP request is the status code, which is a number in
the range 100 to 599. Generally speaking, anything between 200 and 299
denotes success, while other values such as 404 and 500 indicate issues with the
request or server itself. You might expect to get an error condition from
NSURLSession for these error status codes, but NSURLSession works at a lower
level than that. If it is able to contact the server and receive a response, then, as
far as NSURLSession is concerned, the exchange was a complete success.
In other words, NSURLSession only reports an error if a problem occurred at the
network layer and it was unable to complete the request. In a serious application
you should check the status codes returned by the server. NSURLSession provides
the completion handler with a reference to an NSURLResponse. For HTTP
requests, this reference is actually an NSHTTPURLResponse, which is a subclass of
NSURLResponse. The status code is in a property on NSHTTPURLResponse, so to
access it you will need to cast the reference.
You will add handling of HTTP status codes to the NSURLSession completion
handler, but first you will create a helper method that creates NSError objects.
Implement this method in the ScheduleFetcher class:
func errorWithCode(code: Int, localizedDescription:
String) -> NSError {
return NSError(domain: "ScheduleFetcher",
code: code,
userInfo:
[NSLocalizedDescriptionKey: localizedDescription])
}
Now use this method to help flesh out the response checking and error reporting
in fetchCoursesUsingCompletionHandler(_:):
func
fetchCoursesUsingCompletionHandler(completionHandler:

(FetchCoursesResult) -> (Void)) {
let url = NSURL(string:
"http://bookapi.bignerdranch.com/courses.json")!
let req = NSURLRequest(URL: url)
let task = session.dataTaskWithRequest(req,
completionHandler: {
(data, response, error) -> Void in
var result: FetchCoursesResult

if data == nil {
result = .Failure(error)
}
else {
println("Received \(data.count)
bytes.")
result = .Success([]) // Empty array
until parsing is added
}
else if let response = response as?
NSHTTPURLResponse {
println("\(data.length) bytes, HTTP \
(response.statusCode).")
if response.statusCode == 200 {
result = .Success([]) // Empty
array until parsing is added
}
else {
let error = self.errorWithCode(1,
localizedDescription:
"Bad status code
\(response.statusCode)")
result = .Failure(error)
}
}
else {
let error = self.errorWithCode(1,
localizedDescription:

"Unexpected response object.")
result = .Failure(error)
}

NSOperationQueue.mainQueue().addOperationWithBlock({
completionHandler(result)
})
})
task.resume()
}
Run the application and ensure that the success path still works. Try adding
some gibberish characters to the URL and you should see a 404 status code. Do
not forget to change it back!

Add JSON parsing to ScheduleFetcher


The URL you are fetching from returns JSON data that describes upcoming
classes. Here is an abbreviated and formatted sample of that data:
{ "courses": [
{ "title": "Cocoa Fast Track Bootcamp",
"upcoming": [
{
"end_date": "2014-10-31",
"instructors": "Adam Preble",
"start_date": "2014-10-27"
}
],
"url":
"https://training.bignerdranch.com/classes/cocoa-i-
bootcamp"
},
{ "title": "Beginning iOS (iPhone/iPad)",
"upcoming": [
{ "end_date": "2014-10-17",
"instructors": "Christian Keur,
John Gallagher",
"start_date": "2014-10-11"
}
],
"url":
"https://training.bignerdranch.com/classes/beginning-
ios"
ios"
}
]
}
To create the Course objects you will need to parse the JSON. Cocoa provides a
class for parsing JSON, NSJSONSerialization. It can take in JSON data and
produce a property list tree of NSDictionary, NSArray, NSString, and NSNumber
objects corresponding to the structure of the JSON. (NSJSONSerialization can
also work in the other direction and convert property list objects into an NSData
object.) Start by adding a helper method to ScheduleFetcher that will create a
Course object from one of the course NSDictionary objects:
func courseFromDictionary(courseDict: NSDictionary) ->
Course? {
let title = courseDict["title"] as! String
let urlString = courseDict["url"] as! String
let upcomingArray = courseDict["upcoming"] as!
[NSDictionary]
let nextUpcomingDict = upcomingArray.first!
let nextStartDateString =
nextUpcomingDict["start_date"] as! String

let url = NSURL(string: urlString)!

var dateFormatter = NSDateFormatter()


dateFormatter.dateFormat = "yyyy-MM-dd"
let nextStartDate =
dateFormatter.dateFromString(nextStartDateString)!

return Course(title: title, url: url,


nextStartDate: nextStartDate)
}
Notice that the return type is Course?, but this method always returns a Course
object. For the sake of brevity, this method does not check for errors in the
individual fields of the dictionary, nor does it check that the date formatter was
able to parse the date. In the section called “Safely Working with Untyped Data
Structures” , you will see how this code could be made safer to gracefully handle
bugs in or changes to the web service.
Next, implement another helper method which takes the JSON NSData and
returns a FetchCoursesResult value. By breaking the task up into multiple
methods you can keep the methods smaller and more readable.
func resultFromData(data: NSData) ->
FetchCoursesResult {
var error: NSError?
let topLevelDict =
NSJSONSerialization.JSONObjectWithData(data,
options:
NSJSONReadingOptions.allZeros,
error: &error) as!
NSDictionary?
if let topLevelDict = topLevelDict {
let courseDicts = topLevelDict["courses"]
as! [NSDictionary]
var courses: [Course] = []
for courseDict in courseDicts {
if let course =
courseFromDictionary(courseDict) {
courses.append(course)
}
}
return .Success(courses)
}
else {
return .Failure(error!)
}
}
Now update fetchCoursesUsingCompletionHandler(_:) to call
resultFromData(_:) with the data and pass the resulting FetchCoursesResult to
the completion handler.
if response.statusCode == 200 {
result = .Success([]) // Empty array until
parsing is added
result = self.resultFromData(data)
}
Run the application again. You should see some courses being found. Use the
debugger to inspect the courses array to see that the JSON is being correctly
parsed.

Lay out the interface


Open MainWindowController.xib and use the attributes inspector to change the
title of the window to RanchForecast. Next, add a table view to the window. Ensure
that the table view’s Content Mode is set to View Based. Set the table view to have two
columns and name them Next Start Date and Title. Drag a date formatter onto the
Date column cell and set its Date Style to Medium (Figure 28.5).
Figure 28.5 Configuring the date formatter

Now you are going to bind the table view’s content to the window controller’s
courses array via an array controller. When you are done, you will have added
all the bindings in Figure 28.6.
Figure 28.6 RanchForecast’s bindings
In a moment, we will walk you through setting up these bindings. (If you would
like more details, look back at the way you set up the bindings for RaiseMan in
Chapter 9. You will be using exactly the same approach here.) Remember that
within MainWindowController.xib, File's Owner represents the window controller.
Drag an Array Controller onto the canvas. Bind the array controller’s Content Array to
the File's Owner, with a Model Key Path of courses.
Select the Table View and bind its Content to the Array Controller, with a Controller Key of
arrangedObjects. Then bind the Selection Indexes to the Array Controller with a
Controller Key of selectionIndexes. (Don’t see these bindings? Did you select the
scroll view or clip view instead of the table view?)
Finally, bind the Value of the text field in each of the table view cells to its
respective key path. In view-based NSTableViews this can be confusing, as Xcode
defaults to giving the text fields a label of Table View Cell. You can differentiate
them in the document outline by looking for the control icon, which looks like a
very small slider.
In the Next Start Date column’s cell view, bind the text field’s Value to the Table
Cell View, with a Model Key Path of objectValue.nextStartDate. In the Title
column’s cell view, bind the text field’s Value to the Table Cell View, this time with a
Model Key Path of objectValue.title.

Run the application. After a short delay you should see the table view populated
with the data for the upcoming classes. If it did not work, set a breakpoint and
use the debugger to check the value of the courses in the completion handler
within MainWindowController.
Opening URLs
The data for each course contains the URL of a web page that has a description
of that class. The last functionality for this app is to open that web page in the
user’s browser if the user double-clicks on the course.
To get the table view to respond to a double-click you must set the doubleAction
and target of the table view. Unlike most target/action pairs, doubleAction must
be set programmatically. Open MainWindowController.swift and add an outlet to
the table view and the array controller:
class MainWindowController: NSWindowController {

@IBOutlet var tableView: NSTableView!


@IBOutlet var arrayController: NSArrayController!

let fetcher = ScheduleFetcher()


dynamic var courses: [Course] = []
Connect the outlets you just created in MainWindowController.xib: Control-click
on File's Owner and connect the tableView outlet to the table view in the window.
Also, connect the arrayController outlet to the array controller in the document
outline (or dock).
Next, in windowDidLoad, set the doubleAction and target of the table view:
override func windowDidLoad() {
super.windowDidLoad()

tableView.target = self
tableView.doubleAction = Selector("openClass:")

fetcher.fetchCoursesUsingCompletionHandler {
(result) in
Finally, implement openClass(_:). You will use the NSWorkspace class.
NSWorkspace represents the Finder, which knows how to open URLs in the user’s
default web browser.
func openClass(sender: AnyObject!) {
if let course =
arrayController.selectedObjects.first as? Course {

NSWorkspace.sharedWorkspace().openURL(course.url)
}
}
Note that you use the array controller to provide the selected objects. One of the
advantages of array controllers is that they can provide objects for display in a
different sort order (arrangedObjects) from the model (courses). It is easy to
forget this and use NSTableView’s selectedRow as an index into courses, which
would result in the wrong object being returned. The selectedObjects property
will neatly avoid this.
Build and run the application. Double-click on a row and the page for the
selected class should open in your default browser. If it does not, check that you
configured the Selection Indexes binding on the array controller.
Safely Working with Untyped Data
Structures
Congratulations, it works! But are you finished? Your application already
detects errors from JSON parsing (converting the received JSON data into a tree
of NSDictionary, NSArray, and so forth), but it does not check that the structure
of this data satisfies the expectations of the app’s code.
As it stands, the code makes some assumptions about the structure of the JSON.
Consider these lines:
let title = courseDict["title"] as! String
let urlString = courseDict["url"] as! String
let upcomingArray = courseDict["upcoming"] as!
[NSDictionary]
let nextUpcomingDict = upcomingArray.first!
let nextStartDateString =
nextUpcomingDict["start_date"] as! String
This code makes several assumptions:
courseDict has a key named upcoming whose value is an array, and

that array has at least one object in it, a dictionary, and


that dictionary has a key named start_date which is a string
…and so forth.
If there were a bug in the service, or if the API changed, the structure of the
received data could change and your app would crash.
But you can remove those assumptions by using if let optional binding and
taking advantage of the as? operator, which returns nil if the value cannot be
cast to the given type.
if let title = courseDict["title"] as? String,
let urlString = courseDict["url"] as?
String,
let upcomingArray = courseDict["upcoming"]
as? [NSDictionary],
let nextUpcomingDict = upcomingArray.first,
let nextUpcomingDict = upcomingArray.first,
let nextStartDateString =
nextUpcomingDict["start_date"] as? String
{
...
return Course(title: title, url: url,
nextStartDate: nextStartDate)
}
return nil
This code is much safer and gives you the opportunity to report very specific
errors (“expected array at key 'upcoming'”). Error checking is no fun and not
pretty, particularly when conditionally casting and checking for optionals. But
for a serious application, you will want to do it so that your app can recover
gracefully instead of crashing.
For the More Curious: Parsing XML
There are two standard approaches to parsing XML. The high-level method
(DOM parsing) is to use NSXMLDocument and NSXMLNode. If you have an NSData
object containing the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<people>
<person>
<first>Patrick</first>
<last>Robinson</last>
</person>
</people>
NSXMLDocument will parse it into a handy tree (Figure 28.7):

Figure 28.7 Parsed XML document

You can then traverse the tree manually or use XPath queries to extract the
nodes you want. For example, the following listing prints all of the first names
from the given XML document:
var xmlString = "<?xml version=\"1.0\" encoding=\"UTF-
8\"?>"
+ "<people>"
+ " <person><first>Jimmy</first>
<last>Smith</last></person>"
+ " <person><first>Lonnie</first>
<last>Smith</last></person>"
+ "</people>"

var error: NSError?
if let doc = NSXMLDocument(XMLString: xmlString,
if let doc = NSXMLDocument(XMLString: xmlString,
options: 0, error: &error) {
if let people = doc.nodesForXPath("//person",
error: &error) as [NSXMLNode]? {
for person in people {
let firstNameNodes =
person.nodesForXPath("first", error: &error)

as [NSXMLNode]
if let firstNameNode =
firstNameNodes.first {
println(firstNameNode.stringValue!)
}
}
}
}
NSXMLDocument provides a convenient way to query a document, but it can be
memory-intensive for large XML documents as it allocates node objects for each
node of the document.
If you want something a lot leaner, you will want to consider NSXMLParser, an
event-driven (SAX) parser. Instead of creating a tree, NSXMLParser makes calls to
its delegate as it encounters XML elements and other structures. You will need
to write a state machine to deal with these events and extract the data you want.
Another consideration in choosing between the two is portability. Of the two,
only NSXMLParser is available on iOS.
Challenge: Improve Error Handling
Add error handling to courseFromDictionary(_:) (as discussed in the section
called “Safely Working with Untyped Data Structures” ) such that it returns nil
if any of the fields or structure are not as expected. Test the failure modes by
changing the key strings in the code.
Challenge: Add a Spinner
Add an indeterminate progress indicator to the window. Animate it while the
ScheduleFetcher fetches the courses so that the user knows it is working.
Challenge: Parse the XML Courses Feed
Big Nerd Ranch also has a web service that delivers the data for its upcoming
classes as an XML document. Adapt ScheduleFetcher to parse the course data
from http://bookapi.bignerdranch.com/courses.xml.
29
Unit Testing
As you implement a new feature in your application, you will want to ensure that
the feature works the way that you intend it to. The fastest and most reliable way
to verify that your code works is to write automated tests and run them against
your code often. As you are building a new feature, you can repeatedly run your
automated tests to guarantee that the code that you have written results in the
behavior that you want.
Unit testing is a particular kind of automated testing in which the individual
components, the units, of your application are tested. These units are the
building blocks of your application. Typically, they are your application’s
classes.
The advantage of testing units is that you are able to validate each building block
on its own. You do not have to hook the component up to the rest of your app to
determine that it is functioning properly. Instead, you can run your tests on the
component in isolation as you modify it. Once you finish working on a
component and your tests have validated it, you can confidently snap it onto
other validated components in your application.
There are many kinds of testing, but in this book we will only focus on unit
testing.
Testing in Xcode
Open the RanchForecast project from Chapter 28. In this chapter, you will be adding
tests to RanchForecast.
Every Cocoa project that you create comes equipped with a test target, the
container for all your tests. RanchForecast is no exception.
In Xcode, open the project navigator. At the top level, you should see three
groups: RanchForecast, RanchForecastTests, and Products (Figure 29.1).
Figure 29.1 Groups in RanchForecast

Expand the RanchForecastTests group and select RanchForecastTests.swift. Inside,


you should see a subclass of XCTestCase called RanchForecastTests.
Xcode has automatically generated four methods in this class: setUp(),
tearDown(), testExample(), and testPerformanceExample().

When you run your tests, all of the methods in your XCTestCase classes prefixed
with the string test will be executed. In the case of the auto-generated
RanchForecastTests class, both testExample() and testPerformanceExample()
will be called.
A test method makes assertions about the state of the component that is being
tested after the component has been made to do some work. For example,
imagine that you had a Car class in a game you were writing. How would you
test that it accelerates properly? You might write a test like this one:
func testAccelerateTo75() {
let speed = 75
let speed = 75
car.accelerateToSpeed(speed)
XCTAssertEqual(car.speed, speed)
}
First, you would tell an instance of Car to accelerate up to seventy-five miles per
hour – you would make the component do some work. Then, you would assert
that the Car’s speed had in fact changed to seventy-five miles per hour – you
would make an assertion about its state. If the Car’s velocity was not 75mph,
then the test would fail. In general, if the assertions that you make are not
satisfied, then the test that they are made in fails.
These assertions are made by way of the assert functions contained in the
XCTest framework. Here are a few of the assert functions that are commonly
used:
XCTAssertTrue(expression)
XCTAssertEqual(testResultValue, expectedValue)
XCTAssertNotNil(expression)
XCTFail()
When any of these assertions fail, a message describing what went wrong is
generated based on the kind of assertion. You can customize the message by
supplying your own as an additional parameter. This is especially important for
XCTFail. For example:
if let value = dictionary["key"] as? String {
XCTAssertEqual(value, "Value", "dictionary not
properly populated")
}
else {
XCTFail("key not found in dictionary, or value not
a String")
}
To see a complete listing of assertions in the XCTest framework, look in the
Assertions Listed by Category section of Apple’s Testing with Xcode guide.
Your First Test
It is time to start using these asserts.
You will be writing unit tests against RanchForecast, tests that verify that the classes
in the app work individually. If you were writing tests for app-wide
functionality, you might put them in RanchForecastTests.swift. However, as it
is you do not have any use for this file since all of your tests will be class-
specific. Go ahead and delete the file.
The first class that you will write tests for is Course. To make it obvious which
class you are testing in a given test, you will create a separate test file and
XCTestCase subclass for each of the classes in the project. In the project
navigator, right-click on the RanchForecastTests group, and select New File....
Create a new file using the Swift File template. Name the file CourseTests.swift.
At this point, Xcode may ask you whether you would like to configure an
Objective-C bridging header. Select No.
Open CourseTests.swift. At the moment, it contains almost no code. Add an
empty definition for a new subclass of XCTestCase called CourseTests. This will
require you to import XCTest, the framework where XCTestCase is defined.
import Foundation
import XCTest

class CourseTests: XCTestCase {

}
The first test you will write will be very simple: You will create a course object
with a title, a URL, and a date. You will then check that the course’s fields
match the values that you created it with. Create a test called
testCourseInitialization():
class CourseTests: XCTestCase {

func testCourseInitialization() {

}
}
Try running your tests at this point. In Xcode, open the Product menu, and select
Test. (You can also press Command+u.) Your tests should run very quickly, and
you should see a grey overlay (Figure 29.2) indicating that your tests passed.
Figure 29.2 Success

In CourseTests, you should see two little green checkboxes, one indicating that
testCourseInitialization() passed and one indicating that all of the tests in
CourseTests passed.

Make sure the navigtor area is expanded and select the diamond to display the
test navigator. In the test navigator, you should see an entry for the test target,
RanchForecastTests, an entry for each of your XCTestCase subclasses – for now, just
CourseTests – and an entry for each of the tests inside each of the XCTestCase
subclasses – in this case, just testCourseInitialization():
Figure 29.3 Test navigator
Your test testCourseInitialization() passes right now because it does not
make any incorrect assertions. But that does not mean too much, because it does
not make any assertions at all! As a result, nothing is being tested currently. In a
moment, you will address this by giving the test an implementation and by
having it make some assertions.
First, though, add a new Swift file called Constants.swift to the RanchForecastTests
target. Create a struct named Constants. In that struct, define the following
constants:
import Foundation

class Constants {
static let urlString =
"http://training.bignerdranch.com/classes/test-course"
static let url = NSURL(string: urlString)!
static let title = "Test Course"
static let date = NSDate()
}
You will use these constants in your implementation of
testCourseInitialization(). You will learn more about using a constants file in
the section called “Sharing Constants” , later in the chapter. Switch to
CourseTests.swift and add the following lines:
func testCourseInitialization() {
let course = Course(title: Constants.title,
url: Constants.url,
nextStartDate: Constants.date)

XCTAssertEqual(course.title, Constants.title)
XCTAssertEqual(course.url, Constants.url)
XCTAssertEqual(course.nextStartDate,
Constants.date)
}
Your test first sets up an instance of Course, providing a title, URL, and date to
the initializer. It then asserts that each field that the Course object contains is
equal to the corresponding object you passed into the initializer.
Try running your tests now. Xcode will give you a build error: “Use of unresolved
identifier 'Course'”. The problem is that the Course class is not visible from the
CourseTests class. This is caused by two factors, both related to the fact that the
classes Course and CourseTests are in separate modules.
The first problem is that the RanchForecast module is not currently visible from
CourseTests.swift. This means that none of the classes that RanchForecast
exports can be seen or used from CourseTests.swift. To fix this, import the
RanchForecast module into CourseTests.swift:
import XCTest
import RanchForecast
Now all the classes exported by the RanchForecast module are visible from
CourseTests.swift. But this by itself is not enough to fix the error presented
when trying to run the tests.
The second problem is that the RanchForecast module does not currently export
the Course class; in other words, it does not currently make the Course class
accessible from other modules. By default, in Swift, all classes, functions,
methods, and properties are internally accessible; in other words, they implicitly
have the internal access modifier. The internal access modifier means
“accessible from within the same module”, so the default behavior is that every
class, function, method, and property is only accessible from within the same
module – unless specified otherwise. In particular, Course is currently only
accessible from within the RanchForecast.
To address this, you will mark the Course class – as well as the members you are
using in testCourseInitialization() – as public. Open Course.swift and
make the following changes:
public class Course: NSObject {
public let title: String
public let url: NSURL
public let nextStartDate: NSDate
public init(title: String, url: NSURL,
nextStartDate: NSDate) {
self.title = title
self.url = url
self.nextStartDate = nextStartDate
super.init()
}
}
Now try running your tests. Xcode should successfully build the test target and
present the “Test Succeeded” dialog.
Due to the very limited functionality of the Course class, you now have complete
test coverage for it. You have tested all of its methods with every input that
results in different behavior.
A Note on Literals in Testing
When writing tests, it can often at first seem more convenient to use literals
(such as "tomato" and 54). For example, rather than creating separate variables
for each of the arguments, it may at first seem easier to write the following:
let course = Course(title: "Name",
url: NSURL(string:
"http://bignerdranch.com/")!,
nextStartDate: NSDate())
The problem with this approach, of course, is that you have to use another literal
later on when you want to use the value again:
XCTAssertEqual(course.title, "Name")
This is a problem because there is no automated checking that you used the same
literal every time. When you use a constant, by contrast, the compiler will
complain if you misspell its name. For this reason, stylish Swift programmers
use named constants rather than literals in their tests.
Creating a Consistent Testing Environment
Next, you will write tests for ScheduleFetcher. To get started, create a new Swift
source file in the RanchForecastTests called ScheduleFetcherTests.swift. In the file,
add a new class ScheduleFetcherTests, a subclass of XCTestCase.
import Foundation
import XCTest
import RanchForecast

class ScheduleFetcherTests: XCTestCase {

}
Before you can write any tests against ScheduleFetcher, you need to make sure
that the class itself is visible from the RanchForecastTests target. You already
imported the RanchForecast target into ScheduleFetcherTests.swift, but you still
need to mark the class and its initializer public. Edit ScheduleFetcher.swift as
follows:
public class ScheduleFetcher {

enum FetchCoursesResult {
case Success([Course])
case Failure(NSError)
}

let session: NSURLSession

public init() {
You will be writing a number of tests against ScheduleFetcher. Each of those
tests should have a fresh instance of ScheduleFetcher. Having a fresh instance of
ScheduleFetcher for each test guarantees that each test is independent from the
others.
XCTest provides a convenient mechanism for setting up and tearing down an
instance of the class being tested before and after each test in the form of the
methods setUp() and tearDown(). The setUp() method is called before each test
is run and tearDown() is called after. Add a ScheduleFetcher property to
ScheduleFetcherTests and implement setUp() and tearDown() to initialize and
nil out the property:
class ScheduleFetcherTests: XCTestCase {

var fetcher: ScheduleFetcher!

override func setUp() {


super.setUp()
fetcher = ScheduleFetcher()
}

override func tearDown() {


fetcher = nil
super.tearDown()
}

}
At this point, you still have not added any tests to ScheduleFetcherTests. If you
run all your tests in Xcode now, no code in ScheduleFetcherTests.swift will be
executed. Shortly, though, you will add several tests to ScheduleFetcherTests.
When you do add tests, each will run against its own distinct instance of
ScheduleFetcher. Before each test runs, setUp() will be run and fetcher will be
initialized to a new instance of ScheduleFetcher. After each test runs and before
the next test runs, tearDown() will be run and fetcher will be nil’d out. So the
code that you just wrote gives each test a blank slate – a fresh instance of
ScheduleFetcher – to work with.

The first method of ScheduleFetcher that you will test is


courseFromDictionary(_:). Currently that method is not visible from the
RanchForecastTests target. Open ScheduleFetcher.swift and mark the method
public:
public func courseFromDictionary(courseDict:
NSDictionary) -> Course? {
let title = courseDict["title"] as! String
let urlString = courseDict["url"] as! String
You will test that courseFromDictionary(_:) correctly handles a valid JSON
dictionary. To do this, you need to have a valid JSON dictionary. Add one at the
top of Constants.swift:
import Foundation

struct Constants {

static let urlString =


"http://training.bignerdranch.com/classes/test-course"
static let url = NSURL(string: urlString)!
static let title = "Test Course"

static let dateString = "2014-06-02"


static let dateFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
static let date = NSDate()
static let date =
dateFormatter.dateFromString(dateString)!

static let validCourseDict


= [ "title" : title,
"url" : urlString,
"upcoming" : [["start_date" :
dateString]]]
}
Rather than using literals for the values within the dictionary, the dictionary’s
values have been pulled out into separate constants – except for title, which
was already defined in CourseTests.swift. This allows you to test that the
created Course object has the right fields.
Notice that date was redefined to be a specific date rather than the current date.
The date constant is also being used by CourseTests, but any NSDate at all can be
used there. Reusing date allows you to avoid having an extra NSDate constant
floating around unnecessarily.
Open CourseTests.swift and add a new test method
testCreateCourseFromValidDictionary() that uses this dictionary:
func testCreateCourseFromValidDictionary() {
let course: Course! =
fetcher.courseFromDictionary(Constants.validCourseDict)

XCTAssertNotNil(course)
XCTAssertEqual(course.title, Constants.title)
XCTAssertEqual(course.url, Constants.url)
XCTAssertEqual(course.nextStartDate,
Constants.date)
}
Run your tests. They should all pass, including the one you just added. Set
breakpoints in setUp(), testCreateCourseFromValidDictionary(), and
tearDown() and run the tests again. Notice that setUp() is called first, then
testCreateCourseFromValidDictionary(), and finally tearDown().
Sharing Constants
All of the constants that you have used so far you have put into Constants.swift.
It would have been technically possible to add each of these constants to the top
of the test file that you used them in. However, as you have already seen, it is
quite common to use the same constants in multiple tests and in multiple test
files. On the one hand, if you do not put constants in the files where they are first
used, they will be visible from other test files – so long as you do not mark them
private. On the other hand, if you follow this approach, your shared constants
will be scattered throughout your test files and harder to track down.
For this reason, stylish Swift programmers put their shared test constants into a
separate file (or files). If they need constants to be shared only among the tests in
a single file, they mark them private and put them inside that file.
These constants are test fixtures which are shared across a number of tests. A test
fixture is the context in which a test is run; it is made up of all the objects that
the test uses.
Besides these constants, you have already seen another kind of test fixture in
ScheduleFetcherTests.swift. The fetcher property is a test fixture which is set
up implicitly before each test in which ScheduleFetcherTests is run and torn
down (implicitly) afterwards.
Generally, it is very important for tests to operate on different test fixtures.
Otherwise configuration may persist from one test to another, making your tests
be dependent on the ones that were run beforehand rather than standalone.
Making sure your tests do not rely on one another is critical: If a test is
dependent on other tests, it will be unclear what state the fixtures are in when the
test runs. As a result, it will be unclear what exactly the test is testing. Instead,
your each of your tests should be independent, with their fixtures configured
either in setUp() or in the body of the test itself.
This rule only applies to shared fixtures which can be changed by the tests. If a
shared fixture is immutable, then of course it cannot be modified by the tests.
Shared immutable fixtures, such as the constants in Constants.swift, are very
helpful. They allow you to set up only once the components you need to use in
multiple tests that you always want to be configured in the same way.
Refactoring for Testing
When you write an application without testing in mind, you will often write code
that is difficult to test. In Chapter 28, you implemented ScheduleFetcher’s
method fetchCoursesUsingCompletionHandler(_:). In that implementation, you
call NSURLSession.dataTaskWithRequest(_:completionHandler:). and pass in a
complex completion handler which transforms the triple of data, response, and
error into a FetchCoursesResult.

As the method is currently implemented, the logic of the completion handler is


very difficult to test: testing it requires calling
fetchCoursesUsingCompletionHandler(_:) which entails calling an
asynchronous HTTP method. To avoid making an HTTP request, you will
refactor ScheduleFetcher.
In ScheduleFetcher.swift, add the beginnings of a new method:
public func resultFromData(data: NSData!, response:
NSURLResponse!, error: NSError!)
-> FetchCoursesResult {
var result: FetchCoursesResult

return result
}
Currently, this method does not compile because result is never defined. In a
moment, you will cut the core of the completion handler from
fetchCoursesUsingCompletionHandler(_:) and paste it into this method. Once
you have done that, this method will contain the logic for transforming the result
of the attempted HTTP request into a FetchCoursesResult.
In the body of the fetchCoursesUsingCompletionHandler(_:) method, select the
whole conditional block, from if data == nil { to the closing right brace of
the final else block. Cut those lines by clicking Edit → Cut.
func
fetchCoursesUsingCompletionHandler(completionHandler:

(FetchCoursesResult) -> (Void)) {
let url = NSURL(string:
"http://bookapi.bignerdranch.com/courses.json")!
let request = NSURLRequest(URL: url)
let task = session.dataTaskWithRequest(request,
completionHandler: {
(data, response, error) -> Void in
var result: FetchCoursesResult
if data == nil {
result = .Failure(error)
}
else if let response = response as?
NSHTTPURLResponse {
println("\(data.length) bytes, HTTP \
(response.statusCode).")
if response.statusCode == 200 {
result = .Success([]) // Empty array
until parsing is added
}
else {
let error = self.errorWithCode(1,
localizedDescription:
"Bad status code \
(response.statusCode)")
result = .Failure(error)
}
}
else {
let error = self.errorWithCode(1,
localizedDescription:

"Unexpected response object.")
result = .Failure(error)
}

NSOperationQueue.mainQueue().addOperationWithBlock({
completionHandler(result)
})
})
task.resume()
}
With this code still in your clipboard, paste it into
resultFromData(_:response:error:).
public func resultFromData(data: NSData!, response:
NSURLResponse!, error: NSError!)
-> FetchCoursesResult {
var result: FetchCoursesResult
if data == nil {
result = .Failure(error)
}
else if let response = response as?
NSHTTPURLResponse {
println("\(data.length) bytes, HTTP \
(response.statusCode).")
if response.statusCode == 200 {
result = .Success([]) // Empty array
until parsing is added
}
else {
let error = self.errorWithCode(1,
localizedDescription:
"Bad status code \
(response.statusCode)")
result = .Failure(error)
}
}
else {
let error = self.errorWithCode(1,
localizedDescription:
"Unexpected
response object.")
result = .Failure(error)
}
return result
}
Now, call resultFromData(_:response:error:) from the completion handler in
fetchCoursesUsingCompletionHandler(_:):
func
fetchCoursesUsingCompletionHandler(completionHandler:

(FetchCoursesResult) -> (Void)) {
let url = NSURL(string:
"http://bookapi.bignerdranch.com/courses.json")!
let request = NSURLRequest(URL: url)
let task = session.dataTaskWithRequest(request,
completionHandler: {
(data, response, error) -> Void in
var result: FetchCoursesResult
= self.resultFromData(data, response:
response, error: error)

NSOperationQueue.mainQueue().addOperationWithBlock({
completionHandler(result)
})
})
task.resume()
}
Currently, this does not compile. The problem is that the method
resultFromData(_:response:error:) is public but returns a
FetchCoursesResult, whose visibility is currently internal. A method must not
be any more visible than the type of any of its parameters or its return type. To
fix this, mark FetchCoursesResult as public.
public enum FetchCoursesResult {
case Success([Course])
case Failure(NSError)
}
Now make sure that your app compiles and runs as before.
At this point, it is much easier to test the transformation from (data, response,
error) to FetchCoursesResult. You will do so by calling
resultFromData(_:response:error:) with valid JSON data, an ‘OK’ HTTP
response, and no error. To write this test, you will need to have JSON data and a
response to pass. Open Constants.swift and add three new immutable shared
fixtures for the JSON data and the HTTP response inside the Constants struct:
static let coursesDictionary = ["courses" :
[validCourseDict]]
static let okResponse = NSHTTPURLResponse(URL: url,
statusCode: 200,
HTTPVersion: nil,
headerFields: nil)

static let jsonData =


NSJSONSerialization.dataWithJSONObject(coursesDictionary,


options: .allZeros,

error: nil)!
You can now implement the test using these fixtures. Open
ScheduleFetcherTests.swift and add a new test case:
func testResultFromValidHTTPResponseAndValidData() {
let result =
fetcher.resultFromData(Constants.jsonData,
response:
Constants.okResponse,
error: nil)

switch result {
case .Success(let courses):
XCTAssert(courses.count == 1)
let theCourse = courses[0]
XCTAssertEqual(theCourse.title,
Constants.title)
XCTAssertEqual(theCourse.url, Constants.url)
XCTAssertEqual(theCourse.nextStartDate,
Constants.date)
default:
XCTFail("Result contains Failure, but Success
was expected.")
}
}
The test uses resultFromData(_:response:error:), the method you added to
ScheduleFetcher, to create a FetchCoursesResult. It then verifies that the result
is as it should be: that all of its fields are equal to the appropriate values from
Constants.swift. If the result does not match FetchCoursesResult.Success,
then XCTFail is called.
Although you have not passed explicit error messages to the other asserts you
have used, you should always specify one when calling XCTFail. Other asserts
are able to provide some context about the reason for their failure, but XCTFail is
unable to.
You had to do a bit of refactoring to bring this section of code under test. Once
you get into the habit of writing tests for your code as or before you write your
code, you will be able to identify hard-to-test patterns quickly and use patterns
that better lend themselves to testing.
For the More Curious: Access Modifiers
As you have already seen in this chapter, Swift has a mechanism for controlling
access to a class, method, or property – among other things. This is done via
access modifiers, keywords which are applied to the entities whose visibility is
being configured.
In Swift, there are three levels of visibility and three corresponding keywords by
which the visibilities are specified:
internal
A class, method, or other entity which has internal visibility can be seen from
all files inside the same module as the class. An entity with internal visibility
cannot, however, be seen from files in a different module.
internal is the default level of visibility for an entity. When you first add a
class to your app target in a new file, all other files in your app target will be
able to use the class – until you change the visibility.
Since it is the default visibility, the main usage of the internal keyword is to be
explicit about an entity’s visibility:
internal struct Constants { ... }
internal struct Constants { ... }
public
An entity with public visibility can be seen from any module which imports the
module where the public entity is defined.
Consider the places you have used the public keyword over the course of this
chapter. In almost all of those usages, you added the public keyword to make
classes, methods, and properties from the RanchForecast app target visible from the
RanchForecastTests test target. For example, you marked the Course class public:
public class Course: NSObject { ... }

Marking the class public allowed it to be seen from the test target. This in turn
allowed you to write tests against it.
public class Course: NSObject { ... }
private
Entities with private visibility can only be seen from within the same file where
the entity is defined.
This can be useful to ensure that client code does not muck about with your
class’s internals. When you mark methods and properties private, you clarify
which methods and properties are part of your class’s API (those which are not
marked private).
There are a number of subtle constraints which are imposed on an entity’s
visibility depending on the visibility of the entities it is related to. For example, a
method or function cannot be more visible than the least visible parameter or
return type. This prevents issues such as a public function returning a private
type. There are several more constraints, and they are discussed in detail in
Apple’s Swift book.
Swift’s access control (or at least its current implementation) puts testing and
API design in tension. Typically, when designing a class, you want to be very
selective about which methods it exposes to its users through its API. This
means marking many things private. On the other hand, when testing that class,
you often want to test that the implementation details of your class function
properly. This means that you must mark those implementation details public so
that they are visible from the test target.
The approach adopted in this book, which you used over the course of this
chapter, is to sacrifice interface purity for practicality and to mark methods
public whenever you want to test them. The downside of this approach is that
you do not get to make use of different visibility levels to control which of your
class’s methods and properties should be used by client code. The upside of this
approach is that you are testing the code in your app module, and that all of that
code can be tested.
For the More Curious: Asynchronous Testing
Suppose that you want to test a method like the following:
func
doWorkInBackgroundWithCompletion(completionHandler:
(Bool, NSError?) -> (Void))
This method takes a completion handler which returns asynchronously,
sometime after the function returns. You would like to test that this function
does eventually call its completion handler, and that when the completion
handler is called, it is passed true for its first argument, and nil for its second.
The techniques that have been discussed so far in the chapter do not allow for
testing this method. For testing an asynchronous method, XCTest provides
XCTestExpectation.

You create an XCTestExpectation from within a test case using XCTestCase’s


method expectationWithDescription(_:):
let expectation =
self.expectationWithDescription("doWork completes")
After you have created your expectation, you then call the asynchronous method.
In the completion handler, you call fulfill() on the expectation. This lets the
expectation know that it has been satisfied.
worker.doWorkInBackgroundWithCompletion { (success,
error) in
XCTAssertTrue(success)
XCTAssertNil(error)
expectation.fulfill()
}
Finally, at the end of your test implementation, you call
waitForExpectationsWithTimeout(_:handler:) and provide a reasonable timeout
interval:
func testDoWork() {
let expectation =
self.expectationWithDescription("doWork completes")
worker.doWorkInBackgroundWithCompletion {
(success, error) in
(success, error) in
XCTAssertTrue(success)
XCTAssertNil(error)
expectation.fulfill()
}
self.waitForExpectationsWithTimeout(1, nil)
}
Supposing that the completion handler is called, the test will fail if success is not
true or if error is not nil. The test will also fail if the expectation is not
fulfilled within 1 second. In other words, the test will fail if
doWorkInBackgroundWithCompletion(_:) does not complete within 1 second.

Besides this general mechanism for asynchronous testing, XCTestCase also


provides a couple of conveniences for common asynchronous tasks. The method
expectationForNotification(_:object:handler:) allows you to create an
expectation which will be (automatically) fulfilled when the notification is
posted. If you want finer grained control over whether a given notification
should fulfill the expectation, you can provide a completion handler and call
fulfill() on the expectation yourself.

In addition to notifications, you can also create expectations for KVO using
keyValueObservingExpectationForObject(_:keyPath:expectedValue:). This
method creates an expectation which will be automatically fulfilled when the
specified object changes the value at the specified keyPath.
Challenge: Make Course Implement
Equatable
You have implemented several tests (testCourseInitialization(),
testCreateCourseFromValidDictionary(),
testResultFromValidHTTPResponseAndValidData(_:), testFetcherSuccess())
that compare the properties of Course objects to determine that they are equal.
Make Course adopt the Equatable protocol allowing instances of Course to be
compared. Take advantage of this by using XCTAssertEqual in your tests.
To make Course adopt Equatable you will need to implement a free function
with this signature: public func ==(lhs: Course, rhs: Course) -> Bool
Challenge: Improve Test Coverage of Web
Service Responses
In ScheduleFetcherTests, you currently only have a single test
(testResultFromValidHTTPResponseAndValidData(_:)) against
ScheduleFetcher’s method resultFromData(_:response:error:). This test
verifies the behavior of the method when it is passed valid JSON data and an OK
(200) HTTP response.
Write two more tests against resultFromData(_:response:error:). In the first
test, call the method with a 404 response and no data. In the second test, call the
method with a 200 response and invalid JSON data.
Invalid JSON data here means data that cannot be used by NSJSONSerialization.
One way to create invalid JSON data is by turning a string into data:
let invalidJsonDataString = "This string will not
transform into valid JSON data"
let invalidJsonData =
invalidJsonDataString.dataUsingEncoding(NSUTF8StringEncoding,


allowLossyConversion: true)!
Challenge: Test Invalid JSON Dictionary
The first challenge in Chapter 28 was to improve the handling of faulty JSON by
ScheduleFetcher’s courseFromDictionary(_:) method.

In this chapter, you only tested that the method behaved correctly when it was
provided with valid JSON. Create another test where you pass invalid JSON to
the method. Assert that the Course object that is returned is nil.
Think about how much easier it would have been to complete the original
challenge if you were able to run your new unit test that passes invalid JSON to
courseFromDictionary(_:) as you were implementing the method. Having tests
makes writing code easier!
30
View Controllers
Cocoa user interfaces have changed a lot over the years. For many years, it was
not uncommon for a complicated application to have a number of windows, one
for each functional area of the application. NSWindowController is a sufficient
building block for applications like this: one controller for each window, perhaps
with responsibility farmed out to smaller controller objects such as
NSArrayController. Nowadays it is much more common for the bulk of an
application’s UI to be composed within a single window. Applications like Pages,
Numbers, Keynote, and Xcode are examples of this.

Consider the project window in Xcode (Figure 30.1). Imagine implementing this
window with an NSWindowController. It would contain hundreds of outlets and
action methods, countless bindings, auxiliary properties and helper methods.
Thankfully this is just a thought experiment, as Cocoa provides a solution to
make this problem much more manageable: view controllers. View controllers
are much like window controllers, except that they are concerned with
controlling the contents of a view.
Figure 30.1 The Xcode project window

To understand how view controllers are used, first consider how the view
hierarchy of the Xcode window is constructed (Figure 30.2). Below the toolbar, at
the top level there is a split view containing the navigator, center, and utility
areas. The center area is split between the editor and debug areas. The utility
area is split between the inspector and library panes. The individual navigators,
inspectors, and libraries all have their own views.
Figure 30.2 Xcode’s project window is composed of a hierarchy of views
and view controllers

Wouldn’t it make sense to have a separate controller for each of these areas? A
controller for the navigator area, a controller for the inspector area, a controller
for the debugger, and one for each type of editor. Furthermore, it would be ideal
if each of the navigator and inspector tabs had its own controller.
One controller for each chunk of the view hierarchy: a logical place for all of the
action methods, outlets, helpers and so forth that pertain to that area of the UI.
View controllers are that controller, and in Cocoa the NSViewController class
serves this purpose.
NSViewController
As with NSWindowController, you employ view controllers in your project by
subclassing NSViewController. The view hierarchy is lazily loaded. When the
view property is accessed, the view controller calls loadView(), which defaults to
loading the view from a NIB file. However, you can override loadView() to
create the view programmatically, as you will see in Chapter 31. Just like
window controllers, except that the view controller has a view outlet. Consider
the parallels to NSWindowController as you read over this subset of the
NSViewController API:
class NSViewController : NSResponder {

var nibName: String? { get }
var representedObject: AnyObject?
var title: String?
var view: NSView

func loadView()

// Added in OS X 10.10:
func viewDidLoad()
func viewWillAppear()
func viewDidAppear()
func viewWillDisappear()
func viewDidDisappear()

...
}
Starting the ViewControl Application
In Xcode, create a new Cocoa Application named ViewControl. Uncheck Use Storyboards,
Create Document-Based Application, and Use Core Data.

In the first phase of the ViewControl application, you will create a view controller
to display an image. The view controller will be presented as the content view
controller of a window (Figure 30.3). That is, the view controller will manage
the contents of the window.
Figure 30.3 Object diagram for the ViewControl application

Open MainMenu.xib and delete the window supplied by the template. You will
create your own window programmatically in the AppDelegate.
Next, create a new Cocoa Class named ImageViewController. Set it to subclass
NSViewController and check Create XIB file. As with subclassing
NSWindowController, this creates two files: ImageViewController.swift and
ImageViewController.xib.
To tell the view controller which NIB file to open, you will override the nibName
property. Open ImageViewController.swift and make this change. While you
are there, define the image property which you will soon bind the image view to.
class ImageViewController: NSViewController {

var image: NSImage?

override var nibName: String? {


return "ImageViewController"
}

override func viewDidLoad() {


super.viewDidLoad()
// Do view setup here.
}

}
Aside: In 10.10, the default NIB name is the name of the class, which should
mean that you do not need to override this. Unfortunately this only works well
for Objective-C because the string name of this Swift class includes the module
name, which in this case is ViewControl.ImageViewController.
Open ImageViewController.xib. As with window controllers, File's Owner
represents the view controller. Control-click on File's Owner to open the
connections panel. You will see that the view outlet is connected to the view in
the XIB file.
Drag an Image View onto the view and size it to fill the view. Use Auto Layout to
pin it to the edges of the view. Select the image view and switch to the attributes
inspector. Set Scaling to Proportionally Up or Down.
With the image view still selected, in the bindings inspector bind the Value of the
image view to the image property on the view controller. Remember, File's
Owner represents the view controller in this context. Therefore you must bind
the image view’s Value to File's Owner with a Controller Key of image (Figure 30.4).
Figure 30.4 Binding image view's Value attribute to File's Owner
In AppDelegate.swift, change the window outlet to an optional window property.
Then, in applicationDidFinishLaunching(_:), create an instance of the view
controller, assign the image property, and create and show a window with this
new view controller as its contents:
@IBOutlet weak var window: NSWindow!
var window: NSWindow?

func applicationDidFinishLaunching(aNotification:
NSNotification) {
let flowViewController = ImageViewController()
flowViewController.title = "Flow"
flowViewController.image = NSImage(named:
NSImageNameFlowViewTemplate)

let window = NSWindow(contentViewController:


flowViewController)
window.makeKeyAndOrderFront(self)
self.window = window
}
Run the application. You will see a window containing the view controller with
the flow image (Figure 30.5).
Figure 30.5 ImageViewController displayed in a window
If you do not see the window, check the console for an error message, as well as
the nibName override in ImageViewController.swift.
Consider what you have done so far in this exercise. From ten thousand feet, the
structure of the code is not dissimilar from a window controller-based
application, and in the ViewControl application as it stands right now there are not
many wins for using a view controller. As you will soon see, however, designing
an application to use view controllers provides a level of flexibility and
modularity that window controllers alone cannot.
Windows, Controllers, and Memory
Management
Recall that you changed the weak window outlet to be a strong window property.
Without a strong reference, the window will be deallocated (and thus closed).
In past exercises, you have kept a strong reference to the window controller.
This keeps the window in memory because the window controller has a strong
reference to its window. The same is true of view controllers and their views.
The window has a strong reference to its contentViewController.
Meticulous readers may notice that the window outlet provided by the template
was weak. So what was keeping a strong reference to the window when it was
loaded from MainMenu.xib? The answer is that top-level objects loaded from a
NIB file have a +1 retain count. This is accounted for when window and view
controllers load their windows and views.
Container View Controllers
Master/detail is a very common user interface idiom. The interface is split in
two: a list of items to select from on the left (master) and a view for the selected
item on the right (detail). You can see this in many apps. In Xcode the navigator is
the master view and the editor is the detail view.
Common interface arrangements are an excellent opportunity for reusable
software components, and Cocoa provides a container view controller,
NSSplitViewController, to aid in building master/detail interfaces.

Container view controllers, themselves NSViewController subclasses, manage


the display of a collection of child view controllers. How they display their
children depends upon the nature of the specific container. For example,
NSSplitViewController’s view is an NSSplitView and its child view controllers
serve as the panes of the split view. You will see how to use
NSSplitViewController to build a master/detail interface in Chapter 32.

Another container view controller, NSTabViewController, displays its children as


the tabs of an NSTabView. You will learn how to write your own container view
controller in the next chapter, Chapter 31.
Container view controllers provide the framework for the modularity that view
controllers enjoy. By writing UI components of your application as view
controllers you can more easily rework their location within the interface as well
as potentially reuse them.
If you have programmed on iOS before, you may be familiar with this concept.
iOS makes heavy use of container view controllers in the form of navigation
controllers, tab bar controllers, and so forth. Container view controllers were
added in OS X 10.10.
Add a Tab View Controller
Now you will adjust the ViewControl application to display not one but two
ImageViewController instances, contained within an NSTabViewController
(Figure 30.6).
Figure 30.6 Object diagram for the ViewControl application with the
NSTabViewController

In AppDelegate.swift, create the second image view controller as well as the tab
view controller:
func applicationDidFinishLaunching(aNotification:
NSNotification) {
let flowViewController = ImageViewController()
flowViewController.title = "Flow"
flowViewController.image = NSImage(named:
NSImageNameFlowViewTemplate)
let columnViewController =
ImageViewController()
columnViewController.title = "Column"
columnViewController.image = NSImage(named:
NSImageNameColumnViewTemplate)

let tabViewController = NSTabViewController()



tabViewController.addChildViewController(flowViewController)


tabViewController.addChildViewController(columnViewController)

let window = NSWindow(contentViewController:


tabViewController)
let window = NSWindow(contentViewController:
flowViewController)
window.makeKeyAndOrderFront(self)
self.window = window
}
Run the application. Note that NSTabViewController uses the title of the view
controllers to populate the tabs’ labels. The contentViewController’s title is
used as the window title.
How does the tab view controller know what view to display when one of its
tabs is selected? Each tab is associated with one of its child view controllers.
When one of its tabs is selected, the tab view controller asks the corresponding
child view controller for its view, and displays that.
In this chapter you have programmatically created a hierarchy of parent and
child view controllers. You can configure the relationships graphically as well,
using Cocoa storyboards, which you will learn about in Chapter 32.
View Controllers vs. Window Controllers
Now that you have seen the two major controller patterns for Cocoa, you may be
wondering: how do you know which one to use when? Furthermore, why are we
only just now introducing view controllers?
For most of the applications in this book – applications with spartan, relatively
static interfaces, unlikely to be developed past 1.0 – a window controller is
perfectly suitable. Their controllers have been relatively focused and uncluttered.
Real world applications, however, like the kind you intend to write, will not be
so simple. Their interfaces will have multiple areas of focus, they will grow over
time and perhaps even be touched by several Cocoa programmers. For these
applications, we suggest using window controllers and view controllers together.
These window controllers will be relatively thin and focused on matters specific
to the window (the window is about to close – what do I need to do?) or
handling actions that are global to the window. The view controllers will focus
on their own areas of the interface. In other words, splitting responsibility.
Why not build everything using this hybrid approach of both window and view
controllers? Because there is a cost to using view controllers, particularly when
you need to enable them to talk to each other. For trivial applications, it can
distract from just making it work. For real-world sophisticated applications with
a long life in front of them, the investment will pay off in dividends.
Considerations for OS X 10.9 and Earlier
Prior to OS X 10.10, view controllers on OS X were relatively simple and,
frankly, limited:
View controllers were not part of the responder chain.
View life cycle methods such as viewDidLoad(), viewWillAppear(),
viewWillDisappear(), and so forth were not available.

Cocoa did not provide any container view controllers.


NSWindow’s contentViewController was not available.

Despite these limitations, if you are targeting OS X 10.9 and earlier and your
application would benefit from view controllers, we believe it is worth the extra
effort to work around these limitations and use view controllers.
You can include a view controller in the responder chain manually by patching it
in. This should be done whenever the view controller’s view is added to the view
hierarchy. For example, when setting an NSBox’s contentView to the view of a
view controller.
The following extension on NSViewController makes this a snap:
extension NSViewController {

func patchResponderChain() {
if view.nextResponder != self {
let resp = view.nextResponder
view.nextResponder = self
nextResponder = resp
}
}

}
Here is how you might use it:
override func viewDidLoad() {
super.viewDidLoad()
box.contentView = someViewController.view
someViewController.patchResponderChain()
someViewController.patchResponderChain()
}
Challenge: SpeakLineViewController
Create a new NSViewController subclass that contains the functionality of the
SpeakLine exercise you created back in Chapter 6. Add a new tab to the
NSTabViewController containing the SpeakLine UI. All of your SpeakLine code
should be confined to the SpeakLineViewController.
Challenge: Programmatic View Controller
ImageViewController is simple enough that it does not benefit much from having
a XIB file. Create your own ImageViewController which is purely programmatic.
Override loadView() to build the view(s) that you need and assign the root of
that hierarchy to the view property. Do not call super.loadView(), as it will
attempt to load the view hierarchy from a NIB file. As we mentioned at the
beginning of the chapter, loadView() is called to lazily load the view hierarchy
the first time the view property is accessed.
If you need a hint, see NerdTabViewController in Chapter 31.
Challenge: Add a Window Controller
In the exercise above you created a window with a content view controller, but
did not create a window controller. In fact, NSWindowController also has a
contentViewController property, which mirrors the window’s
contentViewController. Create an NSWindowController subclass called
MainWindowController and move the code for instantiating the view controller
within it.
31
View Swapping and Custom Container View
Controllers
In the last chapter you used NSTabViewController to allow the user to flip
between two view controllers. As we discussed, NSTabViewController is an
example of a container view controller, which is a view controller that manages
the display of its child view controllers’ views. NSTabViewController displays its
child view controllers by means of an NSTabView. The controller handles
populating the tab view with the contents of the child view controllers as needed.
But how does the tab view work?
View Swapping
When the tab view changes between tabs it uses view swapping, which is a fancy
way of saying that it changes out its subviews. It removes the view for the pane
that it had been showing and adds the new view. The view hierarchy
management methods discussed in Chapter 17, addSubview(_:) and
removeFromSuperview(), do the heavy lifting.

The following is a basic implementation of the technique:


class NerdBox: NSView {

var contentView: NSView?

func showContentView(view: NSView) {
contentView?.removeFromSuperview()
addSubview(view)
contentView = view
}
}
You will also need to resize the view with its container, likely by adding Auto
Layout constraints. In fact, Cocoa provides a class, NSBox, which can perform
much of this work for you.
Now that you know about view swapping, you have all the tools you need to
build your own tab view controller!
NerdTabViewController
While NSTabView, and thus NSTabViewController, offers several visual styles,
none of them resemble the style of Xcode’s tab views. Emulating that appearance
will make a fine goal. The completed application is shown in Figure 31.1.
Figure 31.1 The completed NerdTabViewController in action

As with NSTabViewController, NerdTabViewController will display a button for


each child view controller. The contents of the selected child view controller will
appear below the buttons. To react to view controllers being added and removed
from the controller you will override insertChildViewController(_:atIndex:)
and removeChildViewControllerAtIndex(_:).
In Xcode, open the ViewControl exercise from the last chapter. Create a new Swift
file named NerdTabViewController.swift and start by defining the class,
overriding loadView() to set up the view programmatically, and overriding the
child view controller management methods to call it:
import Foundation
import Cocoa

class NerdTabViewController : NSViewController {

override func loadView() {


view = NSView()
reset()
}

func reset() {
}

override func
insertChildViewController(childViewController:
NSViewController,

atIndex index: Int) {

super.insertChildViewController(childViewController,
atIndex: index)
if viewLoaded {
reset()
}
}

override func
removeChildViewControllerAtIndex(index: Int) {
super.removeChildViewControllerAtIndex(index)
if viewLoaded {
reset()
}
}
}
By checking viewLoaded in the child view controller insert and remove methods
you can avoid loading the view unnecessarily. Like NSWindowController,
NSViewController loads the view the first time the view property is accessed.

The bulk of the work in this class will be in reset(), which will rebuild the view
hierarchy. Before you implement reset(), add two properties – box and buttons
– and two methods: selectTabAtIndex(_:) and selectTab(_:).
class NerdTabViewController : NSViewController {

var box = NSBox()


var buttons: [NSButton] = []

func selectTabAtIndex(index: Int) {


assert(contains(0..
<childViewControllers.count, index), "index out of
range")
for (i, button) in enumerate(buttons) {
button.state = (index == i) ? NSOnState :
NSOffState
}
let viewController =
childViewControllers[index] as! NSViewController
box.contentView = viewController.view
}

func selectTab(sender: NSButton) {


let index = sender.tag
selectTabAtIndex(index)
}

override func loadView() {


The NSBox, referenced by the box property, will hold the contents of the chosen
tab, while buttons is an array of the NSButtons used for changing between the
tabs. The buttons will trigger the selectTab(_:) action; the sender’s (NSButton’s)
tag property is used to indicate which tab should be shown. The complete view
hierarchy is shown in Figure 31.2.
Figure 31.2 View hierarchy with NerdTabViewController shown

Finally, implement reset() to rebuild the view hierarchy: func reset() {


view.subviews = [] let buttonWidth: CGFloat = 28 let buttonHeight:
CGFloat = 28 let viewControllers = childViewControllers as!
[NSViewController] buttons = map(enumerate(viewControllers)) { (index,
viewController) -> NSButton in let button = NSButton()
button.setButtonType(.ToggleButton)
button.translatesAutoresizingMaskIntoConstraints = false button.bordered
= false button.target = self button.action = Selector("selectTab:") button.tag
= index button.image = NSImage(named:
NSImageNameFlowViewTemplate) button.addConstraints([
NSLayoutConstraint(item: button, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant:
buttonWidth), NSLayoutConstraint(item: button, attribute: .Height,
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0,
constant: buttonHeight) ]) return button } let stackView = NSStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.orientation = .Horizontal stackView.spacing = 4 for button in
buttons { stackView.addView(button, inGravity: .Center) }
box.translatesAutoresizingMaskIntoConstraints = false box.borderType =
.NoBorder box.boxType = .Custom let separator = NSBox()
separator.boxType = .Separator
separator.translatesAutoresizingMaskIntoConstraints = false view.subviews
= [stackView, separator, box] let views = ["stack": stackView, "separator":
separator, "box": box] let metrics = ["buttonHeight": buttonHeight] func
addVisualFormatConstraints(visualFormat:String) {
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
visualFormat, options: .allZeros, metrics: metrics, views: views)) }
addVisualFormatConstraints("H:|[stack]|")
addVisualFormatConstraints("H:|[separator]|")
addVisualFormatConstraints("H:|[box(>=100)]|")
addVisualFormatConstraints( "V:|[stack(buttonHeight)][separator(==1)]
[box(>=100)]|") if childViewControllers.count > 0 { selectTabAtIndex(0) } }
The only new concept in the code above is NSStackView, which simply arranges
one or more views in order, horizontally or vertically, with a certain gravity to
indicate where they should be positioned within its bounds. You are using it here
to position the tab selection buttons at the center.
Now that you have implemented the NerdTabViewController, swap it out in
AppDelegate.swift:
let tabViewController = NSTabViewController()
let tabViewController = NerdTabViewController()
Run the application and try switching between the tabs. The button’s tag is used
to identify the selected view controller by its index, and the box’s contentView is
set to the view controller’s view. For now, both tab buttons will show the flow
icon.
Adding Tab Images
Finish out NerdTabViewController by adding individual images to the tab buttons
for each view controller. The user will almost certainly enjoy being able to
differentiate between the tabs.
To do this you need a way to tell the tab view controller what image to use for
each tab. One approach would be to write methods for adding the child view
controllers which take an image for the tab:
func addChildViewController(childViewController:
NSViewController,
tabImage:
NSImage)
This would work, but it is not a great choice because it calls into question the
fate of the existing addChildViewController(_:) method. Can it still be called?
What happens if it is called? A future developer’s existing knowledge of
NSViewController would likely take them down the wrong path.

Part of the challenge here is that you want NerdTabViewController to work with
any NSViewController. So you look for a property on NSViewController where
you can assign the image and find representedObject. This would work, but it is
not very discoverable. Furthermore its type is AnyObject?. We discourage using
representedObject in general for this reason: it is better to create a strongly-
typed property specific to your model object.
ImageViewController already has an image property. It would not do for
NerdTabViewController to detect that it has an ImageViewController child. It
would defeat the modularity provided by container view controllers, or
necessitate adding another special case to NerdTabViewController every time
you wanted to support a new view controller. You need a way for any view
controller to opt into providing an image in a type-safe, preferably predictable
and self-documenting manner.
Protocols provide a fine means of accomplishing this. A protocol makes it clear
what the expectations are and allows the NerdTabViewController to check for
conformance.
Define the ImageRepresentable protocol in NerdTabViewController.swift:
import Cocoa

@objc
protocol ImageRepresentable {
var image: NSImage? { get }
}

class NerdTabViewController : NSViewController {


In reset(), test the view controller for conformance to the protocol:
button.tag = index
button.image = NSImage(named:
NSImageNameFlowViewTemplate)
if let viewController = viewController as?
ImageRepresentable {
button.image = viewController.image
}
else {
button.title = viewController.title!
}
button.addConstraints([
In ImageViewController.swift, mark ImageViewController as conforming to
ImageRepresentable: class ImageViewController: NSViewController,
ImageRepresentable {
Since ImageViewController already has an image property, you are ready to run
the application.
Challenge: Boxless NerdTabViewController
NSBox makes your task easier in NerdTabViewController by simplifying adding
the content view to the hierarchy and sizing it appropriately. Test your
understanding of the view hierarchy and Auto Layout constraints by replacing
the NSBox in NerdTabViewController with a plain NSView.
Instead of setting the box’s contentView, you will add the selected child view
controller’s view as a subview. Use Auto Layout to ensure that the view resizes
with its container view. Do not forget to remove the previously selected view
controller’s view!
Challenge: NerdSplitViewController
Test your skills further by creating your own split view controller. Use an
ordinary NSView and not NSSplitView – you will do the view arranging yourself
in the view controller. Keep it simple for now: assume there will only be two
child view controllers and always vertically or horizontally split. As
NSSplitViewController does, create a pane for each child view controller. Use
Auto Layout to set the width of the panes to be equal. Do not worry about the
draggable divider for now.
Challenge: Draggable Divider
Now that you have the NerdSplitViewController working, worry about the
draggable divider. You will need a way to receive mouse dragged events. You
will then change out the constraints in response to the dragging.
As you will recall from Chapter 18, there are a few different ways to receive the
mouse events. Because NSViewControllers are in the responder chain, you can
simply implement mouseDragged(_:) and so forth in the
NerdSplitViewController. Another option is to use an NSPanGestureRecognizer
added to a view representing the divider.
32
Storyboards
You have used XIB files to represent a single window (Chapter 28) and a single
view (Chapter 30), but wouldn’t it be nice to see them together on one canvas?
With storyboards, you can combine as much (or as little) of your application as
you want in one canvas, as well as visualize how the parts relate to each other.
Storyboards are organized by scenes (windows and views), which are connected
to each other by segues. Segues describe the scenes’ relationships to one another.
Storyboards can be used to describe a window and its popover or sheet – or a
complicated graph of nested views, each with its own view controller, which
previously would have necessitated programmatic construction.
Storyboards were added to OS X in 10.10 Yosemite and to iOS in version 5.
A New UI for RanchForecast
The RanchForecast UI in Chapter 28 was functional, but it is awkward that the
user has to leave the application to view the course's associated web page (or
open a sheet, if you completed the challenge). The app would be much more
usable if a list of courses were shown in a pane on the left, and a web view were
shown on the right, as in Figure 32.1.
Figure 32.1 Completed application

Create a new Cocoa Application project in Xcode and call it RanchForecastSplit. Set
the language to Swift, and this time check Use Storyboards. Leave Create Document-
Based Application and Use Core Data unchecked.

In the project navigator you will notice that the selection of files is different than
usual. AppDelegate.swift is still there, but MainMenu.xib is gone and in its place
is Main.storyboard. ViewController.swift is new as well. Click on
Main.storyboard to open it in the editor. With the document outline fully
expanded it should resemble Figure 32.2.
Figure 32.2 New storyboard with window and view controller
Notice that there are three scenes in this storyboard: the Application Scene, Window
Controller Scene, and View Controller Scene. A gradient arrow points to the window
controller scene from the left; this indicates that it is the initial controller for the
storyboard.
You learned about window controllers in Chapter 5. View controllers are the
same concept, just for views. To create a view controller you subclass
NSViewController. And just like a window controller has a reference to a
window, so does a view controller have a reference to a view.
Back in the storyboard, an arrow points from the window controller scene down
to the view controller scene. This arrow is the sole segue of this storyboard,
establishing a relationship between the window controller’s window content and
the view controller. You can tell this by selecting the segue. The text in the jump
bar reads, Relationship "window content" to View Controller. In other words, this segue
stipulates that the contents of the view controller's view will be shown as the
window’s content.
The supplied view controller does not fit your needs for this exercise. Expand
the View Controller Scene in the document outline. Select the View Controller and delete
it. The segue between it and the window will disappear and the window will
display the text, No Content View Controller. Select ViewController.swift in the
project navigator and move it to the trash.
Select Main.storyboard once again and drag a Vertical Split View Controller onto the
canvas. It may be tricky to drop it in the correct place; try to steer clear of other
scenes in the storyboard. Drop the Split View Controller below the Window Controller.
To create the relationship segue between the window controller and the split
view controller, control-drag from the blue Window Controller icon in the border on
top of the window controller down to the split view controller. In the pop-up
Relationship Segue window that appears, select window content (Figure 32.3).

Figure 32.3 Setting the window content to the split view controller

When you are done you should see a segue between the two resembling
Figure 32.4. You may need to move the scenes apart from each other to see it.
Figure 32.4 Window content relationship is set
Explore the complete graph by panning within the canvas. As you can see, it can
get rather large, even for a relatively simple application. Use the pinch gesture to
zoom out, or the Editor → Canvas → Zoom menu, to see more of the canvas.
Of the two view controllers connected to the Split View Controller, the topmost one
represents the left pane of the split view. Make this view more narrow and place
it at the left of the other view controller to better represent the actual UI layout at
runtime, as shown in Figure 32.5.
Figure 32.5 Overall storyboard graph
Before continuing, zoom back in to 100%. Some editing is not allowed when the
storyboard editor is zoomed out.
Because this exercise builds on the ScheduleFetcher and Course classes you
created in Chapter 28, you need to add the corresponding Swift files to this
project. Drag ScheduleFetcher.swift and Course.swift into this project’s
RanchForecastSplit group. Check Copy items if needed and make sure that RanchForecastSplit
is checked to add the file to the target.
Note: If you do not check Copy items if needed, Xcode will only reference the files at
their existing path. This means that when you edit a file in one project, it will
also be edited in the other project. Most of the time, you will want to create a
new copy of the file for a new project.

Adding the course list


Before setting up the UI for the course list, create a view controller that will be
responsible for this portion of the interface. Create a new Swift File in the
RanchForecastSplit group of the project navigator. Name it
CourseListViewController.swift. Edit it to look like the following:
import Foundation
import Cocoa

class CourseListViewController: NSViewController {

dynamic var courses: [Course] = []

let fetcher = ScheduleFetcher()

@IBOutlet var arrayController: NSArrayController!

override func viewDidLoad() {


super.viewDidLoad()

fetcher.fetchCoursesUsingCompletionHandler {
(result) in
switch result {
case .Success(let courses):
println("Got courses: \(courses)")
self.courses = courses
case .Failure(let error):
println("Got error: \(error)")
NSAlert(error: error).runModal()
self.courses = []
}
}
}
}
Back in Main.storyboard, select the left, narrower view controller. In the identity
inspector, set the Class to CourseListViewController. Note that the title of the
scene changes to Course List View Controller.
As you did in Chapter 28, you are going to add a table view to the view
corresponding to the left (master) pane of the split view:
1. Add the array controller
Drag an Array Controller onto the Course List View Controller scene, as shown in
Figure 32.7. Drop it onto the top section of the scene on the canvas.
Alternatively, you can drag it into the document outline.
Connect the arrayController outlet on Course List View Controller to the array
controller you just added.
It is important that it is added to the correct scene within the storyboard;
otherwise it will not be available for connections such as the one you just
made, or for binding from the objects within that scene. Although the
scenes within a storyboard are shown on the same canvas, they are only
connected to each other through segues.
Figure 32.6 Dragging the array controller onto the course list view
controller scene

Figure 32.7 Dragging the array controller onto the course list view
controller scene
2. Bind the array controller
Bind the array controller’s Content Array to the Course List View Controller’s
self.courses.
Note that binding to Course List View Controller is analogous to binding to File's
Owner in a traditional XIB file setup.

3. Add the table view


Drag a Table View onto the view within the Course List View Controller Scene (the
view corresponding to the left pane of the split view). Ensure that the table
view is View Based and has two columns.
4. Configure Auto Layout constraints for the table view
Stretch the table view to take up all of the space in its parent view. Use
Auto Layout to pin it on all four sides so that it resizes with the parent view.
Now use Auto Layout to set a minimum width for the table view – or, more
precisely, the scroll view. Select the scroll view, click the Pin button and
check the Width box. Click Add 1 Constraint.
To set the constraint to enforce a minimum size for the table view, instead
of a fixed with, select the constraint you just created. The easiest way to
find it is by expanding the Bordered Scroll View within the document outline.
Inside Constraints you should see a constraint named something like: Width -
(174) - Bordered Scroll View - Table View (Figure 32.9). Select this constraint. In the
attributes inspector, change the Relation to Greater Than or Equal.
Figure 32.8 Selecting the width constraint

Figure 32.9 Selecting the width constraint


5. Bind the table view’s content
Bind the table view’s Content to the Array Controller’s arrangedObjects
controller key. Also bind the table view’s Selection Indexes to the Array
Controller’s selectionIndexes.

6. Configure and bind the date column


Double-click on the header of the first column and change the title to Date.
Bind the Value of the text field within that column’s cell to the Table Cell View’s
objectValue.nextStartDate (this will be the Model Key Path field). Drop a
Date Formatter on the text field.

7. Configure and bind the title column


Now double-click on the header of the second column and change the title
to Title. Bind the Value of the text field within that column’s cell to the Table
Cell View’s objectValue.title (make this the Model Key Path).

Run the app. Make sure that the table view is populated after the schedule is
fetched.

Adding the web view


Next you will create a view controller for the web view portion of the interface.
Create a new Swift File in the RanchForecastSplit group of the project navigator. Name
it WebViewController.swift. Edit it to look like the following:
import Foundation
import Cocoa
import WebKit

class WebViewController: NSViewController {

var webView: WKWebView {


return view as! WKWebView
}

override func loadView() {


let webView = WKWebView()
view = webView
}

func loadURL(url: NSURL) {


let request = NSURLRequest(URL: url)
webView.loadRequest(request)
}
}
Note that this view controller takes a much different approach. By overriding
loadView(), the view from the storyboard is not used. Instead, the
programmatically-created WKWebView is used as the view controller’s view. This
makes the WebViewController reusable: it is not dependent on the storyboard or
XIB to create the view hierarchy.
In Main.storyboard, select the right, wider view controller. In the identity
inspector, set the Class to WebViewController. The title of the scene changes to
Web View Controller.

Run the application. You should see the list of courses in the left pane, but
selecting different courses does not yet load them in the web view. You have yet
to connect the two.
Connecting the Course List Selection with the
Web View
In order to load the course URL in the web view on selection you will need to
trigger a call to loadURL(_:) when the table view selection changes.
Knowing what you know of Cocoa, how would you accomplish this? Come up
with two or three approaches before reading on.
Got two or three? Here are some that we came up with:
Directly couple the two view controllers by adding a reference from
CourseListViewController to WebViewController. Call a method on the
WebViewController whenever the selected course changes.
This would be the simplest approach and it would also be effective. But it
has a serious disadvantage in the way that it couples the two view
controllers directly to each other. This makes it harder to change the design
of the app in the future.
Post a notification from the CourseListViewController when the selection
changes and observe that notification in the WebViewController.
This approach reduces coupling somewhat: it is one-way instead of
bidirectional (the WebViewController needs to know about the notification
originated by the CourseListViewController). It does have the advantage
that multiple parties could be notified of the selection change, however.
Create a CourseListViewControllerDelegate protocol and have the
WebViewController conform to it. A new delegate method,
courseListViewController(_:selectedCourse:), would trigger the URL
loading.
More direct than using notifications, but it does potentially introduce
coupling between sibling view controllers, like the last approach. What if
you wanted multiple parts of the UI to react to a selection change?
Add a closure property to the CourseListViewController to be called when
the selection changes.
This is a pretty good option. Be careful not to create a strong reference
cycle. Where is the closure set, however?
Use KVO to observe the array’s selectedObjects property.
This works, but you should be wary of relying on KVO for simple tasks. It
is often not apparent that KVO behavior is being relied upon, and the
compiler cannot check it for you (for example, if you change the name of
the property being observed).
The actual approach you are going to use in this case is to create a third view
controller which will contain the master and detail view controllers. This parent
view controller will facilitate communication between the two by being a
delegate of the CourseListViewController and doing the work of loading URLs
in the WebViewController. For the parent view controller, you will subclass
NSSplitViewController. See Figure 32.10.

Figure 32.10 Object diagram for the RanchForecastSplit application

Creating the CourseListViewControllerDelegate


Start by adding the delegate functionality to CourseListViewController. Declare
the protocol in CourseListViewController.swift:
protocol CourseListViewControllerDelegate: class {
func courseListViewController(viewController:
CourseListViewController,
selectedCourse:
Course?) -> Void
}
Add a property for the delegate reference and add the action method inside the
CourseListViewController class definition:
weak var delegate: CourseListViewControllerDelegate? =
nil

@IBAction func selectCourse(sender: AnyObject) {


let selectedCourse =
arrayController.selectedObjects.first as! Course?
delegate?.courseListViewController(self,
selectedCourse: selectedCourse)
}
The last step for the course list view controller is to connect the table view to the
selectCourse(_:) action method you just created. Open Main.storyboard and
Control-drag from the table view to the Course List View Controller. Click
selectCourse: in the pop-up to complete the connection.

Creating the parent view controller


Next, create the subclass of NSSplitViewController by creating a new Swift File
named MainSplitViewController.swift. You will create computed properties to
cleanly access the child view controllers via NSSplitViewItem. Implement
viewDidLoad() to set the delegate relationship and load a default URL.
import Cocoa

class MainSplitViewController: NSSplitViewController,



CourseListViewControllerDelegate {

var masterViewController: CourseListViewController


{
let masterItem = splitViewItems[0] as!
NSSplitViewItem
return masterItem.viewController as!
CourseListViewController
}
var detailViewController: WebViewController {
let masterItem = splitViewItems[1] as!
NSSplitViewItem
return masterItem.viewController as!
WebViewController
}

let defaultURL = NSURL(string:


"http://www.bignerdranch.com/")!

override func viewDidLoad() {


super.viewDidLoad()

masterViewController.delegate = self

detailViewController.loadURL(defaultURL)
}

// MARK: CourseListViewControllerDelegate

func courseListViewController(viewController:
CourseListViewController,
selectedCourse:
Course?) {
if let course = selectedCourse {
detailViewController.loadURL(course.url)
}
else {
detailViewController.loadURL(defaultURL)
}
}
}
Finally, you need to make this class be used instead of a regular
NSSplitViewController when the app is run. Open Main.storyboard. Expand the
Split View Controller Scene and select the Split View Controller. In the identity inspector,
change the Class to MainSplitViewController (Figure 32.11).
Figure 32.11 Change the Class attribute to MainSplitViewController
That is it. Run the application and click between courses. You will see the web
view loading the page for the selected course.
For the More Curious: How is the Storyboard
Loaded?
You might be wondering how the storyboard gets loaded in the first place. Just
like with MainMenu.xib, the answer is in the Info.plist.
If you check the Info.plist for this project you will see that the key
NSMainStoryboardFile is set to Main.
Scenes can also be loaded from storyboards programmatically. The
NSStoryboard class provides this, much like NSBundle does for NIB-loading.
Within a storyboard, one scene can be designated as the initial controller. This is
used in the application’s Main.storyboard to designate the application window to
be opened at launch.
Controllers within a storyboard can also be instantiated by their identifier, which
is set in the identity inspector. If you had a view controller with an identifier of
Palette, you could instantiate the view controller and show it in a window using
the following code:
if let storyboard = NSStoryboard(name: "Main", bundle:
nil) {
if let vc =
storyboard.instantiateControllerWithIdentifier("Palette")

as? NSViewController {
paletteWindow =
NSWindow(contentViewController: vc)
paletteWindow.makeKeyAndOrderFront(nil)
}
}
33
Core Animation
In Chapter 17 you subclassed NSView to perform custom drawing in your
application. You issued drawing commands and Quartz drew them on the screen.
This is sufficient for many applications, where you might need to draw graphs or
use basic, relatively static controls. This style of drawing can be intensive,
however, and making it perform well requires that the developer to expend a lot
of effort. For example, you want to only draw the parts of the view that have
changed.
Core Animation takes a much different tack: instead of issuing drawing
commands, the programmer composes the view by creating layers. Layers can
display images or Bezier paths, and their appearance can be further manipulated
by setting their size, opacity, shadow, and more.
Layers are arranged in a tree. When the programmer changes a layer, Core
Animation does the hard work of figuring out how to redraw the tree in a very
efficient manner. Behind the scenes, the drawing is done using OpenGL.
As the name suggests, Core Animation also makes it relatively easy to create
very smooth animations – but it is an extremely useful tool even if you do not
intend to animate anything at all.
CALayer
Layers are represented by the CALayer class, which is the base class for all kinds
of layers. A layer’s appearance can be configured in numerous ways through its
properties. It is also possible to mask a layer with another or to apply a 3D
transform to give the layer perspective. Most properties can be animated simply
by setting a new value.
Layers are similar to views in some ways. Like views, layers are arranged in a
hierarchy. To display a layer, you set the layer upon a view (a layer-hosting
view). This root layer may then have sublayers, and so forth. You can also draw
into a CALayer, not unlike a custom view, but it is more common to rely on
setting the layer’s properties to customize its appearance.
Unlike views, however, layers are not typically subclassed. Also, they do not
receive user input events (mouse, keyboard) and are not part of the responder
chain. If you want the user to be able to manipulate layers you will need to write
the event handling code yourself. Layers do support hit testing to aid in this.
Below is a sampling of the properties available on CALayer:
class CALayer: NSObject {
var bounds: CGRect
var position: CGPoint
var zPosition: CGFloat
var frame: CGRect

var opacity: CGFloat
var hidden: Bool
var mask: CALayer!
var borderWidth: CGFloat
var borderColor: CGColor!
var cornerRadius: CGFloat
var shadowOpacity: CGFloat
var shadowRadius: CGFloat
var shadowOffset: CGSize
var shadowColor: CGColor!

var actions: [NSObject : AnyObject]! // Defaults
var actions: [NSObject : AnyObject]! // Defaults
to nil!
var delegate: AnyObject! // NSObject
(CALayerDelegate)
// ...
}
Manipulating a layer’s properties will animate it, but you sometimes need more
control. CAAnimation and its subclasses let you fine-tune animations.
CATransaction can be used to group and synchronize multiple animations, as
well as to disable animations temporarily.
Scattered
Let’s create an application that uses Core Animation to drive the interface. This
particular application will load all the images it finds in a folder and display
them using CALayer objects (Figure 33.1).
Figure 33.1 Completed application

In Xcode, create a new project called Scattered. Make sure that Swift is selected as
the Language. You will be using a storyboard in this project, so check Use Storyboards.
The project will not be document-based or use Core Data.
Open ViewController.swift and make the following additions:
import Cocoa

class ViewController: NSViewController {

var textLayer: CATextLayer!

var text: String?

func addImagesFromFolderURL(folderURL: NSURL) {


}

override func viewDidLoad() {


super.viewDidLoad()

// Set view to be layer-hosting:


view.layer = CALayer()
view.wantsLayer = true

let textContainer = CALayer()


textContainer.anchorPoint = CGPoint.zeroPoint
textContainer.position = CGPointMake(10, 10)
textContainer.zPosition = 100
textContainer.backgroundColor =
NSColor.blackColor().CGColor
textContainer.borderColor =
NSColor.whiteColor().CGColor
textContainer.borderWidth = 2
textContainer.cornerRadius = 15
textContainer.shadowOpacity = 0.5
view.layer!.addSublayer(textContainer)

let textLayer = CATextLayer()


textLayer.anchorPoint = CGPoint.zeroPoint
textLayer.position = CGPointMake(10, 6)
textLayer.zPosition = 100
textLayer.fontSize = 24
textLayer.foregroundColor =
NSColor.whiteColor().CGColor
self.textLayer = textLayer

textContainer.addSublayer(textLayer)

// Rely on text's didSet to update textLayer's


bounds:
text = "Loading..."

let url = NSURL(fileURLWithPath:


"LibraryDesktop Pictures")!
addImagesFromFolderURL(url)
}

override var representedObject: AnyObject? {


didSet {
}
}

}
Let’s discuss what you just set up. When the application launches, you configure
the view to be layer-hosting by first assigning its layer property and then setting
its wantsLayer property to true. The order of these calls is important. If you had
not assigned the layer first, the view would have been configured as layer-
backed, which is designed for animating views rather than Core Animation layer
hierarchies.
Next, you configure two layers: textContainer and textLayer. The first layer,
textContainer, is an instance of CALayer (the most basic layer type) and will
create a rounded rectangle filled black with a white border and a shadow. The
second layer, textLayer, is a CATextLayer, which, not surprisingly, can be used
to display text. Note how properties are used extensively in Core Animation to
configure the various graphical elements.
The first layer, textContainer is added to the view’s layer, and textLayer is
added to the textContainer (Figure 33.2).
Figure 33.2 Layer hierarchy
Note that the anchorPoint for each layer is set to (0, 0), which equates to the
lower-left corner. The default anchorPoint for a layer is (0.5, 0.5), which is
the center of the layer. The anchorPoint governs the position of the layer relative
to its position property.
Next, the text property is set. In a moment, you will implement this property’s
didSet to set the bounds of the layers. The bounds describe the size of the layer;
by default, layers have bounds of (0, 0, 0, 0). Layers do have a frame
property, like views, but it is more common to set the position and bounds
independently.
Next, implement the text property’s didSet observer.
var text: String? {
didSet {
let font =
NSFont.systemFontOfSize(textLayer.fontSize)
let attributes = [NSFontAttributeName :
font]
var size =
text?.sizeWithAttributes(attributes) ??
CGSize.zeroSize
// Ensure that the size is in whole
numbers:
size.width = ceil(size.width)
size.height = ceil(size.height)
textLayer.bounds = CGRect(origin:
CGPoint.zeroPoint, size: size)
textLayer.superlayer.bounds
= CGRect(x: 0, y: 0, width: size.width
+ 16, height: size.height + 20)
textLayer.string = text
}
}
Then, implement thumbImageFromImage(_:) and addImagesFromFolderURL(_:).
While you may not be familiar with the specific APIs being used, you should
have a general idea of what they are doing.
func thumbImageFromImage(image: NSImage) -> NSImage {
let targetHeight: CGFloat = 200.0
let imageSize = image.size
let smallerSize
= NSSize(width: targetHeight *
imageSize.width / imageSize.height,
height: targetHeight)

let smallerImage = NSImage(size: smallerSize,


flipped: false) {
(rect) -> Bool in

image.drawInRect(rect)
return true
}

return smallerImage
}

func addImagesFromFolderURL(folderURL: NSURL) {


let t0 =
NSDate.timeIntervalSinceReferenceDate()

let fileManager = NSFileManager()


let directoryEnumerator =
fileManager.enumeratorAtURL(folderURL,

includingPropertiesForKeys: nil,

options: nil,

errorHandler: nil)!

var allowedFiles = 10

while let url =


directoryEnumerator.nextObject() as? NSURL {
// Skip directories:

var isDirectoryValue: AnyObject?


var error: NSError?
url.getResourceValue(&isDirectoryValue,
forKey: NSURLIsDirectoryKey,
error: &error)

if let isDirectory = isDirectoryValue as?


NSNumber
where isDirectory.boolValue
== false {
let image = NSImage(contentsOfURL:
url)
if let image = image {
allowedFiles--
if allowedFiles < 0 {
break
}

let thumbImage =
thumbImageFromImage(image)

presentImage(thumbImage)
let t1 =
NSDate.timeIntervalSinceReferenceDate()
let interval = t1 - t0
text = String(format: "%0.1fs",
interval)
}
}
}
}
This will not compile yet, because addImagesFromFolderURL(_:) calls
presentImage(_:), but presentImage(_:) is not implemented. Implement
presentImage(_:) in ViewController to animate the supplied image into view,
starting from a tiny speck in the middle and expanding out to a thumbnail
version at a random point on the view: func presentImage(image: NSImage) {
let superlayerBounds = view.layer!.bounds let center = CGPoint(x:
superlayerBounds.midX, y: superlayerBounds.midY) let imageBounds =
CGRect(origin: CGPoint.zeroPoint, size: image.size) let randomPoint =
CGPoint(x:
CGFloat(arc4random_uniform(UInt32(superlayerBounds.maxX))), y:
CGFloat(arc4random_uniform(UInt32(superlayerBounds.maxY)))) let
timingFunction = CAMediaTimingFunction(name:
kCAMediaTimingFunctionEaseInEaseOut) let positionAnimation =
CABasicAnimation() positionAnimation.fromValue = NSValue(point:
center) positionAnimation.duration = 1.5
positionAnimation.timingFunction = timingFunction let boundsAnimation
= CABasicAnimation() boundsAnimation.fromValue = NSValue(rect:
CGRect.zeroRect) boundsAnimation.duration = 1.5
boundsAnimation.timingFunction = timingFunction let layer = CALayer()
layer.contents = image layer.actions = ["position" : positionAnimation,
"bounds" : boundsAnimation] CATransaction.begin()
view.layer!.addSublayer(layer) layer.position = randomPoint layer.bounds
= imageBounds CATransaction.commit() }
That is it! Run the application. You should see ten images animate out from the
center of the window.
Implicit Animation and Actions
Before we talk about what is going on in presentImage(_:), let’s look at how the
most basic animation is done with Core Animation. Imagine that you have a
CALayer called layer that is displayed on the screen. Suppose that you set its
position like so: layer.position = CGPoint(x: 50, y: 50)
This simple action animates the layer from its current position to the new
position: implicit animation. Many properties of layers can be animated by
simply setting them. The didSet observer of the text property uses implicit
animation to change the size of the black status bubble.
What if you want to customize these animations? As it turns out, there are
several styles for achieving customization, which can make Core Animation
rather confusing. The most straightforward way to customize animations is to
modify a CALayer’s actions property, which contains a dictionary. This
dictionary associates properties (string keys) with the animations (instances of
CAAnimation subclasses) to be used when animating those properties. The
actions dictionary is used by Core Animation to determine what to do when a
property is assigned.
CABasicAnimation is, well, the most basic animation class. It has two important
properties: fromValue and toValue. By setting one or both of these, you will
influence the start and end values of the property you are animating. In
presentImage(_:), note that you set only the fromValue. Thus, later in the
method when you assign property and bounds, Core Animation can look in the
actions dictionary to determine what animation to use for those properties.
Because only fromValue is set, the properties will be animated from fromValue to
the value that you assigned. Specifically, you animate the position from the
center to a random point and the bounds from zero to the size of the thumbnail,
simultaneously.
Note that to have the layer display the image, you simply assign it to the
contents property. The contentsGravity property affects how contents is
scaled (or not scaled). In this application, the layers have been sized to match the
size of the image, so no scaling is necessary.
Last, your use of CATransaction is notable. CATransaction enables you to group
several changes to be executed at once by surrounding them in calls to
CATransaction.begin() and CATransaction.commit(). Try commenting out the
surrounding begin() and commit() calls. You may notice some flickering in the
display as the layers are shown before the animation begins.
CATransaction has methods to affect changes on the actions within the current
transaction, such as changing the duration, timing functions, or – perhaps most
useful – disabling all actions, which turns off animations:
CATransaction.setDisableActions(true)
You may have noticed that Scattered presently limits the number of files loaded to
ten. If you comment out that limit, you will see why. We will address that in the
next chapter.
More on CALayer
CALayer allows you to control quite a bit about its appearance through its
properties. But what if that were not enough: What if you wanted to do custom
drawing in a CALayer? The CALayerDelegate method drawLayer(_:inContext:)
allows you to do just that with Core Graphics/Quartz.
However, much of the time, you will simply want to control a few common
things:
an image
the background color
whether the corners are rounded and, if so, how much
an image filter to run the contents of the layer through
In these cases and others like it, you can simply modify the layer’s properties.
Subclasses of CALayer also make particular kinds of drawing easier:
As you already saw, drawing text on a layer is easier if the layer is an
instance of CATextLayer.
CAShapeLayer makes drawing a stroked and/or filled path simple.
CAGradientLayer displays a configurable gradient.
Getting OpenGL calls onto a layer is easier if the layer is a subclass of
CAOpenGLLayer.

The base layer of a view is an instance of _NSViewBackingLayer (not a


public class!) that knows to draw the contents of the view upon itself.
Challenge: Show Filenames
Add a text layer to each image layer to show the filename of that image. You
will need to supply an additional parameter to presentImage(_:). Experiment
with adding shadows and borders to the layers.
Challenge: Reposition Image Layers
Add a button and a text field to the view controller. When clicked, the button
should reposition all the image layers. But the rounded black text container layer
should stay put! Use the numeric value from the text field to set the duration of
this repositioning animation.
34
Concurrency
All of the applications you have written as you have worked through this book
have been single-threaded. In simplistic terms, this means that only one thing is
happening in the application at any given time, such as responding to a button
click, updating the display, or counting the number of objects in an array.
Of course, in some cases it is very useful to be able to do many things at once. In
a modern OS, each application is running in its own process; this division gives
each application its own memory space, but it also allows the task scheduler to
create the illusion that many applications are running at once.
Multithreading
Threads give this same power to individual processes. Each thread in a process
has its own stack, meaning that each thread can be executing its own code path
and has its own stack variables. The heap, however, where objects are allocated,
is shared among all threads, as are global variables. Each process starts with one
thread, referred to as the main thread. Additional threads, called background
threads, can be created at any time and will be scheduled to run concurrently
with the other threads in the process. This is called multithreading.
In Cocoa, the display is updated by the main thread, which is also responsible for
handling events from the window manager. Thus, if your application’s main
thread is busy calculating the value of pi to 6 trillion digits when the user tries to
resize the window, the window will appear to ignore the mouse input until the
main thread gets back around to processing events. Calculating pi is a great use
for background threads. Hardware I/O is an even better candidate, as hard disks
are notoriously slower than processors and RAM. If your application calls for
synchronous network communication (asynchronous will not do), a background
thread will allow life to go on in your application’s other threads even amidst
network hiccups. By using multithreading, your application can remain
responsive to the user even while it is deep in thought.
The emphasis on multicore processors over the past several years has led to a
number of OS X improvements – notably NSOperationQueue and Grand Central
Dispatch – that make multithreading much more accessible to developers. In this
chapter, we will discuss NSOperationQueue and some other methods for creating
background threads.
A Deep Chasm Opens Before You
While multithreading can be essential in certain applications, it also opens up an
entirely new category of bugs that are notoriously difficult to fix: race
conditions. It is for this reason that we suggest that you carefully weigh the
benefits of multithreading against the significant costs.
Race conditions occur when code is modifying the same data in two or more
threads. Consider the classic case of two threads incrementing a global variable
(Figure 34.1). Here is the global integer variable:
var globalCount = 0
Figure 34.1 A classic race condition

Now imagine the two threads executing this code concurrently:


for i in 0..<2000 {
globalCount++
}
It looks like the programmer’s expectation is that globalCount will be equal to
2,000 when both threads have completed. Depending on how the threads are
scheduled, however, the final value of globalCount may be much less. Why is
this? The reason is that the thread scheduler may interrupt any thread at any time
in order to run another thread, or it may even run multiple threads
simultaneously on the cores of a multicore system. So it becomes somewhat
dangerous that the statement
globalCount++
is actually several instructions:
1. Load the value of globalCount into a CPU register.
2. Add 1 to this value.
3. Store the result back to globalCount.
Consider the effects of two threads running these instructions over and over. The
incremented value of globalCount by one thread runs a very high risk of being
clobbered by the other thread. Worse, the results of such code can be
inconsistent between runs on different systems or even the same system.
This is a rather low-level example of a race condition, but it illustrates the
general problem with multithreading: You cannot make any assumptions about
when a thread will be scheduled, how long it will execute before being
interrupted, what other threads might be running at the same time, or exclusivity
as far as access to data. Usually, a racecondition bug will have much more
serious implications than a not fully incremented integer.
Fortunately, Cocoa provides some tools for dealing with these problems. They
will not be solved magically, and you will need to use the tools with care, as they
can create their own set of problems, such as deadlocks. We will examine one of
these tools at the end of the chapter.
If you plan to use multithreading in your application, take some time early on to
consider how your data structures will be used and try to minimize any sharing
of mutable data structures. Careful design will save you a lot of headaches down
the road when it comes time to debug.
Improving Scattered: Time Profiling in
Instruments
Open the Scattered project from the previous chapter and find
addImagesFromFolderURL(_:) in ViewController.swift. Find the allowedFiles
variable, which is used to limit the number of files opened to ten. Remove all
traces of allowedFiles and run the application.
You should see the Scattered app’s icon bouncing in the dock for several seconds.
When the window appears, all the images will already have been animated to
their destinations. It looks like loading the images is blocking the main thread,
which is a poor user experience. You may have a pretty good idea that the
problem is related to loading images, since you just removed the limit, but let’s
prove it.

Introducing Instruments
Instruments is a tool for analyzing a running program. The tool has many different
plug-ins, called instruments, that enable you to look at various aspects, usually
performance related, of the running application.
In Xcode, open the Product menu and select Profile. Xcode will use the Release build
configuration (configurable in the Scheme Editor) to rebuild the project and will
then start Instruments. In the template chooser, select Time Profiler and click Choose
(Figure 34.2). Instruments will present a new window where all the data that will be
collected will eventually be presented. Click the record button in the top left of
the window. Instruments will then run Scattered and start collecting data. Once the
images have animated, click the black stop button in the top left of Instruments to
stop profiling.
Figure 34.2 Choosing the Time Profiler in Instruments
The Instruments interface has several parts. The instruments list is at the upper left;
in this case, only one instrument, Time Profiler, is being run. To the right is the
track pane, which displays graphical data over time related to each of the
instruments being run. Looking at the graph for the Time Profiler, you can see
that there was a burst of activity, probably related to loading the images. Below
the track is the detail pane, which displays tabular data related to the selected
instrument (Figure 34.3).
Figure 34.3 Running Scattered under the Time Profiler
Time Profiler works by taking snapshots of the application’s call stack
repeatedly while it is running. This enables you to tell where the application is
spending its time. But it does not tell you how many times a particular method
has been called, as it has only been taking snapshots and does not know when
methods are entered and exited.
In the detail pane, you see the symbols sorted by time spent. Note the disclosure
triangles in the Symbol Name column. Try clicking through them to navigate the
stack, or use the keyboard arrow keys to explore the tree if you prefer. Note that
Invert Call Tree is checked by default; this means that you are seeing the deepest
functions where the CPU spent most of its time. You can toggle Invert Call Tree off
to see the individual entry points to the application. Sometimes, this will make it
faster to find the information you need.
If you dig down far enough, you will see that all this time is being spent in the
closure used to create the NSImage in thumbImageFromImage(_:) (a quick way to
find this is to enable Hide System Libraries). If you show the Extended Detail pane (using
the View segments in the toolbar), you can see another view of the stack; this can
be very useful when using memory-related instruments (Figure 34.4).
Figure 34.4 Showing the Extended Detail pane
At this point, it is pretty clear that quite a bit of time is being spent creating the
thumbnails. Try double-clicking on the stack frame for the closure used to create
the NSImage in thumbImageFromImage(_:). The detail pane will change to show
the source code of that method, with highlighting to show how much time is
spent on each line (Figure 34.5). In this case, all the time is spent in
NSImage.drawInRect(_:). Use the jump bar control above the detail view to
return to the Call Stack.
Figure 34.5 Source code display

Another useful feature of Instruments is constraining the Inspection Range. You


can set the range by clicking and dragging over the range of the timeline you
want to examine. The information in the detail view will be limited to the
selected range of time. This is helpful when focusing on a specific performance
problem.
We have only scratched the surface of what is possible with Instruments. As you
have seen, it can be useful as a time profiler, but it also has a number of tools for
dealing with memory issues, including tracking usage and detecting leaks and
strong reference cycles in ARC. As you work to improve the performance of
your applications, you will want to read Apple’s documentation for Instruments.
Videos from Apple’s annual Worldwide Developers Conference (WWDC) can
also be a great resource for learning to use Instruments.

Analyzing output from Instruments


Now you know for sure where the problem is: generating thumbnails is time-
consuming; even worse, you are doing that work in viewDidLoad(), which ties up
the main thread while the directory tree is traversed and each thumbnail is
created.
There are generally two categories of blocking problems like this: CPU-bound
and I/O-bound. I/O-bound problems revolve around waiting for slower hardware
to do its thing and return control to you. In a CPU-bound problem, the CPU is
the bottleneck; decompressing dozens of JPEG images relies heavily on the
CPU. As you saw in Chapter 28, I/O-bound problems can sometimes be solved
using asynchronous I/O, but doing so in this case could be complicated.
Here you seem to have a mixture of both problems: It is disk-intensive to load
many megabytes of data and CPU-intensive to decompress and draw scaled-
down thumbnails. One hint that this is the case is that the CPU is not 100%
utilized (if it were, the Time Profiler’s graph would be straight across the top).
The simplest solution in cases like this is to put the work on a background
thread.
NSOperationQueue
Frequently, multithreading is used for processing chunks of information in the
background. In such cases, Cocoa’s NSOperationQueue provides a very mature
framework for organizing the processing.
NSOperationQueue represents a collection of operations (encapsulated by
NSOperation) and manages the execution of those operations on one or more
threads. Every application has a main queue that represents the main thread; it is
accessed using NSOperationQueue.mainQueue(). If the application needs
additional queues, it can create and configure them simply by initializing a new
NSOperationQueue.

By default, NSOperationQueue objects are configured to run several operations


concurrently. The exact number is determined automatically by the system. You
can override this number by configuring the maxConcurrentOperationCount
property. A maximum concurrent operation count of 1 results in a serial queue.
The main queue is always serial.

Multithreaded Scattered
Let’s modify Scattered to use NSOperationQueue and try to improve it.
Open ViewController.swift and add a property for the NSOperationQueue:
import Cocoa

class ViewController: NSViewController {

let processingQueue: NSOperationQueue = {


let result = NSOperationQueue()
result.maxConcurrentOperationCount = 4
return result
}()

var textLayer: CATextLayer!


Now you will make use of the NSOperationQueue in
addImagesFromFolderURL(_:). Add six lines. Be careful to balance the closures’
braces. If you did not already do so, be sure to remove the code related to
allowedFiles now. You will not be limiting the number of files you process.
func addImagesFromFolderURL(folderURL: NSURL) {
processingQueue.addOperationWithBlock() {
let t0 =
NSDate.timeIntervalSinceReferenceDate()

let fileManager = NSFileManager()


let directoryEnumerator =
fileManager.enumeratorAtURL(folderURL,

includingPropertiesForKeys: nil,

options: nil,

errorHandler: nil)!

while let url =


directoryEnumerator.nextObject() as? NSURL {
// Skip directories:

var isDirectoryValue: AnyObject?


var error: NSError?
url.getResourceValue(&isDirectoryValue,
forKey: NSURLIsDirectoryKey,
error: &error)

if let isDirectory = isDirectoryValue as?


NSNumber
where isDirectory.boolValue
== false {

self.processingQueue.addOperationWithBlock() {
let image = NSImage(contentsOfURL:
url)
if let image = image {
let thumbImage =
self.thumbImageFromImage(image)


NSOperationQueue.mainQueue().addOperationWithBlock() {

self.presentImage(thumbImage)
let t1 =
NSDate.timeIntervalSinceReferenceDate()
let interval = t1 - t0
self.text = String(format:
"%0.1fs", interval)
}
}
}
}
}
}
}
That is it! Run the application and marvel at the new and improved user
experience. You should notice a significant speed increase as well, depending on
your hardware.
Instead of explicitly creating NSOperation objects, you use NSOperationQueue’s
addOperationWithBlock(_:), which creates an NSBlockOperation for you and
adds it to the queue. Note how minimal the changes are; the general flow of the
application is practically unchanged. It will not always be this clean to add
multithreading to an application, but you are seeing more of how closures allow
you to avoid a lot of boilerplate code by enabling you to reference variables that
are in scope.

Thread synchronization
We did not appear to worry about race conditions as we led you through this
exercise. The reason is that the design of the application specifically avoids
using any shared mutable data structures from within the background thread.
Work in the background threads was limited to enumerating the folder, opening
images, and creating thumbnails. The only shared data, the Core Animation
layers, was modified from the main thread only. Multithreading is easiest when
you can avoid race conditions altogether.
Not all multithreading problems will be solvable using this approach, however.
Often you will need to protect a section of code (or multiple sections of code)
such that only one thread can be running it at a time. This is usually done with a
mutex – a mutually exclusive lock. When you need a simple mutex, you can use
Cocoa’s NSRecursiveLock class:
let lock = NSRecursiveLock()
func addImage(image: NSImage) {
lock.lock()
images.append(image)
lock.unlock()
}
An NSRecursiveLock is a mutex: it prevents multiple threads from running the
code between lock() and unlock() at once. An NSRecursiveLock is also
recursive: it can be locked multiple times from the same thread.
You can safely read from a constant Array on any thread. However, if an Array
is not constant, you must take care when reading from or writing to it because it
is not thread-safe – it is not designed to be modified from multiple threads. As a
result, it is recommended that you use a mutex when modifying an instance of
Array in a multithreaded environment. An NSRecursiveLock guarantees that only
one thread will be able to lock it at a time. If two threads were attempting to call
addImage(_:) at the same time, the first thread would obtain the lock and add the
image to the array and the other thread would block and wait for the lock to be
released.
You may be wondering why Array is not thread-safe. One reason is that a mutex
has overhead associated with it, and thread safety would make Arrays
significantly slower. Another reason is that it is often more useful (and common)
to lock a section of code, not just a single method call, such as if you were
moving objects from one data structure to another.
Cocoa provides a number of other tools for thread synchronization, such as
NSLock and NSCondition. These tools are discussed in detail in Advanced
Mac OS X Programming: The Big Nerd Ranch Guide, along with a more
involved look at NSOperationQueue and Grand Central Dispatch.
For the More Curious: Faster Scattered
The goal in this chapter was to make Scattered a better-behaved application by
moving heavy lifting off the main thread. You did not, however, fully address
the performance issues with this application. If you were watching closely, you
may have noticed that you limited the number of concurrent operations in
processingQueue to four. If you remove that constraint (by commenting out the
line), you may find that Scattered runs somewhat faster, or you may find that it
runs even slower. What is going on here?
One of the most useful features of Grand Central Dispatch (GCD), which
NSOperationQueue is built upon, is that GCD manages the number of running
operations (threads) based on the system’s hardware (number of cores) and the
current system load. In higher-level terms, GCD will create as many threads as it
thinks the system can handle in order to process the operations in a queue as
quickly as possible. If every queued operation is uniform as far as its required
resources, this works very well.
Consider how Scattered works, however: Each operation starts by reading data (an
image) from disk. Disk I/O puts very little demand on the CPU, so GCD sees
that the CPU is not being utilized and starts another operation, which starts by
reading data from the disk, and so forth. Perhaps you can see how GCD would
very quickly start a large number of threads to handle the operations in the
queue.
When the image data for the first operation is fully read in, the image is
decompressed and a thumbnail is created, which is somewhat CPU-intensive
work. While this work is being done, the second thread finishes reading from
disk and starts decompressing, and so on. Suddenly, the CPU is being asked to
do quite a bit of work!
In this exercise, you avoided this pile up by limiting the number of concurrent
operations. But this approach is not ideal, because you are frequently wasting
CPU time while waiting on the disk: recall how hilly the CPU graph in Instruments
was. The proper solution to this problem is to use two queues: one queue to load
image data from the disk, limited in the number of concurrent operations it can
conduct (because disks are slow), and another queue to do the work of creating
the thumbnail. This is, however, quite a bit more complicated to do properly.
Challenge: An Even Better Scattered
Adapt Scattered to use the proper solution outlined in the previous section.
Because NSImage avoids doing disk I/O until absolutely necessary, you will need
to read the data in manually and then create the image using that data. NSData
will read the image data. Check the documentation or header file for NSImage to
find a way to create an image from an NSData object.
35
NSTask
Each application that you have created is a bundle, a directory which OS X
displays to the user as a single file. Somewhere down in that directory is an
executable file. To run an executable on a Unix machine, like your Mac, a
process is forked and the new process executes the code in that file. Many
executables are command-line tools, and some are quite handy. In this chapter,
you will learn how to run a command-line tool from your Cocoa application
using NSTask.
NSTask is an easy-to-use wrapper for the Unix functions fork() and exec(). You
give NSTask a path to an executable and launch it. Many processes read data from
standard-in and write to standard-out and standard-error. Your application can
use NSTask to attach pipes to carry data to and from the external process. Pipes
are represented by the class NSPipe.
ZIPspector
The tool usrbin/zipinfo looks at the contents of a zip file. Find a zip file on your
machine and try running zipinfo in the Terminal like this (-1 is dash-one, not
dash-el):
# zipinfo -1 Usersaaron/myfile.zip
rad_file.txt
tubular.pages
swell_file.rtf
magnificent.pdf
You are going to create an application that displays the contents of a zip file in a
table. To achieve this, you are going to use zipinfo, sending it some arguments and
reading from its standard-out (Figure 35.1).
Figure 35.1 Completed application
In Xcode, create a new Cocoa Application named ZIPspector, and enable Create Document-
Based Application. You will not be using storyboards or Core Data in this
application. This program will only view zip files, not edit them. In the Info panel
for the target, update the Identifier and Role fields to set ZIPspector to be a viewer
for files with the UTI com.pkware.zip-archive (Figure 35.2) (This is a system-
defined UTI, so it will know the extension, icon, and so on, for zip files.)
Figure 35.2 Setting the UTI
In Document.swift, add an outlet for an NSTableView, make Document adopt
NSTableViewDataSource, and add an Array for holding the filenames in the zip
file:
class Document: NSDocument, NSTableViewDataSource {

@IBOutlet weak var tableView: NSTableView!

var filenames: [String] = []


Open Document.xib. Delete the label in the center of the window. Add a table
view to the window and set it to have one uneditable column with the title
Filenames. Remember that to make a column uneditable, you need to select it and
uncheck Editable in the attributes inspector. Locate the text field inside the table
column’s NSTableCellView. First, use constraints to make it nearly fill its
superview: it should be inset 2 points from the left and 2 from the right. Second,
bind its Value to the objectValue of the Table View Cell which contains it
(Figure 35.3).
Figure 35.3 Binding the text field’s value

Set the table view’s dataSource to be the Document and set the Document’s
tableView outlet to the table view. (Remember, in a document XIB, File's Owner is
the document.) Inside Document’s implementation, near the bottom, add a new
section for the required NSTableViewDataSource methods. Implement the
required methods so that the strings in filenames are displayed:
// MARK: - NSTableViewDataSource

func numberOfRowsInTableView(tableView: NSTableView) -


> Int {
return filenames.count
}

func tableView(tableView: NSTableView,


objectValueForTableColumn tableColumn:
NSTableColumn?,
row: Int) -> AnyObject?
{
return filenames[row]
}
Now, remove the default readFromData(_:ofType:error:) and override
readFromURL(_:ofType:error:) to create an NSTask that executes zipinfo:
override func readFromURL(url: NSURL,
ofType typeName: String,
error outError: NSErrorPointer) -> Bool
{
// Which file are we getting the zipinfo for?
let filename = url.path!

// Prepare a task object


let task = NSTask()
task.launchPath = "usrbin/zipinfo"
task.arguments = ["-1", filename]

// Create the pipe to read from


let outPipe = NSPipe()
task.standardOutput = outPipe

// Start the process


task.launch()

// Read the output


let fileHandle = outPipe.fileHandleForReading
let data = fileHandle.readDataToEndOfFile()

// Make sure the task terminates normally


task.waitUntilExit()
let status = task.terminationStatus

// Check status
if status != 0 {
if outError != nil {
let errorDomain =
"com.bignerdranch.ProcessReturnCodeErrorDomain"
let errorInfo
= [ NSLocalizedFailureReasonErrorKey :
"zipinfo returned \(status)"]
outError.memory = NSError(domain:
errorDomain,
code: 0,
userInfo:
errorInfo)
}
return false
}

// Convert to a string
let string = NSString(data: data, encoding:
NSUTF8StringEncoding)!

// Break the string into lines


filenames =
string.componentsSeparatedByString("\n") as! [String]
println("filenames = \(filenames)")

// In case of revert
tableView?.reloadData()

return true
}
After creating the NSTask, you attached an NSPipe to standardOutput. The NSPipe
provides a buffer for data from the task’s standard-output to flow into. Finally,
you asked the NSPipe for its fileHandleForReading, which returns to you an
NSFileHandle. You used this file handle to read the data out of the pipe
(Figure 35.4).
Figure 35.4 Object diagram

Your application is read-only – it cannot create or modify zip files – so you can
delete the method dataOfType(_:error:) if you wish. Also, you can open up the
MainMenu.xib file and delete any menu items that are concerned with saving.

Build and run your application. You should be able to see the contents of any zip
file. Note: Since ZIPspector is a viewer, no Untitled document will appear; you will
need to open an existing .zip file.
Asynchronous Reads
As mentioned in Chapter 23, the run loop is the object that waits for events,
which may be keyboard, mouse, or timer events. These are all run loop data
sources. You can also make a file handle a run loop data source.
In the next section, you are going to fork off a process that burps up data
occasionally. You will attach a pipe to standardOutput, but instead of trying to
read all the data from the file handle immediately, you will ask the file handle to
read in the background and send a notification when data is ready.
You can use sbinping to check whether you can make an IP connection to another
machine. Try running it in Terminal:
$ ping -c10 www.bignerdranch.com
PING www.bignerdranch.com (69.39.89.150): 56 data
bytes
64 bytes from 69.39.89.150: icmp_seq=0 ttl=50
time=35.579 ms
64 bytes from 69.39.89.150: icmp_seq=1 ttl=50
time=35.099 ms
64 bytes from 69.39.89.150: icmp_seq=2 ttl=50
time=34.546 ms
64 bytes from 69.39.89.150: icmp_seq=3 ttl=50
time=35.495 ms
64 bytes from 69.39.89.150: icmp_seq=4 ttl=50
time=35.685 ms
64 bytes from 69.39.89.150: icmp_seq=5 ttl=50
time=35.667 ms
64 bytes from 69.39.89.150: icmp_seq=6 ttl=50
time=36.435 ms
64 bytes from 69.39.89.150: icmp_seq=7 ttl=50
time=52.296 ms
64 bytes from 69.39.89.150: icmp_seq=8 ttl=50
time=36.142 ms
64 bytes from 69.39.89.150: icmp_seq=9 ttl=50
time=36.188 ms
--- www.bignerdranch.com ping statistics ---
10 packets transmitted, 10 packets received, 0% packet
loss
round-trip min/avg/max/stddev =
34.546/37.313/52.296/5.021 ms

If you want to end the program prematurely, press Control-C to send it a sigint
signal. This will cause it to write out the stats and terminate.
iPing
Now you are going to write a Cocoa app that uses NSTask to run ping
(Figure 35.5).
Figure 35.5 Completed application

In Xcode, create a new project, iPing, of type Cocoa Application. Uncheck Create
Document-Based Application. You will want a storyboard for this app, so ensure that
Use Storyboards is checked.

Open ViewController.swift. First, remove all of the methods from


ViewController that were added as part of the Xcode template. Then add several
properties and a stub for an action method.
class ViewController: NSViewController {

@IBOutlet var outputView: NSTextView!


@IBOutlet weak var hostField: NSTextField!
@IBOutlet weak var startButton: NSButton!
var task: NSTask?
var pipe: NSPipe?
var fileHandle: NSFileHandle?

@IBAction func togglePinging(sender: NSButton) {

}
Open Main.storyboard. In the Window Controller Scene, locate the Window. In the
attributes inspector, set its Title to be iPing.
Next, switch to the View Controller Scene. Drop a text field, a button, and a text view
onto the view, and set up appropriate constraints among them. Make the text
view be read only by unchecking Editable in the attributes inspector. Change the
button’s Type to Toggle. Set its title to Start Pinging (Figure 35.6) and its alternate title
to Stop Pinging.
Figure 35.6 Button attributes

Make the button’s target be the ViewController and make its action be
togglePinging:. Hook up the outlets you just added to ViewController to the
corresponding views (Figure 35.7).
Figure 35.7 Object diagram
In ViewController.swift, implement togglePinging(_:):
@IBAction func togglePinging(sender: NSButton) {
// Is there a running task?
if let task = task {
// If there is, stop it!
task.interrupt()
} else {
// If there isn't, start one.

// Create a new task


let task = NSTask()
task.launchPath = "sbinping"
task.arguments = ["-c10",
hostField.stringValue]

// Create a new pipe for standardOutput


let pipe = NSPipe()
task.standardOutput = pipe

// Grab the file handle


let fileHandle = pipe.fileHandleForReading

self.task = task
self.pipe = pipe
self.fileHandle = fileHandle
let notificationCenter =
NSNotificationCenter.defaultCenter()
notificationCenter.removeObserver(self)
notificationCenter.addObserver(self,
selector:
Selector("receiveDataReadyNotification:"),
name:
NSFileHandleReadCompletionNotification,
object: fileHandle)
notificationCenter.addObserver(self,
selector:
Selector("receiveTaskTerminatedNotification:"),
name:
NSTaskDidTerminateNotification,
object: task)

task.launch()

outputView.string = ""

fileHandle.readInBackgroundAndNotify()
}
}
While the task is running, the file handle will be posting notifications when data
is ready. Implement the method that will get called:
func appendData(data: NSData) {
let string = NSString(data: data, encoding:
NSUTF8StringEncoding) as! String
let textStorage = outputView.textStorage!
let endRange = NSRange(location:
textStorage.length, length: 0)
textStorage.replaceCharactersInRange(endRange,
withString: string)
}

func receiveDataReadyNotification(notification:
NSNotification) {
let data
= notification.userInfo!
[NSFileHandleNotificationDataItem] as! NSData
let length = data.length

println("received data: \(length) bytes")


if length > 0 {
self.appendData(data)
}

// If the task is running, start reading again


if let fileHandle = fileHandle {
fileHandle.readInBackgroundAndNotify()
}
}
When the process is done, it will post a notification informing you. When you
receive the notification, do some clean-up:
func receiveTaskTerminatedNotification(notification:
NSNotification) {
println("task terminated")

task = nil
pipe = nil
fileHandle = nil

startButton.state = 0
}
Build and run the application. Enter a URL or IP address into the text field and
click the button. The output of ping should appear in the text view.
Challenge: .tar and .tgz Files
A listing of files in a zip file is given by zipinfo. You can get a similar listing for
tar files by using the command-line tool tar:
# usrbin/tar tf MyFiles.tar
If the tar file is also compressed, just add a z to the flags:
# usrbin/tar tzf MyFiles.tgz
Extend ZIPspector to deal with .tar and .tgz files.
36
Distributing Your App
The time will come when you are ready for your app to leave its nest. You have
crushed all the bugs you can find and tested for leaks in Instruments. It is high time
for your app to see the world!
In this chapter, you will learn how to use Xcode to prepare your app for life
outside the debugger. You will follow the steps to distribute an app using the
RanchForecastSplit app from Chapter 32.
Build Configurations
Until now, you have been using debug builds when trying out your apps as you
have been writing them. Debug builds contain additional information that
enables the debugger to show detailed stack information. Furthermore, the
compiler has not performed any optimizations.
This is great for development. It makes the debugger more useful, and builds are
generated more quickly. But it is the opposite of what you want in a build that
you would release to customers: a release build. In a release build, optimizations
are turned on and debugging symbols are stripped (to reduce size and make
inspecting the code more difficult).
There is nothing particularly special about the debug and release build
configurations. They are simply a convention, and all the settings for these
configurations are modifiable within Xcode.
Open your RanchForecastSplit project from Chapter 32. You can find the project’s
existing build configurations in the project editor, on the Info pane (Figure 36.1).
You can also add new build configurations there.
Figure 36.1 Project build configurations

Xcode has several actions available: run, test, profile, analyze, and archive. A
build configuration is associated with each of these actions. You can configure
which configuration is used for each action using the Scheme Editor (Figure 36.2).
To view the scheme editor, select the Product → Scheme → Edit Scheme... menu item.
Figure 36.2 The scheme editor with the Debug build configuration selected
for Run

The specified build configuration will be used when building the target for that
particular action.
Preprocessor Directives: Using Build
Configurations to Change Behavior
One common use of build configurations is to hardcode behavioral settings in
your application. This is done using preprocessor directives.
In Xcode, select the project in the project navigator to bring up the project editor.
In the project editor, select the RanchForecastSplit target and switch to the Build Settings
tab. Scroll nearly to the bottom until you find the Swift Compiler - Custom Flags
heading, where you will see the Other Swift Flags field (Figure 36.3).
Figure 36.3 Other Swift flags

Expand that field, using the disclosure arrow on the left to show that the field
can be configured differently for Debug and for Release. You can use these fields to
define compile-time values which are different for debug and release builds.
Change the Debug field to -DDEBUG as in Figure 36.4. (You can change the field
by selecting it and pressing Return.) This change makes the compile-time value
DEBUG be defined only for builds made using the Debug build configuration.
Figure 36.4 Defining DEBUG for debug builds
You can check whether the DEBUG compile-time value is defined in your code
and perform different work depending on the result:
#if DEBUG
printOutEverything()
#else
printOutOnlyWhatsNeeded()
#endif
How does this work? While it is building your app, the compiler determines
whether DEBUG has been defined. If it has been defined, the compiler will only
emit code to call printOutEverything(). If it has not been defined, the emitted
code will only call printOutOnlyWhatsNeeded().
Note that the other code is completely omitted from the built app: There is not
even a branch to be evaluated when the program runs. The check for whether
DEBUG is defined happens at compile time only. However, even though the
compiler does not emit anything for it, the other code must be syntactically
correct.
To make this more concrete, change the logging behavior of RanchForecastSplit.
Open CourseListViewController.swift and find implementation of
viewDidLoad(). Make the fetched courses only be logged out if the code is build
under the debug configuration:
case .Success(let courses):
println("Got courses: \(courses)")
#if DEBUG
println("Got courses: \(courses)")
#else
println("Got courses")
#endif
self.courses = courses
Run the app. You should see the same thing logged out as before because you
are running the debug build, where DEBUG is defined. Use the Scheme Editor to
change the Run scheme to use the Release configuration instead (Figure 36.5).
Figure 36.5 Make the Run action use the Release build configuration

When you run the program this time, you should see something much shorter,
like the following, in the log:
Received 4915 bytes with status code 200.
Got courses
Before continuing, change the build configuration used by Run back to Debug.
One last note about using preprocessor directives: They can be used a lot more
effectively than just checking whether you are in a debug build all over your
code. For example, you might want to log certain statements only in your debug
build. Your first thought might be something like this:
#if DEBUG
println("This happened.")
#endif
But it would be much less obtrusive to simply use:
debugPrintln("This happened.");
You would implement a debugPrintln function as follows:
func debugPrintln<T>(object: T) {
#if DEBUG
#if DEBUG
println(object)
#endif
}
Creating a Release Build
Now that you know about build configurations – enough to know that when you
are distributing your app, you will want a release build – how do you create one?
The simplest way to do this in Xcode is by archiving your target. Note that in the
Scheme Editor the Release build configuration is selected for the Archive action
(Figure 36.6).
Figure 36.6 Archive uses the Release build configuration

Select Archive in the Product menu. Your target will rebuild.


Xcode’s archiving feature is intended to assist with cataloging an application’s
various release builds and maintaining its debug symbols for use later on with
any crash logs you might gather.
Once your RanchForecastSplit is archived, it will appear in the Organizer on the Archives
tab (Figure 36.7).
Figure 36.7 Archives in the Organizer
You can then extract the app bundle from the archive by using the Export button.
You will be prompted for the format to share it in. Choose Export as a Mac Application
to share only the app bundle (Figure 36.8).
Figure 36.8 Sharing your application

Once Xcode has exported the app bundle, you can compress it in a ZIP archive
and post it on your website or send it to your beta testing team.
A Few Words on Installers
If you are new to Mac, you may be wondering how you are going to create an
installer for your application. Our advice is: Don’t. Application installation on a
Mac is different from other platforms, and in most cases an installer adds
unneeded complexity and hides its actions from the user. Most applications are
installed by simply having the user drag them from their downloads folder into
/Applications. This has the advantage of a very clear uninstallation: Drag the
application to the trash.
There are two common approaches for packaging an application for download.
Many app bundles are simply compressed in a ZIP archive. By default, Safari
unarchives ZIP archives containing app bundles, making drag installation very
easy for the user.
The second approach is to create a DMG (disk image), which has the advantage
of displaying a Finder window with the contents of the image when it is opened
(mounted). This allows for the inclusion of files in addition to the app bundle
itself, such as a readme, a symbolic link to /Applications (to make drag
installation even more convenient), and an optional custom background.
Configuring such a DMG is complicated enough that there are third-party tools
to help with the process.
Both approaches have their pitfalls. For example, users sometimes forget to drag
the application to their /Applications folder, leaving it in their downloads folder
or, worse, running it from the DMG. One solution some developers have
implemented is to detect the app bundle’s location on startup and offer to move
it for the user.
Note that Mac App Store apps cannot use an installer.
App Sandbox
In the old days, an application had all the same rights as the user running it. If
you trust all your applications, this sounds fine. However, most users do not
have the luxury of running only apps they trust, and, more important, no user can
run only bug-free applications. The unpleasant truth is that even a trustworthy
application can have an innocent bug that causes damage to a system or allows
an attacker access to the user’s system. The app sandbox is a big step toward
mitigating this problem.
Sandboxing is a security method that constrains the means by which an
application can interact with the system (filesystem, network). Apple has
required sandboxing of all apps on iOS since the very first 3rd party apps were
introduced in iOS 2.0. Apple first introduced sandboxing to the Mac in OS X
10.7. All applications on the Mac App Store must be sandboxed.

Entitlements
When you opt in to the app sandbox, by default your application will have fairly
limited access to system resources. It can load resources from its bundle and read
and write files within its container (more on those in a moment), but not much
more. If your application needs access to more – such as the network, the user’s
contacts, or the Music folder – you can specifically enable such access by adding
entitlements.
Consider the requirements of the RanchForecastSplit app. It needs to create outgoing
network connections and not much more. It does not need to read or write files
on disk, use the camera or microphone, or open a port for incoming network
connections. By limiting RanchForecastSplit’s entitlements to creating only outgoing
network connections, you have minimized any opportunities for mischief on the
part of this application. More importantly, you have minimized opportunities for
an attacker to exploit a weakness in RanchForecastSplit.
To specify an application’s entitlements, you can use the Capabilities tab of Xcode’s
project editor (Figure 36.9). Note that to specify the entitlements, you must first
enable the App Sandbox.
Try enabling the sandbox on RanchForecastSplit. What happens if you run it without
enabling the Network: Outgoing Connections (Client) entitlement?
Figure 36.9 Application entitlements

Containers
Sandboxed applications are provided with a container: a folder on disk in which
they can store caches and other resources. The container is stored in the user’s
library, under ~/Library/Containers. Paths returned to your code by APIs such
as NSFileManager’s URLsForDirectory(_:inDomains:) will return URLs within
the container.

Mediated file access and Powerbox


Mediated read and write access is the preferred means of file access to
applications. Mediated access includes access to temporary files, such as those in
the application’s container, and also read or read/write access to files that are
explicitly opened by the user using a OS X file-open dialog or files dragged to
the application. That is, when the user chooses a file via an NSOpenPanel or
NSSavePanel, the application’s sandbox is automatically expanded to include the
selected file or directory.
This functionality is provided by a system daemon called Powerbox. When the
developer uses NSOpenPanel or NSSavePanel, the sheets are rendered by the
system daemon, providing a trusted means for file selection that is transparent to
the developer – no code changes are necessary.
If an application that has the mediated read or read/write entitlement is
terminated and restarted, the sandbox is again expanded to include previously
open documents when OS X restores the prior state of the application by
reopening those documents. The standard AppKit Open Recent menu provides
similar capabilities.
For a full description of the entitlements available to applications, see the OS X
Developer Library document App Sandbox Design Guide.
The Mac App Store
If you are writing a commercial app, there is quite a bit of work to do beyond the
writing the app itself in order to release it. The Mac App Store has the advantage
of taking care of a lot of these aspects for you: purchasing,
installation/packaging, and distribution are all handled for you. Much of
licensing is addressed, also. If you are an independent developer, working on
such tasks can feel as though they are taking valuable time away from making
your product better.
The Mac App Store is not for every app, however. If your application cannot
operate in a sandboxed environment or does not conform to the review
guidelines, you will want to use more traditional means of distribution.
Most aspects of distribution in the Mac App Store are fairly straightforward and
similar to the iOS App Store. You will need to use Xcode to sign your application
binary, provide a description of your application and screenshots, and, finally,
submit the app by using Xcode’s Organizer.
Receipt Validation
Mac App Store apps differ from iOS apps in one key area, however: There is no
OS support for license checking. That is, without special effort on your part,
there is no copy protection. If copy protection is important to you, you will need
to implement receipt validation.
When purchased from the Mac App Store, an application is downloaded to the
user’s system. A file containing the application receipt will be placed in the
application bundle. The receipt contains the application’s bundle identifier, its
version string, and a hash of the computer’s GUID. Receipts are
cryptographically signed by Apple.
By verifying the information in this receipt, your application can determine
whether it is authorized to run on the system where it is loaded.

Local receipt verification


To do local verification, you will need to perform the following steps:
1. Verify that the receipt is present.
2. Verify that the receipt is properly signed by Apple.
3. Verify that the bundle identifier in the receipt matches.
4. Verify that the version identifier matches.
5. Verify that the hash contained in the receipt matches the computer’s GUID
hash.
A few notes: although the bundle and version identifiers can be obtained from
the Info.plist file (CFBundleIdentifier and CFBundleShortVersionString
keys, respectively), it is strongly recommended that these values be duplicated as
constants within the application itself. The reason is that the application’s
Info.plist file is easily modified by users; by trusting this information, the
application could be tricked into accepting a valid receipt for another application
on that system.
If validation fails, the application should terminate with a status of 173:
If validation fails, the application should terminate with a status of 173:
if !validated {
exit(173)
}
This instructs the system that validation has failed for this application.
The code for performing this verification process is, frankly, unpleasant for most
developers. Low-level C programmers will feel right at home, but working with
cryptographic APIs can be daunting to most developers. You may be asking,
“Why doesn’t Apple provide a reference implementation?”
The reason is that if Apple did provide a reference implementation, the vast
majority of developers would use it, and a cracking tool could then be used to
disarm the protection in all applications that use this code. By asking developers
to concoct their own methods for verifying this information, the problem of
cracking copy protection is made more difficult.
Apple has provided code snippets for performing parts of this process, as well as
a sample receipt for testing purposes. This is an excellent use of build
configurations: Use the debug build configuration, or create a new one that
directs your code to use the sample receipt for its validation process.
Apple’s code snippets can be found in the Mac OS X Developer Library article
Receipt Validation Programming Guide.

Server-based verification
In addition to the local receipt validation approach discussed above, you can also
ask the Mac App Store to validate receipts.
To use this approach, you will have to set up a server to connect to the Mac App
Store server. Not only does this provide flexibility in how you use the
knowledge that a user has a verified receipt, such as for providing server-based
features only for verified users, but it also prevents a man-in-the-middle attack
from simply verifying all requests from all applications on a system.
To do App Store-based validation, you will need to perform the following steps:
1. In your app, read the receipt file at the location given by
NSBundle.appStoreReceiptURL().

2. In your app, send the data from that file to your server.
3. On your server, send an HTTP POST request with a JSON body containing
the receipt data to https://buy.itunes.apple.com/verifyReceipt.
4. On your server, interpret the response from the Mac App Store and send an
appropriate response to your app.
5. In your app, handle the response from your server, calling exit(173) if
necessary.
Apple has provided documentation for requests and responses to be sent and
received from the Mac App Store in the Receipt Validation Programming Guide.
There are also some helpful snippets to get you started with both the app code
and the server code in this guide.
Overall, be creative in your receipt validation, and remember to use varying
patterns between your applications.
37
Afterword
Congratulations! You have reached the end of the book. The knowledge that you
have earned has not come easy, and you have learned a lot of stuff. Be proud.
The only way to solidify what you have learned is to write applications, and the
sooner you start, the easier it will be. The good news is that while there is still
more to learn, you have gotten over the hump in the learning curve. Matters will
be easier from here, but the only way to progress is to write applications.
If you would like to learn more from Big Nerd Ranch, we offer five-and seven-
day classes. For a schedule, visit bignerdranch.com. Or use the RanchForecast exercise.
Also, look for Big Nerd Ranch Guides on other programming topics.
You can find us on Twitter, where we keep you informed about programming
and entertained about life: @bignerdranch, @aaronhillegass, @preble, and @neightchan.
As you continue programming, you will continue to have questions. We offer the
following list of resources as good places to find answers:
If you have a question about Cocoa, the first place to check is in the
reference documentation. All the classes, protocols, functions, and
constants are listed there. Additionally, Apple’s Programming Guides are a
great resource for practical instructions on using many APIs. You will find
these linked toward the top of many Class Reference pages.
The Mac App Programming Guide in Apple’s developer documentation is a
great resource for learning about the behaviors of a Mac-like app, and what
Cocoa APIs you can use to implement them.
If you have a question about Swift, the first place to check is in the online
Swift reference documentation.
If you have a question about Xcode or Interface Builder, the first place to check is
in the developer tools reference documentation.
Don’t be afraid to experiment—most questions can be answered by creating
a tiny application. Creating this application will probably take you less than
15 minutes.
The website for this book (bignerdranch.com/books) has links to the exercise
solutions and an interactive forum for discussing this book and Cocoa
programming.
Stack Overflow (stackoverflow.com) is an excellent place to find the answers
to your questions, and has a strong Cocoa and iOS presence. Chances are
somebody has faced the same challenge you are facing.
Mark Dalrymple wrote a book on the plumbing of OS X from a
developer’s point of view. If your code is going to do anything with the
operating system (such as multithreading or networking), we strongly
recommend that you pick up a copy of Advanced Mac OS X Programming:
The Big Nerd Ranch Guide.
If you are interested in learning iOS development and the Cocoa Touch
frameworks, check out iOS Programming: The Big Nerd Ranch Guide.
Find a CocoaHeads group near you. CocoaHeads has a mailing list and
groups around the world where Cocoa developers meet to discuss Cocoa
programming. You can find out more at cocoaheads.org.
Join the Mac Developer Program. It will give you access to the latest
developer tools and documentation, as well as prior years’ WWDC videos.
The Developer Programs site is developer.apple.com/programs.
If you have exhausted all other possibilities, Apple’s Developer Technical
Support can answer your questions at developer.apple.com/support/technical/. The
folks there have answered lots of questions for us, and we find them to be
consistently knowledgeable and helpful.
Finally, try to be nice. Help beginners. Give away useful applications and their
source code. Answer questions in a kind manner. It is a relatively small
community, and few good deeds go forever unrewarded.
Thanks for reading our book!
Index
A B C D E F G H I J K L M N O P Q R S T U V W X Z

Symbols
(_:), meaning of, Instance methods
.icns file, Setting the Extension and Icon for the File Type
.lproj files (localization), Localization and Bundles
.tar files, Challenge: .tar and .tgz Files
.tgz files, Challenge: .tar and .tgz Files
.xcdatamodeld (Core Data), Defining the Object Model
// MARK:, Creating the user interface
@IBAction, Defining an action method
@IBDesignable, Inspectable properties and designable views
@IBInspectable, Inspectable properties and designable views
@IBOutlet, Creating an outlet
@NSApplicationMain, NSApplication and NSApplicationDelegate

A
accents, typing, Localizing a XIB File
acceptsFirstResponder (NSResponder), NSResponder
acceptsMouseMovedEvents (NSWindow), For the More Curious: Rollovers
access modifiers, Your First Test, For the More Curious: Access Modifiers
actions
(see also connections (in Interface Builder), controls, NSControl, outlets)
and AnyObject, Connecting the slider’s target and action
connecting in Interface Builder, Connecting actions
defined, Making Connections
feature of NSControl, About Controls
and menu items, Starting the Chatter Application
as messages, About Controls, A continuous control
methods for, Defining an action method, Connecting the slider’s target and action
nil-targeted, Nil-Targeted Actions
and NSApplication, For the More Curious: Which Object Sends the Action Message?
setting programmatically, For the More Curious: Setting the Target Programmatically
actions (CALayer), Implicit Animation and Actions
addChildViewController(_:) (NSViewController), Adding Tab Images
addObserver(_:selector:name:object:) (NSNotificationCenter), NSNotificationCenter
addSubview(_:) (NSView), View Swapping
alerts, Alerts and Closures
alpha values (NSColor), Using the Documentation
ambiguous layouts (Auto Layout), Does Not Compute, Part 2: Ambiguous Layout
animations, Core Animation
and timers, NSTimer-based Animation
AnyObject, Connecting the slider’s target and action, Adding Tab Images
API Reference, Using the Documentation
App Store (distribution), The Mac App Store
AppDelegate
about, NSApplication and NSApplicationDelegate
role, The controller layer
and window controllers, Showing the Window, Improving Controller Design
append(_:), Instance methods
AppKit (framework), The Cocoa Frameworks, Creating the MainWindowController class, frame
Apple Developer Programs, Prerequisites
application architecture
basic, Showing the Window, Improving Controller Design
document-based, NSArrayController, The Document Architecture
master/detail, Container View Controllers
with multiple view controllers, Connecting the Course List Selection with the Web View
and MVC, Model-View-Controller
single-window, Setting up RGBWell, Creating and using an Xcode snippet
and view controllers, View Controllers, Starting the ViewControl Application
and window controllers, Improving Controller Design, Creating an instance of
MainWindowController, View Controllers vs. Window Controllers
application bundles, NSBundle, NSTask
applications
(see also application architecture, projects)
App Store, using, The Mac App Store
build configurations for, Build Configurations
containers for, Containers
copy protection for, Receipt Validation
custom file extensions for, Setting the Extension and Icon for the File Type
custom file icons for, Setting the Extension and Icon for the File Type
distributing, Creating a Release Build, The Mac App Store
document-based, NSArrayController
entitlements of, Entitlements
and event loop, The main event loop
exporting, Creating a Release Build
installers for, A Few Words on Installers
launching, NSApplication and NSApplicationDelegate, The main event loop
lifecycle methods, NSApplication and NSApplicationDelegate
localizing, Localization and Bundles, Localizing a XIB File
locations for data, Application Data and URLs
mediated file access, Mediated file access and Powerbox
and multiple threads, Multithreading
packaging, A Few Words on Installers
printing from, Printing
and release builds, Creating a Release Build
sandboxing, App Sandbox
storage for, Application Data and URLs
and system resources, Entitlements
unit testing, Unit Testing
ARC (Automatic Reference Counting), Automatic Reference Counting, Strong reference
cycles, What is ARC?
(see also memory management)
archivedDataWithRootObject(_:), Saving and NSKeyedArchiver
archiving
about, Archiving
build targets, Creating a Release Build
decoding, Decoding
and document architecture, The Document Architecture
encoding, Encoding
loading objects, Loading and NSKeyedUnarchiver
NSCoder, NSCoder and NSCoding
NSData, Saving and NSKeyedArchiver, Loading and NSKeyedUnarchiver
NSKeyedArchiver, Saving and NSKeyedArchiver
NSKeyedUnarchiver, Loading and NSKeyedUnarchiver
preventing infinite loops in, For the More Curious: Preventing Infinite Loops
saving objects, Saving and NSKeyedArchiver
vs. Core Data, Choosing a Cocoa Persistence Technology
XIB files, XIB files and NIB files
ARepeat (NSEvent), NSEvent
arrangedObjects (NSArrayController), Introducing NSArrayController, Binding the Table
View’s Selection to the Array Controller, Sorting in RaiseMan
array controllers
(see also NSArrayController)
about, Introducing NSArrayController
customizing, Customizing Objects Created by NSArrayController
filtering with, For the More Curious: Filtering
immediate fetching, Configure the Array Controller
labeling in Interface Builder, Configure the Array Controller
and model abstractions, Introducing NSArrayController
and NSManagedObjectContext, Configure the Array Controller
sorting with, Sorting in RaiseMan
as target of controls, Connecting the Add Employee Button
arrays
about, Collection types
append(_:), Instance methods
count, Properties
filtering, For the More Curious: Filtering
memory management of, Deallocating objects in a hierarchy
and NSArray, Bridging with collections
reverse(), Instance methods
subscripting, Literals and subscripting
and traps, Literals and subscripting
as, Basic bridging
assert(), Runtime Errors
assertions (unit testing), Testing in Xcode, Refactoring for Testing
assistant editor, Making a connection with the assistant editor
associated values (enums), NSURLSession and asynchronous API design
astrophysics degrees, Some Advice on Learning
attributes (Core Data), Defining the Object Model
attributes (views), Configuring view objects
attributes inspector (Xcode), Configuring view objects, A continuous control
Auto Layout
(see also constraints (Auto Layout))
adding constraints, Constraints from subview to superview
ambiguous layouts, Does Not Compute, Part 2: Ambiguous Layout
vs. autoresizing masks, For the More Curious: Autoresizing Masks
clip view warning, Adding a Table View
described, What is Auto Layout?
intrinsic content size, Intrinsic Content Size
and NSBox, View Swapping, Challenge: Boxless NerdTabViewController
unsatisfiable constraints, Does Not Compute, Part 1: Unsatisfiable Constraints
Visual Format Language, Visual Format Language
visualizeConstraints(_:), Does Not Compute, Part 2: Ambiguous Layout
with right-to-left languages, Creating Layout Constraints Programmatically
auto-complete (Xcode), Implementing a delegate method
automatic document saving, For the More Curious: Automatic Document Saving
automatic initializers, Structures
autoresizing masks, For the More Curious: Autoresizing Masks
availableTypeFromArray(_:) (NSPasteboardItem), NSPasteboard

B
background threads, NSURLSession and asynchronous API design, Multithreading, Analyzing
output from Instruments
Base.lproj, Localizing String Literals
beginCriticalSheet(_:completi…) (NSWindow), Sheets
beginDraggingSessionWithItems(…) (NSView), Starting a drag
beginSheet(_:completionHandler:) (NSWindow), Sheets, Present the Sheet
beginSheetModalForWindow(_:comple…) (NSAlert), Completion Handlers and
Closures
bindings
array controllers, Introducing NSArrayController
benefits of, Bindings
with Core Data, Basic Core Data
creating, Using bindings
creating programmatically, For the More Curious: Key Paths
debugging, Debugging Bindings, Using the debugger to see bindings in action
and KVC/KVO, Bindings, Key-value observing, Making keys observable
and NSObject, RaiseMan’s Model Layer, RanchForecast Project
patterns for, Connections and Bindings
for table data, Binding the text field to the table cell view
and value transformers, For the More Curious: NSValueTransformer
when to use, Binding other attributes
bindings inspector (Xcode), Binding the text field to the table cell view
blocking
(see also multithreading)
CPU-bound, Analyzing output from Instruments
I/O-bound, Analyzing output from Instruments
and modal windows, Modals and Sheets, Modal Windows
boldSystemFontOfSize(_:) (NSFont), NSFont
Bool, Number and boolean types
boolean types, Number and boolean types
boolForKey(_:) (NSUserDefaults), NSUserDefaults
bounds (NSView), bounds
breakpoint navigator, Deleting breakpoints
breakpoints, Using breakpoints, Deleting breakpoints
bridging, Working with Foundation Types
build actions, Build Configurations
build configurations
changing app behavior with, Preprocessor Directives: Using Build Configurations to
Change Behavior
debug, Debugging Hints, Build Configurations
debugging symbols in, Build Configurations
finding, Build Configurations
and Instruments, Introducing Instruments
and preprocessor directives, Preprocessor Directives: Using Build Configurations to
Change Behavior
release, Debugging Hints, Build Configurations
setting flags in, Preprocessor Directives: Using Build Configurations to Change Behavior
specifying, Build Configurations
build targets, Testing in Xcode, Creating a Release Build
bundles
application, Setting the Extension and Icon for the File Type, NSBundle, NSTask
described, NSBundle
identifiers for, Application Data and URLs, What is the User’s Defaults Database?
and localization, Different Mechanisms for Localization, NSBundle’s role in localization
main, NSBundle
and strings files, NSBundle’s role in localization
buttons
disabling, Updating Buttons
in Interface Builder, Adding view objects
radio, Challenge: Busy Board
recessed, Binding other attributes
titles for, Configuring view objects

C
CAAnimation, CALayer
CABasicAnimation, Implicit Animation and Actions
CAGradientLayer, More on CALayer
CALayer
about, CALayer
actions, Implicit Animation and Actions
delegate of, More on CALayer
described, CALayer
subclasses, More on CALayer
CALayerDelegate, More on CALayer
canvas (Interface Builder), Creating the User Interface in Interface Builder
CAOpenGLLayer, More on CALayer
capture lists, Closures and capturing
caseInsensitiveCompare(_:), For the More Curious: The caseInsensitiveCompare(_:) Method
CAShapeLayer, More on CALayer
casting, Basic bridging
categories, Extensions
CATextLayer, More on CALayer
CATransaction, CALayer, Implicit Animation and Actions
cell-based tables, Tables, Cells, and Views
cells
and controls, A word about NSCell
history in Cocoa, A word about NSCell, Tables, Cells, and Views
in table views, Tables, Cells, and Views
CGFloat, Using the Documentation, Changing the color of the color well
CGRect
contains(_:), Improving Hit Detection
characters (NSEvent), NSEvent
checkboxes (NSButton), Challenge: Busy Board
Clang Static Analyzer, What is ARC?
class methods, Getting Voice Data
classes
(see also individual class names, initializers, methods, objects, properties, types)
about, Application Design
creating new, Creating the MainWindowController class
defining, Classes
extending, Extensions
and inheritance, Inheritance
initializing, Designated and convenience initializers
making @IBDesignable, Inspectable properties and designable views
prefixes for, Adding an Array Controller to the XIB
in product modules, Adding an Array Controller to the XIB
reference pages for, Using the Documentation
vs. structures, Reference and Value Types
clearContents() (NSPasteboard), NSPasteboard
clickCount (NSEvent), NSEvent, Getting Mouse Events
clip views, Table view and related objects
closures, Reference and Value Types, Completion Handlers and Closures
Cocoa
API reference, Using the Documentation
classes in, Choosing between reference and value types, Swift and Objective-C, Using the
Documentation
documentation, Using the Documentation
frameworks in, The Cocoa Frameworks, Creating the MainWindowController class
history of, The Story of Cocoa
Cocoa Touch (framework), Afterword
CocoaHeads, Afterword
code snippet library, Creating and using an Xcode snippet
code snippets, Creating and using an Xcode snippet
color (NSColorWell), Using the Documentation
color wells, NSColorWell and NSColor
com.pkware.zip-archive, ZIPspector
completion handlers
about, Completion Handlers and Closures
with asynchronous API, NSURLSession and asynchronous API design
implementing, NSURLSession and asynchronous API design
testing, For the More Curious: Asynchronous Testing
computed properties
about, Computed Properties
and KVC, KVC and Property Accessors
storage for, KVC and Property Accessors
concludeDragOperation(_:) (NSDraggingDestination), Make DieView a Drag
Destination
concurrency, Concurrency, NSOperationQueue
conditionals
if-else, Creating the user interface
if-let, Optionals
switch, Enumerations and the Switch Statement
connections (in Interface Builder), Making Connections
(see also actions, outlets)
with assistant editor, Making a connection with the assistant editor
to File's Owner, File's Owner and making connections
connections inspector, Connecting actions
connections panel, Connecting an outlet
console
exceptions in, Adding Key-Value validation to RaiseMan
importance in debugging, Debugging Hints
LLDB (debugger), The LLDB console
viewing in playground, Optionals
viewing in project, Connecting the slider’s target and action
constants, Using Standard Types
constraints (Auto Layout)
(see also Auto Layout)
adding in Interface Builder, Constraints from subview to superview
adding programmatically, Challenge: Add Constraints Programmatically
and ambiguous layouts, Does Not Compute, Part 2: Ambiguous Layout
animating, Creating Layout Constraints Programmatically
between siblings, Constraints from subview to superview
creating in Interface Builder, What is Auto Layout?, Constraints from subview to
superview
creating programmatically, Creating Layout Constraints Programmatically
debugging, Does Not Compute, Part 1: Unsatisfiable Constraints
for positioning views, What is Auto Layout?
priorities of, Constraints from subview to superview
size constraints, Size constraints
subview-superview, Constraints from subview to superview
types of, What is Auto Layout?
unsatisfiable, Does Not Compute, Part 1: Unsatisfiable Constraints
containers (for applications), Containers
containers (view controllers), Container View Controllers
contains(_:) (CGRect), Improving Hit Detection
content (NSArrayController), Introducing NSArrayController
Content Array (array controllers), Introducing NSArrayController, Adding an Array Controller
to the XIB
content views, The view layer, Creating an empty XIB file
contexts (graphics), Graphics contexts and states, Saving and Restoring the Graphics State
continuous (NSControl), A continuous control
controller layer (MVC), The controller layer
(see also view controllers, window controllers)
controllers
(see also Model-View-Controller (MVC))
controls
(see also actions, NSControl)
about, About Controls
and action messages, About Controls, A continuous control
array controllers as targets, Connecting the Add Employee Button
and cells, A word about NSCell
creating programmatically, Visual Format Language
enabling/disabling, Disabling a control
formatting, Formatters and a control’s objectValue
making continuous, A continuous control
outlets to, Controls and Outlets
and target-action, About Controls
convertPoint(_:fromView:) (NSView), Improving Hit Detection
convertPoint(_:toView:) (NSView), Improving Hit Detection
copy protection, Receipt Validation
copying-and-pasting (implementing), Pasteboards and Nil-Targeted Actions
Core Animation, Core Animation
Core Data
.xcdatamodeld, Defining the Object Model
attributes, Defining the Object Model
benefits of, Basic Core Data, Choosing a Cocoa Persistence Technology
with bindings, Basic Core Data
data model inspector, Fetching Objects from the NSManagedObjectContext
and data set size, Choosing a Cocoa Persistence Technology
defining object model, Defining the Object Model
entities, Defining the Object Model
explained, How Core Data Works
faulting, Choosing a Cocoa Persistence Technology
fetch requests, Fetching Objects from the NSManagedObjectContext
NSManagedObject, Defining the Object Model
NSManagedObjectContext, Configure the Array Controller, How Core Data Works,
Fetching Objects from the NSManagedObjectContext, Choosing a Cocoa Persistence Technology
NSManagedObjectModel, Defining the Object Model, How Core Data Works
NSPersistentDocument, How Core Data Works
pros and cons, Choosing a Cocoa Persistence Technology
relationships, Defining the Object Model
and SQLite, Persistent Store Types
store types, Persistent Store Types
vs. archiving, Choosing a Cocoa Persistence Technology
Core Graphics (framework), frame, For the More Curious: Core Graphics and Quartz
count (arrays), Properties
createDirectoryAtURL(_:withIntermed…) (NSFileManager), Application Data and
URLs
currentContextDrawingToScreen() (NSGraphicsContext), For the More Curious: Are
You Drawing to the Screen?
cutting-and-pasting (implementing), Pasteboards and Nil-Targeted Actions

D
Dalrymple, Mark, Afterword
Darwin (Unix), OSX, Unix, and Cocoa
data model inspector (Core Data), Fetching Objects from the NSManagedObjectContext
data sources (run loops), Asynchronous Reads
data sources (table views), Delegates and data sources, The table view-data source conversation,
The NSTableViewDataSource Protocol
dataForType(_:) (NSPasteboardItem), NSPasteboard
dataOfType(_:error:) (NSDocument), Saving documents
dataSource (NSTableView), Delegates and data sources
dataSource (property)
exposed as outlet, Connecting the dataSource outlet
setting in Interface Builder, Connecting the dataSource outlet
dataWithPDFInsideRect(_:) (NSView), Getting Your View to Generate PDF Data
date formatters, Formatters, programmatically, Lay out the interface
date pickers, Add the Views
debug builds, Debugging Hints, Build Configurations
DEBUG compile-time value, Preprocessor Directives: Using Build Configurations to Change
Behavior
debug navigator, Using breakpoints
debugger bar, Stepping through code
debugging
(see also debugging tools, errors, exceptions)
Auto Layout constraints, Does Not Compute, Part 1: Unsatisfiable Constraints
bindings, Debugging Bindings, Using the debugger to see bindings in action
exceptions, Setting an exception breakpoint
hints, Debugging Hints
stack trace, Using breakpoints
stepping through methods, Stepping through code
symbols, Build Configurations
with zombie objects, Debugging Hints
debugging tools
breakpoints, Using breakpoints, Deleting breakpoints
debug navigator, Using breakpoints
debugger, Using the Debugger
LLDB (debugger) console, The LLDB console
stack trace, Using breakpoints
variables view, Using breakpoints
decodeBoolForKey(_:) (NSCoder), Decoding
decodeDoubleForKey(_:) (NSCoder), Decoding
decodeFloatForKey(_:) (NSCoder), Decoding
decodeIntForKey(_:) (NSCoder), Decoding
decodeObjectForKey(_:) (NSCoder), Decoding
default: (switch statement), Enumerations and the Switch Statement
defaultCenter() (NSNotificationCenter), NSNotificationCenter
defaults, User Defaults
delegate (property), Being a delegate
(see also delegate methods, delegation)
exposed as outlet, The NSTableViewDelegate Protocol
setting in Interface Builder, Implementing another delegate, The NSTableViewDelegate
Protocol
delegate methods
(see also delegate (property), delegation)
and notifications, Delegate protocols and notifications
optional, Being a delegate, For the More Curious: How Optional Delegate Methods Work
required, Being a delegate
types of, Implementing another delegate
using auto-complete for, Implementing a delegate method
delegation
(see also delegate (property), delegate methods)
about, Delegation
classes using, Cocoa classes that have delegates
errors in implementing, Common errors in implementing a delegate
NSWindowDelegate, Implementing another delegate
and protocols, Being a delegate
steps in implementing, Being a delegate
vs. subclassing, Delegation, Being a delegate
and table views, Delegates and data sources
and web services, NSURLSession and asynchronous API design
dependent keys, For the More Curious: Dependent Keys
developer programs, Prerequisites
dictionaries
about, Collection types
accessing, Subscripting dictionaries
and NSDictionary, Bridging with collections
subscripting, Subscripting dictionaries
didChangeValueForKey(_:), Making keys observable
didSet (property observer), Updating Buttons
directories
(see also bundles)
(see also bundles, files)
.lproj, Localization and Bundles
application, Application Data and URLs
as file wrappers, Saving documents
localization, Localization and Bundles, Localizing String Literals
project source, Localizing String Literals
dirty rects, When is my view drawn?, For the More Curious: Dirty Rects
dismissWithModalResponse(_:), Present the Sheet
distributing (applications), Creating a Release Build, The Mac App Store
DMG (disk image), A Few Words on Installers
dock (Interface Builder), Creating the User Interface in Interface Builder
Document (template-created class), NSArrayController
document architecture, The Document Architecture
document controllers, Info.plist and NSDocumentController
document outline (Interface Builder), Creating the User Interface in Interface Builder, Table
cell views
document-based applications, NSArrayController
and printing, Printing
and responder chain, Nil-Targeted Actions
documentation
for Cocoa classes, Using the Documentation
for protocols, Implementing another delegate
for Swift, Exploring Apple’s Swift Documentation
documents
(see also document architecture, document controllers, files, NSDocument)
automatic saving of, For the More Curious: Automatic Document Saving
extensions for, Setting the Extension and Icon for the File Type
icons for, Setting the Extension and Icon for the File Type
loading, Loading documents
and loading NIB files, Loading documents
printing from, Printing
saving, Saving documents
DOM parsing, For the More Curious: Parsing XML
Double, Number and boolean types
doubleValue, About Controls
drag-and-drop, Drag-and-Drop
draggingEntered(_:) (NSDraggingDestination), Make DieView a Drag Destination,
Implement the dragging destination methods
draggingExited(_:) (NSDraggingDestination), Make DieView a Drag Destination
draggingSession(_:endedAtPoint:operati…) (NSDraggingSource), After the drop
draggingSession(_:sourceOperationM…), Make DieView a Drag Source
draggingUpdated(_:) (NSDraggingDestination), Make DieView a Drag Destination
drawAtPoint(_:) (NSAttributedString), Drawing Strings and Attributed Strings
drawAtPoint(_:withAttributes:) (NSString), Drawing Strings and Attributed Strings
drawFocusRingMask() (NSView), Focus Rings
drawing
(see also animations, views)
and dirty rects, When is my view drawn?, For the More Curious: Dirty Rects
frameworks for, For the More Curious: Core Graphics and Quartz
and graphics contexts, Graphics contexts and states, Saving and Restoring the Graphics State
images, Drawing Images
and layers, Core Animation
PDF data, Getting Your View to Generate PDF Data
and points, frame
printer vs. screen, For the More Curious: Are You Drawing to the Screen?
views, Custom Drawing
drawInRect(_:) (NSImage), Drawing Images
drawInRect(_:fromRect:op…) (NSImage), Drawing images with finer control
drawInRect(_:withAttributes:) (NSString), Drawing Strings and Attributed Strings
drawLayer(_:inContext:), More on CALayer
drawRect(_:) (NSView), drawRect(_:)
dynamic, Making keys observable

E
enabled, Disabling a control
encodeBool(_:forKey:) (NSCoder), Encoding
encodeConditionalObject(_:forKey:) (NSCoder), For the More Curious: Preventing Infinite
Loops
encodeDouble(_:forKey:) (NSCoder), Encoding
encodeFloat(_:forKey:) (NSCoder), Encoding
encodeInt(_:forKey:) (NSCoder), Encoding
encodeObject(_:forKey:) (NSCoder), Encoding
encodeWithCoder(_:) (NSCoding), NSCoder and NSCoding, Encoding
endSheet(_:returnCode:) (NSWindow), Sheets, Present the Sheet
entities (Core Data), Defining the Object Model
entitlements (application), Entitlements
enumerate(), Loops and String Interpolation
enums (enumerations)
with associated values, NSURLSession and asynchronous API design
defined, Enumerations and the Switch Statement
instance methods in, Instance methods
nested, NSURLSession and asynchronous API design
and raw values, Enumerations and raw values
and switch statements, Enumerations and the Switch Statement
errors
(see also debugging, exceptions, NSError)
Auto Layout, Does Not Compute, Part 2: Ambiguous Layout
auto-saving, Undo for Edits
with bindings, Debugging Bindings
and completion handlers, NSURLSession and asynchronous API design
and enums, NSURLSession and asynchronous API design
in event-handling, Adding Key-Value validation to RaiseMan
exceptions, Runtime Errors
HTTP codes, NSURLSession, HTTP status codes, and errors
in delegation, Common errors in implementing a delegate
in playgrounds, Using Standard Types
with KVC, Adding Key-Value validation to RaiseMan
runtime, Runtime Errors
traps, Literals and subscripting, Runtime Errors
with untyped data, Safely Working with Untyped Data Structures
XCTFail(), Refactoring for Testing
event loop, The main event loop
events
(see also mouse events)
errors in handling, Adding Key-Value validation to RaiseMan
in event loop, The main event loop
keyboard, Keyboard Events
mouse (see mouse events)
exception breakpoints, Setting an exception breakpoint
exceptions, Runtime Errors, Setting an exception breakpoint
(see also errors)
expressions
evaluating with LLDB, The LLDB console
and string interpolation, Loops and String Interpolation
extensions (of a class), Extensions

F
factory defaults, NSUserDefaults
fallthrough (switch statement), Enumerations and the Switch Statement
fatalError(), Runtime Errors
faulting (Core Data), Choosing a Cocoa Persistence Technology
fetch requests (Core Data), Fetching Objects from the NSManagedObjectContext
file handles, ZIPspector, Asynchronous Reads, iPing
file wrappers, Saving documents
File's Owner, File's Owner and making connections
files
(see also directories, documents)
copying, A New UI for RanchForecast
custom extensions for, Setting the Extension and Icon for the File Type
custom icons for, Setting the Extension and Icon for the File Type
formats for pasting, Pasteboards and Nil-Targeted Actions
in project, Getting around in Xcode
loading, Loading documents
saving, Saving documents
fileWrapperOfType(_:error:) (NSDocument), Saving documents
fill() (NSBezierPath), Custom Drawing
filter(), For the More Curious: Filtering, For the More Curious: Functional Methods and Minimizing
Closure Syntax
filtering (array controllers), For the More Curious: Filtering
find(), Pre-selecting the default voice
first responder, Keyboard Events, Looking at the XIB file
(see also NSResponder, responder chain)
flagsChanged(_:) (NSResponder), NSResponder
flipped views, For the More Curious: Flipped Views
Float, Number and boolean types
floatForKey(_:) (NSUserDefaults), NSUserDefaults
floating-point types, Number and boolean types, Initializers, Testing in Xcode
floatValue, About Controls
focus rings, Focus Rings
fonts, NSFont, For the More Curious: NSFontManager
for-in, Loops and String Interpolation
forced unwrapping (of optionals), Optionals
formatter (NSControl), Formatters and a control’s objectValue
formatters
about, Formatters and Validation
and controls, Formatters and a control’s objectValue
date, Formatters, programmatically
interaction with locale, Formatters and localization
number, Formatting the Raise Text Field, Formatters, programmatically
vs. KVC validation, Validation with Key-Value Coding
writing custom, Formatters and localization
forwardInvocation(_:) (NSInvocation), Message Passing and NSInvocation
Foundation (framework), The Cocoa Frameworks, Working with Foundation Types, RaiseMan’s
Model Layer
frame (NSView), Views, Rectangles, and Coordinate Systems
frameworks
AppKit, The Cocoa Frameworks, Creating the MainWindowController class
Cocoa, The Cocoa Frameworks, Creating the MainWindowController class
Cocoa Touch, Afterword
Core Data, Basic Core Data, How Core Data Works, Choosing a Cocoa Persistence Technology
Core Graphics, frame, For the More Curious: Core Graphics and Quartz
defined, The Cocoa Frameworks
documentation for, Using the Documentation
for drawing, For the More Curious: Core Graphics and Quartz
Foundation, The Cocoa Frameworks, Working with Foundation Types, RaiseMan’s Model
Layer
importing, Creating the MainWindowController class, RaiseMan’s Model Layer
Quartz, For the More Curious: Core Graphics and Quartz
shipped with OS X, The Cocoa Frameworks
XCTest, Testing in Xcode, Your First Test
func, Instance methods
functional programming, For the More Curious: Functional Methods and Minimizing Closure
Syntax
functions
(see also closures, initializers, methods)
for functional programming, For the More Curious: Functional Methods and Minimizing
Closure Syntax
as types, Reference and Value Types

G
generalPasteboard() (NSPasteboard), NSPasteboard
genstrings (localization), Localizing String Literals, Demystifying NSLocalizedString and
genstrings
gesture recognizers, Gesture Recognizers
Grand Central Dispatch (GCD) (multithreading), For the More Curious: Faster Scattered
graphics contexts, Graphics contexts and states, Saving and Restoring the Graphics State
drawing to screen, For the More Curious: Are You Drawing to the Screen?
graphics states, Graphics contexts and states, Saving and Restoring the Graphics State
groups (project files), Getting around in Xcode

H
Hashable, Collection types
helper objects, Delegation
hierarchies, view, The view layer
hit testing/detection, Improving Hit Detection, Challenge: NSBezierPath-based Hit Testing
HTTP, Web Services
HTTP status codes, NSURLSession, HTTP status codes, and errors

I
.icns file, Setting the Extension and Icon for the File Type
identity inspector, File's Owner and making connections
if-else, Creating the user interface
if-let, Optionals
image wells, Add the Views
images, Drawing Images
implicit animation, Implicit Animation and Actions
implicitly unwrapped optionals, Implicitly unwrapped optionals
importing frameworks, Creating the MainWindowController class, RaiseMan’s Model Layer
importing modules, Your First Test
Info.plist, Info.plist and NSDocumentController
init (keyword), Structures
init(coder:) (NSCoding), NSCoder and NSCoding, Decoding
init(…) (see initializers)
initialFirstResponder (NSWindow), Starting the Chatter Application, The Key View Loop
initializers
about, Initializers
automatic, Structures
chaining, Structures
for classes, Classes
designated, Designated and convenience initializers, Decoding
inheriting, Inheritance, Decoding
parameters, Structures
and properties, Structures
for standard types, Initializers
for structures, Structures
writing, Structures
inspectors
attributes, Configuring view objects
bindings, Binding the text field to the table cell view
connection, Connecting actions
data model, Fetching Objects from the NSManagedObjectContext
identity, File's Owner and making connections
installers (application), A Few Words on Installers
instances, Initializers
Instruments, Tools for Cocoa Programming, Introducing Instruments
Int, Number and boolean types
integer types, Number and boolean types
integerForKey(_:) (NSUserDefaults), NSUserDefaults
integerValue, About Controls
Interface Builder
adding menu items, Getting Your View to Generate PDF Data
adding views in, Adding view objects, Configuring view objects
assistant editor, Making a connection with the assistant editor
connecting dataSource in, Connecting the dataSource outlet
connecting delegate in, Implementing another delegate
connecting objects in, Making Connections, File's Owner and making connections
connections panel, Connecting an outlet
copying and pasting in, Adding two more sliders
creating bindings in, Using bindings
designing custom classes in, Inspectable properties and designable views
File's Owner, File's Owner and making connections
inspecting custom properties in, Inspectable properties and designable views
navigating, Creating the User Interface in Interface Builder
overview, Tools for Cocoa Programming
placeholders, File's Owner and making connections
view hierarchy popover, For the More Curious: Using Interface Builder’s View Hierarchy
Popover
internal (access modifier), Your First Test, For the More Curious: Access Modifiers
interpretKeyEvents(_:) (NSResponder), Receive keyboard events
intrinsicContentSize, Cleaning up with Auto Layout
invalidate() (NSTimer), NSTimer-based Animation
isEmpty (strings), Properties

J
Jobs, Steve, The Story of Cocoa, From NeXTSTEP to OS X to iOS
JSON parsing, Add JSON parsing to ScheduleFetcher
jump bar (Xcode), Creating the user interface

K
key paths, For the More Curious: Key Paths
key view loop, The Key View Loop
key windows, Keyboard Events, Nil-Targeted Actions
key-value coding (see KVC (key-value coding))
key-value observing (see KVO (key-value observing))
key-value pairs
(see also KVC (key-value coding), KVO (key-value observing))
in dictionaries, Collection types
in strings files (localization), Demystifying NSLocalizedString and genstrings
key-value validation, Formatters and Validation, Validation with Key-Value Coding
keyboard events, Keyboard Events
keyCode (NSEvent), NSEvent
keyDown(_:) (NSResponder), NSResponder
keyPathsForValuesAffectingFullName(), For the More Curious: Dependent Keys
keys
(see also KVC (key-value coding), KVO (key-value observing))
dependent, For the More Curious: Dependent Keys
in dictionaries, Collection types
in key-value coding, KVC, KVO, and Bindings
making observable, Making keys observable
observing, Key-value observing
keyUp(_:) (NSResponder), NSResponder
Knight Rider, Delegation
knowsPageRange(_:) (NSView), Dealing with Pagination
KVC (key-value coding)
(see also key-value validation, KVO (key-value observing))
about, KVC, KVO, and Bindings
and proxy objects, Key-Value Coding and To-Many Relationships
and to-many relationships, Key-Value Coding and To-Many Relationships
and bindings, Bindings
and computed properties, KVC and Property Accessors
empty string exceptions, Adding Key-Value validation to RaiseMan
in undo, Key-Value Coding and To-Many Relationships
method naming conventions, Key-Value Coding and To-Many Relationships
methods, KVC, KVO, and Bindings, KVC and nil
and nil, KVC and nil, Adding Key-Value validation to RaiseMan
and predicates, For the More Curious: Filtering
and property accessors, KVC and Property Accessors
and type safety, Making keys observable
validateKEY(_:error:), Adding Key-Value validation to RaiseMan
validation for, Validation with Key-Value Coding
KVO (key-value observing)
about, Key-value observing
and bindings, Key-value observing, Making keys observable
compliance, Making keys observable
dependent keys, For the More Curious: Dependent Keys
in undo, Key-Value Observing
methods, Making keys observable
and Swift, Making keys observable
L
labelFontOfSize(_:) (NSFont), NSFont
labels, Working with Controls
layer (NSView), Scattered
layers
animating, Implicit Animation and Actions
creating, Scattered
drawing, Core Animation
lazy copying (pasteboard), For the More Curious: Lazy Copying
length (NSRange), NSAttributedString
let, Using Standard Types
level indicators, Add the Views
libraries
code snippet, Creating and using an Xcode snippet
object, Adding view objects
lineToPoint() (NSBezierPath), drawRect(_:)
literal values, Literals and subscripting
in testing, A Note on Literals in Testing
LLDB (debugger), Tools for Cocoa Programming, The LLDB console
(see also debugging)
loading documents, Loading documents
loading NIB files, Connecting a window controller and its window
loading objects, Loading and NSKeyedUnarchiver
loadView() (NSViewController), NSViewController, Adding the web view
Localizable.strings, Localizing String Literals
localization
adding localizations to projects, Localizing a XIB File
and NSBundle, NSBundle’s role in localization
base directory, Localizing String Literals
described, Localization and Bundles
directories, Localizing String Literals
and formatters, Formatters and localization
genstrings, Localizing String Literals
global resources, NSBundle’s role in localization
images, Different Mechanisms for Localization
language-specific resources, NSBundle’s role in localization
and NIB files, Different Mechanisms for Localization
NSLocalizedString, Localizing String Literals
of XIB files, Different Mechanisms for Localization, Localizing a XIB File
and plurals, For the More Curious: Localization and Plurality
region-specific resources, NSBundle’s role in localization
replacing string literals, Localizing String Literals
of resources (non-XIB), Different Mechanisms for Localization
and strings files, Localization and Bundles, Different Mechanisms for Localization, Localizing
a XIB File
token ordering in strings, Explicit Ordering of Tokens in Format Strings
ways to achieve, Different Mechanisms for Localization
location (NSRange), NSAttributedString
locationInWindow (NSEvent), NSEvent, Improving Hit Detection
loops
event, The main event loop
examining in Value History, Loops and String Interpolation
for, Loops and String Interpolation
for-in, Loops and String Interpolation
run, For the More Curious: NSRunLoop
in Swift, Loops and String Interpolation
.lproj files (localization), Localization and Bundles

M
Mac App Store (distribution), The Mac App Store
Mac Developer Program, Prerequisites
main bundle, NSBundle
main thread, Multithreading
managed object model (Core Data), Defining the Object Model
managedObjectContext (NSArrayController), Configure the Array Controller
map(), For the More Curious: Functional Methods and Minimizing Closure Syntax
// MARK:, Creating the user interface
master/detail interfaces, Container View Controllers
maxValue (NSSlider), Setting the slider’s range values
mediated file access, Mediated file access and Powerbox
memory management
and arrays, Deallocating objects in a hierarchy
in closures, Closures and capturing
and delegate, Setting the delegate property
and Instruments, Introducing Instruments
manual reference counting, What is ARC?
need for, Memory Management
and notifications, Using Notifications in Chatter
of windows, Windows, Controllers, and Memory Management
for reference types, Memory Management
reference counting, Automatic Reference Counting
strong reference cycles, Strong reference cycles
strong references, Strong and Weak References
and timers, NSTimer and Strong/Weak References
unowned references, Unowned references
for value types, Memory Management
weak references, Strong and Weak References, Strong reference cycles
and zombie objects, Debugging Hints
menu items
creating in Interface Builder, Getting Your View to Generate PDF Data
disabling, Menu Item Validation
hooking up, Starting the Chatter Application
and keyboard events, The Key View Loop
and NSDocument, Saving documents, Loading documents
and NSDocumentController, Info.plist and NSDocumentController
state, Menu Item Validation
targets of, Nil-Targeted Actions
validating, Menu Item Validation
messageFontOfSize(_:) (NSFont), NSFont
messages
(see also methods)
action, About Controls
explained, Swift and Objective-C
and NSInvocation, Message Passing and NSInvocation
methods
(see also individual method names, initializers, messages, properties)
(_:), meaning of, Instance methods
about, Instance methods
action, Connecting the slider’s target and action
application lifecycle, NSApplication and NSApplicationDelegate
class, Getting Voice Data
in classes, Add an instance method
data source, The table view-data source conversation, Connecting the dataSource outlet,
Implementing data source methods
defined, Types in Swift, Instance methods
delegate, For the More Curious: Delegates and Notifications
in enums, Instance methods
in extensions, Extensions
KVC, KVC, KVO, and Bindings
KVO, Making keys observable
naming conventions, Instance methods
optional, Being a delegate, For the More Curious: How Optional Delegate Methods Work
parameters, Instance methods
in protocols, Being a delegate
required, Being a delegate
spelling errors in, Common errors in implementing a delegate
static, Types in Swift
stepping through, Stepping through code
in structures, Instance methods
minValue (NSSlider), Setting the slider’s range values
modal alerts, NSAlert, Modals and Sheets
modal windows, Modal Windows
model key path, Using bindings
model layer (MVC), The model layer
binding to array controller, Binding the Array Controller to the Model
encapsulating in web services, NSURLSession and asynchronous API design
and table views, Delegates and data sources
Model-View-Controller (MVC)
(see also application architecture, controller layer (MVC), model layer (MVC), view layer
(MVC))
defined, Model-View-Controller
and web services, RanchForecast Project, NSURLSession and asynchronous API design
modifierFlags (NSEvent), NSEvent, NSEvent
modules, Your First Test
modules (product), Adding an Array Controller to the XIB
mouse events
(see also events, first responder, NSEvent)
checking click count, Getting Mouse Events
double-clicks, Getting Mouse Events
gesture recognizers, Gesture Recognizers
handler methods, NSResponder
hit testing, Improving Hit Detection
mouseDragged(_:), Starting a drag
rollovers, For the More Curious: Rollovers
mouseDragged(_:) (NSResponder), Starting a drag
mouseEntered(_:) (NSResponder), For the More Curious: Rollovers
mouseExited(_:) (NSResponder), For the More Curious: Rollovers
mouseMoved(_:) (NSResponder), For the More Curious: Rollovers
moveToPoint() (NSBezierPath), drawRect(_:)
multithreading
background threads, NSURLSession and asynchronous API design, Multithreading,
Analyzing output from Instruments
complexities in using, A Deep Chasm Opens Before You
considerations with mutable Array, Thread synchronization
Grand Central Dispatch (GCD), For the More Curious: Faster Scattered
main thread, Multithreading
mutex, Thread synchronization
NSOperationQueue, NSOperationQueue
NSRecursiveLock, Thread synchronization
operation queues, NSURLSession and asynchronous API design
race conditions, A Deep Chasm Opens Before You
thread synchronization, Thread synchronization
threads, Multithreading
and web services, NSURLSession and asynchronous API design
mutability, Implications of reference and value types
mutex (multithreading), Thread synchronization
MVC (see Model-View-Controller (MVC))

N
navigators (Xcode)
about, Getting around in Xcode
breakpoint, Deleting breakpoints
debug, Using breakpoints
project, Getting around in Xcode
needsDisplay (NSView), When is my view drawn?
nested types, NSURLSession and asynchronous API design
nextKeyView (NSView), The Key View Loop
nextResponder (NSResponder), Nil-Targeted Actions
NeXTSTEP, The Story of Cocoa, The view layer
NIB files
(see also XIB files)
defined, XIB files and NIB files
loading, Connecting a window controller and its window
and loading documents, Loading documents
and localization, Different Mechanisms for Localization
names of, Improving Controller Design
naming conventions for, Creating an empty XIB file
nil-targeted actions, Nil-Targeted Actions
notifications
about, What Notifications Are
adding observers for, NSNotificationCenter
constants for, Using Notifications in Chatter
in delegate methods, Delegate protocols and notifications, For the More Curious: Delegates
and Notifications
and memory management, Using Notifications in Chatter
observing, What Notifications Are
posting, NSNotificationCenter, Using Notifications in Chatter
registering for, NSNotificationCenter, Using Notifications in Chatter
removing observers of, NSNotificationCenter, Using Notifications in Chatter
responding to, Using Notifications in Chatter
unregistering for, NSNotificationCenter, Using Notifications in Chatter
and web services, NSURLSession and asynchronous API design
NS prefix, The view layer
NSAlert, Alerts and Closures
NSApplication
(see also AppDelegate, applications)
about, NSApplication and NSApplicationDelegate
in responder chain, Nil-Targeted Actions
sendAction(_:to:from:), For the More Curious: Which Object Sends the Action Message?
NSApplicationDelegate, NSApplication and NSApplicationDelegate
@NSApplicationMain, NSApplication and NSApplicationDelegate
NSArray, Bridging with collections
(see also arrays)
NSArrayController
(see also array controllers)
arrangedObjects, Introducing NSArrayController, Binding the Table View’s Selection to the
Array Controller, Sorting in RaiseMan
content, Introducing NSArrayController
managedObjectContext, Configure the Array Controller
selectionIndexes, Introducing NSArrayController, Binding the Table View’s Selection to the
Array Controller
subclassing for custom objects, Customizing Objects Created by NSArrayController
NSAttributedString, NSAttributedString
(see also NSString, strings)
NSBezierPath, Custom Drawing
NSBox, View Swapping, NerdTabViewController, Challenge: Boxless NerdTabViewController
NSBundle, NSBundle
NSButton, Adding view objects
(see also buttons)
NSCell, A word about NSCell
NSClipView, Table view and related objects
NSCoder, NSCoder and NSCoding
NSCoding (protocol), NSCoder and NSCoding
NSColor, Using the Documentation, For the More Curious: More on NSColor
NSColorWell, Using the Documentation
NSComparisonResult, For the More Curious: The caseInsensitiveCompare(_:) Method
NSControl
(see also controls)
action, About Controls
continuous, A continuous control
enabled, Disabling a control
formatter, Formatters and a control’s objectValue
inheritance hierarchy of, About Controls
setting target/action programmatically, For the More Curious: Setting the Target
Programmatically
target, About Controls, Connecting the slider’s target and action
value properties, About Controls
NSData, Saving documents, Loading documents, Saving and NSKeyedArchiver, Loading and
NSKeyedUnarchiver
NSDateFormatter, Formatters, programmatically
NSDatePicker, Add the Views
NSDictionary, Bridging with collections
(see also dictionaries)
NSDistributedNotificationCenter, What Notifications Are Not
NSDocument
(see also documents, NSDocumentController)
about, NSArrayController, NSDocument
and archiving, The Document Architecture
dataOfType(_:error:), Saving documents
fileWrapperOfType(_:error:), Saving documents
NSDocumentChangeType, For the More Curious: Document-Based Applications Without
Undo
printOperationWithSettings(_:error:), Printing, Adding Printing to RaiseMan
readFromData(_:ofType:error:), Loading documents
readFromFileWrapper(_:ofType:error:), Loading documents
readFromURL(_:ofType:error:), Loading documents
in responder chain, Nil-Targeted Actions
updateChangeCount(_:), For the More Curious: Document-Based Applications Without
Undo
windowControllerDidLoadNib(_:), Loading documents
writeToURL(_:ofType:error:), Saving documents
NSDocumentController, Info.plist and NSDocumentController
(see also NSDocument)
in responder chain, Nil-Targeted Actions
NSDraggingDestination (protocol), Make DieView a Drag Destination
NSDraggingInfo (protocol), Make DieView a Drag Destination
NSDraggingItem, Starting a drag
NSDraggingSource (protocol), Make DieView a Drag Source
NSDragOperation, Drag-and-Drop
NSError, Saving documents, Understanding NSErrorPointer
(see also errors)
NSErrorPointer, Understanding NSErrorPointer
NSEvent
(see also events)
and keyboard events, NSEvent
and mouse events, NSEvent
NSFetchRequest, Fetching Objects from the NSManagedObjectContext
NSFileHandle, ZIPspector, Asynchronous Reads, iPing
NSFileManager, Application Data and URLs
NSFont, NSFont
NSFontAttributeName (NSAttributedString), NSAttributedString
NSFontManager, For the More Curious: NSFontManager
NSForegroundColorAttributeName (NSAttributedString), NSAttributedString
NSFormatter, Formatters and localization
NSGradient, Challenge: Gradients, Add highlighting
NSGraphicsContext, Saving and Restoring the Graphics State, For the More Curious: Are You
Drawing to the Screen?
NSImage
drawing on, Starting a drag
drawInRect(_:), Drawing Images
drawInRect(_:fromRect:op…), Drawing images with finer control
NSImageView, Add the Views
NSInvocation, NSUndoManager
NSKeyedArchiver, NSCoder and NSCoding, Saving and NSKeyedArchiver
NSKeyedUnarchiver, Loading and NSKeyedUnarchiver
NSLevelIndicator, Add the Views
NSLocalizedString (localization), Localizing String Literals
NSMakeRange(), NSAttributedString
NSManagedObject (Core Data), Defining the Object Model
NSManagedObjectContext (Core Data), Defining the Object Model, Configure the Array
Controller, How Core Data Works, Fetching Objects from the NSManagedObjectContext, Choosing a
Cocoa Persistence Technology
NSManagedObjectModel (Core Data), Defining the Object Model, How Core Data Works
NSMatrix, Challenge: Busy Board
NSMenuItem (see menu items)
NSMutableAttributedString, NSAttributedString
NSMutableURLRequest, Web Services APIs
NSNotification, NSNotification
(see also notifications)
NSNotificationCenter
about, What Notifications Are, NSNotificationCenter
commonly-used methods, NSNotificationCenter
and memory management, Using Notifications in Chatter
NSNumber, Basic bridging
NSNumberFormatter, Formatters, programmatically
NSObject
base Cocoa class, Choosing between reference and value types
required for bindings, RanchForecast Project
NSOperationQueue (multithreading), NSURLSession and asynchronous API design,
NSOperationQueue
NSParagraphStyleAttributeName (NSAttributedString), NSAttributedString
NSPasteboard, Pasteboards and Nil-Targeted Actions, For the More Curious: UTIs and the Pasteboard
NSPasteboardItem, NSPasteboard, For the More Curious: Lazy Copying
NSPasteboardReading (protocol), NSPasteboard
NSPasteboardWriting (protocol), NSPasteboard
NSPersistentDocument (Core Data), Defining the Object Model, How Core Data Works
NSPipe, NSTask, ZIPspector, iPing
NSPoint, frame
NSPredicate, For the More Curious: Filtering, Fetching Objects from the NSManagedObjectContext
NSPrintInfo, Challenge: Persist Page Setup
NSPrintOperation, Printing
NSRange, NSAttributedString
NSRect, frame
NSRecursiveLock (multithreading), Thread synchronization
NSResponder
about, About Controls
first responder methods, NSResponder, Accept first responder, Receive keyboard events
mouse event handler methods, NSResponder, Getting Mouse Events
mouseDragged(_:), Starting a drag
nextResponder, Nil-Targeted Actions
responder chain, Nil-Targeted Actions
NSRunLoop, For the More Curious: NSRunLoop
NSSavePanel, Getting Your View to Generate PDF Data
NSScroller, Table view and related objects
NSScrollView, Scroll Views
NSShadow, Saving and Restoring the Graphics State
NSShadowAttributeName (NSAttributedString), NSAttributedString
NSSlider, Working with Controls, Setting the slider’s range values
NSSliderCell, A word about NSCell
NSSortDescriptor, Sorting in RaiseMan, For the More Curious: Sorting Without NSArrayController
NSSpeechSynthesizer
implementing, Synthesizing Speech
voices available for, Getting Voice Data
NSSpeechSynthesizerDelegate (protocol), Being a delegate
NSSplitView, Container View Controllers
NSSplitViewController, Container View Controllers
NSStackView, NerdTabViewController
NSString
(see also NSAttributedString, strings)
drawAtPoint(_:withAttributes:), Drawing Strings and Attributed Strings
drawInRect(_:withAttributes:), Drawing Strings and Attributed Strings
sizeWithAttributes(_:), Drawing Strings and Attributed Strings
and String, Basic bridging
NSSuperscriptAttributeName (NSAttributedString), NSAttributedString
NSTableColumn, Table columns
NSTableHeaderView, Table view and related objects, Table columns
NSTableView
(see also NSTableViewDataSource, NSTableViewDelegate, table views)
dataSource, Delegates and data sources
reloadData(), Challenge: Make a Data Source
sortDescriptors, Sorting in RaiseMan
NSTableViewDataSource
implementing, The NSTableViewDataSource Protocol
numberOfRowsInTableView(_:), The table view-data source conversation, Connecting
the dataSource outlet, Implementing data source methods
tableView(_:objectValueForTa…), The table view-data source conversation, Connecting
the dataSource outlet, Implementing data source methods
NSTableViewDelegate, The NSTableViewDelegate Protocol
NSTabView, Container View Controllers
NSTabViewController, Container View Controllers, View Swapping
NSTask, NSTask, ZIPspector, iPing
NSTextField (see text fields)
NSTextFieldCell, A word about NSCell
NSTimer, NSTimer
NSUnderlineColorAttributeName (NSAttributedString), NSAttributedString
NSUnderlineStyleAttributeName (NSAttributedString), NSAttributedString
NSUndoManager, Using NSUndoManager, For the More Curious: Windows and the Undo Manager
NSURL, Web Services APIs
NSURLRequest, Web Services APIs
NSURLSession, Web Services APIs, NSURLSession, HTTP status codes, and errors
NSURLSessionDataTask, NSURLSession and asynchronous API design
NSURLSessionTask, Web Services APIs, NSURLSession and asynchronous API design
NSUserDefaults, User Defaults
NSValueTransformer, For the More Curious: NSValueTransformer
NSView
(see also NSViewController, views)
addSubview(_:), View Swapping
beginDraggingSessionWithItems(…), Starting a drag
bounds, bounds
convertPoint(_:fromView:), Improving Hit Detection
convertPoint(_:toView:), Improving Hit Detection
custom subclasses of, NSView and Drawing, Creating a view subclass
dataWithPDFInsideRect(_:), Getting Your View to Generate PDF Data
drawFocusRingMask(), Focus Rings
drawRect(_:), drawRect(_:)
flipped, For the More Curious: Flipped Views
frame, Views, Rectangles, and Coordinate Systems
intrinsicContentSize, Cleaning up with Auto Layout
knowsPageRange(_:), Dealing with Pagination
layer, Scattered
needsDisplay, When is my view drawn?
nextKeyView, The Key View Loop
_NSViewBackingLayer, More on CALayer
rectForPage(_:), Dealing with Pagination
registerForDraggedTypes(_:), Make DieView a Drag Destination,
registerForDraggedTypes(_:)
removeFromSuperview(), View Swapping
setNeedsDisplayInRect(_:), For the More Curious: Dirty Rects
viewDidMoveToWindow(), For the More Curious: Rollovers
wantsLayer, Scattered
_NSViewBackingLayer, More on CALayer
NSViewController
(see also NSView, view controllers)
about, NSViewController
loadView(), NSViewController, Adding the web view
nibName, Starting the ViewControl Application
in responder chain, Nil-Targeted Actions
view, NSViewController
viewLoaded, NerdTabViewController
NSWindow
(see also NSWindowController, NSWindowDelegate (protocol), windows)
beginCriticalSheet(_:completi…), Sheets
beginSheet(_:completionHandler:), Sheets, Present the Sheet
endSheet(_:returnCode:), Sheets, Present the Sheet
firstResponder, Keyboard Events
initialFirstResponder, The Key View Loop
sheet methods, Sheets
sheetParent, Present the Sheet
visualizeConstraints(_:), Does Not Compute, Part 2: Ambiguous Layout
NSWindowController
(see also NSWindow, window controllers)
and NSDocument, NSWindowController
in responder chain, Nil-Targeted Actions
window, Connecting a window controller and its window
windowDidLoad(), Controls and Outlets
windowNibName, Improving Controller Design, Connecting a window controller and its
window
NSWindowDelegate (protocol), Implementing another delegate
NSWorkspace, Opening URLs
NSXMLDocument, For the More Curious: Parsing XML
NSXMLNode, For the More Curious: Parsing XML
number formatters, Formatting the Raise Text Field, Formatters, programmatically
numberOfRowsInTableView(_:), The table view-data source conversation, Connecting the
dataSource outlet, Implementing data source methods

O
object library (Xcode), Adding view objects
object-oriented programming, Application Design
objectForKey(_:) (NSUserDefaults), NSUserDefaults
Objective-C
about, Introducing the Swift Language
and bindings, RanchForecast Project
dynamic, Making keys observable
in documentation, Using the Documentation
messages, Swift and Objective-C, Message Passing and NSInvocation
reference types, Choosing between reference and value types
and Swift, Swift and Objective-C
Objective-⁠C
(see also Cocoa, KVC (key-value coding), KVO (key-value observing))
objects
(see also classes, methods, properties)
about, Application Design
and memory management, Memory Management
objectValue (NSControl)
about, About Controls
binding to, Binding the text field to the table cell view
formatting, Formatters and a control’s objectValue
and table data, Table cell views
observers (notifications), What Notifications Are
observers (property), Updating Buttons
OpenStep, NeXTSTEP and OpenStep
operation masks (drag-and-drop), For the More Curious: Operation Mask
operation queues, NSURLSession and asynchronous API design, NSOperationQueue
operators, overloading, Operator Overloading
optional (protocol methods), Being a delegate, For the More Curious: How Optional Delegate
Methods Work
optional binding, Optionals
optional chaining, For the More Curious: How Optional Delegate Methods Work
optionals
about, Optionals
as?, Bridging with collections
and casting, Bridging with collections
chaining, For the More Curious: How Optional Delegate Methods Work
and dictionary subscripting, Subscripting dictionaries
forced unwrapping of, Optionals
if-let, Optionals
implicitly unwrapped, Implicitly unwrapped optionals
and optional binding, Optionals
syntax for, Optionals
unwrapping, Optionals
origin (NSRect), frame
OS X
(see also Cocoa)
frameworks for, The Cocoa Frameworks
history of, From NeXTSTEP to OS X to iOS
as Unix-based, OSX, Unix, and Cocoa
outlets
(see also properties)
assistant editor, connecting with, Making a connection with the assistant editor
connecting in Interface Builder, Connecting an outlet, Making a connection with the
assistant editor
creating in code, Creating an outlet
dataSource, Connecting the dataSource outlet
defined, Making Connections
delegate, Implementing another delegate, The NSTableViewDelegate Protocol
as implicitly unwrapped optionals, Implicitly unwrapped optionals
as weak references, Strong reference cycles
when to use, Controls and Outlets
overloading operators, Operator Overloading

P
packaging (applications), A Few Words on Installers
pagination (printing multiple pages), Dealing with Pagination
parameter names, Instance methods
pasteboards, Pasteboards and Nil-Targeted Actions, For the More Curious: UTIs and the Pasteboard
pasting (implementing) (see pasteboards)
PDFs, generating, Getting Your View to Generate PDF Data
performance issues, Introducing Instruments, Analyzing output from Instruments
performDragOperation(_:) (NSDraggingDestination), Make DieView a Drag Destination
persistence (see archiving, Core Data)
pipes, ZIPspector, iPing
placeholder text, Creating the user interface
placeholders, File's Owner and making connections
playgrounds (Xcode), Using Standard Types
errors in, Using Standard Types
Value History, Loops and String Interpolation
viewing console in, Optionals
pointers, Reference and Value Types
points (in drawing), frame
postNotification(_:) (NSNotificationCenter), NSNotificationCenter
postNotificationName(_:object:) (NSNotificationCenter), NSNotificationCenter
Powerbox, Mediated file access and Powerbox
predicates, For the More Curious: Filtering, Fetching Objects from the NSManagedObjectContext
preferences (user), User Defaults
prepareForDragOperation(_:) (NSDraggingDestination), Make DieView a Drag
Destination
preprocessor directives, Preprocessor Directives: Using Build Configurations to Change Behavior
pressure (NSEvent), NSEvent
Printable (protocol), Making Types Printable
printing, Printing
printOperationWithSettings(_:error:) (NSDocument), Printing, Adding Printing to
RaiseMan
private (access modifier), For the More Curious: Access Modifiers
product modules, Adding an Array Controller to the XIB
programmer errors, Runtime Errors
programming
functional, For the More Curious: Functional Methods and Minimizing Closure Syntax
object-oriented, Application Design
project navigator, Getting around in Xcode
projects
(see also applications)
copying files into, A New UI for RanchForecast
creating, Creating an Xcode Project
source directories of, Localizing String Literals
targets in, Testing in Xcode
properties
(see also methods, outlets)
in attributes inspector, A continuous control
computed, Computed Properties, KVC and Property Accessors
default values, Classes
defined, Properties
didSet, Updating Buttons
and extensions, Extensions
initializing, Structures, Classes
making @IBInspectable, Inspectable properties and designable views
shadowing, Showing the Window
stored, Computed Properties
willSet, Updating Buttons
property observers, Updating Buttons
propertyListForType(_:) (NSPasteboardItem), NSPasteboard
protocols
CALayerDelegate, More on CALayer
conforming to, Being a delegate, Conforming to a protocol, Implementing a delegate method
creating, For the More Curious: Creating a Protocol, Adding Tab Images
defining roles with, Being a delegate
documentation for, Implementing another delegate
header files for, Common errors in implementing a delegate
NSApplicationDelegate, NSApplication and NSApplicationDelegate
NSCoding, NSCoder and NSCoding
NSDraggingDestination, Make DieView a Drag Destination
NSDraggingInfo, Make DieView a Drag Destination
NSDraggingSource, Make DieView a Drag Source
NSPasteboardReading, NSPasteboard
NSPasteboardWriting, NSPasteboard
NSSpeechSynthesizerDelegate, Being a delegate
NSTableViewDataSource, The NSTableViewDataSource Protocol
NSTableViewDelegate, The NSTableViewDelegate Protocol, Implementing a delegate
method
NSWindowDelegate, Implementing another delegate
optional methods in, Being a delegate, For the More Curious: How Optional Delegate
Methods Work
Printable, Making Types Printable
reference pages for, Implementing another delegate
required methods in, Being a delegate
public (access modifier), Your First Test, For the More Curious: Access Modifiers

Q
Quartz (framework), For the More Curious: Core Graphics and Quartz
Quick Help (Xcode), Inferring types

R
race conditions (multithreading), A Deep Chasm Opens Before You
radio buttons (NSButton), Challenge: Busy Board
Range, Loops and String Interpolation
rawValue (enums), Enumerations and raw values
readFromData(_:ofType:error:) (NSDocument), Loading documents
readFromFileWrapper(_:ofType:error:) (NSDocument), Loading documents
readFromURL(_:ofType:error:), ZIPspector
readFromURL(_:ofType:error:) (NSDocument), Loading documents
readObjects(_:options:) (NSPasteboard), NSPasteboard
receipt validation, Receipt Validation
receivers, Swift and Objective-C
recoverable errors, Runtime Errors
rectForPage(_:) (NSView), Dealing with Pagination
redo stack, How the NSUndoManager Works
reduce(), For the More Curious: Functional Methods and Minimizing Closure Syntax
reference counting, Automatic Reference Counting, What is ARC?
reference types, Reference and Value Types
references, strong, Strong and Weak References
references, unowned, Unowned references
references, weak, Strong and Weak References
registerDefaults(_:) (NSUserDefaults), NSUserDefaults
registerForDraggedTypes(_:), Make DieView a Drag Destination
registerForDraggedTypes(_:) (NSView), Make DieView a Drag Destination,
registerForDraggedTypes(_:)
relationships (Core Data), Defining the Object Model
release builds, Debugging Hints, Build Configurations, Creating a Release Build
reloadData() (NSTableView), Challenge: Make a Data Source
removeFromSuperview() (NSView), View Swapping
removeObjectForKey(_:) (NSUserDefaults), NSUserDefaults
removeObserver(_:) (NSNotificationCenter), NSNotificationCenter
representedObject, Adding Tab Images
resignFirstResponder() (NSResponder), NSResponder
resources
(see also bundles)
application access to, Entitlements
for future learning, Afterword
in bundles, NSBundle
localizing, Localization and Bundles
responder chain, Nil-Targeted Actions
restoreGraphicsState() (NSGraphicsContext), Saving and Restoring the Graphics State
reverse(), Instance methods
RoboCop, Delegation
run loops, For the More Curious: NSRunLoop, Asynchronous Reads
runModal() (NSAlert), NSAlert
runModalForWindow(_:) (NSApplication), Modal Windows
runtime errors, Runtime Errors

S
sandboxing (applications), App Sandbox
saveGraphicsState() (NSGraphicsContext), Saving and Restoring the Graphics State
saving documents, Saving documents, For the More Curious: Automatic Document Saving
saving objects, Saving and NSKeyedArchiver
SAX parsing, For the More Curious: Parsing XML
scenes (storyboards), Storyboards, Adding the course list, For the More Curious: How is the
Storyboard Loaded?
scheduledTimerWithTimeInterval (NSTimer), NSTimer-based Animation
Scheme Editor, Build Configurations, Preprocessor Directives: Using Build Configurations to Change
Behavior, Creating a Release Build
scroll views, Table view and related objects, Scroll Views
scrollers, Table view and related objects
Sculley, John, The Story of Cocoa
segues (storyboards), Storyboards, A New UI for RanchForecast
selectionIndexes (NSArrayController), Introducing NSArrayController, Binding the Table
View’s Selection to the Array Controller
selectors, Swift and Objective-C
selectTab(_:), NerdTabViewController
selectTabAtIndex(_:), NerdTabViewController
self
in closures, Closures and capturing
in initializers, Structures
in instance methods, Using self in instance methods
and property names, Showing the Window
sendAction(_:to:from:) (NSApplication), For the More Curious: Which Object Sends the
Action Message?
sender (action methods), Connecting the slider’s target and action
setBool(_:forKey:) (NSUserDefaults), NSUserDefaults
setData(_:forType:) (NSPasteboardItem), NSPasteboard
setFloat(_:forKey:) (NSUserDefaults), NSUserDefaults
setInteger(_:forKey:) (NSUserDefaults), NSUserDefaults
setNeedsDisplayInRect(_:) (NSView), For the More Curious: Dirty Rects
setNilValueForKey(_:), KVC and nil
setObject(_:forKey:) (NSUserDefaults), NSUserDefaults
setPropertyList(_:forType:) (NSPasteboardItem), NSPasteboard
sets, Collection types, Initializers
setString(_:forType:) (NSPasteboardItem), NSPasteboard
setUp(), Testing in Xcode, Creating a Consistent Testing Environment
setValue(_:forKey:), KVC, KVO, and Bindings
shadowing (properties), Showing the Window
shadows, drawing, Saving and Restoring the Graphics State
Shared User Defaults Controller, Using bindings
sharedDocumentController() (NSDocumentController), Info.plist and
NSDocumentController
sheetParent (NSWindow), Present the Sheet
sheets
and alerts, Modals and Sheets
vs. modal window, Modal Windows
presenting, Present the Sheet
Visible At Launch, Lay Out the Interface
size (NSAttributedString), Drawing Strings and Attributed Strings
size (NSRect), frame
sizeWithAttributes(_:) (NSString), Drawing Strings and Attributed Strings
sliders
about, Working with Controls
setting range of, Setting the slider’s range values
snippets (code), Creating and using an Xcode snippet
sort descriptors, Sorting in RaiseMan, For the More Curious: Sorting Without NSArrayController,
Fetching Objects from the NSManagedObjectContext
sortDescriptors (NSTableView), Sorting in RaiseMan
sorting (array controllers), Sorting in RaiseMan
sorting (table views), For the More Curious: Sorting Without NSArrayController
speech synthesis, implementing, Synthesizing Speech
split view controllers, A New UI for RanchForecast
springs (autoresizing), For the More Curious: Autoresizing Masks
SQLite, Persistent Store Types
stack (memory), Using breakpoints
stack trace, Using breakpoints
stacks, undo and redo, How the NSUndoManager Works
standardUserDefaults() (NSUserDefaults), NSUserDefaults
states, graphics, Graphics contexts and states, Saving and Restoring the Graphics State
static methods, Types in Swift
stopModalWithCode(_:) (NSApplication), Modal Windows
storage, application, Application Data and URLs
store types (Core Data), Persistent Store Types
storyboards
about, Storyboards
loading, For the More Curious: How is the Storyboard Loaded?
scenes, Storyboards, Adding the course list, For the More Curious: How is the Storyboard
Loaded?
segues, Storyboards, A New UI for RanchForecast
string interpolation, Loops and String Interpolation
stringForType(_:) (NSPasteboardItem), NSPasteboard
strings
(see also NSAttributedString, NSString)
initializers for, Initializers
interpolation, Loops and String Interpolation
isEmpty, Properties
literal, Literals and subscripting
NSAttributedString, NSAttributedString
and NSString, Basic bridging
strings files (localization), Localization and Bundles, Different Mechanisms for Localization,
Localizing a XIB File, NSBundle’s role in localization
strings tables (localization), Localizing String Literals
stringValue, About Controls
stroke() (NSBezierPath), Custom Drawing, drawRect(_:)
strong reference cycles, Strong reference cycles
strong references, Strong and Weak References
structures, Structures
vs. classes, Reference and Value Types
struts (autoresizing), For the More Curious: Autoresizing Masks
subclassing
vs. extensions, Extensions
vs. helper objects, Delegation
subscripting
arrays, Literals and subscripting
dictionaries, Subscripting dictionaries
subviews (NSView), Deallocating objects in a hierarchy
Swift, Types in Swift
about, Introducing the Swift Language , Swift Types
documentation for, Exploring Apple’s Swift Documentation
and Objective-C, Swift and Objective-C
switch, Enumerations and the Switch Statement
switch statements, Enumerations and the Switch Statement
systemFontOfSize(_:) (NSFont), NSFont

T
tab images, Adding Tab Images
tab view controllers, Add a Tab View Controller, View Swapping, NerdTabViewController
table cell views
about, Table cell views
with checkbox, Add the Views
different views in, Add the Views
with formatters, Add the Views
with images, Add the Views
table columns, Table columns
table header views, Table view and related objects, Table columns
table view
delegate’s role, Delegates and data sources
table view cells, Table cell views
table views
(see also NSTableView, NSTableViewDataSource, NSTableViewDelegate)
about, About Table Views
Apple’s guide to, Pre-selecting the default voice
binding to array controllers, Introducing NSArrayController, Binding the Table View’s
Content to the Array Controller
bindings, data supplied from, Connecting the dataSource outlet
cell-based, Tables, Cells, and Views
cells in, Tables, Cells, and Views
and clip views, Table view and related objects
as collections of classes, Table view and related objects
columns in, Table columns
and data sources, Delegates and data sources, The table view-data source conversation, The
NSTableViewDataSource Protocol
data source methods vs. bindings, Connecting the dataSource outlet, Pre-selecting the
default voice
delegate for, The NSTableViewDelegate Protocol, Implementing a delegate method
displaying data with bindings, Binding the text field to the table cell view
header views, Table view and related objects, Table columns
in Interface Builder, Adding a Table View
and scroll views, Table view and related objects
and scrollers, Table view and related objects
Selection Indexes, Introducing NSArrayController
sorting in, Sorting in RaiseMan, For the More Curious: Sorting Without NSArrayController
view-based, Tables, Cells, and Views
tableView(_:objectValueForTa…), The table view-data source conversation, Connecting the
dataSource outlet, Implementing data source methods
tableViewSelectionDidChange(_:), Implementing a delegate method
Taligent, Controls
.tar files, Challenge: .tar and .tgz Files
target (NSControl)
setting programmatically, For the More Curious: Setting the Target Programmatically
target-action (NSControl), About Controls, Opening URLs
targets
(see also actions, controls)
about, About Controls
array controllers as, Connecting the Add Employee Button
nil, Nil-Targeted Actions
project, Creating a Release Build
as weak references, Connecting the slider’s target and action
targets (in projects), Testing in Xcode
tearDown(), Testing in Xcode, Creating a Consistent Testing Environment
test fixtures, Sharing Constants
testExample(), Testing in Xcode
testing (see unit testing)
testPerformanceExample(), Testing in Xcode
text fields
alignment of, Configuring view objects
as table view cells, Table cell views
behavior, setting, Configuring view objects
cut, copy, and paste, Connecting the Model Layer to the Controller
editable, Configuring view objects
changing fonts, Configuring view objects
in Interface Builder, Adding view objects
as labels, Working with Controls
and placeholder text, Creating the user interface
selectable, Configuring view objects
styles of, Working with Controls
.tgz fies, Challenge: .tar and .tgz Files
thread synchronization (multithreading), Thread synchronization
threads (see multithreading)
Time Profiler (Instruments), Introducing Instruments
timers, NSTimer
timestamp (NSEvent), NSEvent
titleBarFontOfSize(_:) (NSFont), NSFont
toll-free bridging, Working with Foundation Types
toolTipsFontOfSize(_:) (NSFont), NSFont
top-level objects, Windows, Controllers, and Memory Management
trailing closure syntax, For the More Curious: Functional Methods and Minimizing Closure Syntax
traps, Literals and subscripting
tuples, Loops and String Interpolation
type inference, Inferring types
type safety, Making keys observable
types
(see also classes, enumerations, structures, UTIs (universal type identifiers))
boolean, Number and boolean types
bridging, Working with Foundation Types
casting, Basic bridging
floating-point, Number and boolean types, Initializers
hashable, Collection types
inference of, Inferring types
instances of, Initializers
integer, Number and boolean types
nested, NSURLSession and asynchronous API design
reference, Reference and Value Types
sets, Collection types, Initializers
specifying, Specifying types
tuples, Loops and String Interpolation
values, Reference and Value Types
types (NSPasteboardItem), NSPasteboard

U
unarchiveObjectWithData(_:), Loading and NSKeyedUnarchiver
unarchiving (NIB files), XIB files and NIB files, Connecting a window controller and its window
undo
about, NSUndoManager
implementing, Undo for Edits
undo stack, How the NSUndoManager Works
Unicode warning for Localizable.strings, Localizing String Literals
unit testing
about, Unit Testing
assertions, Testing in Xcode
asynchronous tasks, For the More Curious: Asynchronous Testing
planning for, Refactoring for Testing
refactoring for, Refactoring for Testing
test fixtures, Sharing Constants
using constants in, A Note on Literals in Testing
Unix, NeXTSTEP and OpenStep, OSX, Unix, and Cocoa
unowned references, Unowned references
updateChangeCount(_:) (NSDocument), For the More Curious: Document-Based
Applications Without Undo
URLForResource(_:withExtension:) (NSBundle), NSBundle’s role in localization
URLsForDirectory(_:inDomains:) (NSFileManager), Application Data and URLs
user defaults, User Defaults
user interfaces
(see also Interface Builder, views)
with bindings, Bindings, Binding other attributes
creating in Interface Builder, Creating the MainWindowController class
master/detail, Container View Controllers
as view layer in MVC, The view layer
user preferences, User Defaults
userFixedPitchFontOfSize(_:) (NSFont), NSFont
userFontOfSize(_:) (NSFont), NSFont
utilities (Xcode), Getting around in Xcode
UTIs (universal type identifiers)
about, For the More Curious: Universal Type Identifiers
exported, Setting the Extension and Icon for the File Type
and pasteboards, NSPasteboard, For the More Curious: UTIs and the Pasteboard

V
validateKEY(_:error:), Adding Key-Value validation to RaiseMan
validateMenuItem(_:) (NSMenuValidation), Menu Item Validation
validation
key-value, Formatters and Validation, Validation with Key-Value Coding
value transformers, For the More Curious: NSValueTransformer
value transformers, For the More Curious: NSValueTransformer
value types, Reference and Value Types
valueForKey(_:), KVC, KVO, and Bindings
var, Using Standard Types
variables, Using Standard Types
capturing in closures, Closures and capturing
variables view, Add an instance method, Using breakpoints
view (NSViewController), NSViewController
view controllers, A New UI for RanchForecast
(see also NSViewController, views)
about, View Controllers
architecture, Connecting the Course List Selection with the Web View
benefits of using, View Controllers, Starting the ViewControl Application
container, Container View Controllers
instantiating in storyboards, For the More Curious: How is the Storyboard Loaded?
loading views, NerdTabViewController
making reusable, Adding the web view
and memory management, Windows, Controllers, and Memory Management
and NIB files, Starting the ViewControl Application
pre-OSX 10.0, Considerations for OS X 10.9 and Earlier
reason for, View Controllers
split, A New UI for RanchForecast
and swapping views, View Swapping
tab, Add a Tab View Controller, View Swapping
views of, NSViewController, Adding the web view
ways to connect multiple, Connecting the Course List Selection with the Web View
when to use, View Controllers vs. Window Controllers
vs. window controllers, Starting the ViewControl Application, View Controllers vs. Window
Controllers
view hierarchies, The view layer, Adding view objects
and responder chain, Nil-Targeted Actions
view hierarchy popover, For the More Curious: Using Interface Builder’s View Hierarchy Popover
view layer (MVC), The view layer
(see also views)
view swapping, View Swapping
view-based tables, Tables, Cells, and Views
viewDidMoveToWindow() (NSView), For the More Curious: Rollovers
viewLoaded (NSViewController), NerdTabViewController
views
(see also NSView, view controllers)
adding in Interface Builder, Adding view objects, Configuring view objects
archiving, XIB files and NIB files
attributes, configuring, Configuring view objects
binding to array controller, Introducing NSArrayController
connecting in Interface Builder, Making Connections, Making a connection with the
assistant editor
content views, Creating an empty XIB file
copying and pasting, Adding two more sliders
creating custom, Creating a view subclass
creating programmatically, Creating Views Programmatically
described, NSView and Drawing
drawing, bounds
and first-responder status, Keyboard Events
flipped, For the More Curious: Flipped Views
and focus rings, Focus Rings
hierarchies of, The view layer, Adding view objects
in MVC, The view layer
and key view loop, The Key View Loop
in NIB files, XIB files and NIB files
positioning, Views, Rectangles, and Coordinate Systems
unarchiving, XIB files and NIB files, Connecting a window controller and its window
WKWebView, Adding the web view
in XIB files, XIB files and NIB files
Visible At Launch, Creating an empty XIB file, Connecting a window controller and its window,
Lay Out the Interface
Visual Format Language (Auto Layout), Visual Format Language
visualizeConstraints(_:) (NSWindow), Does Not Compute, Part 2: Ambiguous Layout

W
wantsLayer (NSView), Scattered
weak, Strong and Weak References
weak references, Strong and Weak References, Strong reference cycles
web services
about, Web Services
and asynchronous tasks, NSURLSession and asynchronous API design
and completion handlers, NSURLSession and asynchronous API design
and completion handlers, NSURLSession and asynchronous API design
and HTTP, Web Services
making requests, Web Services APIs, NSURLSession and asynchronous API design
reusing classes with, NSURLSession and asynchronous API design
synchronous API, NSURLSession and asynchronous API design
testing asynchronous tasks, For the More Curious: Asynchronous Testing
and threads, NSURLSession and asynchronous API design
ways to fetch asynchronously, NSURLSession and asynchronous API design
willChangeValueForKey(_:), Making keys observable
willSet (property observer), Updating Buttons
window (NSEvent), NSEvent
window (NSWindowController), Connecting a window controller and its window
window controllers
(see also NSWindowController, windows)
and AppDelegate, Showing the Window, Improving Controller Design
and documents, NSWindowController
initializing, Showing the Window
instantiating in storyboards, For the More Curious: How is the Storyboard Loaded?
loading NIB files, Connecting a window controller and its window
loading windows, Connecting a window controller and its window
and NIB names, Improving Controller Design
vs. view controllers, Starting the ViewControl Application, View Controllers vs. Window
Controllers
when to use, View Controllers vs. Window Controllers
and windows, Connecting a window controller and its window
window servers, NeXTSTEP and OpenStep, OSX, Unix, and Cocoa
windowControllerDidLoadNib(_:) (NSDocument), Loading documents
windowDidLoad(), Controls and Outlets
windowNibName, Improving Controller Design
windowNibName (NSWindowController), Improving Controller Design, Connecting a
window controller and its window
windows
(see also NSWindow, window controllers)
and content views, The view layer
described, NSView and Drawing
disabling resizing of, Disabling resizing of the window
first responders of, Keyboard Events
key, Keyboard Events
loading, Connecting a window controller and its window
modal, Modal Windows
resizing, Disabling resizing of the window
showing, Creating an instance of MainWindowController
and view hierarchies, The view layer, Adding view objects
Visible At Launch, Creating an empty XIB file, Connecting a window controller and its
window, Lay Out the Interface
and window controllers, Connecting a window controller and its window
windowShouldClose(_:) (NSWindowDelegate), Implementing another delegate
WKWebView, Adding the web view
writeObjects(_:) (NSPasteboard), NSPasteboard
writeToURL(_:ofType:error:) (NSDocument), Saving documents

X
.xcdatamodeld (Core Data), Defining the Object Model
Xcode
(see also debugging tools, Interface Builder)
adding localizations in, Localizing a XIB File
assistant editor, Making a connection with the assistant editor
attributes inspector, Configuring view objects
auto-complete, Implementing a delegate method
Cocoa documentation in, Using the Documentation
code snippets in, Creating and using an Xcode snippet
connections inspector, Connecting actions
creating classes in, Creating the MainWindowController class
creating projects in, Creating an Xcode Project
data model inspector, Fetching Objects from the NSManagedObjectContext
debugger, Using the Debugger
files in, Getting around in Xcode
groups, Getting around in Xcode
identity inspector, File's Owner and making connections
Instruments, Introducing Instruments
jump bar, Creating the user interface
modules, Your First Test
navigator area, Getting around in Xcode
navigators, Getting around in Xcode
overview, Tools for Cocoa Programming
playgrounds, Using Standard Types
project navigator, Getting around in Xcode
project source directories, Localizing String Literals
project window, Getting around in Xcode
Quick Help, Inferring types
saving files in, XIB files and NIB files
testing in, Unit Testing
Time Profiler, Introducing Instruments
using // MARK:, Creating the user interface
utilities area, Getting around in Xcode
variables view, Add an instance method, Using breakpoints
XCTAssert(expr), Testing in Xcode
XCTAssertEqual(expr), Testing in Xcode
XCTAssertEqualWithAccuracy(expr), Testing in Xcode
XCTAssertFalse(expr), Testing in Xcode
XCTAssertNotEqual(expr), Testing in Xcode
XCTAssertNotEqualWithAccuracy(expr), Testing in Xcode
XCTAssertNotNil(expr), Testing in Xcode
XCTAssertTrue(expr), Testing in Xcode
XCTest (framework), Testing in Xcode, Your First Test
XCTestCase
setUp(), Testing in Xcode, Creating a Consistent Testing Environment
tearDown(), Testing in Xcode, Creating a Consistent Testing Environment
testExample(), Testing in Xcode
testPerformanceExample(), Testing in Xcode
XCTFail(), Testing in Xcode, Refactoring for Testing
XIB files
(see also NIB files)
archiving files in, XIB files and NIB files
connections in, Making Connections, File's Owner and making connections
defined, XIB files and NIB files
File's Owner, File's Owner and making connections
localizing, Different Mechanisms for Localization, Localizing a XIB File
naming conventions for, Creating an empty XIB file
and NIB files, XIB files and NIB files
placeholders, File's Owner and making connections
pronounced as zib, Creating the MainWindowController class
XML parsing, For the More Curious: Parsing XML

Z
zip files
for distribution, Creating a Release Build
inspecting, ZIPspector
zipinfo utility, ZIPspector
zombie objects, Debugging Hints

You might also like