Implementing preferences in iOS – the wrong way
It’s not really my fault. Okay, it is my fault, but I have an excuse. I was a beginning iOS programmer. How was I supposed to know?
I’ve been developing software for money since 1985. In college, we used an IBM 360 mainframe, segued gradually to “mini” computers – a variety of DEC VAXes – and finally got a bit of exposure to the original IBM PC, running MS-DOS. We learned FORTRAN, Ada, Pascal, PL/1, Prolog, C, and assembly language and had a tiny bit of exposure to Unix.
When I got to industry, it was back to the mainframe. I developed flight software for the Atlas launch vehicle and its Centaur upper stage. It was programmed in assembly language and hosted on a CDC Cyber mainframe running a cross-assembler written by us (in FORTRAN!). The Cyber also hosted our written-in-house linker/loader/simulator. It was a pretty slick setup for the time, in spite of non-interactivity of the tools. We spent a lot of time waiting for printouts. But there were no punchcards! Unlike the college mainframe.
As the years went by, we graduated to VAXes and high-level languages (Ada!) and then eventually to Sun workstations and other high-level languages (C++!). And the flight computers became smaller, lighter, and faster as time went on. Although space-qualified computers are never going to be particularly small, light, or fast. My iPhone has way more power than the best flight computer I’ve ever worked on.
But there’s a common element among all those machines and my work on them. There was no such thing as a GUI. In all my professional rocket science work, I never wrote a single line of software for a person to use. Quite a handicap as I transitioned to iOS programming.
So it wasn’t entirely my fault when I wrote my first iOS preferences view. How do you move from your app’s main view to a preferences view, change defaults, and have them apply to the main view when you get back, and do all this while following the sacred object-oriented religion, not sharing any global data between objects, and making preferences persist between executions of the app? I finally figured it out – but I had no idea a couple years ago when I wrote my first app. But I worked something out.
So how do you do it the wrong way? The file system. Every time the Morse Trainer app is launched, it checks for the existence of its preferences file. If it doesn’t exist, it creates it and puts in default values for its two preferences. It goes through the same routine when transferring from another view (in this case, the only other view is the preferences setter), ensuring that it always knows the user’s current preferences. Behold:
- (void)viewWillAppear:(BOOL)animated {[super viewWillAppear:animated]; . . . // Get the current stored preferences // Get the full path of our preferences data archive file NSString *prefsDataPath = [self preferencesDataPath];// Attempt to unarchive it PreferencesData *preferencesData = [NSKeyedUnarchiver unarchiveObjectWithFile:prefsDataPath];// If it didn't exist, set it to defaults if (!preferencesData) { // Set to the default data sendSpeed = 2; // fast textSource = 0; // quotes } else { sendSpeed = [preferencesData sendSpeed]; textSource = [preferencesData textSource]; } . . . }
Pretty much the same thing happens in the settings controller, except that all changes are written to disk immediately, just in case the view is swapped out:
-(IBAction) sourceControlIndexChanged { textSource = self.sourceControl.selectedSegmentIndex; // Save the current preferences data to disk PreferencesData *preferencesData = [[PreferencesData alloc] init]; [preferencesData setTextSource:textSource]; [preferencesData setSendSpeed:sendSpeed]; // Get the full path of our preferences data archive file NSString *prefsDataPath = [self preferencesDataPath];// Archive the preferences data to file [NSKeyedArchiver archiveRootObject:preferencesData toFile:prefsDataPath]; [preferencesData release];}
And how did I implement the Settings view? A tab view controller. Sheesh. I don’t think I could have done it any more awkwardly. All I needed to do was push the Settings view controller via a button or something and pop it back off when I was done with it. But that never occurred to me. And the Apple reviewers let me get away with it, so I guess it wasn’t too terribly abnormal.
I didn’t know about passing information back and forth between controllers (prepareForSegue in the forward direction and delegation for going back). So I guess I did the best I could at the time.
I’m about to start a rewrite of the app to add a whole bunch of new content – see this post for a partial shopping list – and I’ll redo both the data storage/retrieval setup as well as how the inter-view navigation takes place. Watch this space for details on how I’ll do it The Right Way.