Retrospective for the Androider New to IOS

This is a mini-retrospective after working on my first iPhone app, having started from scratch with iOS programming a little more than a month ago.  I am coming from an Android background, and these casual reflections are intended to help other Android developers who are beginning to dabble in the iOS world.

Long-term, I think the future of app development, especially for resource-limited IT departments, is likely to be a cross-platform tool like PhoneGap(html5) or Mono.   However, I personally want to have some feeling for what's under the hood of iOS, because cross-platform abstractions could leak.

So, in no particular order, here are a few things that might help you come up to speed, or allay puzzlement so you can move along on your own journey from Android into iOS.





Objective-C Syntax

When you first see it, it's weird as heck.  But you get used to it and it's a non-issue. The named parameters for functions is really nice, too, as I am partial to that kind of thing.

Header Files

This isn't Objective-C's fault, but it's something that's not in the Android/Java world.  I've always felt that header files kind of break Don't-Repeat-Yourself (DRY) in some way.  But it is what it is, and you get used to it.  There are apparently some plugins for XCode to help deal with this, as well as stubbing out protocol methods, but I haven't yet felt it was necessary to use them yet.

Screen Layouts - iOS Absolut

This is a big one.  In Android, placing views via absolute coordinates is a big no-no.  This is because there are so many different screen sizes and resolutions that your screens must look reasonable on.  So, you end up getting to solve all sorts of (interesting) puzzles with LinearLayout/RelativeLayout/FrameLayout /-land/-large/-small/-hdpi/alignBelow/etcetera in order to get a screen to look ok in all the combinations it could occur in.  And to do this requires having all these devices or working with the fairly slow Android emulator to confirm that screens look ok in the various permutations.

For iOS, there are not (yet) many screen sizes or resolutions.  So, the equivalent of Android's maligned and deprecated Absolute layout seems to be the rule.  Now, you might need to have separate layout files for iPhone and iPad (xib files), and you need to set some view properties to handle landscape vs. portrait but you don't have to worry yourself much about all the possible permutations of the device sizes and resolutions... because there aren't many.

Connecting Views to Code

In iOS, the layouts for screens are stored in xib files, which are just XML files that the Interface Builder (IB) manipulates (sometimes not very well).  Connecting a view to a variable/field in the controller for it seems to be possible only via IB, as you awkwardly "control drag" from the view to the variable... the variable is referred to as an "IBOutlet".  This is equivalent to doing the following in your activity code in Android:

View mView = findViewById(R.id.IfYouUsedForViewInAndroidLayoutFile);

If you look at the raw xib file in XCode, you can see the hash-like ID's generated for the different views you added, but I don't think you can do anything about them.

Adding actions for the views - like what happens when a button is clicked - can be done with IB or in code.  I prefer to do it in code via [myView addTarget...] in the viewDidLoad method because it's easier to see how everything is hooked up in one place, and it means that if you use different xib files for iPhone and iPad, you don't have to manually set up the connections to the methods with IB twice.

Android Emulator vs. iOS Simulator

No comparison - iOS simulator is way better.  Way.  It is really beautiful and fairly responsive for what I needed it for.

Database

I used FMDB to facilitate working with an SQLite database, and I think it's great.  It should be checked out before executing your first sql against an SQLite database in your code.  I wish I had done that.

Logging

No major things bothered me about NSLog compared to Log.d/e.  btw, I've read that Apple appstore gatekeepers may reject an app if there are lingering NSLog's in the code, but I don't know if that's true or not.   Coming from the Android side of the house, where there are no gatekeepers, it's odd to even have to consider that kind of thing.

Exceptions

"Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors." Apple Developer Docs

This stackoverflow question is also a nice jumping off point to learn about the relevant issues.

I find it hard to believe that there could be performance problems, but I have done no tests myself. The only explicit use of exceptions I have used so far was to catch an uncaught exception when the app crashes so I can get a usable (for me) stack trace.

Debugging

It was a little more effort (for me) to get a usable stack trace in iOS vs. Android, but adding some code to my AppDelegate worked (learned this from this stack overflow answer):

void uncaughtExceptionHandler(NSException *exception) {
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    // Internal error reporting
}

and in the didFinishLaunchingWithOptions method of the app delegate, add this:

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

Then, when an exception occurs, you'll see a nice readable little stack trace in the console log.

Preferences/App Settings

Implementing user preferences in Android is fairly straightforward and customizable out of the box.  This is not the case, imo, for iOS.  In fact, there is no in-app preferences infrastructure like there is for Android. However, there is the free InAppSettingsKit that is awesome and easy to use for doing in-app preferences.

 Lists of Items

The equivalent of an Android ListActivity is a UITableView/UITableCell with a controller behind it.   The UITable cell is put into a XIB separate from the one that has the UITableView, and the controller instantiates the row from the UITableCell XIB.   Somehow it felt easier to do this in iOS than mess with cursor adapters like you do in Android.  The built-in animation when you delete a row is nice, too.

AlertViews

