Tuesday, September 27, 2011

Converting a phone app to Android tablets and the iPad

Just as I thought I was winding down my Android and iPhone development the Doctors tell us they have bought iPads and Android Tablets to use to enter all the data. Can't blame them as it is much easier to enter data on a tablet. The problem is we were going to target those devices on the next release. Time to quickly switch gears.

The app ran fine on the Xoom but the home screen needed to scale better. I searched the web and found new graphics for that area. With more room using larger fonts also seemed the way to go so I tweaked a few other XML files. I ended up creating a new values-xlarge directory under the res directory to hold a styles.xml file for the tablet. I find the way Android handles this to be super easy. I had to go back into a few of my other XML files to make sure they were using the named styles but that was all. I only have a few files in my layout-xlarge and layout-xlarge-land directories to handle all the changes I wanted to make on the Android Tablet.

Android does handle your app getting kicked out out memory in a way that screwed with our app. We have some global static data (I know wrist slap in order) but I need it for the SQLite database and some other login credentials. When you get booted from memory the OS tries to run your onCreate method of the visible view again. For most apps this would be great, you just pretend you started fresh and go. But with the global variables this is not so fun. I added enough checks that I don't [Force Close] any more but tell the user that the session has expired and they need to login in again. I kick them back to the login screen to get going with a fresh database and session variables.

Initially testing this case was a royal pain. You needed to leave your phone on overnight or run so many apps that it got kicked out of memory. Of course that is stupid and a few internet searches pointed me in the right direction. Fire up the emulator, run your app, use "adb shell" from the command line then "ps" then "kill #id" of your process. This will cause same sequence of events to occur. I brought up each possible activity / view and did that over and over until they all exiting clean to the login screen. You can't do this when running on a device but you can use the DDMS view in Eclipse and hit the red [stop] button to achieve the same results.

Once you start running on a real device, especially a dual core tablet, you hate to go back to the emulator. Of course QA came and took the tablet a few days after I had it in my hot little hands so I am still missing it. So much nicer to type on it and code changes are sent over to it and are up and running in a blink of an eye.

Now for my iPad experience. You can set up a universal binary in Xcode. Once you do that it will automatically switch you to the latest version of iOS as your target which is not a good thing. I wanted to target 3.0x as the minimum iOS version. Since Xcode is smarter than me things quit working on my iTouch until I figured out what it did. Simple enough to change that back.

Initial runs on the iPad looked like initial Android tablet runs. Most things looked pretty good as I was using list views and text editors. The home screen looked crappy though. I was able to use my @2x images for the retina display on the iPad. I used the stretchableImageWIhtLeftCapWidth to simulate the nine-patch image I was using for the shelves on the Android.

topShelf = [[UIImageView alloc] initWithImage:[shelf stretchableImageWithLeftCapWidth:25 topCapHeight:0]];

Worked like a charm and was much easier than I assumed it would be. I had to put in manual positioning code for each of the icons on the shelves for the iPad but there are only 6 images so that was easy enough.

I am adjusting for the height of the status bar in my code too. On the iPhone the status bar height is different between portrait and landscape but it is one height on the iPad. I put code in place to handle those special situations and everything looks pretty darn nice.

Since I have more title bar room I wanted to show the practice name on the home screen. Of course that changed the "back" button on any screen that launched from the home screen which is not what I wanted. Turns out you can do this:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {

    self.navigationItem.title = [NSString stringWithFormat:@"Home - %@", [RESTCaller practiceName]];
    UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStylePlain target:nil action:nil] autorelease];
    [self.navigationItem setBackBarButtonItem:backButton];
}

That code allows you to set the title text to one thing but the back button used by all other areas to something else. Not the way my mind thinks but I understand the logic behind it now that it is in place.

I want to increase the font size on various screen on the iPad. Since there is no real layout manager for iOS, everything is hard coded for widths / heights in the XIB file, this is a royal pain. You can either do secondary XIB files where you have to tie everything exactly back to the code via drag and drop or you can try and tweak things in code playing with bounds and what not. Both are sorry excuses for layout editing. I have made slow progress in this area but find the amount of work involved to be stupid.

The iPad cannot use the same call to show the picture gallery as the iPhone. I had to update the code to make the new call to show a pop-up image picker instead of a full screen gallery picker. I hope that is the only API I am using that they changed on me. Rough one to find, good thing we have QA to beat on it.

UIActivitySheets don't exactly work the same on the iPad either. They show up nice and centered but if you rotate the device while you are displaying one it will not stay centered. It stays relative to its original position. Looks ugly but this will be a really rare case so I did not fix it. There are some suggestions on the web as to how to fix it but nothing worked for me. The only thing left to consider is killing the sheet when you get a rotation notice and recreating it so it shows up in new orientation centered again. Of course if you do that really quickly you will have a new set of issues. Not worth it at this point. I see Apple cheated in this respect. Long tap a link on a web page. It pops up a menu. Rotate the screen. The Screen will not rotate until you dismiss the menu. Not what I would call a particularly user friendly way of doing things.

