
- •Contents at a Glance
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Preface
- •What This Book Is
- •What You Need
- •Developer Options
- •What You Need to Know
- •What’s Different About Coding for iOS?
- •Only One Active Application
- •Only One Window
- •Limited Access
- •Limited Response Time
- •Limited Screen Size
- •Limited System Resources
- •No Garbage Collection, but…
- •Some New Stuff
- •A Different Approach
- •What’s in This Book
- •What’s New in This Update?
- •Are You Ready?
- •Setting Up Your Project in Xcode
- •The Xcode Workspace Window
- •The Toolbar
- •The Navigator View
- •The Jump Bar
- •The Utility Pane
- •Interface Builder
- •New Compiler and Debugger
- •A Closer Look at Our Project
- •Introducing Xcode’s Interface Builder
- •What’s in the Nib File?
- •The Library
- •Adding a Label to the View
- •Changing Attributes
- •Some iPhone Polish—Finishing Touches
- •Bring It on Home
- •The Model-View-Controller Paradigm
- •Creating Our Project
- •Looking at the View Controller
- •Understanding Outlets and Actions
- •Outlets
- •Actions
- •Cleaning Up the View Controller
- •Designing the User Interface
- •Adding the Buttons and Action Method
- •Adding the Label and Outlet
- •Writing the Action Method
- •Trying It Out
- •Looking at the Application Delegate
- •Bring It on Home
- •A Screen Full of Controls
- •Active, Static, and Passive Controls
- •Creating the Application
- •Implementing the Image View and Text Fields
- •Adding the Image View
- •Resizing the Image View
- •Setting View Attributes
- •The Mode Attribute
- •Interaction Checkboxes
- •The Alpha Value
- •Background
- •Drawing Checkboxes
- •Stretching
- •Adding the Text Fields
- •Text Field Inspector Settings
- •Setting the Attributes for the Second Text Field
- •Creating and Connecting Outlets
- •Closing the Keyboard
- •Closing the Keyboard When Done Is Tapped
- •Touching the Background to Close the Keyboard
- •Adding the Slider and Label
- •Creating and Connecting the Actions and Outlets
- •Implementing the Action Method
- •Adding Two Labeled Switches
- •Connecting and Creating Outlets and Actions
- •Implementing the Switch Actions
- •Adding the Button
- •Connecting and Creating the Button Outlets and Actions
- •Implementing the Segmented Control Action
- •Implementing the Action Sheet and Alert
- •Conforming to the Action Sheet Delegate Method
- •Showing the Action Sheet
- •Spiffing Up the Button
- •Using the viewDidLoad Method
- •Control States
- •Stretchable Images
- •Crossing the Finish Line
- •The Mechanics of Autorotation
- •Points, Pixels, and the Retina Display
- •Autorotation Approaches
- •Handling Rotation Using Autosize Attributes
- •Configuring Supported Orientations
- •Specifying Rotation Support
- •Designing an Interface with Autosize Attributes
- •Using the Size Inspector’s Autosize Attributes
- •Setting the Buttons’ Autosize Attributes
- •Restructuring a View When Rotated
- •Creating and Connecting Outlets
- •Moving the Buttons on Rotation
- •Swapping Views
- •Designing the Two Views
- •Implementing the Swap
- •Changing Outlet Collections
- •Rotating Out of Here
- •Common Types of Multiview Apps
- •The Architecture of a Multiview Application
- •The Root Controller
- •Anatomy of a Content View
- •Building View Switcher
- •Creating Our View Controller and Nib Files
- •Modifying the App Delegate
- •Modifying BIDSwitchViewController.h
- •Adding a View Controller
- •Building a View with a Toolbar
- •Writing the Root View Controller
- •Implementing the Content Views
- •Animating the Transition
- •Switching Off
- •The Pickers Application
- •Delegates and Data Sources
- •Setting Up the Tab Bar Framework
- •Creating the Files
- •Adding the Root View Controller
- •Creating TabBarController.xib
- •The Initial Test Run
- •Implementing the Date Picker
- •Implementing the Single-Component Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller As a Data Source and Delegate
- •Implementing a Multicomponent Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller
- •Implementing Dependent Components
- •Creating a Simple Game with a Custom Picker
- •Writing the Controller Header File
- •Building the View
- •Adding Image Resources
- •Implementing the Controller
- •The spin Method
- •The viewDidLoad Method
- •Final Details
- •Linking in the Audio Toolbox Framework
- •Final Spin
- •Table View Basics
- •Table Views and Table View Cells
- •Grouped and Plain Tables
- •Implementing a Simple Table
- •Designing the View
- •Writing the Controller
- •Adding an Image
- •Using Table View Cell Styles
- •Setting the Indent Level
- •Handling Row Selection
- •Changing the Font Size and Row Height
- •Customizing Table View Cells
- •Adding Subviews to the Table View Cell
- •Creating a UITableViewCell Subclass
- •Adding New Cells
- •Implementing the Controller’s Code
- •Loading a UITableViewCell from a Nib
- •Designing the Table View Cell in Interface Builder
- •Using the New Table View Cell
- •Grouped and Indexed Sections
- •Building the View
- •Importing the Data
- •Implementing the Controller
- •Adding an Index
- •Implementing a Search Bar
- •Rethinking the Design
- •A Deep Mutable Copy
- •Updating the Controller Header File
- •Modifying the View
- •Modifying the Controller Implementation
- •Copying Data from allNames
- •Implementing the Search
- •Changes to viewDidLoad
- •Changes to Data Source Methods
- •Adding a Table View Delegate Method
- •Adding Search Bar Delegate Methods
- •Adding a Magnifying Glass to the Index
- •Adding the Special Value to the Keys Array
- •Suppressing the Section Header
- •Telling the Table View What to Do
- •Putting It All on the Table
- •Navigation Controller Basics
- •Stacky Goodness
- •A Stack of Controllers
- •Nav, a Hierarchical Application in Six Parts
- •Meet the Subcontrollers
- •The Disclosure Button View
- •The Checklist View
- •The Rows Control View
- •The Movable Rows View
- •The Deletable Rows View
- •The Editable Detail View
- •The Nav Application’s Skeleton
- •Creating the Top-Level View Controller
- •Setting Up the Navigation Controller
- •Adding the Images to the Project
- •First Subcontroller: The Disclosure Button View
- •Creating the Detail View
- •Modifying the Disclosure Button Controller
- •Adding a Disclosure Button Controller Instance
- •Second Subcontroller: The Checklist
- •Creating the Checklist View
- •Adding a Checklist Controller Instance
- •Third Subcontroller: Controls on Table Rows
- •Creating the Row Controls View
- •Adding a Rows Control Controller Instance
- •Fourth Subcontroller: Movable Rows
- •Creating the Movable Row View
- •Adding a Move Me Controller Instance
- •Fifth Subcontroller: Deletable Rows
- •Creating the Deletable Rows View
- •Adding a Delete Me Controller Instance
- •Sixth Subcontroller: An Editable Detail Pane
- •Creating the Data Model Object
- •Creating the Detail View List Controller
- •Creating the Detail View Controller
- •Adding an Editable Detail View Controller Instance
- •But There’s One More Thing. . .
- •Breaking the Tape
- •Creating a Simple Storyboard
- •Dynamic Prototype Cells
- •Dynamic Table Content, Storyboard-Style
- •Editing Prototype Cells
- •Good Old Table View Data Source
- •Will It Load?
- •Static Cells
- •Going Static
- •So Long, Good Old Table View Data Source
- •You Say Segue, I Say Segue
- •Creating Segue Navigator
- •Filling the Blank Slate
- •First Transition
- •A Slightly More Useful Task List
- •Viewing Task Details
- •Make More Segues, Please
- •Passing a Task from the List
- •Handling Task Details
- •Passing Back Details
- •Making the List Receive the Details
- •If Only We Could End with a Smooth Transition
- •Split Views and Popovers
- •Creating a SplitView Project
- •The Storyboard Defines the Structure
- •The Code Defines the Functionality
- •The App Delegate
- •The Master View Controller
- •The Detail View Controller
- •Here Come the Presidents
- •Creating Your Own Popover
- •iPad Wrap-Up
- •Getting to Know Your Settings Bundle
- •The AppSettings Application
- •Creating the Project
- •Working with the Settings Bundle
- •Adding a Settings Bundle to Our Project
- •Setting Up the Property List
- •Adding a Text Field Setting
- •Adding an Application Icon
- •Adding a Secure Text Field Setting
- •Adding a Multivalue Field
- •Adding a Toggle Switch Setting
- •Adding the Slider Setting
- •Adding Icons to the Settings Bundle
- •Adding a Child Settings View
- •Reading Settings in Our Application
- •Retrieving User Settings
- •Creating the Main View
- •Updating the Main View Controller
- •Registering Default Values
- •Changing Defaults from Our Application
- •Keeping It Real
- •Beam Me Up, Scotty
- •Your Application’s Sandbox
- •Getting the Documents Directory
- •Getting the tmp Directory
- •File-Saving Strategies
- •Single-File Persistence
- •Multiple-File Persistence
- •Using Property Lists
- •Property List Serialization
- •The First Version of the Persistence Application
- •Creating the Persistence Project
- •Designing the Persistence Application View
- •Editing the Persistence Classes
- •Archiving Model Objects
- •Conforming to NSCoding
- •Implementing NSCopying
- •Archiving and Unarchiving Data Objects
- •The Archiving Application
- •Implementing the BIDFourLines Class
- •Implementing the BIDViewController Class
- •Using iOS’s Embedded SQLite3
- •Creating or Opening the Database
- •Using Bind Variables
- •The SQLite3 Application
- •Linking to the SQLite3 Library
- •Modifying the Persistence View Controller
- •Using Core Data
- •Entities and Managed Objects
- •Key-Value Coding
- •Putting It All in Context
- •Creating New Managed Objects
- •Retrieving Managed Objects
- •The Core Data Application
- •Designing the Data Model
- •Creating the Persistence View and Controller
- •Persistence Rewarded
- •Managing Document Storage with UIDocument
- •Building TinyPix
- •Creating BIDTinyPixDocument
- •Code Master
- •Initial Storyboarding
- •Creating BIDTinyPixView
- •Storyboard Detailing
- •Adding iCloud Support
- •Creating a Provisioning Profile
- •Enabling iCloud Entitlements
- •How to Query
- •Save Where?
- •Storing Preferences on iCloud
- •What We Didn’t Cover
- •Grand Central Dispatch
- •Introducing SlowWorker
- •Threading Basics
- •Units of Work
- •GCD: Low-Level Queueing
- •Becoming a Blockhead
- •Improving SlowWorker
- •Don’t Forget That Main Thread
- •Giving Some Feedback
- •Concurrent Blocks
- •Background Processing
- •Application Life Cycle
- •State-Change Notifications
- •Creating State Lab
- •Exploring Execution States
- •Making Use of Execution State Changes
- •Handling the Inactive State
- •Handling the Background State
- •Removing Resources When Entering the Background
- •Saving State When Entering the Background
- •A Brief Journey to Yesteryear
- •Back to the Background
- •Requesting More Backgrounding Time
- •Grand Central Dispatch, Over and Out
- •Two Views of a Graphical World
- •The Quartz 2D Approach to Drawing
- •Quartz 2D’s Graphics Contexts
- •The Coordinate System
- •Specifying Colors
- •A Bit of Color Theory for Your iOS Device’s Display
- •Other Color Models
- •Color Convenience Methods
- •Drawing Images in Context
- •Drawing Shapes: Polygons, Lines, and Curves
- •The QuartzFun Application
- •Setting Up the QuartzFun Application
- •Creating a Random Color
- •Defining Application Constants
- •Implementing the QuartzFunView Skeleton
- •Creating and Connecting Outlets and Actions
- •Implementing the Action Methods
- •Adding Quartz 2D Drawing Code
- •Drawing the Line
- •Drawing the Rectangle and Ellipse
- •Drawing the Image
- •Optimizing the QuartzFun Application
- •The GLFun Application
- •Setting Up the GLFun Application
- •Creating BIDGLFunView
- •Updating BIDViewController
- •Updating the Nib
- •Finishing GLFun
- •Drawing to a Close
- •Multitouch Terminology
- •The Responder Chain
- •Responding to Events
- •Forwarding an Event: Keeping the Responder Chain Alive
- •The Multitouch Architecture
- •The Four Touch Notification Methods
- •The TouchExplorer Application
- •The Swipes Application
- •Automatic Gesture Recognition
- •Implementing Multiple Swipes
- •Detecting Multiple Taps
- •Detecting Pinches
- •Defining Custom Gestures
- •The CheckPlease Application
- •The CheckPlease Touch Methods
- •Garçon? Check, Please!
- •The Location Manager
- •Setting the Desired Accuracy
- •Setting the Distance Filter
- •Starting the Location Manager
- •Using the Location Manager Wisely
- •The Location Manager Delegate
- •Getting Location Updates
- •Getting Latitude and Longitude Using CLLocation
- •Error Notifications
- •Trying Out Core Location
- •Updating Location Manager
- •Determining Distance Traveled
- •Wherever You Go, There You Are
- •Accelerometer Physics
- •Don’t Forget Rotation
- •Core Motion and the Motion Manager
- •Event-Based Motion
- •Proactive Motion Access
- •Accelerometer Results
- •Detecting Shakes
- •Baked-In Shaking
- •Shake and Break
- •Accelerometer As Directional Controller
- •Rolling Marbles
- •Writing the Ball View
- •Calculating Ball Movement
- •Rolling On
- •Using the Image Picker and UIImagePickerController
- •Implementing the Image Picker Controller Delegate
- •Road Testing the Camera and Library
- •Designing the Interface
- •Implementing the Camera View Controller
- •It’s a Snap!
- •Localization Architecture
- •Strings Files
- •What’s in a Strings File?
- •The Localized String Macro
- •Real-World iOS: Localizing Your Application
- •Setting Up LocalizeMe
- •Trying Out LocalizeMe
- •Localizing the Nib
- •Localizing an Image
- •Generating and Localizing a Strings File
- •Localizing the App Display Name
- •Auf Wiedersehen
- •Apple’s Documentation
- •Mailing Lists
- •Discussion Forums
- •Web Sites
- •Blogs
- •Conferences
- •Follow the Authors
- •Farewell
- •Index

