Google+ Badge

Tuesday, January 10, 2012

Saving database favorites on mobile devices

After a number of missteps I think I have database favorites savings in place on both our Android and iOS applications. How hard can saving favorites be you ask? Much worse than I originally thought for a number of reasons.

With a mobile device a number of things come into play. First there is no log out. As programmers we like symmetry. Open file / close file, log in / log out etc. Matching pairs are awesome. Load when they start, save when the exit, what a beautiful world that would be. You really can't guarantee log in / log out pairing on a desktop application but you have a better shot of it happening. On the mobile side your app can be kicked out due to low memory conditions or the user Task Manager killing it.

My users can log into a specific URL instance then pick the practice which has an associated provider name for their log in name. There are some tables that are static per URL instance, in my case ASA, CPT and Diagnosis, and some tables, which I call session tables, that need to be reloaded per log in. The user wants their favorites and default favorite to carry over between log in sessions and between table reloads from the server.

Between log in sessions the system administrator can add, remove or modify records. That is why we reload things per session. These are generally small tables such as Facilities, Referring Physicians and the like. The large ASA, CPT and Diagnosis tables are updated on a yearly basis. I download a permanent cache table per log in session to see if the ASA, CPT or Diagnosis code table needs to be refreshed even if all my other rules make it look stable. Even when the powers that be decide there are new Diagnosis codes I don't want to lose your list of favorites.

Just after a successful log I check to see if there was a previous successful log in. If there was and the URL is not the same I save all database table favorites to a favorites table if the current database table has at least one row. All tables are loaded on demand meaning I only load them if you need data from them, I don't just load them at start up all while you twiddle your thumbs, that would be a huge waste of time and bandwidth. If the URL is the same but the Practice or Provider is different I save favorites for session tables before I clear them. I don't actually clear the table but I mark it as stale. If during this session a user needs data from that table it will be cleared and reloaded.

Once all that is done I save the URL + Practice + Provider to user settings. That would be NSUserDefaults on iOS and getSharedPreferences on Android. This is the last known good log in. Basically I check for previous session log out during the log in processing. I use the special preferences area on each platform as that is maintained even if the program is ejected from memory or killed by the user. If the user uninstalls the application then all settings are gone and a new install will start the process all over.

In general the ASA, CPT and Diagnosis tables are the same across URL instances but not always. The tables are loaded from the same text file but ProgreSQL does not maintain the same order during record inserts for some reason. We were saving the unique key in the favorites table but now need to save the Code + optional Modifier instead allowing me to match favorites across URL instances. I convert back and forth between ID and Code + Mod as I want to do comparisons on the much faster integer instead of slow string checks once things are up and running.

If you log in to the same URL as the same user and select the same practice every time then I don't save / load favorites to the backing table for the big three. They are just flags set on each table as you toggle the favorite flag on records. The main ASA, CPT and diagnosis tables will only refresh from the server when they get a new master load. Session tables will get a save / load of favorites per log in. It will all be pretty transparent the user. They get a quick busy screen as I load session tables from first time use and after that the data is quickly available for the rest of the log in session.

Keeping it all straight was fun. I need a fully qualified previous log in that I don't touch until all favorite saving is done. I don't want to gut any of the old table data until all favorites have been saved and I have to use backing table data for everything as just saving to the program objects is not persistent enough. When I access the table for favorites saving I need to make sure I don't do any of my stale data checks which would force the table to clear and be repopulated with new data. Post successful log in I don't want to know about the old log in. Tables can be reloaded from the server at any time as the user does an ASA search for example. When the table is stale I need to load it from the server then apply the saved favorites, if any, from the last time they were saved for the current log in URL + Practice + Provider log in. Keep in mind those might have been save 20 log ins ago because the user may have used the ASA table just once in the past 20 sessions even though they logged in over and over.

There can only be one default favorite set per table per unique log in so I have to delete the old before I set the new if we have one.

The best way to handle all of this in testing was to use the emulator for Android and the simulator for iOS. You can't get to the database on a device unless you have a rooted device. Great for security, not so fun for a developer. The emulator in Android is not exactly fast so it made testing less than fun. Plus I have to copy the database file from the device to my hard drive then open it with a third party tool to view the data. I had to log in repeatedly as different users. At last both platforms support the keyboard so I did not have to mouse click on-screen keyboards for the URL, User Name and Password over and over.

The simulator for iOS is faster than the device which is very nice. I use SQLite Manger under FireFox to view the database. The only issue there is anytime you update the simulator you have to track down the database under /Users/{your name}/Library/Application Support/iPhone Simulator/{iOS version}/Applications/{GUID}/Documents/{app}.db and you have to remember to click "All Files" under Format: before you do this as the default is *.sql.  If you navigate all the way down the directory path and go "where the heck is my file?" then change to "All Files" you get to navigate all the way down there again which is not user friendly. SQLite Manager remembers the last file opened so you don't have to do this very often. Of course if you test under iOS 3.x, 4.x and 5.x you will have to navigate to each directory for that test. At least the iPhone and iPad share directory for a given iOS version. At first that threw me off, I thought when I switched between iPhone and iPad simulators I would get different data but it loaded up my favorites from the iPhone.

The updated applications are in the hands of QA now. Hopefully I will get them submitted in the next couple of days and go back to my JIDE scheduler table work. It has been a rough couple of days under the old wireless headphones pounding away on both platforms.