Tuesday, December 16, 2008

PRI app

Our latest app, made for "Public Radio International (PRI) is now available in the iTunes app store here.

NOTE: this is published under PRI's developer account, but you'll see Stormy Production credits on the "info" screen of the app. PRI provided all the artwork. Stormy Productions just did all the programming.

Friday, December 12, 2008

When is an error not an error?

I got this funny message today when trying to log onto Apple's web site for managing iPhone apps.



Gotta love this method of handling errors. If we deny it happened, it's not a problem, right?

Thursday, December 4, 2008

MPVolumeView Demo Project

Several people have asked for clarification on using the MPVolumeView, so I've put together a full XCode demo project to show off its usage and features.

Note: In order for the slider to respond to the volume buttons, you need to be playing audio. Otherwise you won't see anything happen.

Here's a link to the project: MPVolumeView Demo.

I included some extra features in the code I hadn't mentioned previously, such as how to change the color of portions of the slider, add icons for "soft" and "loud" ends of the slider, etc.

Sunday, November 23, 2008

The new negative bias in iTune App ratings

About a month or two ago, Apple did a great thing with the iTunes App store review process. They required that reviews had to be written by people who actually downloaded the app. This eliminated a bunch of the "noise" reviews where it seemed you had people with free time on their hands and nothing better to do that post pointless reviews that said things like, "Is this app good? If so, click yes.", or those where someone wrote something like, "This app is so stupid I'm not even gonna try it!" and then gave it a 1 star review.

With the 2.2 iPhone OS update, Apple made another change to the review process. When you delete an app off your iPhone, an alert box pops up asking you to give a quick rating of the app - no text description, just a 1 to 5 star rating.



It's a clever idea, but it's one that is going to give a heavy bias toward low star ratings. If someone is deleting the app, odds are they didn't find the app useful, so odds are they are going to give it a low rating. Now the problem is, all the people who really like the app continue to keep it on their phone, so they'll never give it a positive rating unless they return to iTunes to specifically write a review.

I've already noticed this bias showing up with my apps. Over the past few days since the 2.2 OS update has been released, I noticed the average rating of my apps steadily going down while at the same time, all the reviews with a text description were pretty much all positive 4 and 5 star reviews. Since the apps I've been writing are targeting very specific audiences, someone who downloads the app just because they see it's a new free app will most likely not be interested in it, so will tend to delete the app and then give it a low rating when prompted.

Hopefully Apple will reconsider the idea of prompting for app reviews whenever the app is removed. Or maybe they could add the idea of prompting the user to write a review of an app they after they use it x number of times.

Friday, November 21, 2008

Radio Paradise

I have yet another "radio player" app in the iTunes App store. It's called Radio Paradise and, as the name implies, allows listening to music from the Internet radio station, radioparadise.com. I've been a listener for years, so I asked the owner of the station for permission to make an official iPhone app for the station.

I also used the app as an opportunity to test out a few additional features. The app will detect the type of connection you have (either cellular or WiFi) and will automatically use either a 64K or 128K stream. I originally had buttons in the app to allow the user to select the stream quality, however Apple rejected that idea because it would be possible to a user to select a 128K stream over cellular and in Apple's judgement that consumed too much bandwidth. I could have restrict the choice when on a cellular network, but for now I just simplified it and the app makes the decision for you.

The second feature this app uses is the inclusion of album artwork for the current track playing. Radio Paradise embeds in the audio stream the URL for the current artwork. So, it was simply a matter of having my app fetch the image as the song is playing.



The third feature is the implementation of a mini web browser. This will allow the user to visit the Radio Paradise web site while still listening to the music. Apple doesn't allow third party apps from running in the background, so normally if you were to launch Safari, the radio player is forced to exit. The mini browser also includes a button to allow the user to launch Safari if they really want to.



This app took by far the longest yet for gaining Apple's approval. It was almost a month! The biggest issue I kept running into was Apple claiming my app didn't throw up a message box informing the user when there was no network. I kept testing it and it always worked for me. After about 4 re-submission attempts, I finally discovered Apple was testing using the 2.0 iPhone OS and I was testing with 2.1. (It sure would have been helpful had Apple actually told me what OS they were testing with. I even asked, but never got an answer.) It turns out the "reachability" flags returned are slightly different between 2.0 and 2.1. Once I finally discovered that issue the app was quickly approved ("quickly" in Apple-speak means a few days).

Oh, and check out the size of this app - all those features and it's only 130K! Yes, K! I put my code on a diet! (Actually, it's because Apple's libraries, or frameworks, do much of the work and the frameworks are already installed on the phone.)

