Archive

Author Archive

On App Store Download Statistics

January 15th, 2009 Comments off

It seems that nobody wants to release statistics about how their apps are doing on the iTunes App Store, yet a lot of people want to know how well various apps are doing. I’ve seen a couple of examples of income and download numbers, but I’d still like to see more. So, I’m putting my money where my mouth is and publishing the download statistics for my first app, Prayer Book.

For some background about the app, you may want to check out my earlier post. After submitting Prayer Book to Apple on September 24th, they approved it for sale on October 2nd. It was immediately placed on the App Store, but it was never at the top of the recently released apps list, because I had set the release date to be September 24th. It wasn’t until 3 months later that I learned that your order in the App Store has nothing to do with the date Apple approves your app.

Prayer Book Monthly Download Stats

Up until the writing this post, there have been 9,448 downloads of Prayer Book, with an average of 91 downloads a day. Not bad I think. December was a particularly interesting month, so I’ll share some more detailed numbers there.

Prayer Book December Downloads

December was shaping up to be a pretty regular month, and then on the 22nd, there was a spike to 118 downloads, then a drop, and then another spike that lasted from the 25th-29th.

I only have data up to the 13th of January included in the monthly table above, but that’s already further ahead in downloads than the same time in December (941 downloads from December 1-13). Granted, December will probably still be better than January because of the Christmas activity.

Have any links to download numbers for other apps? Please share them in the comments. :-)

APXML: NSXMLDocument ‘substitute’ for iPhone/iPod Touch

January 14th, 2009 3 comments

After spending some time working on Jabeh, my latest creation for iPhone/iPod Touch, I’m taking some time to dump a little learned knowledge into my blog.

In my first app, my XML needs weren’t that great, so putting up with the lack of NSXMLDocument in the iPhone SDK was not a big deal. However, in Jabeh I was changing the XML format so often and using so much of it for my network communication creating delegates for NSXMLParser quickly became a huge time sink. After a little hacking, I came up with APXML to solve my DOM problem. It’s not a perfect implementation of the W3C XML 1.0 standard, but it’s close enough for a lot of usage. One particular shortcoming is its lack of support for namespaces but maybe somebody else can add that support. If you just want to jump in and start using it (LGPL license), you can get the code from github:

http://github.com/arashpayan/apxml/

Most of my XML manipulation experience has been with various Java libraries (org.w3c.dom interface, JDOM and XOM), and the only one that I enjoyed using was XOM, because of its simplicity and licensing. Almost all of my design decisions were based on how XOM does things.

Let’s say we want to represent the following XML document in memory using APXML:

<books>
    <book id="1" author="Michael Pollan">The Omnivore’s Dilemma</book>
    <book id="2" author="Foley, van Dam, Feiner, Hughes">Computer Graphics: Principles and Practices</book>
</books>

In code, we do the following:

#import "APXML.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // create the document with it’s root element
    APDocument *doc = [[APDocument alloc] initWithRootElement:[APElement elementWithName:@"books"]];
    APElement *rootElement = [doc rootElement]; // retrieves same element we created the line above
    
    // create the first book entry (The Omnivore’s Dilemma)
    APElement *book1 = [APElement elementWithName:@"book"];
    [book1 addAttributeNamed:@"id" withValue:@"1"];
    [book1 addAttributeNamed:@"author" withValue:@"Michael Pollan"];
    [book1 appendValue:@"The Omnivore's Dilemma"];
    [rootElement addChild:book1];
    
    // create the second book entry (Computer Graphics)
    APElement *book2 = [APElement elementWithName:@"book"];
    [book2 addAttributeNamed:@"id" withValue:@"2"];
    [book2 addAttributeNamed:@"author" withValue:@"Foley, van Dam, Feiner, Hughes"];
    [rootElement addChild:book2];
}

@end

And if we want to convert the document to an NSString*, we use one of the two methods in APDocument:

    // converts the xml to a compact string with no newlines or tabs (good for production)
    NSString *xml = [doc xml];

or

    // converts the xml to an easy to read string with newlines and tabs (good for debugging)
    NSString *prettyXML = [doc prettyXML];

Often times when I’m working with XML, I like to see what the current element contains, so for added convenience, you can obtain an XML string containing the element you’re working with, its attributes and all its children directly from the APElement by calling one of two methods:

