Tuesday, June 29, 2010

Ad Viewer: My first app rejection story

This isn't a rail against the arbitrary policies of the Apple Store, instead it is the story of my somewhat cheesy application, Ad Viewer and its trip through the application approval process.

With the advent of iAd I thought there was an opportunity for an application that displayed ads, they would be interesting in and of themselves, and a possible market.  I thought that there was a good chance that Apple would reject it, all it does is display ads.

The timeline:
Submitted on June 17, 2010 @ 20:08
In Review on June 23, 2010 @ 20:16
Rejected on June 25, 2010 @ 09:37


We've reviewed your application Ad Viewer and we have determined that this application contains minimal user functionality and will not be appropriate for the App Store.

We encourage you to add additional user functionality to Ad Viewer and resubmit it for review.



I wasn't too surprised, I knew my application had limited functionality.  Just to explore my options I sent an email in response, asking if it was even possible for an application whose purpose in life is to show ads, was acceptable.

My surprise was that Monday evening I received a call from Apple.  A representative called to discuss the my application.  He told me that the form emails often do not give a proper representation.   We discussed the application, and he told me that the basic idea was not going to be something Apple allows in the application store.   I certainly understand this policy, although I think my Ad Viewer would have been useful and profitable, I understand why Apple would disallow it.    He was very professional, courteous, and spent a good deal of time talking to me.   I was impressed with the way Apple handled my very minor application.



Saturday, June 26, 2010

Ad Viewer: Shameless attempt to cash in.

It looks like iAd, Apple's new framework is the future of advertising on the iphone.  They are even attempting to shut out some of the other larger players, such as admob (based on interpretation of the new SDK).

We'll these new ads are supposed to be spectacular, so that they will be entertaining by themselves.  Well, the best way to test this out is to create an adView, its sole function is to display ads.   I'm going to submit it to the Apple store, they might reject it because it doesn't have much functionality, although I think it has at least as much as a fart application or an application that displays a frame and calls it a mirror.


I created a new xcode project and choose my target.  I then added the iAd.Framework.














Next I went into interface builder, and created a view with some iAdbanners.
Running my application I get:

And choosing an ad gives: 


As a developer it could hardly be simpler.  There really is some more work to make things nice, but this gets an ad running SO quickly compared to some other frameworks I tried.

The main worry I have is that I would like to have access to other ad networks so that I could support iphones running  3.1.3.  Iad will  not work on those.

Ad Viewer: supporting more interaction

Next I need want to implement the delegate actions for the ads.  These are the interaction the iAD network has with my class.

Add protocol to my view controller




#import

@interface adViewerViewController : UIViewController {

}

This way I will have the definition as an ADBanner delegate.  I first implemented a very simple set of callbacks to verify that I set my delegate in interface builder correctly.

//The Add banner delgate methods.
// This method is invoked each time a banner loads a new advertisement. Once a banner has loaded an ad, 
// it will display that ad until another ad is available. The delegate might implement this method if 
// it wished to defer placing the banner in a view hierarchy until the banner has content to display.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
self.view.backgroundColor=[UIColor redColor];
}

// This method will be invoked when an error has occurred attempting to get advertisement content. 
// Possible error codes defined as constants in ADManager.h.
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
// Update the text display with informaito about the error.
}

// This message will be sent when the user taps on the banner and some action is to be taken.
// Actions either display full screen content in a modal session or take the user to a different
// application. The delegate may return NO to block the action from taking place, but this
// should be avoided if possible because most advertisements pay significantly more when 
// the action takes place and, over the longer term, repeatedly blocking actions will 
// decrease the ad inventory available to the application. Applications may wish to pause video, 
// audio, or other animated content while the advertisement's action executes.
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
self.view.backgroundColor=[UIColor cyanColor];
// All we do is display ads, we have NO desire to block it.
return true;
}

// This message is sent when a modal action has completed and control is returned to the application. 
// Games, media playback, and other activities that were paused in response to the beginning
// of the action should resume at this point.
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
}

Ad Viewer: Supporting rotation, adding clear button

The next step of my adviser program is to support rotation of the view, and move stuff back into place.  I ran into a problem briefly where I overrode the loadVIew method, that caused NOTHING to work.

I implemented the following methods to enable more than one ad to appear on the page (I have no idea if apple will allow this), and to support rotation.  Finally I added a clear button, so that more ads can be requested.

Below are screen shots in both portrait and landscape mode.







The following is the code.

When I first wrote this code I was worried about the resolution difference between the new IPhone 4 and older iphones, so I made sure to use frames as generated by the iPhone os.  I later read in the documentation that the iPhone OS is now using points instead of pixels.  This will automatically scale for the hardware factor.

I'm leaving the code as is, so that if Apple creates additional ad sizes, the code will work fine.  Right now with the screen sizes, 7 ads can fit in both of my dialogs.