556 |
CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
self.segmentedControl = nil;
}
Now, move to the end of the viewDidLoad method, where we’ll create the segmented control and add it to the view:
.
.
.
self.smileyView.image = self.smiley;
self.segmentedControl = [[UISegmentedControl alloc] initWithItems: [NSArray arrayWithObjects:
@"One", @"Two", @"Three", @"Four", nil]] ; self.segmentedControl.frame = CGRectMake(bounds.origin.x + 20,
CGRectGetMaxY(bounds) - 50, bounds.size.width - 40, 30);
[self.view addSubview:segmentedControl];
[self.view addSubview:smileyView]; [self.view addSubview:label];
}
Build and run the app. You should see the segmented control and be able to click its segments to select them one at a time.
Here's where we should mention a slight backward-compatibility issue, since something subtle but important has changed between iOS 4 and iOS 5. To see the difference, we're going to blast into the past, to the dark days of iOS 4.3, and see what happens. Don't worry; you don't need to downgrade a device. We'll do it in Xcode and the iOS simulator.
A Brief Journey to Yesteryear
In the project navigator, select the topmost item, the one that represents your project, to examine your project details. You've seen this view before, which shows you various settings for the project and the application target. Select the State Lab item in the TARGETS section, and make sure the Summary tab is selected at the top of the detail view. The uppermost section, iOS Application Target, contains a Deployment Target popup, currently set to the latest version of iOS that your copy of Xcode knows about. Click the control and choose 4.3. This tells Xcode not only that it should build the app with iOS 4.3 in mind, but also run the iOS simulator using iOS 4.3. Next, click the scheme/device popup control near the upper left of the window, and choose iPhone 4.3 Simulator from the popup. You're now ready to take a trip in the way-back machine!
If you build and run your app at this point, you’ll see one glaring problem: the segmented control doesn’t seem to work! You can tap those segments all you like, and nothing will happen. The problem actually lies with the animation. By default, the Core Animation method we used to set up animation actually prevents some amount of user input from being collected while animations are running (presumably this is a performance optimization). The key difference here between iOS 4 and iOS 5 is that
www.it-ebooks.info

CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
557 |
while iOS 5 turns off user interaction for the views that are currently animated, iOS 4 turns off user interaction for the entire application!
Fortunately, there is an optional way to enable user interaction, which requires us to use a longer method name in each of our rotate methods. Modify them as shown here:
- (void)rotateLabelDown {
[UIView animateWithDuration:0.5 delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
label.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){ [self rotateLabelUp]; }];
}
- (void)rotateLabelUp {
[UIView animateWithDuration:0.5 delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
label.transform = CGAffineTransformMakeRotation(0);
}
completion:^(BOOL finished){ if (animate) {
[self rotateLabelDown];
}
}];
}
Build and run the app again, and see what happens. That’s more like it, eh? As we said, this difference between iOS 4 and iOS 5 is subtle, but quite important if your apps use Core Animation and you need to support iOS 4. Though you could strip that code back out, since we’re about to return to the iOS 5 simulator, there’s really no harm in leaving it in and allowing the code to work under iOS 4 as well.
Back to the Background
Let’s return to the present. Select iPhone 5.0 Simulator from the popup menu in the upperleft part of the project window. Now, touch any one of the four segments, and then go through the now-familiar sequence of backgrounding your app and bringing it back up. You’ll see that the segment you chose (bet it was Three) is still selected—no surprise there. Background your app again by clicking the home button, bring up the taskbar (by double-clicking the home button) and kill your app, and then relaunch it. You’ll find yourself back at square one, with no segment selected. That’s what we need to fix next.
www.it-ebooks.info