- (NSString*)prettyXML:(int)tabs;
- (NSString*)xml;

Now for the best part of the library, which is the ability to read in XML and represent it in APXML. All you have to execute is one simple line:

    APDocument *doc = [APDocument documentWithXMLString:xmlString];

Hopefully this will be helpful to other developers out there. I may post another article soon if anybody has some questions.

UPDATE Sep 5, 2009: Here’s an example that demonstrates traversing the XML document.

    APElement *rootElement = [doc rootElement];
    NSLog(@"Root Element Name: %@", rootElement.name);
    
    // get all the child elements (each book)
    NSArray *childElements = [rootElement childElements];
    
    for (APElement *child in childElements)
    {
        // returns the tag name
        NSLog(@"Child Name: %@", child.name);
        
        // reads the attribute named ‘author’
        NSLog(@"Author: %@", [child valueForAttributeNamed:@"author"]);
        
        // returns the text content of the element
        NSLog(@"Title: %@", [child value]);
    }

In the console you’ll see (I’ve removed the NSLog markup):

Root Element Name: books
Child Name: book
Author: Michael Pollan
Title: The Omnivore’s Dilemma
Child Name: book
Author: Foley, van Dam, Feiner, Hughes
Title: Computer Graphics: Principles and Practices

Prayer Book for iPhone and iPod Touch

October 2nd, 2008 13 comments

My first iPhone app has just been posted to the iTunes app store. It’s called Prayer Book and it contains 231 English prayers from the Writings of the Bahá’í Faith. They’re organized by categories and you can bookmark your favorite prayers for easy access as well. I’ll be updating the program over time with new features and prayer translations, and eventually I’d like to have all the Writings of the Bahá’í Faith in there.

It’s available for free on the iTunes store, so go ahead and take it for a spin.

Installing iPhone firmware 2.1

September 14th, 2008 3 comments

Here’s a step by step on how to install the new 2.1 firmware using PwnageTool. I’m assuming that you’ve alread Pwned your phone before when you follow this tutorial. If you haven’t, the steps at the end may be a little different for you.

  1. Update to iTunes 8 and restart
  2. After the reboot, plug in your phone, then open up iTunes. It will ask if you want to update your phone. Click ‘Download only’. If you accidentally clicked cancel, just click on your phone in the iTunes sidebar, then click ‘Update’ to get the latest firmware. Then once the download starts, unplug your iPhone from the USB port.
  3. Once the download finishes, close iTunes
  4. Open up the ‘Activity Monitor’ app from /Applications/Utilities. Find the ‘iTunes Helper’ in the list of running programs and kill it (the red button at the top left).
  5. Set your phone’s auto-lock to never (Settings->General->Auto Lock->Never)
  6. After downloading PwnageTool and copying it to your /Applications folder, launch the program.
  7. Select your device (I’m using the original iPhone) and click the next arrow.
  8. PwnageTool will search for the ipsw firmware file you just downloaded (and it should find it). Select it and click the next arrow.
  9. Now PwnageTool will look for the bootloader v4.6 and 3.9 files. If you don’t have them, you can download them here. After downloading the files, click ‘No’ to searching the web for the bootloader file. At the next dialog, click ‘Yes’ to browse for the location where you downloaded the file.
  10. Now PwnageTool will ask you if you have an iPhone contract that would normally activate through iTunes. If you are using the Apple approved carrier in your country, you can click ‘Yes’, and this will skip the unlocking process on your phone. Just say ‘No’ if you want your phone unlocked.
  11. Now PwnageTool will build an ipsw file for your phone.
  12. After the file is created, close PwnageTool, disconnect and turn off your phone, and open up iTunes.
  13. Now we need to put the phone into recovery mode. While the phone is off, hold down the ‘Home’ button, and at the same time plug your phone into your Mac. Keep holding down the ‘Home’ button until iTunes tells you that your phone is in recovery mode and needs to be restored.
  14. In iTunes, hold down the option key and click ‘Restore.’ Select the new firmware file that PwnageTool just created, and you should be good to go in about 10 minutes.
  15. Enjoy your updated phone!
Categories: iPhone, Tutorials Tags:

Open links from UIWebView in MobileSafari

September 6th, 2008 1 comment

