What’s New in Objective-C and Foundation in iOS 7

iOS 7 brought some interesting new features to Objective-C and Foundation. New compiler features and improved Foundation classes – read all about them! By Matt Galloway.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

NSData

Data is one of those things you deal with a lot when you’re programming. NSData is the Foundation class that encapsulates raw bytes and provides methods for manipulating those bytes, as well reading and writing data to and from a file. But one very common task for which there’s been no native implementation is Base64 encoding and decoding. At least that was the case until the release of iOS 7.

Base64 is a group of binary-to-text encoding schemes that represent binary data in ASCII format. These schemes are commonly used where there’s a need to encode binary data to be stored on or transferred over media designed to deal solely with textual data. This ensures the data remains intact without modification during transport. The most common use of Base64 encoding is handling attachments in email, as well as encoding small images that form part of a JSON response returned by a web based API.

Prior to iOS 7.0, Base64 encoding and decoding tasks required you to implement your own method or include part of a third party framework.In typical Apple fashion, it’s now very easy to use this functionality. There are four core Base64 methods as follows:

- (id)initWithBase64EncodedString:(NSString *)base64String 
      options:(NSDataBase64DecodingOptions)options;

- (NSString *)base64EncodedStringWithOptions:
      (NSDataBase64EncodingOptions)options;

- (id)initWithBase64EncodedData:(NSData *)base64Data 
      options:(NSDataBase64DecodingOptions)options;

- (NSData *)base64EncodedDataWithOptions:
      (NSDataBase64EncodingOptions)options;

The first two methods deal with strings, while the latter two deal with UTF-8 encoded data. Both pairs of methods perform the same action, but sometimes using one over the other will prove more effective. If you were to Base64 encode a string and then write it to a file, you may decide to use the pair that handles UTF-8 encoded data. On the other hand, if you were to Base64 encode a string and then use that in some JSON, you may decide to use the pair that handles strings.

So if you’ve ever included a Base64 method or two in your project, now is the time to remove that unnecessary code and use Apple’s implementations instead!

NSTimer

Timers often find homes in apps that perform periodic tasks. As useful as they may be, the problem is that they may fire off constantly when several timers are in use. This means the CPU is constantly active; it would be much more efficient if the CPU woke up, performed a batch of tasks and then went back to sleep. To solve this issue, Apple has added a tolerance property to NSTimer to help accommodate this behavior.
The tolerance property provides the system with a guide as to how late a timer is permitted to fire after its schedule time. The underlying system will then group actions accordingly to reduce CPU overhead. The methods for accessing this new property are as follows:

- (NSTimeInterval)tolerance;
- (void)setTolerance:(NSTimeInterval)tolerance;

You may find you never need to use this property, but if you’re firing several timers in very close succession, you may find it useful to benchmark your app’s CPU usage using Instruments while tinkering with this setting.

NSProgress

It’s not often that entirely new classes get added to Foundation. It’s a pretty stable framework, mainly because new core classes aren’t required too often. However, iOS 7.0 presents an entirely new class named NSProgress.

In essence, NSProgress aims to deliver progress reporting throughout Objective-C code, neatly separating the progress of individual components. For example, if you perform a few different tasks on some data, then each task can monitor its own progress and report back to its parent task.

The structure of NSProgress

The simplest way of using NSProgress is to use it to report progress on a set of tasks. For example if you have 10 tasks to achieve, then you can report progress as each task finishes. As each task finishes the progress goes up by 10%. Then, using Key Value Observing (KVO) on the NSProgress instance, you will be notified about this increase in progress. You might use this notification as an opportunity to update a progress bar, or set the text on a label to give an indication of the progress.

But there is more to NSProgress than just this. Apple have made it incredibly powerful, mainly through the use of a child-parent relationship structure. The structure of NSProgress is much like a nested tree: each instance can have one parent and many children. Each instance has a total number of units of work to be performed, and as the task progresses the completed number of units is updated to reflect the current state. In doing so, the parent (if one exists) is notified of the progress as well.

To reduce the need to pass around NSProgress instances, each thread has its own NSProgress instance and child instances can be created directly from this instance. Without this functionality, every task that wanted to report progress in this way would have to be altered to take an NSProgress parameter.

Reporting progress

Using NSProgress is simple. It all starts with the following method:

+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount;

This creates a new instance of NSProgress as a child of the current instance and initializes it with the total number of units of work that will be performed overall. For example, if the task were to loop through an array, then you would probably initialize the NSProgress instance with the count of the array, like so:

NSArray *array = /* ... */;

NSProgress *progress = 
    [NSProgress progressWithTotalUnitCount:array.count];

[array enumerateObjectsUsingBlock:
    ^(id obj, NSUInteger idx, BOOL *stop) {
        // Perform an expensive operation on obj
        progress.completedUnitCount = idx;
    }];

As the iteration progresses, the above code updates the instance of NSProgress to reflect the current progress.

Receiving progress updates

You can determine the progress of the task at any point through the following property:

@property (readonly) double fractionCompleted;

This returns a value from 0 to 1, indicating the total progress of the task. When there are no child instances in play, fractionCompleted is simply the completed unit count divided by the total unit count.

Key Value Observing (KVO) is the best way to be notified when the fractionCompleted property changes its value. Doing so is simple. All you need to do is register as an observer of the fractionCompleted property of the relevant NSProgress object like so:

[_progress addObserver:self 
            forKeyPath:@"fractionCompleted" 
               options:NSKeyValueObservingOptionNew 
               context:NULL];

Then, override the method that KVO uses to notify you of changes, like so:

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context
{
    if (object == _progress) {
        // Handle new fractionCompleted value
        return;
    }
    
    // Always call super, incase it uses KVO also
    [super observeValueForKeyPath:keyPath 
                         ofObject:object 
                           change:change 
                          context:context];
}

In this method you would handle the change in fractionCompleted value. For example, you might change the value of a progress bar or a label to indicate the current level of completion.

Of course, it’s important to remember to unregister from KVO once you’re done, like so:

[_progress removeObserver:self 
               forKeyPath:@"fractionCompleted" 
                  context:NULL];

You must always unregister and your app will crash if you don’t unregister by the time the registered object (self in this example) is deallocated. So ensure that you unregister as a last resort in dealloc if necessary.

Contributors

Over 300 content creators. Join our team.