First App on the App Store! But wait.. what’s this?

2014-02-27 00.14.01

My first iPhone app, Urban City Wallpaper, just got published to the app store today! *confetti*

But that’s not the interesting bit. What’s interesting is that I actually submitted 2 apps. Both with identical source code. The only exception was that one app is calling /r/earthporn end points and the other is calling /r/cityporn endpoints, both on imgur.

One got rejected, the other got accepted. The earthporn app got rejected because it crashed when there wasn’t an active internet connection present on the device. It’s a legitimate bug which I’ve since patched and resubmitted. Now the same bug probably exists on the cityporn app but it got through. Actually it got reject also but it was due to keyword stuffing on the app title. I removed it and it got through. Funny enough, the earthporn app is also keyword stuffed but Apple hasn’t said mentioned it yet. Let’s see if it gets through. If it does, I’ll essentially have two different rejections reasons for 2 idential apps, each getting away with the other’s rejection.

Advertisements

FacebookSDK/FacebookSDK.h file not found

If you’re messing about with the Facebook SDK (version 3.12 as of my writing) for iOS and you’re getting “FacebookSDK/FacebookSDK.h file not found” error while building, you may need to re-add the framework.

  1. Build Phases
  2. Link Binary With Libraries
  3. Here FacebookSDK.framework is already added but click on + symbol to re-add it
  4. Add other
  5. If you didn’t change the install path when you installed the pkg file, it should be located in ~/Documents/FacebookSDK
  6. Build and run again

Cell Views in UICollectionView

When I started using UICollectionView, I thought the design pattern would be similar to that of a UITableView.

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:...];
if (!cell) {
  cell = [[UITableViewCell alloc] initWithStyle:... reuseIdentifier:...];
}

So it was curious to me to NOT see this set of seemingly boilerplate code for the UICollectionView equivalent. The reason for this is because doing something like this

ImgurCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
                                                                forIndexPath:indexPath];

is guaranteed to return a cell. 

Python to Objective-C: Initial Reactions

Here are some of my initial thoughts and reactions coming from a scripting (Python) background on Objective-C and iOS:

  • Overwhelming API documentation. To me this is just a case of hunkering down and mugging like a good A level student.
  • Obsolete Stackoverflow answers. With every yearly release of a new iOS version, it kinda makes an entire generation of SO answers redundant. The game changes very quickly
  • There’s quite a fair bit of plugins out there in the wild. Initially, I was rolling my own image downloader and caching mechanism only to find quite a few libraries out there doing exactly that (and better) already

Some lingering questions still on my mind which I hope to be cleared up as I go along:

  1. In a segue, when to use push and when to use modal
  2. Modification of UI elements in the storyboard or in the code
  3. Passing on values from one view controller to another. Direct assignment in the prepareForSegue method or what?
  4. How do I quickly check in XCode what methods are needed to be implemented for a new delegate or data source
  5. The difference between @class and #import
  6. strong/weak references in declaration of properties
  7. Project and folder layout hierarchy
  8. Paths and inclusion of new files

Updated with answers!

  1. The way I think about when to use push or modal segues is to use a RPG example: if you’re exploring a dungeon and you go into a room, if the room leads to more rooms, use a push segue. If the room is a dead end, use a modal segue.
  2. Where possible, reduce code bloat and use storyboard modifications. This is kind of subjective
  3. Direct assignment is fine. I think I was being overly paranoid with this question
  4. cmd + click is your friend. Then look for @required methods. This should get better with experience
  5. General rule of thumb: if you need to alloc/init something, you have to import it. @class is almost always used only in header files
  6. When in doubt, use strong
  7. I’m actually still figuring this one out
  8. If you click on a file and goto the file inspector, you can see a Location drop down that allows you to set the path. The thing that threw me off initially was that your project path directory need not match your Explorer folder path directory.

Loading Images in UICollectionViewCell: Naive to the Clever.

I’m working on an iOS app called Scenery. It pulls images from /r/earthporn via imgur’s API. It’s a simple 2 screen app that uses UICollectionViewCell to display images in a grid format. I went through a few code iterations in getting the images to display. Let’s go through them.

2014-02-05 12.19.28 2014-02-05 12.19.36

Iteration 1: The “Hello World” of downloads

NSURL *url = [NSURL URLWithString:@"https://api.imgur.com/3/gallery/r/earthporn/time/"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

This works as expected except that it’s a synchronous request. This means the screen is blank to the user until all the images have been downloaded and displayed. In this case, each API request to imgur is returning 52 images making for a poor user experience.

Iteration 2: The Sensible Asynchronous Method

What’s obviously required here is to first load the screen and then download the images asynchronously.

        // download the image asynchronously
        NSString *imageString = [[_imgurArray objectAtIndex:indexPath.row] objectForKey:@"link"];
        NSURL *imageURL = [NSURL URLWithString:imageString];

        [self downloadImageWithURL:imageURL
                   completionBlock:^(BOOL succeeded, UIImage *image) {
            if (succeeded) {
                    imageView.image = image;
                }
            }
        }];

I’m now downloading the images asynchronously with downloadImageWithURL:. Great! Now the screen loads first and I can see the images pop up one by one as it finishes the download. However, every time I scroll down the screen and up again, the app re-downloads and re-requests the images again. Not great.

Note that all of this action is taking place in – (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath.

Iteration 3: The Sensible Asynchronous Method with Makeshift Cache

So to deal with the multiple requests to imgur’s server each time a user scrolls up and down, I loaded all the images into a NSMutableDictionary as a sort of cache.

    if ([_cachedImages objectForKey:identifier] != nil) {
        imageView.image = [_cachedImages valueForKey:identifier];
    } else {
        // download the image asynchronously
        NSString *imageString = [[_imgurArray objectAtIndex:indexPath.row] objectForKey:@"link"];
        NSURL *imageURL = [NSURL URLWithString:imageString];

        [self downloadImageWithURL:imageURL
                   completionBlock:^(BOOL succeeded, UIImage *image) {
            if (succeeded) {
                if ([_cachedImages objectForKey:identifier] == nil) {
                    [_cachedImages setValue:image forKey:identifier];
                    imageView.image = [_cachedImages valueForKey:identifier];
                }
            }
        }];
    }

Getting there! The scroll speed is smooth and the download is asynchronous. But look, what do we have here

Screen Shot 2014-02-05 at 12.40.16 pm Screen Shot 2014-02-05 at 12.40.49 pmThe memory footprint is out of this world. On first load, the app consumes 300MB. When I load the 2nd page, it also doubles. Each subsequent page load adds roughly 200MB. As you might have guessed, it crashes once I tried running this on my iPhone.

Note: the original code was heavily inspired by this post: http://natashatherobot.com/ios-how-to-download-images-asynchronously-make-uitableview-scroll-fast/

Iteration 4: The Real Sensible Method

We’re all standing on the shoulders of giants in the software industry. Image caching and url requests are a well trodden path that have been solved by other folks much smarter than myself. A cursory search reveals a few prominent libraries:

Using SDWebImage, I managed to reduce my memory footprint by 1 order of magnitude. And it does all the asynchronous loading as well.

    NSString *imageString = [[_imgurArray objectAtIndex:indexPath.row] objectForKey:@"link"];
    [imageView setImageWithURL:[NSURL URLWithString:imageString]];
    cell.backgroundView = imageView;

Screen Shot 2014-02-05 at 12.50.19 pm

 

 

Haha! Mind blown!