If you’ve embedded a UIWebView in your iPhone/iPod app, you may not want the user to suffer through surfing all successive pages through it. Instead, you can open up your first page inside a UIWebView and any links the user tries to follow will instead open up in MobileSafari.

All you need to do is set the UIWebView‘s delegate, to an object that implements the optional method webView:shouldStartLoadWithRequest:navigationType: (part of the UIWebViewDelegate protocol). Then whenever the initial page is loaded or a link is followed in that view, the delegate’s method will be called. You’ll want to return YES for the page you initially load, and then return NO for all others, and open the URL in MobileSafari. Here’s what the method implementation should look like:

- (BOOL)webView:(UIWebView *)webView
    shouldStartLoadWithRequest:(NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] absoluteString] isEqual:@"http://arashpayan.com/myInitialPage/"])
        return YES;
    
    [[UIApplication sharedApplication] openURL:[request URL]];
    
    return NO;
}

By returning NO from the delegate method, we’re telling the web view not to load the link the user tapped, but instead we redirect the opening of the link to [UIApplication openURL:] (MobileSafari).

Change iPhone/iPod app orientation within a UITabBarController

September 4th, 2008 12 comments

The documentation for the official iPhone SDK is less than stellar, and as a result I’ve encountered lots of frustrating little problems. In particular, I wasn’t able to get my app to change its orientation when the phone was rotated. UIViewController has a method shouldAutorotateToInterfaceOrientation: that gets called when the iPhone/iPod orientation is changed by the user, and overriding this method to return YES should cause your view to be rotated. You may have implemented this method and to your frustration, nothing happened when you rotated the device. The trick is that Cocoa only calls shouldAutorotateToInterfaceOrientation: on the top most UIViewController. Meaning if your program lives inside a UITabBarController (possibly with a UINavigationController inside some of the tab bar items), the UIViewController that the user is currently interacting with, is not necessarily the one receiving the method call.

In my app, everything lives inside a UITabBarController, so that’s what was receiving the shouldAutorotateToInterfaceOrientation: call. In order to get orientation changes to work you need to subclass UITabBarController (contrary to Apple doc recommendations) and override the method so you can return YES when it’s called. Below is some code for a simple UITabBarController subclass used inside a program that supports a horizontal view.

RotatingTabBarAppDelegate.h

#import <UIKit/UIKit.h>

#import "RotatingTabBarController.h"

/*
 Nothing special implemented in our delegate for this example.
 */

@class RotatingTabBarAppViewController;

@interface RotatingTabBarAppAppDelegate : NSObject <UIApplicationDelegate> {
    IBOutlet UIWindow *window;
}

@property (nonatomic, retain) UIWindow *window;

@end

RotatingTabBarAppDelegate.m

#import "RotatingTabBarAppAppDelegate.h"

@implementation RotatingTabBarAppAppDelegate

@synthesize window;

/*
 We’ll programmatically lay out a simple GUI for this demonstration.
 */

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    
    // Just make two empty view controllers for fun
    UIViewController *tab1 = [[UIViewController alloc] init];
    tab1.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemTopRated tag:0];

    UIViewController *tab2 = [[UIViewController alloc] init];
    tab2.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemSearch tag:1];
    
    // Now create an instance of our rotating tab bar controller
    RotatingTabBarController *tbc = [[RotatingTabBarController alloc] init];
    // Add the two view controllers to the tab bar
    [tbc setViewControllers:[NSArray arrayWithObjects:tab1, tab2, nil]];
    
    // Add the tab bar controller’s view to the window
    [window addSubview:tbc.view];
    
    // Make our program visible
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

RotatingTabBarController.h

#import <UIKit/UIKit.h>

/*
 The subclass doesn’t need any new methods or members.
 */

@interface RotatingTabBarController : UITabBarController {

}

@end

RotatingTabBarController.m

#import "RotatingTabBarController.h"

@implementation RotatingTabBarController

/*
 Just override this single method to return YES when we want it to.
 */

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Always returning YES means the view will rotate to accomodate any orientation.
    return YES;
}

@end

The one thing this doesn’t address, is when you only want certain views in your program to rotate. In that case you just need to make your UITabBarController ask the currently visible view controller in the program if the app should rotate, and return that BOOL.

T-Zones on iPhone 2.0

July 26th, 2008 8 comments

