Ad

Our DNA is written in Swift
Jump

App Information String Tokens

For my current project DTAboutViewController I need to be able to specify tokens in my strings that would be replaced by information about the app at runtime. Of specific interest are CFBundleDisplayName and CFBundleVersion which are both in info.plist. Usually you could hard-code these into your app strings or maybe do a global #define, but for my component I wanted to have the most flexibility with the least amount of work in subsequent project that would use that.

So I put together a category extension for NSString which also shows off how to use NSScanner to find tokens and replace them with values from your info dictionary.

NSString+Helpers.h

#import 
 
@interface NSString (Helpers)
 
- (NSString *) stringBySubstitutingInfoTokens;
 
@end

NSString+Helpers.m

#import "NSString+Helpers.h"
 
@implementation NSString (Helpers)
 
- (NSString *) stringBySubstitutingInfoTokens
{
	NSMutableString *tmpString = [NSMutableString stringWithString:self];
	NSScanner *scanner = [NSScanner scannerWithString:self];
 
	NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
 
 
	while (![scanner isAtEnd])
	{
		if ([scanner scanString:@"$" intoString:nil])
		{
			NSString *tokenName;
 
			if ([scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] intoString:&tokenName])
			{
				id value = [infoDict objectForKey:tokenName];
 
				if (value && [value isKindOfClass:[NSString class]])
				{
					[tmpString replaceOccurrencesOfString:[@"$" stringByAppendingString:tokenName] withString:value options:NSLiteralSearch range:NSMakeRange(0, [tmpString length])];
				}
			}
		}
 
		[scanner scanUpToString:@"$" intoString:nil];
	}
 
	return [NSString stringWithString:tmpString];
}
@end

The code works by skipping all characters until it encounters a $, which will be our token identifier. From this position the name of the token is until the first non-alphanumberic character, like a space or period. Then it tries to get a value from the bundle’s info dictionary. If it succeeds the token is replaced in a temporary string, if not it is left unchanged.

Note how I am passing nil into scan functions if I am only interested in skipping characters. If I am interested in the scanned result, then I am passing the address of a pointer to an NSString, that’s the reason for the &tokenName. The scan method creates an autoreleased copy of the scanned string and puts the pointer to this string into the parameter the address of which you have passed. That’s unusual in objective-C and seen more often in pure C code, so be aware of that.

Now with this code I gain a very useful way of displaying copyright information as the section footer of my settings table.

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
	return [@"© 2010 Drobnik.com. $CFBundleDisplayName $CFBundleVersion" stringBySubstitutingInfoTokens];
}

This will look like this, the app’s bundle display name is “About” and the version string is “1.0”.

With this method it’s even thinkable to put your own information into info.plist. Like for example the ID of your app which you would use for a direct link to the review page. Some people complain that Cocoa does not have regular expression support, but actually if you know how to use NSScanner properly you can do even more with it than trying to piece together working regexps.


Categories: Recipes

Leave a Comment