Camera support on iOS devices is totally hit or miss. If you want to determine if a camera is there you need to use iOS 3.2 and above calls. Seems Apple is not very forward thinking on these things and a lot of stuff gets added in later releases but I can't trust a user to be up to date. Instead I am using a call to get the machine name and basing the availability of a camera off of that. Crappy way to do things but it is working for me so far. I know iPad v1 does not have a camera and you have to be up to iPod (the iTouch) version 4 before you have a camera. The simulator never has a camera which sucks. On the Android side it simulates the camera by showing interface then returning a stock image. I had to test camera usage on an actual iOS device and I have to make sure it has a camera otherwise you get a nice crash. I don't even want to show the [Take picture with Camera] button in the UI if you don't have one.

Which brings me to the another WTF? for Apple. Why are Action Sheets index based? If I remove a button from my Action Sheet it shifts the index of all the buttons below that. Hard to write generic code and you always have to edit two places for each action sheet change you make - once where you create the buttons on once where you respond to their presses. Each button should call its own method or at least you should be able to assign tags to them so you can have one case statement. Honestly assigning tags to UI items seems pretty stinking crappy too. I do it where I can to keep track of things but it is not the most object oriented way of programming.

Some of my buttons had the dreaded "..." on them depending on what I ran it on. No device fragmentation here boys, move along. They were working find on the iPad 2 and both iTouch devices but not on the iPad 1 or an iPhone with an older version of iOS. I had to shorten the text on all devices to make sure it fits.

I have no idea if the Doctors will keep their phones up to date. At this point - pre iOS 5 - there are not OTA updates. If a Doctor never connects his iOS device back to a PC running iTunes he will have no idea there is an update out there. Will be interesting to see what iOS versions we have connecting once we roll this thing out into the wild. I have tried to test on as many iOS flavors as possible and each time I seem to find a new issue.

One other big area I can into was the 1 meg blob limit of SQLite. We are taking pictures with the built in cameras and they can easily be over 1 meg on an Android. The lackluster camera on the iPad and the iTouch don't get you into trouble but an iPhone can. I use JPEG compression to shrink the image down as needed to avoid SQLite issues. It is a shame the iPad camera is so limiting in resolution. It is an unusable solution for our doctors, the images of sheets of medical paperwork are not readable with that camera. We also allow you to choose images from the device gallery which could easily be from another device and over the 1 meg limit.

Overall it was not very difficult to get a universal app running on either the Android or iOS platforms. We are not taking full advantage of the tablet form factor but at least we are not just doing a 2X version of our iPhone app, the images scale up nicely without being blurry, you get to utilize the extra space of the device and things look native in general. 

QA is doing final testing then it will be off to the beta clients. It will be really interesting to get their feedback. Now I just need a little time to destress and I get ready to head back into Java + Swing + MigCalendar land to fix some issues in our appointment application.

Wednesday, September 14, 2011

Which way is up?

Do you ever have days where you can't even begin to tell which way is up? I have been having that feeling for the past week at least.

I am winding down the Android and iPhone development so it is at least ready to go to beta. We have a doctor that is ready to test it. We thought he was going to do it on phones and 7" tablets but he bought 10" tablets instead. I needed to add an xlarge portrait and landscape version of the home screen to the layout directory. Not a big deal but the button images need to be 256x256 to not look stupid small on the screen. My original images are 128x128 and the phone version I use 100x100. Any time you scale up things look crappy but I don't have time right now to find new base images or to totally pretty up the ones I have. I ran the sharpen tool in Paint.NET so they look acceptable.

QA has not had enough time to look it all over either. The main QA person was hit by a car while riding her bike to work. Bike is total but she is doing OK and is now back at work but under the loopy state of pain meds due to a fractured hip. To toss in irony the driver of the car is also a cyclist who has been hit by a car. Pay it forward revenge style it appears.

I think the app is pretty solid as I have run it through its paces a number of times. There are only so many screens and so many data types meaning a lot of the code is hit over and over as you create a case. I installed it a Xoom today and it ran perfectly. I have run it in the emulator but not on the actual device. The big worry is connectivity. If it drops in the middle of some areas it could be bad. Something you think about from a desktop but generally things work but on a phone walking through a hospital could cause the connection to drop easily.

I have researched the universal iPhone / iPad build setup for Xcode and I really want to take that route before we ship. Currently the iPad would just show a 2X iPhone version and I think that would be crappy. It appears Apple is using something similar to Android where you can name your XIB files with a ~ and have an iPad layout without code changes. You do have to tie all the controls to the extra XIB file but that should not be too bad. I will need better resolution images here too. Only the home screen with the buttons that launch you off to other areas should be affected but since I have not tried doing anything native iPad I would guess there are some other gotchas.