Monday, November 17, 2008

Building static libraries with the iPhone SDK

I have a client that wants to use my iPhone Radio Player code for their own radio station, however they want to be able to submit the app to iTunes under their own developer account. The gotcha is they are not purchasing my source code - they want to pay a smaller fee and just have me supply them with a binary image which they can then submit to Apple.

I do not have access to my client's Apple developer account, so I needed a way to supply the client with an Xcode project that includes a skeleton of source code along with a binary library that includes all the custom radio player functionality I have developed. This will allow the client to build the iPhone app and submit it via their account without giving the client access to my source code. Normally, one would probably create a custom framework to wrap up the custom radio player code, but in the case of the iPhone, this is not an option. Apple forbids the use of external frameworks or dynamic library linking in an iPhone app. Instead, I needed to create a static library containing my radio player code.

Fortunately, creating a static library for the iPhone turned out to be a fairly simple, but not at all well documented, process. If you already have an Xcode project, you can easily turn any portion of the source code into a static library. Here's how.

1) In your Xcode project, locate the "Targets" section under the "Groups & Files" sidebar. Right click on "Targets" and choose to add a new target:



Then, in the following dialog box that appears, choose a Cocoa Touch "Static Library" target:



Name the library whatever you'd like. In this example, I named the library "TestStaticLib."

2) Now, assign source code files to your static library. You can simply drag existing files from your project list. Note: don't add the .h files.



3) Then, remove those same source code files from your app target:



4) Add the new static library to your target app via the "General" settings for the target:



5) Edit your target app's linking settings to add "-ObjC" to the "Other Linker Flags". This is only required if your static library defines Objective-C classes that your app is going to reference:



6) Build your new static library for all the SDK targets (such as simulator, iPhone Device, etc.).

7) Build your app. It should now be referencing the static library rather than compiling from the original source code.

If you need to then distribute the app to a client, you can simply remove the source code for the static library and the client will still be able to build the app without having access to the original source code.

This technique might also be useful if you have some shared code you'll use in multiple applications, but don't want to recompile the shared code each time you make a new app that references it. Simply copy the shared library binary images to a specific location in your development environment and then have all your apps link with that code.

Monday, October 20, 2008

Classical Philippines Radio

I am happy to announce that the Classical Philippines Radio app is now available for download in the iTunes store.

In their own words, Classical Philippines Radio is "the only station in the planet that broadcast the forgotten melodies of the Philippines. Classical Philippines Radio (Classical PH) plays unique blend of traditional spanish guitar, kundiman, harana and other regional music." Read more about the station here.

While working on this app I had to do a lot of testing, which meant a lot of listening to classical Filipino music. I must admit, I really found myself enjoying most of the tunes. Some of it even reminded me of traditional Japanese music, oddly enough.

Friday, October 3, 2008

WRNI feedback

The WRNI app has been available now as a free download in the iTunes store for 2 weeks. So far, it has received very positive feedback - all 6 reviews in the iTunes store gave it 5 stars!

My favorite quote so far about the app comes from a Newspaper Association of America blog post: "Poised like a fragile little flower among heavily plowed fields is WRNI radio -- NPR from Rhode Island." The app was only mentioned in passing, and this sentence was the complete mention, but still, it was nice to get the mention.

As for usage, over the past 2 weeks the app has been downloaded about 5000 times - not bad for something specifically designed for a Rhode Island public radio audience.

Wednesday, October 1, 2008

iPhone NDA finally dropped!

Good news today for all iPhone developers. Apple has officially dropped the NDA!

Here's a quote from their iPhone developer web site:



We have decided to drop the non-disclosure agreement (NDA) for released iPhone software.

We put the NDA in place because the iPhone OS includes many Apple inventions and innovations that we would like to protect, so that others don’t steal our work. It has happened before. While we have filed for hundreds of patents on iPhone technology, the NDA added yet another level of protection. We put it in place as one more way to help protect the iPhone from being ripped off by others.

However, the NDA has created too much of a burden on developers, authors and others interested in helping further the iPhone’s success, so we are dropping it for released software. Developers will receive a new agreement without an NDA covering released software within a week or so. Please note that unreleased software and features will remain under NDA until they are released.

Thanks to everyone who provided us constructive feedback on this matter.


I look forward to now being able to be a bit more descriptive in future posts regarding iPhone development.

Wednesday, September 17, 2008

WRNI Radio - now available in the iTunes App Store!

I'm happy to announce that WRNI Radio, our first iPhone application, is now available as a free download in the iTunes App Store.