558 |
CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
CAUTION: When you kill your app in the simulator, it’s possible (depending on which version of Xcode you are running) that upon relaunching your app, you’ll find yourself back in Xcode as the result of a SIGKILL signal. This is perfectly normal. If this happens, click the stop button at the top
left of the project window, and then rerun your project to bring your project back to life in the
simulator.
Saving the selection is simple enough. We just need to add a few lines to the end of the applicationDidEnterBackground method in BIDViewController.m:
- (void)applicationDidEnterBackground { NSLog(@"VC: %@", NSStringFromSelector(_cmd)); self.smiley = nil;
self.smileyView.image = nil;
NSInteger selectedIndex = self.segmentedControl.selectedSegmentIndex; [[NSUserDefaults standardUserDefaults] setInteger:selectedIndex
forKey:@"selectedIndex"];
}
But where should we restore this selection index and use it to configure the segmented control? The inverse of this method, applicationWillEnterForeground, isn’t what we want. When that method is called, the app has already been running, and the setting is still intact. Instead, we need to access this when things are being set up after a new launch, which brings us back to the viewDidLoad method. Add the bold lines shown here at the end of the method:
.
.
.
[self.view addSubview:label];
NSNumber *indexNumber;
if (indexNumber = [[NSUserDefaults standardUserDefaults] objectForKey:@"selectedIndex"]) {
NSInteger selectedIndex = [indexNumber intValue]; self.segmentedControl.selectedSegmentIndex = selectedIndex;
}
}
We needed to include a little sanity check here to see whether there’s a value stored for the selectedIndex key, to cover cases such as the first app launch, where nothing has been selected.
Now build and run the app, touch a segment, and then do the full background-kill- restart dance. There it is—your selection is intact!
Obviously, what we’ve shown here is pretty minimal, but the concept can be extended to all kinds of application state. It’s up to you to decide how far you want to take it in order to maintain the illusion for the users that your app was always there, just waiting for them to come back!
www.it-ebooks.info

CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
559 |
Requesting More Backgrounding Time
Earlier, we mentioned the possibility of your app being dumped from memory if moving to the Background state takes too much time. For example, your app may be in the middle of doing a file transfer that it would really be a shame not to finish, but trying to hijack the applicationDidEnterBackground method to make it complete the work there, before the application is really backgrounded, isn’t really an option. Instead, you should use applicationDidEnterBackground as a platform for telling the system that you have some extra work you would like to do, and then start up a block to actually do it. Assuming that the system has enough available RAM to keep your app in memory while the user does something else, the system will oblige you and keep your app running for a while.
We’ll demonstrate this not with an actual file transfer, but with a simple sleep call. Once again, we’ll be using our new acquaintances GCD and blocks to make the contents of our applicationDidEnterBackground method run in a separate queue.
In BIDViewController.m, modify the applicationDidEnterBackground method as follows:
- (void)applicationDidEnterBackground { NSLog(@"VC: %@", NSStringFromSelector(_cmd));
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier taskId;
taskId = [app beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"Background task ran out of time and was terminated."); [app endBackgroundTask:taskId];
}];
if (taskId == UIBackgroundTaskInvalid) { NSLog(@"Failed to start background task!"); return;
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"Starting background task with %f seconds remaining",
app.backgroundTimeRemaining); self.smiley = nil; self.smileyView.image = nil;
NSInteger selectedIndex = self.segmentedControl.selectedSegmentIndex; [[NSUserDefaults standardUserDefaults] setInteger:selectedIndex
forKey:@"selectedIndex"];
// simulate a lengthy (25 seconds) procedure [NSThread sleepForTimeInterval:25];
NSLog(@"Finishing background task with %f seconds remaining", app.backgroundTimeRemaining);
[app endBackgroundTask:taskId];
});
}
www.it-ebooks.info