I am working on our scheduler written in Java using MigCalendar while I await QA and beta feedback on the mobile side. I have never used MigCalendar before so I have to learn those controls first. It has been a tough row to hoe. The MigCalendar API is not straight forward. Lots of parameters to lots of methods and it is not taking advantage of generics and enumerations as much as it could. The documentation is sparse and on-line help is limited. It seems to be very powerful but not super easy to understand. I have some of the requested enhancements in place I need like a tree control going more than 2 levels deep and multi-line header control for those instances. I now understand why our code was artificially limiting things to this depth as it had categories under categories that are hidden from the user as we filter 3 ways (ugly GUI here) - one from a tab, one from a combobox and one from the tree control. I need to clean all of this up without breaking how existing people use it.

I started with a test application just to learn the basics of the calendar controls. Once I got the header doing what I wanted I moved into our real code and started stripping things out and redoing what is created for the CategoryDepository which happens to be a singleton but not in a good way. This lead me to figure out why the old code that was executed every time any little thing happened in the UI did what it did. Of course nearly everything is broken now. I may have to abandon my changes due to the next item on the list.

Our C# developer has left the building but we need updates the the C# appointment scheduler. I know C# too having written a number of utilities in it and doing a conversion of a real-time stock market program from Java to C# a number of years back. Nothing like doing Java + Swing, Java + Android SDK, Objective C + iOS SDK and C# + WinForms at the same time.

There are 54 open issues on the C# scheduler and customers want them now. I have to get a handle on the code first. Initial glance shows a strong preference to cut and paste versus make a method, lack of any real comments and hard coded SQL strings that repeat every column name in every command instead of a nice collection of column names and the commands being built. You have to search a lot of code if you need to add a new column to a table and make sure everything works around it as the code stands. I can look at the code but not build and run it as it uses a couple of third party control packages and we need to transfer the developer license from the departed developers machine to mine once we sort them all out. He was not much on source control or documenting what he did so it has been a trial by fire.

I will be in another situation where I am using third party controls I don't have experience with just like I am doing with MigCalendar under Java. Mind set switching between Java and C# has been pretty easy in the past. There are some really nice things in C# and some annoyances. Visual Studio is a bit lacking compared to IntelliJ and Eclipse but is generally better than Xcode.

Yep another IDE to use. The Android side is Eclipse, Java + Swing is IntelliJ, Objective C is Xcode and now I get to use Visual Studio too! Different hot keys abound. I can easily be in all four of them in the same day with 3 running on a single machine. Keeps food on the table and clothes on the kids but my mind is turning to mush as keeping things straight during the context switches of the day can really wear you out. Nothing ever seems to be totally done and the list of open tabs I have in PSPad keeps growing. I have a tab with project notes for each item I am working on to keep track of what is left to do and my random thoughts on areas that could be improved. I jot stupid stuff on paper too during the day but try to keep a digital version of my thoughts so I don't forget things I need to revisit when I am in the code or to keep track of what is implemented on the Android that I have not ported to the iPhone or what has not gone in the opposite direction.

Wednesday, August 24, 2011

Android and iPhone apps in sync - here are the code results

I sent the Android version of my app off to QA yesterday. Both the iPhone and Android versions are now in sync and here are some code stats.

*.m   100 files   589,431 bytes
*.h   109 files   123,079 bytes
      =========   =============
      209 files   712,510 bytes


*.java 97 files   633,177 bytes

A lot more comments in the Java code as I have JavaDoc comments over each method but even with that the code base is a lot smaller and a lot less files. Anytime you have less code and less files it is easier to maintain and will tend to have less bugs.

I was surprised at how quickly the conversion went. A couple of things play into that, first I started on the more difficult of the two platforms - the iPhone. It is more difficult due to my experience with Objective C and the Mac in general. Some things are easier on the iPhone such as only having one screen size to deal with. Second once you have the database schema in place and the logical layout of the program ready to roll you are doing more straight coding and less designing.

Doing double development has some benefits. There are things on each platform that are pretty easy to do. They force you to look for a solution on the other platform. I don't want either version to outshine the other. The end user should be able to pick up any device with the app installed and be able to use it. There are differences in button placement - iPhone on bottom, Android on top - and the iPhone always has the "Back" operation as a button on screen where Android users know to press the dedicated back button on the device.

