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:

<key>UIBackgroundModes</key>
<array>
	<string>audio</string>
</array>

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;
}

// ..
@end

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.

July 29, 2013
The Git Add Command's Update Option

The discovery of hunk staging in Git resulted in me spending some time looking through the other options available with Git’s add command.

The only option that jumped out at me was the update command (git add -u). The update command allows you to stage all tracked files for commit, but not any newly created, untracked files.

The situation that I found this command useful for was when deleting a lot of files or directories. The Terminal’s auto-complete shortcuts tend to fail when the file no longer exists, which means the remove command (git rm) can be a bit tedious. The add command’s update option allows you to stage all removed files at once.

It’s not as paradigm shifting as hunk staging, but the add command’s update option can certainly save you a bit of time in the right situation.

July 27, 2013
Git's Hunk / Patch Staging

“Yeah, I think they’re called Chunks,” one of my fellow developers said. I was in the process of moving our iOS frameworks from a Subversion repository over to individual Git repositories, the hierarchy of which I plan to share in a future post.

“Or maybe it’s Hunks,” he continued. We went on to have a lengthy conversation about this horribly named featured of Git. And, if you don’t think “hunks” is all that bad, try having a conversation about it in an office with an open layout. Let me know how many odd looks and raised eyebrows end up pointed your way.

Bad naming and colleague judgment aside, I’m glad this developer turned me on to hunks (see how easy it is for the conversation to get weird). Instead of staging a whole file with multiple changes for different purposes, hunk staging allows developers to choose which changes within the file to stage, and which to leave for later stagings.

To admit, after learning of hunks, I wasn’t convinced of its usefulness. Sure, I could see how it would allow for more granular, descriptive committing practices, which we should certainly strive for. Even with that – maybe due to the name itself, I’m not sure – but I didn’t expect hunks to become a part of my development process. I especially didn’t expect it to happen almost immediately. I certainly didn’t imagine that git add -p would soon become the default way that I stage anything and everything for committing. But, it did. And, it is.

By the way, that command is just how simple the feature is to use from the command line. It basically turns your terminal into a very simple code review application that allows you to make choices about what to stage.

The screen capture below gives you an idea of the presentation (click to expand). As you can see, there’s quite a few options, the main ones in my opinion being:

y - Oh yeah, stage that hunk.
n - Eew, gross. Get that hunk away from me.
s - Split that hunk. Yeah, just like that.
e - If you want a hunking done right, do it yourself.

As I mentioned already, I’m hooked on hunks. Adding a simple code review to the commit process, even if its only a review of your own code, is invaluable. Give it a try. I’m sure you’ll also appreciate the power of hunk staging.

Check out this documentation for more details on staging with patches and hunks.

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
    [eventStore
     requestAccessToEntityType:EKEntityTypeEvent
     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:
          @selector(presentEventEditViewControllerWithEventStore:)
                                withObject:eventStore
                             waitUntilDone:NO];
     }];
} 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.

[[UINavigationBar
  appearanceWhenContainedIn:[MPMoviePlayerViewController class], nil]
 setBackgroundImage:nil
 forBarMetrics:UIBarMetricsDefault];

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 = ...;

[[UINavigationBar
  appearanceWhenContainedIn:[SuperViewController class], nil]
 setBackgroundImage:navBg
 forBarMetrics:UIBarMetricsDefault];

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:
    (UIInterfaceOrientation)interfaceOrientation
{
    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
        supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
    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:
        (UIInterfaceOrientation)interfaceOrientation
{
    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.

June 12, 2012
Googleanalytics

I added Google Analytics to the site today, with the goal of confirming with solid statistical data how few viewers of these pages there actually are.

As with anything that I add to the site, mainly because I won’t bother otherwise, it was super easy to integrate with my Jekyll project. After signing up with Google Analytics, and creating an account of fostah.com, it was just a matter of navigating to the Admin section and pasting the code provided in the Tracking Code tab to the header of my base.html layout file. I chose to go with the Standard tracking code.

That’s all there is to it, another piece of presumed value added with to little to no effort.

May 25, 2012
New Site Feature! Disqus Comments

In the time it takes you to read this post, you could easily integrate comments into a site generated by Jekyll. Disqus makes it that easy. Is it the best solution? I can’t answer that. There might even be an easier solution, though my initial research, and my experience thus far, makes that hard for me to believe. All that matters is it took me no more than ten minutes to get setup, and brought a ton of value to my site. The comment related functionality that it provides – commenting, like / dislike, subscribing via email and RSS – is way beyond anything that I would be willing to integrate myself. It’s a really nice utility, and they did a great job making it easy for sites to adopt it, which should always be priority number one if you’re trying to grow a community.

With that, here’s all you have to do to get your site setup… Actually, just read this blog post from Eric Tang. There’s better uses for my time than covering something that someone else has already documented so well.

May 22, 2012
Note on Highslide - Override hs.graphicsDir

After posting that I had a cool new thumbnail viewer, that was super easy to use, I ran into an issue. The viewer wasn’t working within sub-directories, which is a big deal considering that every post generated by Jekyll gets dumped into a sub-directory.

The issue turned out to be that, by default, the Highslide API assumes to be in the same directory as the HTML file attempting to use it. When this is not the case, it breaks. Though the solution took some digging, and isn’t obvious at all, it is pretty easy to fix. You just need to override hs.graphicsDir to point to the actual location of the API. Here’s a snippet:

<script type="text/javascript" src="/highslide/highslide.js"></script>
<script type="text/javascript">
    hs.graphicsDir = '/highslide/graphics/';
</script>

So much for being plug-and-play easy. I still think it was a relatively painless setup and will be a nice addition to the site. I look forward to utilizing it.