Introducing Blocks, Threads, and Notifications - Using the Technologies - Learning iCloud Data Management (2014)

Learning iCloud Data Management (2014)

Part III: Using the Technologies

7. Introducing Blocks, Threads, and Notifications

In Part II, “Using the APIs,” you saw how to use APIs to get at the iCloud data that users enter and manage. Contacts, Reminders, and Calendar work with iCloud if the user has registered and turned it on, but the APIs work just as well if the user doesn’t use iCloud or does use it but has no network connection at the time. In this part of the book, “Using the Technologies,” you start to deal explicitly with iCloud itself.

As you saw in Chapter 2, “Setting Up iCloud for Development,” iCloud is not a single framework or API. You won’t be sitting down to write line after line of code. Instead, you’ll be setting up the iCloud structure for your app with Apple IDs, a bundle ID, entitlements, and in many cases, a ubiquity container. The tools you use for these tasks are developer.apple.com and Xcode. As you’ll see in this chapter, you’ll be using notifications along with other pieces of code that you insert into various parts of your app.

Also in this chapter, you’ll find a basic overview of blocks (relatively new in Objective-C) and threading. As with much of iCloud, existing technologies are being adopted and pressed into service. Threads, for example, have been around for a long time to help you optimize your app’s performance, but in the context of iCloud, they’re not just an optimization tool. Certain iCloud operations, such as obtaining the URL of the ubiquity container for the user’s account, must not be performed on the app’s main thread because the latency in getting a result returned can be unacceptable to the user. Using blocks, notifications, and multithreading means that you can perform a multistep process as independent steps that are performed as necessary.

Syncing blobs of data across devices can be a lengthy process. (In this case, “blob” is used both in its technical sense of binary large object and its common sense of a shapeless mass). Anything that you can do to structure the data is likely to make it easier to sync. The most commonly cited example is that many people may have an address book with hundreds or even thousands of contacts, but at any given time, most of those entries probably don’t need to be synced. From time to time, one or two may require syncing. Part II of this book helps you use technologies to structure data to make it easier to manage and to sync.


iCloud May Change User Behavior

iCloud is the start of a new era in computing. It builds on the general concept of cloud computing, but in its focus on app-centered content rather than a file system with files and folders, it breaks new ground. How the technology and its use evolve remains to be seen. In addition to studying technical documentation, spend some time keeping up to date with the behavioral changes people adopt with mobile devices.

One particularly useful resource is the Pew Research Center’s Project for Excellence in Journalism. In its paper, “The Future of Mobile News: The Explosion in Mobile Audiences and a Close Look at What it Means for News,” published in October 2012 and available online atwww.journalism.org/files/legacy/Futureofmobilenews%20_final1.pdf, Pew’s research shows that mobile readers of news are reading more and longer news stories on their mobile devices.

This flies in the face of some early theories that mobile devices were suited to shorter articles (these theories were particularly popular with regard to smartphones with small screens, but the mobility and ease of access may be counterbalancing the small screens). It is quite possible that iCloud is also going to change the way people use apps and devices. The ability to continue reading, listening to, or working with data over time and across devices as well as while moving around may well change behavior.


Catching Up with Blocks and Threads

Many experienced OS X and iOS developers haven’t used blocks and threads. They are relatively new to both operating systems, having been introduced in iOS 4 and in OS X Snow Leopard (10.6). Grand Central Dispatch (GCD) provides support for concurrent code execution on multicore systems. Blocks (sometimes called closures in other programming languages) often are used together with threads.

Queues and Threads

Before GCD, it was possible to use multiple threads on a multicore system, but it was a somewhat complex process. The complexity arose from the fact that software engineers basically had to construct their own threading mechanism, sometimes even modifying it depending on the number of cores in the processor running the app. This complexity didn’t cause too many problems because multicore processors were not in widespread use. However, the trend was clear, so Apple developed GCD to allow people to use threads for multicore processing.

There are two abstractions at the heart of GCD:

Image A thread is a sequence of instructions that is executed on a single processor or core.

Image A queue is a collection of tasks that are managed on available threads by the operating system.

Most often, the tasks are actually blocks. There are three types of queues in GCD:

Image Main is the main thread. It is where most tasks are performed. The tasks (blocks) queued on the main thread are executed serially.

Image Concurrent queues have their tasks dequeued serially in first-in/first-out (FIFO) order. When multiple threads are available, the operating system assigns them to threads as they become available. Although the dequeuing sequence is FIFO serial, the completion sequence is undetermined.

Image Serial queues execute their tasks in FIFO order and one at a time. Thus, the sequence of completion is the same as the sequence of enqueuing.

You can further manage your app’s multiprocessing by requesting concurrent queues that run at varying priorities. This structure provides abstractions that work across various multicore configurations without your having to change your code.

Blocks

The blocks that you enqueue in a queue are a relatively new (iOS 4 and OS X Snow Leopard (10.6)) feature. Whether or not their appearance a year before the launch of the iPad may be part of the story, they certainly are important on all the devices today. A block is a section of code that is much like a function. Like a function, it can be named. It can also be pointed to just as a pointer can point to a function’s inline code.