Programming differences

  • I found programming for SQLite much simpler on the Android. I pretty much had to build SQL statements by scratch on the iPhone and pass them off to the database. The Android helper methods make this much easier. Android made it super simple to handle cursor data in a scrolling list. All of that had to be written manually under iOS. 
  • I had to write the multi-part entity code to send images to the server on the iPhone where I was able to use the Apache libraries under Android.
  • Hiding buttons on a toolbar is as simple as setting visibility state on Android. Under iOS I had to totally recreate the toolbar for each unique button layout. Some views are reused for editing vs. viewing of the same data. I needed to hide buttons such as [Save] in the read-only version.
  • The home screen with icons laid out in 2x3 for portrait and 3x2 for landscape was easier to do on Android as I just defined two XML layouts and put them in the proper directories. All of this had to be done in code on the iPhone although I was able to use the animation framework to have a cool looking transition under iOS that I did not port to the Android.
  • Handling JPG images was easier on iOS. Pretty simple access to camera, gallery and image viewer. It was also simple to get to the raw data to store in blobs under SQLite.
  • Doing background / busy threads is much easier under Android. On the iPhone you have to control everything, shut off the UI, get the spinning gear going, dimming screen, etc. On Android I just put the busy work in an async activity and invoked it.
  • Date manipulation is much easier in Java. The method names make sense  startDate.after(endDate) where it is [startDate timeIntervalSinceDate:endDate] > 0 on iOS. 
  • Settings seconds to zero startData.set(Calendar.SECONDS, 0);  for Android and    NSTimeInterval time = floor([startDate timeIntervalSinceReferenceDate] / 60.0) * 60.0;  startDate = [NSDate dateWithTimeIntervalSinceReferenceDate:time]; for iOS
  • iOS wins for speed of simulator (much faster than actual device) and speed when running in debugger but Android wins for actually being able to see variable values, do ad hoc expression evaluation while in debugger and separating output log messages into tabs in the LogCat output window.
  • Android wins for being able to run on a multitude of devices via one code base. I have run it on various phones, the older Samsung Galaxy Tablet and a Motorola Xoom. The iOS version is for the iTouch and iPhone. I can run it on the iPad but only as a double pixel app. Some call it fragmentation but with very few program tweaks having the ability to run on a variety of hardware is just fine with me. Heck I do that with PC programs all the time. I rarely know the monitor resolution up front. Use the space you are given.
  • Easier to view the SQLite database under iOS. I used the SQLite Manger for Firefox on the Mac and was able to point to the DB in the simulator directory and look at all my tables with ease. On the Android side I had to export the DB from the device to a temp directory and use SQLite Database Browser to see the table contents. 
  • Releasing to the clients is a huge win for Android. I can just put it on any device I want. I can submit it to Market and have it available in minutes and the same for updates. For the iPhone I can submit it and wait until Apple approves it. I can not give our clients a solid date as to when that will happen. Same thing for bug fixes. This means our Beta cycle will be pretty simple on the Android and a royal pain on the iPhone. Our clients are not local, I can't just have them stop by for new version. Doing it ad hoc might be possible if we can get a doctor to understand iTunes and how to drag and drop things I send them along with getting their unique phone ID to me so I can provision it and build it into the app. This is a gigantic pain.
No matter which device I was programming against I spent a lot of time in Stack Overflow looking for answers. As I have gone back to the iPhone side to do some program tweaks and add missing features I put into the Android side I again realize how much Objective C and I don't think alike. I still have to look up method names as they just don't roll off my brain. Trying to think how many staring '[' I need before I get to type code just seems silly. Having to type multiple lines to do simple things gets old.

I really don't care for Xcode, I really want tabs that keep open the code I am looking at instead of reusing themselves as they see fit.

Even though I was used to it with C/C++ I really don't like having two files for every object - the H and M files. I just want to add a method and have it there to use instead of adding it twice. Then you have the fun of static variables you want others to access which is tons of lines of code.

It will be interesting to see how the users respond to the application. Will any switch between devices and will they spot differences they don't like between them?

The application is used by anesthesiologist to do charge capture and billing. You have to be a client of ours for it to be of any use to you. All you could see without that access is our login screen so I can't give a link the program for anyone else to give me feedback on why I am insane to prefer Android over iPhone or to tell me where I totally hosed up doing something on either platform.

Tuesday, August 9, 2011

Remember when hardware was fun and exciting?

Back in the day - this would be the 90's - InfoWorld, PC Week (now eWeek), PC Magazine and Computer Shopper were huge publications. They had massive hardware reviews, dozens of printers, 10 graphics cards, 8 sound cards, etc. It was a blast to read. The industry was exciting, new hardware was on the way every month. Now they are all electronic only or pamphlets. Printers cost $100 and you can hardly go wrong with any of them, just get what is on sale. Graphics cards are down to two major players with not much excitement around them. Hardly anyone cares about the sound card, use what comes on the motherboard and move along.