The Header


My header defines a screen area for ads, an action for clearing ads.

IBOutlet UIView* bannerViews;
NSMutableArray *currentAds;
}
@property (nonatomic,retain) UIView *bannerViews;
@property (nonatomic,retain) NSMutableArray *currentAds;
// Clear all ads, and request new ones.
- (IBAction) clearAds:(id)sender;
- (void) moveAds;

Setting up the views


I created a routine to make the banner views, it initializes it for both portrait and landscape, as well as choosing the proper orientation for the current view.:
- (ADBannerView *) createBannerView
{
ADBannerView *view=[[ADBannerView alloc]init];
view.requiredContentSizeIdentifiers=[NSSet setWithObjects:ADBannerContentSizeIdentifier320x50,
ADBannerContentSizeIdentifier480x32,nil];
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
view.currentContentSizeIdentifier=ADBannerContentSizeIdentifier480x32;
else {
view.currentContentSizeIdentifier=ADBannerContentSizeIdentifier320x50;
}
return view;
}
The viewDidLoad calls clear ads to set up the initial set of banners.  DO NOT USE loadView.  Then your nib is not loaded.
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
 [self clearAds:self];
}


The clear ads callback erases all current ads and then creates new ads to fit the bannerViews UIView.  

- (IBAction) clearAds:(id)sender
{
if (currentAds!=nil)
{
for (ADBannerView*view in currentAds)
{
[view removeFromSuperview];
}
}
currentAds=nil;

currentAds=[[NSMutableArray alloc] init];
ADBannerView *currentView= [self createBannerView];
int y=0;
while (y <bannerViews.bounds.size.height)
{
[bannerViews addSubview:currentView];

NSLog(@"using y %d",y);
currentView.frame =CGRectMake(currentView.frame.origin.x, y+bannerViews.bounds.origin.y
  currentView.frame.size.width
  currentView.frame.size.height);
[currentAds addObject:currentView];
y+= currentView.bounds.size.height;
if (y!=0)
{
currentView=[self createBannerView];
}
}
}

Handling rotation


The rotation is fairly simple under willRotate I set all the banner bars to the proper orientation.  Then in didRotate, I animate a change to the new spots.

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
NSString *newOrientation;
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation))
newOrientation=ADBannerContentSizeIdentifier480x32;
else {
newOrientation=ADBannerContentSizeIdentifier320x50;
}
if (currentAds==nil)
return;
[UIView beginAnimations:@"rotateView" context:NULL];
for (ADBannerView *view in currentAds)
{
view.currentContentSizeIdentifier=newOrientation;
}
[UIView commitAnimations];

}
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[UIView beginAnimations:@"rotateView" context:NULL];
[self moveAds];
[UIView commitAnimations];
}

Moving views to their new spots

- (void) moveAds
{
int y=0;
for (ADBannerView * currentView in currentAds)
{
currentView.frame =CGRectMake(currentView.frame.origin.x, y+bannerViews.bounds.origin.y
  currentView.frame.size.width
  currentView.frame.size.height);
y+= currentView.bounds.size.height;
}
}

Ad Viewer: Rejected

I can't say its a surprise, but my Ad Viewer application was rejected for minimal functionality.  That's a fair statement, I spent minimal time developing it.   While I think it would have been useful  and done OK if Apple had approved it, I'm not surprised it was rejected.

The NDA has been released on iOS 4.0 it, so I'm publishing my blog articles.  I'll post my code soon.   I didn't publish my old posts in order, so there will be some confusion I think. 

Ad viewer: Finishing up.

This is actually all the functionality I'm going to put into adViewer.  I have no idea if Apple will let it into the store, or if it will reject it because of 'limited functionality'.  I also don't know if they are going to allow multiple banners per page.

I think its fair enough to let it in, it provides the ability to see the new iAds and request new ads.  As a developer I would probably download something like it, just so I could see how the new iAd stuff works.

The next thing I need to do is create some icons for my app, and set the startup image to match the initial image of the ads.

Then I'm going to submit it and see how it does.

Friday, June 18, 2010

Watch your method names.

OK, I was cruising along nicely, when I started to get the following error:
exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the  nib but the view outlet was not set.

After some debugging I found that I had overridden setView function for another purpose.  This messed up the property causing the above error to occur.   


Saturday, June 12, 2010

View controller not loading view from .nib

I ran into a new debugging problem where a view controller was not showing anything from it's nib file.  It was just transparent.  I attempted to debug it, reconnected all the interface builder items.  I finally figured out that I had implemented loadView instead of viewDidLoad to do some initialization.  This caused the nib file to be ignored, the code was expecting me to programmatically create my view, which I wasn't doing.

There was no obvious error for this one, figuring it out cost a couple of hours.