Unlike a function, a block can contain bindings to variables from the scope of the block’s declaration. Functions only manage data passed in through argument or global to the function (which is a poor programming practice). A block locks onto variables and can use them whenever it executes. Thus, if you enqueue a block that is bound to variable X outside the block, control may pass out of the code that created the block and X is no longer present—except that the block is still locked to it. Whenever the block is executed by the queue, it has access to that variable. This makes it an excellent tool for asynchronous or concurrent processing. (The access is actually to a copy unless the variable is declared with __block preceding the type in the declaration.)

Block declarations and definitions use the ^ character to introduce the declaration or definition. Thus, a block declaration of myBlock can be written as

int (^myBlock)(int)

This describes myBlock, which takes an int argument and returns an (int) value.

A block definition can be written as

^(int amount) {return amount + runningTotal;}:

In the definition, the int argument is given a name: amount. The body of the block definition contains the return value.

You can combine them in

int RunningTotal = 0;
int (^myBlock)(int) = ^(int amount) {return amount + runningTotal;}:

Because blocks often are relatively short, and also because they commonly are used only in one place, it is common not to bother with a declaration.

Getting Up to Speed with Notifications

Blocks and GCD (not to mention iCloud) are new technologies. There’s another technology that is very important in using iCloud: notifications. Notifications aren’t new. In fact, they are one of the basic building blocks of the operating systems. There are three ways in which objects can communicate with one another:

Image An object can communicate with another if it has a reference to the second object (and vice versa). Most often, this reference is made by setting a field in the first object to the second object.

Image A looser link occurs when the first object communicates with a delegate, which, in turn, finishes the communication. The first object isn’t aware of the object that will be receiving the message: it just knows about the delegate (the intermediary).

Image Notifications are the loosest coupling. There is no linkage between the two objects. It uses a broadcast model: the first object broadcasts a notification to any object that happens to be listening. The second object receives the notification. The first object posts a notification; other objects register for notifications. The intermediary is an NSNotificationCenter, which each process has access to at runtime.

Notifications are one of the features of Cocoa that are used constantly by some developers and never by others. This section provides a high-level overview to get you up to speed for using notifications with iCloud.

The reason that notifications are so important for iCloud is that, due to their very loose coupling, they work very well in asynchronous processing. When you tightly couple two objects together, the communication between them is as fast as possible. Even delegation is normally quite speedy. But when you are dealing with processes that may be running on different computers and across the network, you need a communication mechanism that doesn’t assume instantaneous and always-available communication. Notifications fit the bill. In fact, it is hard to think of a way to implement an iCloud-enabled app without using notifications.

The basics of notifications are quite simple:

Image You obtain a reference to the process’s notification center.

Image If you want to register to receive specific notifications, you register as an observer of the specific notification.

Image If you want to send notifications, you post them to the notification center.

Image Notifications themselves are lightweight objects of the NSNotification class. Among their properties are a name: that’s how the linkage between registration and posting is made.

Notification Properties

A notification is a lightweight object that contains three properties:

Image name—An NSString containing the name of the notification object. Both observer and poster need to know this name. Usually it is defined as a constant in a project so that there’s no possibility of confusing the name between observer and poster. This property is required.

Image object—The id of the object posting the notification or, occasionally, another object related to the notification. When you observe a notification, you supply the name of the notification to observe; by passing a non-nil value for object, you can refine the observation so that only notifications from object are observed.

Image userInfo—An NSDictionary containing any information you want to pass along with the notification. Like object, it is optional.

If you want to create a notification, you can use either of the following NSNotification class methods:

+ (id)notificationWithName:(NSString *)aName object:(id)anObject
+ (id)notificationWithName:(NSString *)aName object:(id)anObject
userInfo:(NSDictionary *)userInfoGetting the Notification Center

As is the case with blocks (and for many of the same reasons), it is common not to bother with explicitly creating notifications. Instead, they are created as part of the methods that post them to the notification center, as you will see in the following sections.


Note

This section discusses local notifications within a single process. Distributed notifications are also available.


Registering for Notifications

To register for a notification, get the process’s notification center and register as an observer. The code to get the notification center for a process is

[NSNotificationCenter defaultCenter]

Whether you store the notification center or obtain it as you need it is up to you.

To register for a notification, you become an observer of it. The method is

- (void)addObserver:(id)notificationObserver
selector:(SEL)notificationSelector
name:(NSString *)notificationName
object:(id)notificationSender

The parameters are

Image notificationObserver: This is often self; it cannot be nil.

Image notificationSelector: This is a message that will be sent when the notification is received. It must have one parameter: an NSNotification, which is the incoming notification.

Image notificationName: This identifies the notification you want to observe by name.

Image notificationSender: This identifies the notification by object (usually the sender).

Note that the last two parameters are optional. By setting only one of them, you can receive all notifications from a specific object, or you can receive all of a given notification no matter who sent it.

Posting Notifications

You usually create and post a notification with one message:

- (void)postNotificationName:(NSString *)notificationName
object:(id)notificationSender
userInfo:(NSDictionary *)userInfo