To me this is all pretty sad. I still build my own machines. Every time I think I can just do something off the shelf I can never find what I want. I really want a better sound card, I want something above the mid-line graphics card, a SSD drive at least for the boot drive, a big case where I can add things and a power supply I trust to run the whole thing for years to come. I do buy off the shelf machines for relatives, they don't play games or care about the best. By the time you buy an official copy of Windows you can't build one for them at the price you can get from Dell or random Best Buy vendor.

Since magazines don't have new hardware innovations to talk about and there seems to not be much in the way of software innovation either they have been spewing out top ten lists and comparing everything else to Android device X and Apple device Y. It has gotten damn boring. They also keep running "Study shows iPad will be the champ until 20xx". Give me a break, how can you predict any of that? Some of this stuff came out of nowhere and took the world by storm. In 9 years it will be ancient stuff and something new will be kicking it in the tail. Did they predict this same crap for the BlackBerry? You bet someone did. Just filling space so they can publish some article every day. Content has fallen off and most of the news is now just noise. I end up deleting most of the articles the minute they hit an inbox. Even Slash Dot articles have been more "maybe this will happen" or complaining about DRM or a movie review.

I want some excitement in the PC industry. We are not getting it from hardware or software. Both Lion and Windows 8 appear to be nice incremental updates but nothing to wow your socks off. Everyone has a color printer, LCD monitor, plenty of processing power and a big enough HD. Heck I just got a USB 1t HD for backup for all of $70! Terabyte was unheard of in a house a few years back.

My machines at work and home compile my Java and Objective C code in a matter of seconds. My phone beats the power of machines I had not long ago. My Xoom tablet does pretty much anything I need for the web and plays some decent games. I still enjoy playing Wii sports Tennis.

Tablets are cool but they are just what we had in a different form factor and we use our finger instead of a mouse. I am not doing anything that ONLY a tablet could do making it a must have. Sure some games let you tilt it to move your character but I had that on the Wii with its remote. I like it but it is not earth shattering. Most of the time I use the tablet it is just to read content from a website on the couch.

People talk about all the apps available on the Android Market and the Apple App Store. I don't know if you have checked many of them out but most are pretty crappy. You hit a block buster from time to time like Angry Birds or Monsters Ate my Homework but honestly most of the games are fun for just a few minutes. Even those games are just level after level of more of the same. They don't hold your attention for nearly as long as a reasonable Nintendo DS game. Games are not easy to write, they are one of the harder things you can do for any device as you are going to push the limits of the hardware. Stuff is being cranked out but you can tell. Some may be pretty but there is no substance behind it.

Doing a productivity app on a mobile device is also not easy. Typing much on any of them is just not fun. I don't want to do word processing or spreadsheets on my phone. I can stomach them a little bit on my tablet but really I consume data and don't create data on those devices. I can type 100+ WPM on my computer so I go there when I want to create much of anything including graphics. I am not finding must have productivity apps on mobile devices either.


Guess I am going to have to find my excitement elsewhere but I not sure what it will revolve around. Anyone have an idea where that could be?

Friday, July 29, 2011

Android vs iPhone - SQLite and Table Views

I am still in the process of converting my iPhone project to Android. Main goal is to allow doctors to create cases out in the field and submit them to the back office for case workers to finish up and send off to insurance companies to get paid. All the rest of the software is written in Java and Ruby and has been operational for a number of years.

There are currently 24 of tables involved in the process with some containing over 17,000 records. I am storing as much of the data on the device as possible but some of it needs to be requested per login session as it can change on the back end - facilities, referring physicians, providers, etc.

The basics of getting the table data in XML format, converting it via SAX parsing to table rows and executing inserts to populate the tables is very similar between the platforms. Where the fun begins is displaying the data to the user.

The iPhone has a standard Table View but it is up to you to do all the work. You have to build out the rows, you have to get them displayed, you have to deal with rows that have different amounts of data. You have to convert clicks back into rows of your table. It appears one developer did the SQLite side and another did the Table View side but neither talked to each other. Doing all the work yourself is not fun. I have hundreds of lines of code to handle what I am doing trying to be generic as possible. Even showing a simple string when you have an empty table is a pain. Finally you have the fun of performing the SQL queries in a simulated background thread. You are responsible for disabling the UI, showing the busy spinner, positioning the busy spinner and if you feel really adventurous showing some status text.

Moving over to the Android has been a pleasure. Sure, I have the whole database schema in place giving me a big head start but that is not what is making it so easy. The Android team decided to do a lot of the hard work for me and provided a SimpleCursorAdapter. I can query the table, get a cursor back and pass it to this adapter and my table automatically populates. In a ton less code I actually have better functionality. The rows automatically size to the strings I provide. If my cursor has no rows a simple addition to the XML file shows the "No matches found" message. Background activities automatically take care of disabling the UI and I get to show status strings with ease.

