August 03, 2013
UIBackgroundMode audio for AirPlay Streaming

If this post comes up in your search result, it’s likely you’re facing one of two problems. And, if you’re facing the first problem, then you’re sure to be facing the second one soon. So, which is it for you? Is it:

When streaming a video in my iOS app through AirPlay, video playback stops when the phone sleeps…

Or, is it:

Apple rejected my build for utilizing the audio UIBackgroundMode property…

Either way, then this post can help. And, I’ll waste no more of your time as I’m sure your stressing.

By default Apple stops video playback through AirPlay when the device sleeps. I don’t agree with this, but it’s not a bug. It’s expected. With that said, try streaming a video using Apple’s own Trailers app over AirPlay. Playback continues when the device sleeps, doesn’t it? So, obviously it’s not only possible, but some factions within Apple don’t seem to agree with the default behavior either.

To allow your app to stream videos over AirPlay when the device is asleep you first need to declare within your app’s Info.plist file that your app requires the “audio” UIBackgroundMode. The image below shows how this key/value pair appears when shown with Xcode’s plist viewer (“Required Background Modes”).

If you’re looking at the plist source code directly, then it’s declared like so:


The next thing you have to do is utilize the audio background mode within your app, which just requires setting some AVAudioSession properties when your app launches. Do the following in your app’s AppDelegate’s application:didFinishLaunchingWithOptions method:

#import <AVFoundation/AVFoundation.h>

@implementation YourAppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    // …
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    [audioSession setActive: YES error: nil];
    return YES;

// ..

And, that should be it. You should be good to go. Your app should keep streaming over AirPlay when the device sleeps. Now get that app submitted to Apple, so they can rejected it for utilizing the audio background mode.

Yes, I am serious. The first time I released an app that utilized the audio background mode, Apple rejected it. However, I was able to successfully dispute the rejection, because this is a valid use of the audio background property.

What I recommend to do is be proactive with the review process. Apple allows you to provide notes for the reviewer. Be up front. Let the reviewer know you are using the audio background mode, and let them know why. And, I recommend providing this link to an Apple Technical Q&A article that advices developers to utilize this technique. It even has an “Important” note that states:

The UIBackgroundModes audio key will also allow apps to stream audio or video content in the background using AirPlay.

So, there you go. Be up front, point to that article and you’ll be good to go.

September 28, 2012
iOS 6 Event Edit and Calendar Access

Github Repository: iOS-Event-Edit-Example

An instance of the iOS EKEventStore class provides a reference to the Calendar database. It’s needed if your app plans to access the calendar in anyway.

In iOS 6, Apple introduced a new requirement for an application to gain access to the Calendar through EKEventStore. Basically, you have to ask permission first:

On iOS 6 and later, you must request access to an entity type after the event store is initialized with requestAccessToEntityType:completion: for data to return.

Why the OS can’t ask for permission on behalf of the application is beyond me. All I know is that any code that utilized EKEventStore in an application on iOS 5, won’t work in iOS 6, without some changes.

The purpose of this post is to point you to an example of how to utilize the EKEventEditViewController class to allow your application’s users to add an event to their calendar, relative to an entity within your app. For instance, maybe your app lists exciting activities and events happening in the user’s area. You would probably like to allow them to easily add to their calendar reminders of events they’re interested in.

Beyond showing how easy it is to add this very powerful piece of functionality to your app, the main purpose is to show how to properly support iOS 5 and iOS 6 devices. I’ve added that snippet below, in case that’s all you’re interested in figuring out.

EKEventStore* eventStore = [[EKEventStore alloc] init];

