Thursday, April 22, 2010

Tunemark Radio 1.8, now available

Tunemark Radio is now available for both iPhone and iPad on iTunes. There’s still just one app to download but it is now optimized for iPhone and also the larger iPad screen.

This app is designed to be a FREE alternative for playing Internet radio stations, without the annoying ad banners. If you have any suggestions for improvements to this app, please contact Stormy Productions and pass along your thoughts. You can read a full description of the app here.

This latest version significantly improves the “Buy on iTunes” feature. When you now choose that option, you will be presented with a list of matching songs currently available on iTunes. This way, you can verify the song you want to buy is going to be available before you launch iTunes and exit the app. There’s no sense stopping the music and launching iTunes only to be disappointed to find the song isn’t for sale there.

PLEASE NOTE: this most recent version of Tunemark Radio was required by Apple to remove the feature for playing 128K and higher bitrate streams over the 3G and Edge networks. This was not something we wanted to do, but Apple would not approve this update without the bandwidth limit for cellular usage. I’m sure many of you will be disappointed by this change, but please understand it was a requirement from Apple. We will continue to investigate whether Apple may allow this feature again some time in the future, and if the policy is changed, we will update the app accordingly.

Thanks for all the comments, suggestions, and even the complaints. All the feedback is helpful to continuously improve this app.

Tuesday, April 20, 2010

More Thoughts on iPad Development Decisions

I currently have two iPad apps available in iTunes. One, Artificial Life HD, is an iPad specific app which is a modified version of my Artificial Life iPhone app. I initially struggled with the decision of whether to release the iPad version of the app as a universal app, compatible with both iPhone and iPad, or as two separate apps. After doing some initial work in pursuing a universal app design, I decided it would be better to have an iPad-specific version.

There were mainly 3 reasons for this decision.

First, the iPhone app is currently slightly under 20 meg in size. 20 meg is an important number for iPhone apps. If an app is larger than 20 meg, Apple will not allow the app to downloaded via the cellular network. This has the potential for lost sales. Adding support for the iPad required adding higher resolution artwork and some iPad-specific code additions which made it very likely that the app would be larger than 20 meg.

Second, due to some early decisions I had made in the design of the the iPhone app, it was going to be very tedious to make the app run-time compatible with both iPad and iPhone. It was definitely possible, but a lot of time could be saved by making the iPad changes compile-time rather than run-time.

Third, while both the iPad and iPhone versions are nearly identical in functionality right now, I hope to be adding features to the iPad version which take better advantage of the larger screen area. Having the apps as two separate and distinct versions in the app store made the most sense.

My second iPad app is a universal app - Tunemark Radio. Tunemark Radio is a free streaming radio player which supports the full SHOUTcast directory as well as adding custom streams via pls, m3u or other custom URL formats. It supports both mp3 and AAC+ audio formats. It also has a few unique features such as support for custom wall-paper and a built-in fullscreen visualizer which reacts to the music. In the case of this app, a universal app made the most sense. The app binary is quite small (about 1 meg) and there will be little change in functionality between the two apps. The iPad version required a new layout, which meant new iPad specific view controllers, but the code differences were minor and were easy to handle during run-time.

In addition to becoming more familiar with the iPad vs. iPhone differences while adding iPad support for these apps, I learned a few important details during the app submission process.

1) When using a UIPopoverController, it is very important that you test that the popover continues to point to the correct section of the iPad screen as the screen is rotated to various orientations. This means testing rotating the screen while the popover is being displayed, not just testing the popover’s initial appearance in each orientation. I had the Artificial Life HD app rejected because one of my popovers did not redisplay itself with the arrow pointing to the proper button after the screen was rotated. I had never thought to test that behavior and has falsely assumed it would be handled automatically via the iPhone SDK internal logic. This is not the case.

In order to have your popover redisplayed properly when the screen is rotated, you must first save all the popover attributes to some state variables. This includes saving the sender which initiated the popover, the containing view, the popover arrow position, and a pointer to the popover itself. Here’s a snippet of code displaying a popover and saving some of the creation details:

                Class classPopoverController = NSClassFromString(@"UIPopoverController");
                
id aPopover = [[classPopoverController alloc] initWithContentViewController:navController];
                
activePopover = aPopover;
                
activePopoverSender = sender;
                
activePopoverView = sidebarView;        
                
activePopoverArrowDirection = UIPopoverArrowDirectionLeft;
                [aPopover
presentPopoverFromRect:[activePopoverSender frame] inView:activePopoverView permittedArrowDirections:activePopoverArrowDirection animated:YES];

I suggest saving the sender rather than the sender’s frame because when the screen orientation changes, the sender’s frame could change position and size, so you want to be able to get the current frame size and position, not the size and position when the popover was first displayed.

Then, you need to add some code in the view controller’s didRotateFromInterfaceOrientation to check for the presence of a popover, and if one is active, redisplay it. Here’s an example:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{        
        
if (activePopover){
                [
activePopover presentPopoverFromRect:[activePopoverSender frame] inView:activePopoverView permittedArrowDirections:activePopoverArrowDirection animated:NO];
        }
}

Even though the popover is already being displayed, it’s OK to call presentPopoverFromRect: again. Doing this has the effect of simply redisplaying the existing popover at the proper new orientation. If you don’t do this, sometimes the popover will rotate properly, but in most cases it will not and if Apple notices this, your app will be rejected.

2) On the iPad, Apple strongly suggests supporting all orientations. If you do support all orientations, then it is critical that all views display at the proper current orientation.

This seems like an obvious point, however if you are converting an app from an iPhone-specific project and you have lots of view controllers, it’s easy to overlook adjusting a view controller so it supports all the orientations. As a result, you need to make sure you test every single view at every single orientation while testing the app. It may not be immediately obvious if some buried menu item is only displaying at one orientation. It’s an easy enough fix if you do discover something comes out wrong - just find the view controller responsible and make sure shouldAutorotateToInterfaceOrientation returns the proper value for iPad.

If you are doing this in a universal app and the iPhone version only supports one orientation, here’s an easy way to handle it:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
        if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad){
                
return (interfaceOrientation == UIInterfaceOrientationPortrait);
        }
else{
                
return YES;
        }
}

3) If using a UIPicker (such as date picker) on iPad, it must appear within a UIPopoverController. This is mentioned in the HIG, but it's just one sentence and is easy to overlook. If you don't display a picker within a popover, Apple will reject your iPad app. I'm not sure why this is a requirement, but rules are rules, as they say.

These are some fairly simple examples, but may be easy to overlook the first time you create an iPad app from an existing iPhone project. Hopefully they can help someone else avoid time lost in resubmitting an app.

Saturday, April 17, 2010

"The iPad isn't a computer"

You aren't buying a computer when you buy an iPad, you are buying a 16GB Walmart store shelf that fits on your lap - complete with all the supplier beat downs, slotting fees, and exclusive deals that go with it - and Apple got you to pay for the building.
-Jim Stogdill, O'Reilly Radar

The above is just a short quote from a very thought provoking article regarding the closed ecosystem of Apple's new iPad. It's got me feeling a bit conflicted since I agree most of what he wrote, I just I hadn't thought of it from that perspective before.

As I've mentioned in another forum, I can understand the idea of having to lock down the iPhone for potential adverse affects on the cell network, but the iPad is another story.

All content copyright © 2009  Brian Stormont, unless otherwise noted.   All rights reserved.