Class

Progress

The Progress class provides a self-contained mechanism for progress reporting. It makes it easy for code that does work to report the progress of that work, and for user interface code to observe that progress for presentation to the user. Specifically, it can be used to show the user a progress bar and explanatory text, both updated properly as progress is made. It also allows work to be cancelled or paused by the user.

Overview

Reporting Progress

Using the methods of this class, your code can report the progress it’s currently making toward completing some task, including progress in related subtasks. You can create instances of this class using the init(parent:userInfo:) instance method or the init(totalUnitCount:) class method.

Progress objects have a number of properties that you can use to observe and report current progress. For instance, the totalUnitCount property represents the total number of units of work that need to be performed, and the completedUnitCount and fractionCompleted properties represents how much of that work has already been completed. The fractionCompleted property is useful for updating progress indicators or textual descriptors; to check whether progress is complete, you should test that completedUnitCount >= totalUnitCount (assuming, of course, that totalUnitCount > 0).

The following listing shows a sample method that reports the progress of performing some operation on a piece of data. When the progress object is first created, the value of its totalUnitCount property is set to some suitable batch size for this operation, and the completedUnitCount count is 0. Each time the loop executes and the batch of data is processed, the progress object’s totalUnitCount property is incremented appropriately.

- (void)startTaskWithData:(NSData *)data {
    NSUInteger batchSize = ... use a suitable batch size
    NSProgress *progress = [NSProgress progressWithTotalUnitCount:batchSize];
 
    for (NSUInteger index = 0; index < batchSize; index++) {
        // Check for cancellation
        if ([progress isCancelled]) {
             // Tidy up as necessary...
             break;
        }
 
        // Do something with this batch of data...
 
        // Report progress (add 1 because we've completed the work for the current index).
        [progress setCompletedUnitCount:(index + 1)];
    }
}

Each of the properties of a progress object, including totalUnitCount, completedUnitCount, and fractionCompleted, support Key-Value Observing. This makes it extremely easy for a view or window controller object to observe the properties, and update UI elements such as progress indicators when the values change. It also means that there is a non-zero cost to updating the values of these properties, so you should avoid using a unit count that is too granular—if you’re iterating over a large data set, for example, and each operation takes only a trivial amount of time, you should divide the work into batches so you can update the unit count once per batch rather than once per iteration.

Creating a Tree of Progress Objects

Often, your code may need to report the overall progress of an operation that is composed of several sub-operations. To accomplish this, your code can report the progress of each sub-operation by building up a tree of progress objects.

The NSProgress reporting mechanism supports a loosely coupled relationship between progress objects. Sub-operations don’t need to know anything about the parent progress item—you can simply create new progress objects that are added as a child of another NSProgress instance. The child object is assigned a portion of the parent’s pending unit count. When the children complete, the parent’s completedUnitCount property is automatically increased by a predefined amount.

You add child progress objects to your tree implicitly or explicitly.

Implicitly Adding a Child

Add a child implicitly by setting a pending unit count for the parent and creating a new NSProgress instance. That new instance will be set as a child of the parent and be assigned the pending unit count.

As an example, consider that you are tracking the progress of code downloading and copying files on disk. You could use a single progress object to track the entire task, but it’s easier to manage each subtask using a separate progress object. You start by creating an overall parent progress object with a suitable total unit count, then call becomeCurrent(withPendingUnitCount:), then create your sub-task progress objects, before finally calling resignCurrent().

The pending unit count that you specify in the first method is divided equally among the child progress objects you create between these two method calls. Each child progress object maintains its own internal unit count, but when it completes its units (that is, the child object’s completedUnitCount == totalUnitCount), the parent progress object’s completedUnitCount is increased by the assigned portion of the original pending unit count.

In the following example, the overall parent progress has 100 units; the two child objects therefore get 50 pending units each, and keep track internally of 10 units of work each. When each child completes its 10 units, the parent’s completed unit count is increased by 50.

- (void)startLongOperation {
    self.overallProgress = [NSProgress progressWithTotalUnitCount:100];
 
    [self.overallProgress becomeCurrentWithPendingUnitCount:50];
    [self work1];
    [self.overallProgress resignCurrent];
 
    [self.overallProgress becomeCurrentWithPendingUnitCount:50];
    [self work2];
    [self.overallProgress resignCurrent];
}
 
- (void)work1 {
    NSProgress *firstTaskProgress = [NSProgress progressWithTotalUnitCount:10];
    // Perform first task...
}
 
- (void)work2 {
    NSProgress *secondTaskProgress = [NSProgress progressWithTotalUnitCount:10];
    // Perform second task...
}