Things are falling into place so quickly on the Android side I am able to add extra features as I go. This equals some pain as I will get to back port those to the iPhone before shipping.

This is not the only area where I have found Android to have a more complete API. I can quickly create two views, one for landscape, one for portrait and have them appear without any special code. Doing that on the iPhone is a major hassle. You can either shuffle the controls around in code, which is what I have done, or you can attempt to have to XIB files and do double control hookups which seems like a massive waste.

The code base I am generating on the Android side is massively smaller than on the iPhone. Pretty much anything I do takes less lines and much shorter lines of code to accomplish. Objective C is not a terse language. The less I have to type the faster I can get code done even with auto-complete available on both platforms. I also don't mind refactoring code in Eclipse where it is s crap shoot in Xcode. If I have to do anything massive I do it in AppCode which helps a lot. Not having two files for every class is also nice, cuts the size of the code tree in half.

It is going to be interesting to see what the doctors think. Many have iPhones but are thinking of getting Android tablets to do their case work. Why an Android tablet? Because you can get it in a 7" form factor and that size fits in a lab coat pocket where the 10" iPad does not. My job is to be cross platform and as feature equal as possible. This gives them a wide variety of device choice between iOS and Android.

Beta testing will be straight forward on the Android. I can send them the file and have them install it or they can stop by my cube, plug in the device to my USB cable and I can copy it right over after I kick the device into debug mode. iPhone, well buddy you better tell Apple about the device, wait for it to get provisioned, build the code with the provision in place then copy it on to the device. If you want them to install it they have to play funky monkey with iTunes. Can't go over a 100 device limit either without buying a second license.

The other area of fun will be release time. The initial version of the app just handled appointments and not completing the case. I was able to release the Android Market in 10 minutes. It took a full week for Apple to approve my app and have it hit the iPhone App Store. If I want to fix a bug on the Android I fix it and release it on my own. I have to restart the process on the iPhone side. With the current, very simple app, out there I have not had to do a bug release. The new version is much more complicated which increase the odds of nasty bugs.

If we update our back end we can easily sync that with an Android release. With the iPhone we are in the total dark even if I am really sure I have not used anything in the code that Apple with frown upon. It could take weeks to get something accepted. Can't even do a proper marketing campaign when you can't give a real release date. Apple is not really enterprise friendly and they are not really all that developer friendly either.

I am pretty happy with what I have been able to pull off on the iPhone. It looks good and according to QA is working nicely.  I am very interested to see how the Android side turns out. So far it is matching the iPhone punch for punch and coming out better in a number of areas. Any time I have less code to write I know I will have less bugs and thus happier customers.

Thursday, July 28, 2011

Lessons learned by programmer doing the work of a graphics artist

At my previous position we had a graphics team that handled all the icons and other art needs. Wonderful group of people and they were very open to you handing them a less than spectacular image as a base idea and then making it into something professional. They also made sure icons followed a theme. At times they threw away my idea and went another direction. No big deal to me, they are the professionals.

Jump into my current position at a different company and we had a graphics artist but he has decided to move on and they are not in a hurry to replace him. He will do some freelance work for us and the rest they will farm out. That pretty much leaves me to do all the graphics work for the iPhone and Android devices on my current project.

I know my way around various graphics programs so this is not a huge deal and it does give a nice break from coding. I don't need a ton of assets and I have just enough of an eye for visual design that everyone has been pretty happy so far with what I am churning out. I do hit the web to find as much free art as I can. I always make sure it is free for commercial use. They rest I draw myself.

What are the lessons I have learned?

1. Always find the asset in the largest format possible. Sure you will be scaling it down but scaling down beats the crap out of scaling up any day. If the download page has it already scaled in multiple sizes grab all of them as a lot of time an artist will scale from a vector image giving much better results than a simple pixel scaling.

2. Once you find the asset in the large format save it in that format and only edit copies. I have screwed this up multiple times. Maybe I worked on a copy first then deleted the original or put it in some obscure directory. At times I find something while on the Mac and copy it to the PC to edit it or do the opposite. Then I can't find the original when I want to scale it again or tweak a color. The best solution is to have an assets area in your version control system to keep the originals.

3. Be consistent. Don't have a round add button (+) and a square [-] delete button. If one is gradient the other should be gradient too. Use consistent background images and colors. Make it look like all the screens belong to an app written by a person and not like it has been written by 10 people who never met each other.

4. Don't be afraid to throw away images even if you have done a lot of work on them. They might have looked good when you first put them in but the rest of the system has surpassed them so that one asset you loved now looks like it was drawn with a crayon.

5. Get feedback and accept it. If every person you show the app too says "Dude that background is ugly" then fix it. You may love it and may have done too many hours of work on it but the feedback of the masses is generally correct. If they can't read the white text on the background image change the text color until it is readable don't question their eyesight.