// iOS 6 introduced a requirement where the app must
// explicitly request access to the user's calendar. This
// function is built to support the new iOS 6 requirement,
// as well as earlier versions of the OS.
if([eventStore respondsToSelector:
    @selector(requestAccessToEntityType:completion:)]) {
    // iOS 6 and later
     completion:^(BOOL granted, NSError *error) {
         // If you don't perform your presentation logic on the
         // main thread, the app hangs for 10 - 15 seconds.
         [self performSelectorOnMainThread:
} else {
    // iOS 5
    [self presentEventEditViewControllerWithEventStore:eventStore];

First, you need to check if the EKEventStore instance responds to the method used to request access. If it does, then go ahead an make the request. If it doesn’t, then you can just move on to presenting the instance of EKEventEditViewController.

Once you’ve been granted access, the completion method is called. At that point, you’ll want to call the function that presents EKEventEditViewController.

There’s one important thing to note within the completion method. If you don’t use the performSelectorOnMainThread: method to call your present method, the app will hang for 10 to 15 seconds before displaying the EKEventEditViewController's view. Just something to be aware of.

If you’re interested in the complete example, you can clone or download the code from its repository on github. I hope it’s useful.

iOS 6 UINavigationBar Appearance Customization

In iOS 6, you can still universally customize your app’s navigation bar through the [UINavigationBar appearance] object, as you would expect. However, in iOS 6 the impact is even more universal, impacting views that were previously immune to such customization. For instance, MPMoviePlayerViewController now inherits from the [UINavigationBar appearance] object. In iOS 5 the navigation bar of MPMoviePlayerViewController remains the same whether or not the app has a custom navigation bar appearance. It appears as if it is floating over the playback, which happens to be the preferred visual for my application.

This isn’t a huge deal to address, of course. My app can easily single out that specific view and apply whatever customization of the navigation bar that is required, even nullifying customization so that the default is used, as demonstrated in the following example.

  appearanceWhenContainedIn:[MPMoviePlayerViewController class], nil]

This basically creates a black list in which you can add any classes you don’t want impacted by the custom navigation bar appearance.

There is a problem, however, with the black list approach. If you happen to have YouTube videos available in your application, you may assume that the example above will take care of its navigation bar’s appearance as well. That’s what I assumed, at least. As I found out from this answer on Stackoverflow, the YouTube player is actually an instance of a private class called MPInlineVideoViewController. And since it’s private, that throws the black listing idea out the window.

To be honest, black listing wasn’t a great approach anyways. There could certainly be other views that I’m not thinking about that also shouldn’t be customized. Specifying exactly which views should be customized would be more manageable. A white listing approach would be better in this situation.

In my app, all views that are specifically implemented for the app extend from one super view controller. This allows for custom configurations to be easily applied across the app. This same class can be used as the app’s white list filter, as this example shows.

UIImage* navBg = ...;

  appearanceWhenContainedIn:[SuperViewController class], nil]

It’s a great solution, mainly because it’s simple, but also because it will be obvious when a view that is suppose to have a custom navigation bar appearance doesn’t have it. If it’s missing the navigation bar customization, it’ll be missing a lot of other configurations as well, which will boldly stand out to developers and tester alike.

September 27, 2012
iOS 6 Orientation Handling

Updating an app from iOS 5 to iOS 6 is equivalent to death by 1000 papercuts, though you do get to live afterwards. So, it’s not quite as bad.

The main issue is that there’s several items within the SDK that are deprecated. I don’t even think it’s fair to call them deprecated. Abandoned would be a better description. Usually when something is deprecated, it still functions as expected. This is not turning out to be the case with iOS 6, at least in certain situations.

In order to help others possibly avoid some of these papercuts, I’ll post about any issues I run into, and provide the solutions I come up with. This is the first of those post. Hopefully there won’t be too many to follow.

The Problem

The app has a modal view, a subclass of MPMoviePlayerViewController, that must be locked to landscape orientation. The rest of the app must be locked to portrait orientation.

iOS 5 Solution

In the app’s -Info.plist file, Supported interface orientations should only contain one item, Portrait (bottom home button).

Next, in the modal view controller class that needs orientation locked to landscape, you need to override the - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation method and return YES or NO relative to a boolean check against the interfaceOrientation argument.

Here’s what the function looks like.

- (BOOL)shouldAutorotateToInterfaceOrientation:
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeRight);

Pretty simple stuff, eh?

iOS 6 Solution

In iOS 6, the -(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation method is deprecated, and appears to not be called at all anymore. It’s replaced by a set of methods; -(BOOL)shouldAutorotate and -(NSUInteger)supportedInterfaceOrientations. Both need to be utilized in the solution I came up with. Here are the details.

First, as with the iOS 5 solution, the app’s -Info.plist file still only contains one item, Portrait (bottom home button). Doing this, however, locks the app to portrait orientation, no matter what you specify in your view controllers. The App’s UIApplicationDelegate now needs to get involved.

To make other orientations available throughout the app, you must override -(NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window within the app’s UIApplicationDelegate, and return UIInterfaceOrientationMaskAll:

- (NSUInteger)application:(UIApplication*)application
    return UIInterfaceOrientationMaskAll;

With this current setup, the app is no longer locked at all, free to rotate to all supported orientation. To accomodate this issue, changes need to be made to the rootViewController of the app.

In the app I’m working on, the rootViewController is a UITabBarController, which I happen to already override for other reasons. You’ll need to override your app’s rootViewController class and override -(BOOL)shouldAutorotate, and have it return NO:

// iOS6 support
// ---
- (BOOL)shouldAutorotate
    return NO;

You don’t want to override -(NSUInteger)supportedInterfaceOrientations in this case. I initially assumed you should override this method. Nope. Doing so caused a bug where returning from a modal view resulted in the navigation bar sliding up under the status bar. Oops. It turns out just specifying that you don’t want the rootViewController to autorotate is enough to lock it to portrait orientation.

Finally, in the modal UIViewController you want presented in landscape orientation, you need to override both -(BOOL)shouldAutorotate and -(NSUInteger)supportedInterfaceOrientations:

// Deprecated in iOS6, still needed for iOS5 support.
// ---
- (BOOL)shouldAutorotateToInterfaceOrientation:
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeRight);

// iOS6 support
// ---
- (BOOL)shouldAutorotate
    return NO;

- (NSUInteger)supportedInterfaceOrientations
    return UIInterfaceOrientationMaskLandscape;
// ---

And, that’s pretty much it. You should be good to go. Now on to the next papercut.

September 24, 2012
file is universal (3 slices) but does not contain a(n) armv7s slice

That’s an error message I received today when first attempting to build an app after updating to Xcode 4.5. It’s pretty cryptic, but basically it has to do with one of the third party libraries the app relies on not being compiled for a specific architecture, armv7s.

This is easy enough to temporarily address, until the third party releases a version that does target the specific architecture. There’s actually two solutions. The first is to remove armv7s from the list of ‘Valid Architectures’ in the project’s Build Settings. This doesn’t seem ideal, as explained by this answer on Stackoverflow. But, it’s only suppose to be a temporary solution, and your app will still be supported by the armv7s architecture. You just may not get all the architecture has to offer, which is probably not a huge deal.

The other solution is to set the ‘Build Active Architecture Only’ setting to YES. This is not a complete solution, since if armv7s is your active architecture, you’ll still run into this issue, but it should suffice for most cases.