If you’re trying to get T-Zones working on the iPhone 2.0 software, you don’t need to follow the old manual way of doing it. Just go into Cydia and search for ‘TZones Hack’ (without the quotes) and install the BigBoss tweak that comes up. Restart your phone, and you’re good to go.

As usual, the YouTube app still won’t work with this hack.

Categories: iPhone, Tutorials Tags: ,

Updating 1st gen iPhone to 2.0 software

July 19th, 2008 14 comments

The new PwnageTool is out, and I’m gonna document my steps to install the 2.0 software on my 1st gen iPhone. Note that this is not an upgrade. The process will wipe your phone, so backup any pictures or notes that you may have on there.

  1. First thing’s first. Downoad the latest PwnageTool.
  2. Unzip the file and drop it in your /Applications folder
  3. Make sure you have the latest version of iTunes (Apple->Software Update). Start up iTunes after updating to make sure it starts.
  4. Plug in your iPhone, and iTunes will ask if you want to update your phone. Click ‘Download Only’. After it finishes downloading, close iTunes.
  5. Download the version 3.9 and 4.6 boot loaders. I used this link, but that link might not work after awhile. Extract the file (and extract the .zip file inside of it again) until you see the bl39.bin and bl46.bin files.
  6. After the download is finished, close iTunes.
  7. Open the ‘Activity Monitor’ application. (/Applications/Utilities/Activity Monitor). Find the ‘iTunes Helper’ process and force quit the program.
  8. Plug in your iPhone and turn off the auto lock. (Settings->General->Auto-Lock->Never)
  9. Launch PwnageTool, then click on the iPhone on the left.
  10. Select the 2.0 firmware that it finds (iPhone1,1_2.0_5A347)
  11. Pwnage tool should be able to find your boot loaders (mine were in my Documents directory). If not, make sure you’ve extracted the boot loader file you downloaded. Worst case, just browse for it manually.
  12. Pwnage will ask you if you’re legit. I use T-Mobile in America, so I clicked ‘No.’
  13. Pwnage may ask you for your admin password.
  14. Now it will ask if your phone has been ‘Pwned’ before. I presume this is your first time (seeing as how the application just came out), so click ‘No’.
  15. This part is tricky. Watch the PwnageTool screen carefully as it will prompt you to press combinations of your Home and Power buttons in order to put the phone into DFU mode. It took me several times to do it, but just keep trying by following the on screen instructions. (When Pwnage says you have successfully entered DFU mode, your screen might be off. That’s ok)
  16. Once Pwnage tells you that you’re in DFU mode, open up iTunes. iTunes will say that the phone needs to be restored. Hold down the ‘option’ key and click on the ‘Restore’ button. iTunes will prompt you for the location of a firmware file. Select the one on the Desktop that PwnageTool created (iPhone1,1_2.0_5A347_Custom_Restore.ipsw).
  17. Go make yourself a cup of tea. :-) After the restore, the phone will reboot and Boot Neuter will flash the boot loader and base band. Your phone will reboot again, and you’re done!

If iTunes doesn’t immediately recognize your phone, try restarting iTunes. It seems the iTunes Helper is necessary for syncing the iPhone.

Categories: iPhone, Tutorials Tags:

FileTree update (v1.1)

June 4th, 2008 Comments off

I recently received an email from Ryan McFall that he is using FileTree, and he noticed that the FileTree doesn’t actually display everything accurately on Windows. The shortcoming was in the fact that Windows explorer displays all directories first before displaying files in a tree. He fixed the problem, and kindly sent the changes back to me for posting on the website github.

I hope this makes FileTree useful for many more people.

Categories: Open Source Tags: , ,

t-zones on 1.1.4

March 2nd, 2008 Comments off

I finally upgraded my iPhone from 1.1.1 to 1.1.4, and after dealing with the Installer.app “main script execution failed” error (solution here), I began to setup my EDGE access. The steps are mostly the same as before, with the exception of the location of the preferences.plist file. Previously, it was located in the user partition, but now the file is located in:

/Library/Preferences/SystemConfiguration/preferences.plist

In addition to the upgrade, I also took the opportunity to install Telesphoreo/Cydia, which is a port of APT to the iPhone by Jay Freeman. There seems to be lots of interesting ports available to install, and a Java VM, which is of particular interest to me. If I make any progress in developing with the new tools, I’ll post any helpful info I find.

Categories: iPhone Tags: ,