Although you often observe as well as post, with iCloud apps you are more frequently an observer than a poster.

Receiving Notification of iCloud Availability Changes

The most basic notification you need to register for is NSUbiquityIdentityDidChangeNotification. You receive this notification when the user logs out and logs in again; you also receive this notification for a log out from iCloud without a new log in. After receiving this notification, use ubiquityIdentityToken to find the new identity, if any, as described in Chapter 2. If the token is nil, the user has signed out of iCloud or turned off Data & Documents for the iCloud account. Note that turning on Airplane mode does not generate this notification. iCloud just stores changes until Airplane mode is turned off.

This code shows how you can combine obtaining a reference to the notification center with registration for NSUbiquityIdentityDidChangeNotification. It is assumed that you have a method called handleiCloudAvailabilityChange that will be triggered by the notification. You typically register for this notification (indeed, for many notifications that are not specific to a given window) in your application’s launch process. On iOS, that is didFinishLaunchingWithOptions: for the App Delegate. On OS X, that isapplicationDidFinishLaunching:, also for the App Delegate.

[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector (handleiCLoudAvailabilityChange:)
name: NSUbiquityIdentityDidChangeNotification
object: nil];

Introducing the Second Project

This part of the book focuses on the techniques for managing data on iOS and OS X as well as on iCloud’s interaction with these data technologies. Accordingly, the second project in this book provides hands-on examples of the various types of data management. It builds on the first project by letting the user enter text data and categorize it. Because the app is iCloud-enabled, the data can be synchronized across a user’s devices.

This project brings together the generalized snippets you have seen about connecting to iCloud and maintaining that connection even when iCloud is not available. The project also lets you adjust settings and preferences and save them. In general, settings are managed on iOS by the Settings app (it manages them for all apps). Preferences are normally used to describe items that are managed by the app itself. In the case of iCloud-enabled apps, settings often are device specific, and preferences are often data-specific. Until iCloud, it wasn’t necessary to make this distinction.

Getting Ready to Move On

In order to test your work from this point onward, you need to be a registered developer and have a test device. To test synchronization across iOS devices, you’ll actually need at least two test devices, and preferably three; likewise, you’ll need two OS X devices. You won’t have to break the bank for this: you only need OS X and iOS devices with Internet connectivity. On OS X, that can be wired or WiFi, and on iOS it generally is WiFi (at least for testing). At this point, you don’t need to test Internet connectivity over the mobile network, so you don’t need a data plan.

You also don’t need the latest devices, although ideally you should have iOS 7 or later and OS X Mavericks (10.9) or later. Fortunately, these run on several recent generations of hardware. Before you go shopping on eBay, ask around because you are likely to find people who have older devices sitting in drawers or closets.

With Xcode 5, the simulator is now able to simulate iCloud access. However, iCloud synchronization is a menu command you use on the simulator. Only with actual devices will you see the synchronization process in action on its own. The menu command is a great way to test your synchronization code, but once you have done that, you must use actual devices to ensure that the synchronization happens when you’re not around to start it from the simulator menu command.

One strategy for setting up test devices to test iCloud development is to create a new Apple ID (there’s no cost for this). Your Apple ID lets you register for iCloud with 5 GB of storage (at the time of this writing). Keeping your testing Apple ID separate from your own Apple ID (or IDs) is a good idea. In fact, it’s a very good idea.

For your testing environment, you may not want to link to iTunes. If you start synchronizing your books and music with your test environment, first of all, you will start to mingle test and actual data. Second, you may find that you’re using up your basic iCloud storage unnecessarily. If you want to listen to music, check your email, or look up contacts, keep your personal iPhone handy while you work on other devices for testing.

Chapter Summary

This chapter has shown you some of the basic techniques that you’ll use in actually managing iCloud data. In particular, you’ve seen how block and multithreading with Grand Central Dispatch along with notifications let you take advantage of multiple core processors as well as how to manage the latency that frequently accompanies networked operations such as iCloud.

Exercises

1. Within your testing Apple ID, experiment with the basics of iCloud—contacts, reminders, and events. Do some informal testing with your three (ideally) devices. First, enter a new contact on one of the devices, and then watch it propagate to the other devices. Now, start experimenting with Airplane mode on the iOS devices and turning off WiFi on the OS X devices. Try to create collisions so that you get the hang of it. With all devices on, enter a new contact. Now, turn on Airplane mode and disable WiFi. Make a controlled change to the data you entered (like just changing a telephone number). Make a different change on each device. Then, reenable WiFi on two devices. Practice until you can consistently create conflicts. The reason for doing this is so that when you test this situation on your own code, you’re confident that you’re properly exercising the code. Not having conflicts when you should have them may encourage you to think that your code is working, but the absence of errors or warnings may, in and of itself, be an error.

2. Pay attention to the latency involved in iCloud. As you start testing your own code, you may fall into the trap of assuming that updates propagate instantaneously. You may discover certain patterns of iCloud performance. They may actually be patterns of iCloud performance, or they may turn out to be patterns of Internet access performance. Particularly on shared connections such as cable TV, look for certain times of day or days of the week where connectivity is better or worse.