WRNI is Rhode Island's only NPR radio station. The WRNI Radio app allows one to listen to the radio station live over the Internet via the iPhone Edge, 3G, of WiFi connection. This app was made as a free public work for WRNI in partnership with Glad Works, the ad agency responsible for WRNI's web site.

SynergyKM Mouse Scrolling Fix

SynergyKM is a very handy little Mac utility that allows you to share one keyboard and mouse across multiple computers. It's based on the utility called Synergy, which runs on many different computer platforms (Mac, Windows, Linux, etc.). So, if you are sitting at a desk with multiple monitors for various systems, you can choose one system as the master and use its sole keyboard and mouse.

Here's an example of using SynergyKM to designate the layout of the various monitors.



Once you define the layout of the various monitors and have Synergy running on all the machines, you can simply move the mouse off of one screen and it will automatically appear on the next screen. You can even share the cut-and-paste buffer across the machines.

All this is very nice, but SynergyKM hasn't been updated since 2006. One problem I've discovered is if you are on a Mac and are using a scroll wheel or trackpad to perform scrolling on a different monitor, the scrolling is super crazy fast.

Fortunately, Synergy is an open source project, so I was able to figure out the problem. SynergyKM is using a deprecated function for generating mouse scrolling events. The problem - for those interested - was the older method for generating a mouse scrolling event was based on scrolling numbers of "lines" of text. However, the values being uses for the scrolling deltas were based on pixel differences, not line differences. The newer method (CGEventCreateScrollWheelEvent) for generating scrolling events can be configured to use either line or pixel counts.

It's only a few lines of code that need changing. Specifically, edit the COSXScreen.cpp file, and search for the function COSXScreen::fakeMouseWheel(). Replace it with this:


void
COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
{
CGEventRef my_event;

if (xDelta != 0 || yDelta != 0) {
my_event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, mapScrollWheelFromSynergy(yDelta), -mapScrollWheelFromSynergy(xDelta));
CGEventPost(kCGHIDEventTap, my_event);
CFRelease(my_event);
//CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta), -mapScrollWheelFromSynergy(xDelta));
}
}


Also, look for the function COSXScreen::mapScrollWheelFromSynergy() and replace it with this:


SInt32
COSXScreen::mapScrollWheelFromSynergy(SInt32 x) const
{
// use server's acceleration with a little boost since other platforms
// take one wheel step as a larger step than the mac does.
// return static_cast(3.0 * x / 120.0);

// No boost in acceleration
return static_cast(x / 120.0);
}


Then, just recompile the project and take the new synergyc file generated and replace the existing synergyc executable on all your client machines. If you are using SynergyKM, synergyc will be located in:

/Library/PreferencePanes/SynergyKM.prefPane/Contents/Resources/Synergyd.app/Contents/Resources/synergyc

(NOTE: you can not replace the file if Synergy is currently running.)

If you'd prefer to not have to recompile the file yourself, you can get my compiled version here. It's about 370K in size (compressed).

Thursday, September 11, 2008

iPhone Music Sleep Timer tip

Here's a very simple tip for using the iPhone and iPod Touch on a "sleep" timer when listening to music.

Open the Clock app and choose the Timer feature. Choose how long you'd like the device to stay on, and for the "When Timer Ends" setting, choose "Sleep iPod/iPhone". This will cause whatever app is running when the timer occurs to stop, even if the display of the iPhone/iPod is already shut off. It's quite handy if you are listening to music or using a third party app that provides streaming media and you want the iPhone/iPod to auto quit after a certain period of time.

I've seen several cases in iPhone forums where people have been requesting this feature in various apps, so I assume it must not be a well known that this feature is already built into the iPhone / iPod Touch.

Wednesday, September 10, 2008

Controlling iPhone UI elements from a background thread

