iOS Configuration with .ini
It seems like an unlikely candidate, but I’ve recently started using .ini files for iOS app configuration. I know what you’re thinking: “Wait - are you talking about those text files on Windows?”
Yep, those.
Common Formats
Let’s have a quick look at some of the most popular options for expressing configuration in text format.
XML
Historically, XML is probably the most popular choice - it’s widely supported, well understood by most developers, and expresses hierarchy quite well. In fact, plists are most often represented in XML, so they’re a natural choice for iOS and Mac developers.
<configuration>
<views>
<view>
<!-- displayed when app launches -->
<name>main</name>
<textcolor>#0000ff</textcolor>
<backgroundcolor>#ff0000</backgroundcolor><backgroundcolor>
</backgroundcolor></view>
<view>
<!-- contains copyright, version information -->
<name>about</name>
<textcolor>#777777</textcolor>
<backgroundcolor>#afafaf</backgroundcolor><backgroundcolor>
</backgroundcolor></view>
</views>
</configuration>
JSON
JSON is more fashionable these days, since it translates to dictionary/map structures quite well, and is also supported in most modern runtimes. It handles types very nicely, including lists and nested objects. It’s also noticeably lighter than XML, which makes it great for sending over networks.
{
"views" : {
"main" : {
"textColor" : "#0000ff",
"backgroundColor" : "#ff0000",
},
"about" : {
"textColor" : "#777777",
"backgroundColor" : "#afafaf"
}
}
}
You’ll notice the lack of comments - one of JSON’s biggest drawbacks for configuration is not being able to place comments in a file. There are some parsers that will accept certain sequences as comments, but that’s highly variable. For what it’s worth, the following NSJSONSerialization
example produces an error:
NSString *json = @"{ \"rawr\" : \"bam\" /* no comments */ }";
NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];
NSError *parseError = nil;
id obj = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&parseError];
NSLog(@"parseError: %@", parseError.localizedDescription);
Output:
parseError: The operation couldn’t be completed. (Cocoa error 3840.)
YAML
YAML is probably the least common, though quite popular within certain development communities. The syntax is very readable, and it’s arguably the best at representing hierarchy. It actually has quite a bit in common with INI, which makes it a compelling option for cases where .ini may be too light on features.
mainView: #displayed when app launches
textColor: #0000ff
backgroundColor: #ff0000
aboutView: # contains copyright, version information
textColor: #777777
backgroundColor: #afafaf
YAML has a large featureset, which is a contributing factor to the complexity of most parsers. YAMLKit looks like the best parser for Objective-C, but requires LibYAML. LibYAML is a classic configure/make/install project:
$ ./configure
$ make
$ make install
If you were already going to be supporting cross-platform software, this wouldn’t be a big deal (though you’d also probably not care about the Objective-C aspects either), but we’re specifically talking about iOS here, so this is a fair issue. Getting this built for iOS isn’t as trivial as the steps above, so roll up your sleeves for a cross-compile if you head down this path.
A Common Problem
Each of the examples above has a subtle problem that would cause most parsers to fail, typically leaving the entire document unusable. In each case, the error is so understated that it may only be caught by a runtime error.
- XML: careful to properly close those tags!
- JSON: curly here, curly there, and oops! too many commas
- YAML: derp - whitespace requirements around comments
Of course, the delicateness of these syntaxes is ultimately up to the implementation of the parser in use (and how strictly it conforms to spec). As you’ll see in a moment, I’ve skirted around those problems by implementing the ini parser myself.
INI
Let’s see what an ini file looks like.
[mainView] ; displayed when app launches
textColor = #0000ff
backgroundColor = #ff0000
[aboutView] ; contains copyright, version information
textColor = #777777
backgroundColor = #afafaf
There are a few things to like about the .ini version of this configuration. The file is incredibly readable. I prefer the section headings to separate objects in the file. The syntax isn’t dependent on whitespace, so there’s lots of freedom in formatting. In fact, the syntax of ini is so simple that there are relatively few things you can do to break it. When things do break, they typically only ruin the current line, which doesn’t prevent the rest of the file from being parsed.
BTINIParser
Of course, it wouldn’t be fair to try and advocate such an absurd notion without giving you the chance to try it for yourself, so I’ve included BTINIParser
in my BTToolkit project. It’s very much a work-in-progress, but includes tests and is powering a theming engine I’ve built into a little iOS project I’ve been working on. It could use a few more public methods, as well as some error reporting, so fork and submit PRs, please.
Try It Out
Hopefully this has piqued your interest in exploring an uncommon file format for iOS developers. If you’re working on an app that supports themes or other variable runtime definitions, it’s definitely worth a look. The ini file is a nice option that easily covers the key-value pair requirements of most configuration needs.
Let me know how it works out by chatting with me on ADN or Twitter.