Aug 302013
 

Lets say you have an app that you don’t quite know what some parameters should be. This isn’t that uncommon, it might be as simple as not knowing what a default should be, or maybe you’ll have a list of in-app content to sell. The possibilities are pretty huge.

This is surprisingly easy to do. First, make a JSON file and host it somewhere.

{ "ShadowHeight":"20" }

Make a property to hold this value and initialize it.

@property(nonatomic, assign) int shadowHeight;
self.shadowHeight = 15;

Next you need to start a background query to get that file from the net.

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://yourdomain.com/yourfile.json"]];
        [self performSelectorOnMainThread:@selector(parseJSONFile:)
                               withObject:data waitUntilDone:NO];
    });

Your app will run with the default value of 15 until the net query returns. When it does, the parseJSONFile: selector is called.

- (void)parseJSONFile:(NSData *)responseData {
    NSError* error;
    if (responseData) {        
        NSDictionary* json = [NSJSONSerialization
                              JSONObjectWithData:responseData
                              options:kNilOptions
                              error:&error];
        
        NSNumber *shadowHeight = [json objectForKey:@"ShadowHeight"];
        self.shadowHeight = [shadowHeight integerValue];
    }
}

And that’s it. We run with the compiled in value and switch to the runtime value when (or if) the file is served by the host. It couldn’t be easier.

Aug 302013
 

I’ve released 2 apps both with iAds. I used Apple’s BannerView sample code to implement this. Basically, in your delegate you don’t set root to your expected root UIViewController but rather you set root to a BannerView which contains your real root. When an iAd is available, your main view shrinks and the iAd is displayed at the bottom. When an ad isn’t available, your view expands to its “normal” size.

This worked very well in testing so I released both apps to the app store. They’re Done Yet? and Wheelin. However, when I first downloaded the versions from the store I was quite surprised to see no ads ever. It turns out that at least right now, iAd had a pretty horrible fill rate. So I wanted to show another ad when an iAd wasn’t available.

I found LARSAdController, an open source project by larsacus on GitHub. He makes ad integration very easy except for one thing. When you go down his quick development route you get the ads covering your view, it doesn’t shrink to accommodate  the ad. This is a completely reasonable design decision, just not one I wanted.

So I decided to modify Apple’s BannerView to use LARSAdController. It was pretty easy.

The first thing you do is remove iAd from BannerView’s .h file and ad in the LARS TOLAdViewController class.

#import "TOLAdViewController.h"

#define KVO_AD_VISIBLE @"KVO_AD_VISIBLE"

@interface BannerViewController : TOLAdViewController

(Just ignore the KVO_AD_VISIBLE define for now, I’ll cover that later.) In the .m file also remove iAd and make these changes:

@implementation BannerViewController {
    UIView *_bannerView;
    UIViewController *_contentController;
    BOOL isLoaded;
}

We changed _bannerView from an ADBannerView into a plain old UIVIew. ADBannerView also has a bannerLoaded property which we’ll have to replace with our isLoaded boolean. initWithContentViewController is also easy to modify.

        // IAPHelper *sharedInstance = [//IAPHelper sharedInstance];
        //if ([sharedInstance showBannerAds]) {
        if (YES) {
            _bannerView = [[UIView alloc] initWithFrame:CGRectZero];
        } else {
            _bannerView = nil;      // not showing ads since the user has upgraded
        }

Notice the commented out section. If you are using in-app purchases to transform an ad supported version into an ad free version you can do that right there.

At the end of the method well use Key-Value-Observing (KVO) to watch LARS and see when an ad is served or removed. (I’ll probably cover KVO in a future blog entry.)

    [[LARSAdController sharedManager] addObserver:self
                                       forKeyPath:kLARSAdObserverKeyPathIsAdVisible
                                          options:0
                                          context:KVO_AD_VISIBLE];

And the observing code:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context;
{
    if(context == KVO_AD_VISIBLE) {
        NSNumber *isVisible = [object valueForKey:kLARSAdObserverKeyPathIsAdVisible];

        if ([isVisible boolValue]) {
            _bannerView.frame = [[LARSAdController sharedManager] containerView].frame;
            isLoaded = YES;
        } else {
            isLoaded = NO;
        }
    }

    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}