If you don’t create any child progress objects between the calls to becomeCurrent(withPendingUnitCount:) and resignCurrent(), the “parent” progress automatically updates its completedUnitCount by adding the pending units.

Explicitly Adding a Child

As of iOS 9.0 and OS X v10.11 you can explicitly add a child to a progress tree.

To add a child, call addChild(_:withPendingUnitCount:) on the parent. The value for pending unit count is the amount of the parent’s totalUnitCount consumed by the child. The child usually follows the ProgressReporting protocol.

In the following example, the overall parent progress has 10 units. The child progress for the download gets 8 units and tracks the download of a photo. The progress for the filter takes a lot less time and gets the remaining 2 units. When the download completes the parent’s completed unit count is updated by 8. When the filter completes it is updated by the remaining 2 units.

- (void)startLongOperation {
    self.overallProgress = [NSProgress progressWithTotalUnitCount:10];
 
    [self.overallProgress addChild:download.progress withPendingUnitCount:8];
    // Do the download…
 
    [self.overallProgress addChild:filter.progress withPendingUnitCount:2];
    // Perform the filter…
}

Symbols

Creating Progress Objects

init(parent: Progress?, userInfo: [AnyHashable : Any]? = nil)

Initializes a newly allocated NSProgress instance.

class func discreteProgress(totalUnitCount: Int64)

Creates and returns an NSProgress instance with the specified totalUnitCount that is not part of any existing progress tree. The instance is initialized using init(parent:userInfo:) with the parent set to nil.

init(totalUnitCount: Int64)

Creates and returns an NSProgress instance, initialized using init(parent:userInfo:).

init(totalUnitCount: Int64, parent: Progress, pendingUnitCount: Int64)

Creates and returns an NSProgress instance attached to the specified parent with the totalUnitCount set to portionOfParentTotalUnitCount.

Current Progress Object

class func current()

Returns the NSProgress instance, if any, associated with the current thread by a previous invocation of becomeCurrent(withPendingUnitCount:).

func becomeCurrent(withPendingUnitCount: Int64)

Sets the receiver as the current progress object of the current thread and specifies the portion of work to be performed by the next child progress object of the receiver.

func addChild(Progress, withPendingUnitCount: Int64)

Add a process object as a child of a progress tree. The inUnitCount indicates the expected work for the progress unit.

func resignCurrent()

Balance the most recent previous invocation of becomeCurrent(withPendingUnitCount:) on the same thread by restoring the current progress object to what it was before becomeCurrent(withPendingUnitCount:) was invoked.

Reporting Progress

var totalUnitCount: Int64

The total number of units of work tracked for the current progress.

var completedUnitCount: Int64

The number of units of work for the current job that have already been completed.

var localizedDescription: String!

A localized description of progress tracked by the receiver.

var localizedAdditionalDescription: String!

A more specific localized description of progress tracked by the receiver.

Observing Progress

var fractionCompleted: Double

The fraction of the overall work completed by this progress object, including work done by any children it may have.

Controlling Progress

var isCancellable: Bool

Indicates whether the receiver is tracking work that can be cancelled.

var isCancelled: Bool

Indicates whether the receiver is tracking work that has been cancelled.

func cancel()

Cancel progress tracking.

var cancellationHandler: (() -> Void)?

The block to invoke when progress is cancelled.

var isPausable: Bool

Indicates whether the receiver is tracking work that can be paused.

var isPaused: Bool

Indicates whether the receiver is tracking work that has been paused.

func pause()

Pause progress tracking.

var pausingHandler: (() -> Void)?

The block to invoke when progress is paused.

func resume()

Resume progress tracking.

var resumingHandler: (() -> Void)?

The block to invoke when progress is resumed.

Progress Information

var isIndeterminate: Bool

Indicates whether the tracked progress is indeterminate.

var kind: ProgressKind?

A string identifying the kind of progress being made.

var userInfo: [ProgressUserInfoKey : Any]

A dictionary of arbitrary values associated with the receiver.

Constants

General progress user info dictionary keys

Set values for these keys in the user info dictionary to affect the auto-generated localizedAdditionalDescription string.

Progress kinds

When working with files, set this string for the kind of progress.

File operation user info dictionary keys

Set values for these keys in the user info dictionary to provide more information about file operations for the auto-generated localizedDescription and localizedAdditionalDescription strings.

File operation kinds

When tracking file operations with the progress kind set to file, you must provide a value for the fileOperationKindKey in the user info dictionary. There are four possible values.

Instance Properties

Relationships

Inherits From

Conforms To