If you are trying to control UI elements (such as setting a UILabel's text) from a background thread, it will not work using the normal methods provided by the UI class.

For example, suppose you want to change the text of a UILabel. Normally, you'd just call:

[myLabel setText:@"this is my text"];


However, this won't work if you make that call from a background thread. The problem is the main thread needs to perform the UI updates. (Cocoa Fundamentals Guide, page 134: "All UIKit objects should be used on the main thread only.")

Fortunately, Apple provides a method to call an object's selector, but have the processing occur in the main thread. This is done via the performSelectorOnMainThread method. Here's how one would set a UILabel object's text from a background thread:

[myLabel performSelectorOnMainThread : @ selector(setText: ) withObject:@"this is my text" waitUntilDone:YES];


This is also very handy to know if you are trying to control the status of a UIActivityIndicatorView from within a background thread. Since the UIActivityIndicatorView is something that is normally used to indicate a background task is busy doing something, it's nice if you can control it's visible status via the background thread. Here's how you would do it:

[busyIcon performSelectorOnMainThread : @ selector(stopAnimating ) withObject:nil waitUntilDone:YES];


It's important to note that unlike the call when using the setText: selector, in this case you must omit the colon (:) in the selector name. If you don't your app will throw an exception when this message is passed to your object.

Monday, September 8, 2008

Proper usage of the MPVolumeView class

IMPORTANT EDIT! 11/14/09
This post is no longer relevant regarding the details on keeping the volume slider in sync with the system volume. The undocumented call is no longer needed. In fact, if you use the call to "_updateVolumeFromAVSystemController", Apple will now reject your app during the review process.

I leave this post here for historical reference, but it is no longer relevant and should NOT be used!!

When dealing with audio in an app on the iPhone and iPod Touch, it is sometimes useful to have a volume control slider displayed onscreen. Apple supplies a class called MPVolumeView which is supposed to perform this function, however the documentation on it is quite poor.

Specifically, the MPVolumeView class provides a slider, and as you drag it, it adjusts the system volume. However, what the class doesn't provide is a method to automatically have the slider move in response to someone adjusting the volume via the volume buttons on the side of the iPhone. So, you can set the volume with the slider, but then if you adjust the volume with the buttons on the side of the phone, the slider is not reflecting the proper volume. Many iPhone apps exhibit this problem and personally I find it annoying.

It turns out, the reason so many apps behave this way is because there's not an easy documented method to query the system volume (at least not one I could find!). The main method for querying the system volume is in the Celestial private framework and you aren't supposed to use private frameworks in released apps.

Fortunately, someone discovered there is a system notifications that is generated whenever the volume changes. Also, the MPVolumeView slider can respond to a message called "_updateVolumeFromAVSystemController" which will cause it to adjust it's position based on the current system volume level.

So, given the two pieces of information, one can register a notification process to automatically adjust the MPVolumeView slider whenever the volume is changed using the buttons on the side of the phone.

Here's how it can be done - check out the code at pastebin. I didn't write this code, I just happened to run into it when searching for better information about MPVolumeView. There isn't really much in the way of comments in the code, but hopefully it's easy enough to follow.

UPDATE: the pastebin code expired, so here's my own version of the code. It was based on the pastebin code. I don't include the declarations from the .h file, but it should be pretty easy to figure out that part. You'll want to put this in the viewDidLoad method for whatever view will be holding the MPVolumeView.


- (void)viewDidLoad {

// create a frame to hold the MPVolumeView
CGRect frame = volumeViewHolder.bounds; // CGRectMake(0, 5, 180, 0);
volumeView = [[[MPVolumeView alloc] initWithFrame:frame] autorelease];
[volumeView sizeToFit];
[volumeViewHolder addSubview:volumeView];

// Find the volume view slider - we'll need to reference it in volumeChanged:
for (UIView *view in [volumeView subviews]){
if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) {
volumeViewSlider = view;
}
}

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
}


- (void) volumeChanged:(NSNotification *)notify
{
//NSLog(@"volume changed");

// NOTE::: Do NOT use this call. It is no longer necessary and Apple will reject
// your app if you use it!

[volumeViewSlider _updateVolumeFromAVSystemController];
}


NOTE: when you compile this, you will get a warning that says "'UIView' may not respond to '- updateVolumeFromAVSSystemController'" but it can be safely ignored. The compiler is complaining that it can't verify volumeViewSlider is an object that can receive that message. This is some of the magic that makes Objective-C so powerful. You can send messages to objects even though the compiler doesn't know if the object supports the message. It's also an easy way to shoot yourself in the foot if you aren't certain of what you are doing.

In this instance, we know the volumeViewSlider object does support that message since we hand picked it by recursively searching the MPVolumeView until we got to the MPVolumeSlider piece of the object.

Thanks go out to the anonymous person who posted that original snippet of pastebin code. It was exactly what I was looking for.