We save the frame of the new ad and also update the isLoaded variable. (Originally thought I would need to call setNeedsLayout and layoutIfNeeded but in practice I didn’t.) The mods to viewDidLayoutSubviews were also pretty straightforward. The only odd part was removing the references to ADBannerContentSizeIdentifierPortrait and ADBannerContentSizeIdentifierLandscape and just replacing that all with a single line:

    bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];

And a few lines later you use the new isLoaded variable

    if (isLoaded) {

Don’t forget to clean up your observer in dealloc:

-(void) dealloc;
{
    [[LARSAdController sharedManager]  removeObserver:self forKeyPath:kLARSAdObserverKeyPathIsAdVisible];
}

All that remains is to tell LARS about your ads in your app delegate:

        [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapterGoogleAds class] withPublisherId:@"a14e55c99c24b43"];
        [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapteriAds class]];

        LARSBannerViewController *root = [[LARSBannerViewController alloc] initWithNibName:@"LARSBannerViewController" bundle:nil];

        _bannerViewController = [[BannerViewController alloc] initWithContentViewController:root];

        [self.window setRootViewController:_bannerViewController];

And that’s it. Now your app should show iAD or AdMob ads and your view will shrink to accommodate them.

Of course there’s a bug, I don’t know if this is in AdMob server or in LARS but when you are in Landscape mode on an iPhone the ad’s size and the reported size are different leaving a black bar at the bottom of the screen. I’ve pinged larsacus about it and will update this post when I know more.

I’ve forked LARSAdController and submitted the above sample code in a full project on my github.

Aug 032013
 

I recently had a recruiter ask me if I had experience with UICollectionView. Coincidentally, I had just read about that a day or two before. Now, that’s not really experience that gets you the gig so I threw together a sample app that used UICollectionView.

The app, Image Fool, implements an unusual, and not very useful, way of browsing images on Flickr. When first launched, it shows 26 images, the first of which matches the Flickr image search for ‘a’ and the second for ‘b’ and so on.

imageFool

The problem I had was that it was slow, I mean, really slow. To show the 26 images I need to hit the Flickr server 26 times fast.

The first thing I tried was the standard Grand Central Dispatch method of putting your slow operations on a thread and then doing your UI work on the main thread.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // switch to a background thread and perform your expensive operation
    dispatch_async(dispatch_get_main_queue(), ^{
        // switch back to the main thread to update your UI
    });
});

This code snippet is from Ray Wenderlich’s wonderful iOS blog. I like how with just 2 comments the author, Marcelo Fabri,  tells us what goes in the first part and what goes in the 2nd dispatch_async.

I immediately realized I wanted finer control over my threads, both the number of concurrent operations and their priority. That meant I should use NSOperation.

for (int i = 0; i < kIMAGE_FOOL_MAX_CELLS; i++) {
        __weak IFViewController *weakSelf = self;
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            IFFlickrPhoto *newFlickr = [[IFFlickrPhoto alloc] init];
            [newFlickr loadFlickrPhotos:searchString];
            [self.collectionView reloadData];
        }];

        [operation setQueuePriority:NSOperationQueuePriorityVeryLow];
        [self.flickrImageQueue addOperation:operation];
}

And when I setup the queue I told it to perform only 2 concurrent operations:

    self.flickrSearchQueue = [[NSOperationQueue alloc] init];
    self.flickrSearchQueue.maxConcurrentOperationCount = 2;

Performance was still bad. After a lot of head scratching I realized that my call to reloadData wasn’t happening at the right time. I tried putting the reloadData onto the main thread.

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
});

And it was fast enough but each image was redrawing like crazy over and over again. Image flicker in a Flickr program is not desirable.

And then the lightbulb went on, I was asking it 26 times to reload the data, that was ludicrous. I needed 26 cells to reload, but each of them only once. That’s where the reloadItemsAtIndexPaths method is useful

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
});

Of course I had to figure out the indexPaths but that’s easy in this case, I have a for loop and the index path just contains the index of the for loop.

And now the UICollectionView cells scroll smoothly. You can download the full source from github.