In Android, when you instantiate an alert box, you can associate specific methods in your class to the alert's buttons.  In iOS, I don't know if this possible, versus implementing the UIAlertViewDelegate protocol and getting the button clicks for ALL alertviews the controller is a delegate for.  Since the particular alertview that was clicked is passed into the method, you can check to see which one it is and then act on the button, but this mechanism initially seemed awkward.  In thinking about it, however, it centralizes ALL code you might have for responding to alertviews.

Timers

iOS has a nice helper class called NSTimer that definitely simplifies doing something at specific intervals. It's not hard in Android with Handlers, but I liked the Apple way better.

Animation

The animation I have done so far, on both Android and iOS, is simple and primitive.  But it can add a nice little touch. I don't feel strongly as to either way's methods of doing it - they work fine for me.

Showing other Screens

In Android, the different Activities are kind of in their own world, and showing other screens has a certain "letting go" aspect to it. You start an activity with an intent, and can receive data from the called activity via onActivityResult.

In iOS, you have that screen in your hand, rather than calling out to the ether with an intent.  You instantiate the view controller, can set some of its relevant properties (which takes the place of stuffing parameters in the Intent data in Android as you handle a lot of marshaling yourself), and can call presentModalViewController to fire it up.  When its finished, your calling controller will have its viewWillAppear method called. Well, it almost always will.  There seem to be some bugs in certain cases where this does not happen that did not bite me, but I will be on the lookout for it in the future.

Just like in Android, there seems to be some confusion about the app lifecycle/Activity-Screen stack, and when the lifecycle "hook" methods get called.  I think this can get surprisingly tricky sometimes.

Dealing with the Network not being Available

I read somewhere that if your app does not deal gracefully with the network being down, you risk having your app rejected.  So, I am using Apple's Reachability framework to disable a button that takes you to the web when there is no network connection.

Showing Ads

This first iOS app I am developing is using Admob for ads, and overall there haven't been any major issues in testing.  Make sure and use different sizes for iPhone and iPad.  Apple may reject your app if the ad banner does not gracefully handle the network being unavailable.  To handle this, your controller can implement the GADBannerViewDelegate protocol, make sure and set the GADBannerView's rootViewController to your controller, and implement the following two methods: 

-(void) adViewDidReceiveAd:(GADBannerView *)banner

and

-(void) adView:(GADBannerView *)banner didFailToReceiveAdWithError:(GADRequestError *)error

If the first one gets called, then you know that you can show the banner, and if the second one gets called, you can hide the banner.

Storyboards

Something called Storyboards are new in the latest version of XCode (4.2).  Be careful with these things, as they are not compatible with iOS 4.3, which still accounts for about 20-30% of the iOS market.  There is no simple "break the storyboard into separate xibs" command, and it is a mildly tedious manual process to do so.

Checking for Memory Leaks

This is something you rarely think about with Android/Java.  Not never, of course - bitmap memory issues anyone?  Anyway, XCode has incorporated some instrumentation tools to help track down a lot of potential problems.  I have used this in only a limited way so far, and it seemed to mainly point out a leak in the way I was using the Admob stuff (which itself is copy-paste from the Admob documentation).

Automated Testing

For Android, I have used Robotium, which works great.  The iOS equivalent seems to be Frank, although I have yet to try it.


ARC - Automatic Reference Counting

I'm glad I waited until I did to start iOS programming.  Prior to XCode 4.2, you had to write a LOT of boilerplate code to make sure objects got cleaned up properly, and it's clear from trawling the web that this was a hard thing to get completely right.  What is so hard about the claim/release cycle?  It pops up in all sorts of contexts (object reference counts, cursor open/close, file handles), and really smart people miss edge cases.  Anyway, something called ARC ("Automatic Reference Counting") was introduced with XCode 4.2, and this means you don't have to write all that code.  In fact, you might not even know what it's doing for you.  If you use 3rd party libraries, though, you'll get some strange errors, because ARC is an all-or-nothing thing for a particular file.  To turn off ARC for the libraries using the "old" memory management techniques, you add the -fno-obj-arc compiler flag to the file (see the answer to this stack overflow question).

Out of curiosity, I read up a little on ARC (here's a nice blog about it).  Apparently, it's inserting the needed cleanup source code at the right places, in what seems like a primitive kind of way to do it, but whatever.


Lack of Access to iOS SDK Source Code

When working with Android, one thing I had gotten used to doing was browsing the Android SDK source code when needed.  This has turned out to be a time-saver when the documentation was unclear and/or I needed to see how things were really being done under the hood.  I had expected that not being able to do this would have been an issue while working with iOS.  Somewhat surprisingly, it has not been an issue yet.  


Conclusion

Overall, programming for iOS is fun, and if you are comfortable programming in Android then I wouldn't expect a very steep learning curve to get going. Hopefully this informal article can make it easier for you.  However, don't rush it if you don't have to.   There is a different culture at work, and it's worth it to let some of that seep in - this is still happening for me.

And of course, I have yet to deal with the Apple app store gatekeepers :)

No comments:

Post a Comment

Popular Posts