[Edit: I must emphasize, the above code should NOT be used!! Apple will now reject an app which uses it. There's a demo project showing how to use some other features of the MPVolumeView class here.

Sunday, September 7, 2008

Possible first iPhone app

A friend of mine works for a local web design company. This past Friday (3 days ago) he asked whether I might be able to make an iPhone app to complement a client's new web site. The client is a radio station, so he wanted an app which would be able to play the radio station's Shoutcast stream. I had a bit of free time this weekend and was able to spend a large chunk of time on it.

Today I'm happy to say I have now written an iPhone app that supports streaming media over WiFi, Edge, and 3G. It still need s a bit of polish - mainly to handle a few random error conditions (related to the unreliability of AudioFileStreamParseBytes() to properly parse the mp3 frame headers) - but the bulk of the work is done.

The biggest struggle was wading through Apple's documentation on Audio File Streams, Network Streams, and the like. The documentation is fine as a reference guide for specific function definitions, but it's really very very weak on examples. And that's the challenging part - tying it all together - so examples would have made it a much easier task.

Hopefully the app will be sent to the iTunes Store for approval within the week. At least, I hope there won't be any technical reasons it shouldn't be. I don't yet know about any bureaucratic ones, though. Maybe there might be some non technical hold-ups related to client approval and what-not.

Tuesday, September 2, 2008

Squash those bugs!

I just had to laugh when reading this sentence in the iPhone OS Programming Guide:

Xcode provides several debugging environments you can use to find and squash bugs in your code

So, with Xcode, unlike other IDEs, we don't just debug code. We get to squash bugs! I never really thought of debugging as such an aggressive activity.

If we squash bugs in our code, won't their guts still be splattered among the other lines of code, though?

This whole thing is just a really poor analogy...

Friday, August 29, 2008

The most useful iPhone app I can't release

PLEASE NOTE: This technique described here was originally done for iOS 2.0. It no longer works under iOS 4. If you want to turn on Bluetooth programmatically, you can do so using the iPhone GameKit SDK. However, this does not allow you to turn Bluetooth off. It only provides a method to turn it on.


I have long since abandoned using private APIs for any of my projects, so I unfortunately can not provide any advice on how you might be able to toggle Bluetooth under iOS 4.


Here's the most simple, yet useful application I've made for the iPhone. You click on the Bluetooth® icon and Bluetooth is toggled on or off. That's it. The app even immediately exits after you click on it.

This is much simpler than how you normally have to turn Bluetooth on or off via the iPhone settings. Compare. Here's a screen shot of my app. One tap and Bluetooth turns on or off.



Now here are the four screen shots of turning Bluetooth on or off via the iPhone settings menu.









Now, counting the final click of the home button, that's 5 clicks vs. just one with my app. Granted, it's a very simple app and many other people have suggested doing this same thing. However, what is frustrating is there's no way anyone can legally give this app to anyone. The Apple iPhone SDK specifically prohibits using private framework APIs, and unfortunately, to access the Bluetooth functionality requires using the private frameworks.

Hopefully some day soon Apple will change this policy. There's a lot more useful things that can be done in these apps if Apple would just allow developers to use the private frameworks. I currently have an outstanding query into Apple's developer support regarding this issue (I get two free tech support queries with my developer license) but it's been almost a month now and Apple still hasn't responded.

Interestingly, as of today, there are actually several applications in the iTunes App Store which do make use of the private frameworks - in direct violation of the SDK license agreement. It appears the people in charge of approving apps either have no way to automatically scan an app for private framework API calls or they are turning a blind eye on it since Apple gets 30% cut of all money these applications earn.

As for me, I'm currently not willing to risk my iPhone developer license by knowingly submitting an app that is in violation of this policy.


The Bluetooth® word mark and logos are registered
trademarks owned by Bluetooth SIG, Inc. and any use of
such marks by Stormy Productions is under license. Other
trademarks and trade names are those of their respective
owners.

Changing the Font in iPhone Notes

I just accidentally discovered a way to disable the default "handwritten" style font (officially called Marker Felt) used in the iPhone Notes app. This can be done on a note-by-note basis. Here's a screen shot illustrating the differences between the two fonts:


It's quite simple to get this behavior. First (and you only need to do this once) go into your main iPhone settings and enable an international keyboard. You can do it via the Keyboard settings (as shown below) or via the International settings. Choose to add a new keyboard, specifically one with Japanese support.


Then, when you are editing a note, press the world icon next to the space bar:



This will toggle your keyboard mode amongst the keyboard settings you chose earlier. When you get to the Japanese keyboard (as can be seen by the funky space bar) simply press any character on the keyboard and your current note will instantly revert to a normal non-handwritten font. And, as an added benefit, you can type Japanese characters.



Even if you delete the international characters from the note, it will remain in this font. I'm assuming the iPhone must be saving either font or character set information on each individual note. Too bad there's no way to select a specific font to make each note stand out from others on the main list.

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