6. When looking for assets download anything that you think looks cool even if you don't have a current need for it. Never know when it will come in handy and finding it again might be impossible. I have directories full of potential icons I don't need right now.

7. Learn how to use various paint programs. I use Paint.NET, PhotoShop Elements, GIMP and some other utility programs. I was transferring everything form my Mac to my PC for editing. That was getting silly especially for simple resize and clip operations. I installed GIMP on the Mac, it is free and very full featured. MS Paint really is not what you should be using especially with so many free options available. You can use GIMP on pretty much any platform for free so it is a great place to start. Each program has strengths and features others don't and they all handle the same basic file formats so learn to use more than one.

8. Layers are massive time savers so learn and use them. If you watch a real PhotoShop pro like the graphics artist at my last job they have a lot of layers going even for something as a simple icon. It allows you to tweak various aspects of the image without redoing all the work or messing up the background. For the home page of my current project I wanted icons that appeared to be sitting on shelves. I have multiple layers: background, each shelf, each icon and each piece of text under each icon. I can quickly change the text or text color without screwing up my background. I can move elements around. I can resize the shelf. If this was a flat image that would take forever. You can save as a flattened image once you are done but keep in mind lesson 2 and save the PSD (or layer format of the software you are using) first then save the flattened image as a PNG. Don't lose your layers!

9. Nine-patch is a cool format for use on the Android. Make sure you investigate it. It allows you to quickly create scalable images when working on Android devices. Android docs on nine-patch is a good place to start. I am using this format for the shelves as there are various device sizes along with landscape and portrait orientations to do layouts against. Nine-patch is perfect for this situations.

10. Don't be afraid to experiment, it is the only way to learn. You may not be a graphics god but I am sure you can get place holder art ready to at least express the action of the icon. There is plenty to grab from the web even if it is just temporary. Everyone should know how to load an image and size and scale it. I cringe when I see a web page load super slow only to find out they loaded a 1 meg image and allowed the browser to scale it to 200x200.

11. Appreciate your graphics artist if you have one available to you. Playing around in a paint program is fun from time to time but you will soon realize it takes a lot of talent to produce professional looking applications. Back in the day everything was green, white or orange text on a black background. That does not cut it any longer.

12. Don't go overboard. Everyone has seen an application that looks like someone chugged a couple of cans of paint and threw up. Every label in a dialog is bold or italic and has a unique color. Use color to indicate something like an empty required fields but don't just use colors because you can. Multiple fonts can really mess with your eyes. For the love of {deity of choice} never blink / flash anything. I will stop using software instantly when that happens. I get migraines from strobes. I can't eat at Joe's Crab Shack or go with my kids to the roller rink with the disco ball. Blink screams amateur.

Monday, July 25, 2011

Switched to libxml2 ~ 25% less time and a lot less method calling

Since I was still disappointed with the XML parsing speed on the iTouch I decided to give libxml2 a shot to replace NSXMLParser. I got a 25% boost in processing speed, less memory thrashing and less method messaging.

iTouch Rev 1 hardware
Original TouchXML code (full DOM) 79  seconds
SAX parsing NSXMLParser           42  seconds
SAX parsing libxml2               30  seconds


iTouch Rev 3 hardware
SAX parsing libxml2              7.5 seconds

This is for 17,000 XML records in NSData format in memory to be written to an SQLite table. I still wish it could run faster but a speed up from 80 seconds a table to 30 seconds is pretty darn massive. In the simulator it takes 0.5 seconds. I have a feeling it could be a bit faster if I wrote out the records in this code instead of doing the callback but this is much more generic and reusable.

This was much easier to write than I thought it would be although it took a lot of research to get just the lines I needed. The pure documentation for libxml2 is not aimed at Objective C programmers so I had to find bits and pieces here and there. One site would talk about parsing from a URL and another how to do it from memory. One would show a big structure with statics for callbacks and another with a SWITCH / CASE statement. None of them showed how to clean up memory at the end.

Here is what the final code looks like. Its job is to take XML looking like this crappy made up sample (real stuff is diagnosis codes from doctors but the structure is the same):

<itemnames>
  <itemname>
    <id>10</id>
    <name>soup</name>
    <desc>soup you fool</desc>
  </itemname>
  <itemname>
    <id>15</id>
    <name>cat food</name>
    <desc>food for your cat</desc>
  </itemname>
</itemnames>

Passing in @"itemname" for elementName your SAXParserDeletgate would get two callbacks, each with a dictionary holding the following keys: id, name, desc and their associated values. As I get the callback I convert the dictionary items to an SQLite insert command (that code is not shown).