560 |
CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
Let’s look through this code piece by piece. First, we grab the shared UIApplication instance, since we’ll be using it several times in this method. Then comes this:
__block UIBackgroundTaskIdentifier taskId;
taskId = [app beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"Background task ran out of time and was terminated."); [app endBackgroundTask:taskId];
}];
The call to beginBackgroundTaskWithExpirationHandler: returns an identifier that we’ll need to keep track of for later use. We’ve declared the taskId variable it’s stored in with the __block storage qualifier, since we want to be sure the identifier returned by the method is shared among any blocks we create in this method.
With the call to beginBackgroundTaskWithExpirationHandler:, we’re basically telling the system that we need more time to accomplish something, and we promise to let it know when we’re finished. The block we give as a parameter may be called if the system decides that we’ve been going way too long anyway and decides to stop running.
Note that the block we gave ended with a call to endBackgroundTask:, passing along taskId. That tells the system that we’re finished with the work for which we previously requested extra time. It’s important to balance each call to beginBackgroundTaskWithExpirationHandler: with a matching call to endBackgroundTask: so that the system knows when we’ve completed the work.
NOTE: Depending on your computing background, the use of the word task here may evoke
associations with what we usually call a process, consisting of a running program that may contain multiple threads, and so on. In this case, try to put that out of your mind. The use of task in this context really just means “something that needs to get done.” Any task you create here is
running within your still-executing app.
Next, we do this:
if (taskId == UIBackgroundTaskInvalid) { NSLog(@"Failed to start background task!"); return;
}
If our earlier call to beginBackgroundTaskWithExpirationHandler: returned the special value UIBackgroundTaskInvalid, that means the system is refusing to grant us any additional time. In that case, you could try to do the quickest part of whatever needs doing anyway and hope that it completes quickly enough that your app won’t be terminated before it’s finished. This is mostly likely to be an issue when running on older devices, such as the iPhone 3G, that let you run iOS 4 but don’t support multitasking. In this example, however, we’re just letting it slide.
Next comes the interesting part where the work itself is actually done:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"Starting background task with %f seconds remaining",
app.backgroundTimeRemaining);
www.it-ebooks.info

CHAPTER 15: Grand Central Dispatch, Background Processing, and You |
561 |
self.smiley = nil; self.smileyView.image = nil;
NSInteger selectedIndex = self.segmentedControl.selectedSegmentIndex; [[NSUserDefaults standardUserDefaults] setInteger:selectedIndex
forKey:@"selectedIndex"]; // simulate a lengthy (25 seconds) procedure
[NSThread sleepForTimeInterval:25];
NSLog(@"Finishing background task with %f seconds remaining", app.backgroundTimeRemaining);
[app endBackgroundTask:taskId];
});
All this does is take the same work our method was doing in the first place and place it in a background queue. At the end of that block, we call endBackgroundTask: to let the system know that we’re finished.
With that in place, build and run the app, and then background your app by pressing the home button. Watch the Xcode console as well as the status bar at the bottom of the Xcode window. You’ll see that this time, your app stays running (you don’t get the “Debugging terminated” message in the status bar), and after 25 seconds, you will see the final log in your output. A complete run of the app up to this point should give you console output along these lines:
2011-10-30 22:35:28.608 State Lab[7449:207] application:didFinishLaunchingWithOptions:
2011-10-30 22:35:28.616 State Lab[7449:207] applicationDidBecomeActive: 2011-10-30 22:35:28.617 State Lab[7449:207] VC: applicationDidBecomeActive 2011-10-30 22:35:31.869 State Lab[7449:207] applicationWillResignActive: 2011-10-30 22:35:31.870 State Lab[7449:207] VC: applicationWillResignActive 2011-10-30 22:35:31.871 State Lab[7449:207] applicationDidEnterBackground: 2011-10-30 22:35:31.873 State Lab[7449:207] VC: applicationDidEnterBackground
2011-10-30 22:35:31.874 State Lab[7449:1903] Starting background task with 599.995069 seconds remaining
2011-10-30 22:35:56.877 State Lab[7449:1903] Finishing background task with 574.993956 seconds remaining
As you can see, the system is much more generous with time when doing things in the background than in the main thread of your app, so following this procedure can really help you out if you have any ongoing tasks to deal with.
Note that we asked for a single background task identifier, but in practice, you can ask for as many as you need. For example, if you have multiple network transfers happening at Background time and you need to complete them, you can ask for an identifier for each and allow them to continue running in a background queue. So, you can easily allow multiple operations to run in parallel during the available time. Also consider that the task identifier you receive is a normal C-language value (not an object), and apart from being stored in a local __block variable, it can just as well be stored as an instance variable if that better suits your class design.
www.it-ebooks.info