This is a very special case where I am simply getting a list of data. The only items in my XML data are records. All the data is in the TEXT area. You should be able to look up the XML_READER_???? info to parse attributes or other aspects of the XML. Already feels like I spent more time trying to put this into a blog posting than it did to get the code working but I don't want my efforts to go to waste.

This is my first attempt a using a syntax highlight editor in the blog so sorry if any of the code is screwy when you copy / paste it out of here. I picked C++ syntax as the closest as the syntax highlighter helper code I am using did not have anything specific for Objective C.

I really hope this can help someone out and show you how easy it is to actually use nearly straight C code to help speed up Objective C when parsing large XML data record sets. I ran it against the memory leak checker and it came out clean. The key was the final call to xmlFreeTextReader(reader);


If it does help please post a simple thank you comment. Always nice to know when the effort makes the programming life of another easier.


You MUST go to Project - targets (your app) -> Build Phase -> Link Binary with Libraries -> (+) to add libxml2.2.7.3.dylib to your overall iPhone project to be able to use libxml2. I am using Xcode 4.03, don't know how to do that in older versions but I assume it means adding it to the framework area. Doing the (+) way in Xcode 4 auto added the headers to the correct path. If it is not version 2.2.7.3 I don't think that will be an issue. It appears I am using some pretty basic libxml2 calls.

Call back delegate
//  SAXParserDelegate.h

#import <foundation/foundation.h>

@protocol SAXParserDelegate 
- (void) SAXDictionaryElement:(NSDictionary *)dictionary;
@end

Header file
//  SAXParser.h

#import <foundation/foundation.h>
#import "SAXParserDelegate.h"

@interface SAXParser : NSObject {
    id<saxparserdelegate> saxDelegate;
    NSData *data;
    NSString *name;
    NSMutableDictionary *dictionary;
    
    bool inZone;
    NSString *keyName;
    NSString *valueData;
}

- (id) initWithData:(NSData *)xmlData elementName:(NSString *)elementName delegate:(id<saxparserdelegate>)delegate;
- (void) parse;

@end

And finally the parsing code itself

//  SAXParser.m

#import "SAXParser.h"
#import <libxml/parser.h>
#import <libxml/tree.h>
#import <libxml/xmlreader.h>

@implementation SAXParser

// Initial with data, element name to find and callback delegate
- (id) initWithData:(NSData *)xmlData elementName:(NSString *)elementName delegate:(id<saxparserdelegate>)delegate{
    self = [super init];
    if (self != nil) {
        data = xmlData;
        name = elementName;
        saxDelegate = delegate;
        inZone = NO;
        
        dictionary = [[NSMutableDictionary alloc] init];
    }
    return self;
}

// Clean up any memory we used
- (void) dealloc {
    [keyName release];
    [dictionary release];
    
    [super dealloc];
}

// Start the data parse
- (void) parse {
    xmlTextReaderPtr reader = xmlReaderForMemory([data bytes], [data length], NULL, NULL, 
                                                 (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING));

    if (!reader) {
        NSLog(@"Failed to create xmlTextReader");
        return;
    } 
    
    while (xmlTextReaderRead(reader)) {
        switch (xmlTextReaderNodeType(reader)) {
            case XML_READER_TYPE_ELEMENT: {
                NSString *elementName = [NSString stringWithCString:(char *)xmlTextReaderConstName(reader) 
                                                    encoding:NSUTF8StringEncoding];                
                if (!inZone && [elementName compare:name] == NSOrderedSame) {
                    inZone = YES;
                    [dictionary removeAllObjects];
                    [keyName release];
                    keyName = nil;
                } else if (inZone) {
                    [keyName release];
                    keyName = [elementName copy];
                }
            }
            break;
                
            case XML_READER_TYPE_TEXT: {
                if (inZone && keyName != nil) {
                    NSString *string = [NSString stringWithCString:(char *)xmlTextReaderConstValue(reader) encoding:NSUTF8StringEncoding];
                    valueData = [string retain];
                }
            }
            break;
                
            case XML_READER_TYPE_END_ELEMENT: {
                if (inZone) {
                    NSString *elementName = [NSString stringWithCString:(char *)xmlTextReaderConstName(reader) 
                                                               encoding:NSUTF8StringEncoding];                
                    if ([elementName compare:name] == NSOrderedSame) {
                        [saxDelegate SAXDictionaryElement:dictionary];
                        [keyName release];
                        keyName = nil;
                        inZone = NO;
                    } else {
                        if (valueData != nil) {
                            [dictionary setObject:[[valueData copy] autorelease] forKey:keyName];
                            [valueData release];
                            valueData = nil;
                        } else {
                            [dictionary setObject:@"" forKey:keyName];
                        }
                    }
                }
            }
            break;
        }
    }
    
    xmlFreeTextReader(reader);
}

@end