How do I know what environment variables are on iOS?

Apple’s iOS operating system is built on Unix, which implies that you can take your Unix-based C code, wrap it around some Apple iOS UI goodness, and have an iPhone application.

(Well, it’s more complicated than that. But if you’re a crusty ol’ Unix hacker like myself, there is something gratifying being able to use fopen() and getenv() instead of Apple’s NS-wrapped calls to these same routines.)

So how do you know what the environment variables are that are available on your phone?

Simple: I wrote a simple iOS program which neatly displays all of the available environmental variables.

And the variables available appear to be:

  • PATH: The path of available unix commands
  • TMPDIR: The temporary directory path (points to the temp folder in your sandbox)
  • SHELL: /bin/sh, natch.
  • HOME: The home directory of your application (points to the root of your sandbox)
  • USER: mobile, natch.
  • LOGNAME: mobile

and some odd ones I’ve never seen:

  • __CF_USER_TEXT_ENCODING = 0x1F5:0:0
  • CFFIXED_USER_HOME, which appears to be the same as HOME

In a debug environment I’m seeing the additional oddball variables:

  • CFLOG_FORCE_STDERR = YES
  • NSUnbufferedIO = YES
  • DYLD_INSERT_LIBRARIES = some path apparently on my host computer. (?)

Now this is on my iPhone running iOS 5.1; YMMV. Which is why I uploaded the program. Though it appears I would trust that both HOME and TMPDIR will both be available and point to the right place, and constructing the paths to the Documents and Library folders is just a matter of concatenating the path string returned from HOME. So if you need to write a new file to the Documents folder in the home directory of your application you can write:

char buffer[256];
strcpy(buffer,getenv("HOME"));
strcat(buffer,"/Documents/myfile.txt");
FILE *f = fopen(buffer,"w");
...
fclose(f);

UDIDs are gone.

After warning from Apple, apps using UDIDs now being rejected

UDIDs are now gone.

But it’s easy enough if you need a device unique identifier to generate one and track the device that way. Of course because you have your own unique identifier you cannot match numbers against other software makers, and you can’t guarantee the user will uninstall and reinstall the application–but on the other hand, if you save the unique identifier with the device and the user upgrades his phone, the identifier will move to the new phone, following the user.

Step 1: Create a UUID.

You can create the UUID using Apple’s built in UUID routines.

	CFUUIDRef ref = CFUUIDCreate(nil);
	uuid = (NSString *)CFUUIDCreateString(nil,ref);		CFRelease(ref);

Step 2: Write the UUID out to the Documents folder, so the UUID gets backed up with the phone.

Because under the hood iOS is basically Unix, we can use the C standard library to handle creating and writing the file for us:

	char buf[256];
     /* HOME is the sandbox root directory for the application */
	strcpy(buf,getenv("HOME"));
	strcat(buf,"/Documents/appuuid.data");

	f = fopen(buf,"w");
	fputs([uuid UTF8String], f);
	fclose(f);

Step 3: If the file is already there, use what’s in the file rather than generating a new UUID. (After all, that’s the whole point of this exercise; to have a stable UUID.)

Putting all of this together, we get the following routine, which can be called when your application starts up in the main UIApplicationDelegate when you load the main window:

- (void)loadUUID
{
	char buf[256];
	strcpy(buf,getenv("HOME"));
	strcat(buf,"/Documents/appuuid.data");
	FILE *f = fopen(buf,"r");
	if (f == NULL) {
		/*
		 *	UUID doesn't exist. Create
		 */
		
		CFUUIDRef ref = CFUUIDCreate(nil);
		uuid = (NSString *)CFUUIDCreateString(nil,ref);
		CFRelease(ref);
		
		/*
		 *	Write to our file
		 */
		
		f = fopen(buf,"w");
		fputs([uuid UTF8String], f);
		fclose(f);
	} else {
		/*
		 *	UUID exists. Read from file
		 */
		
		fgets(buf,sizeof(buf),f);
		fclose(f);
		uuid = [[NSString alloc] initWithUTF8String:buf];
	}
}

This will set the uuid field in your AppDelegate class to a unique identifier, retaining it across application invocations.

Now any place where you would need the UDID, you can use the loaded uuid instead. This also has the nice property that the generated uuid is 36 characters long, 4 characters narrower than the 40 character UDID returned by iOS; thus, you can simply drop in the uuid into your back-end database code without having to widen the table column size of your existing back-end infrastructure. Further, because the UDID format and the uuid formats are different, you won’t get any accidental collisions between the old and new formats.

And then it turns upside-down in an instant.

Cartifact is in the business of making print maps for high end real-estate companies, and I was brought in to help build a technical team, to make Cartifact a tech-oriented company in order to position it for an acquisition.

But sometimes things don’t work out the way you plan.

At the same time we brought in a new company President who spent the last several months going around and talking to different companies with whom Cartifact had a pre-existing relationship. And after doing this for several months, the conclusion was that Cartifact made beautiful maps, and so Cartifact should continue making beautiful maps, increasing their exposure via better marketing and trying to capture more of the market for making hand-drawn beautiful maps.

This means the idea of building a technology team and positioning Cartifact–well, that went away. I’m not sure exactly what happened behind the scenes, but the feeling I get was that they didn’t want to continue bleeding red for something they had no intention of building. And so there was no more money for me.

It’s troubling when you find out the opportunity you left a rather secure job for no longer exists. And it’s troubling to discover this opportunity no longer exists with perhaps a half-hour’s notice. But that’s the way of the world sometimes.

And the important thing is to re-orient yourself, shake out your contacts, and see where you want to go.

Ten years ago, nearly to the month, I wound down In Phase Consulting, a consulting company I had run with my wife for 9 years and took a job at Symantec for two reasons. One, right after 9/11, the market for freelancing was drying up thanks to the Dot Com crash and the worries over 9/11. And two, I found that I was bumping up against my limits: I had never managed a team, I had never hired people, I had never really learned how to delegate tasks or break down larger projects into smaller components.

And I wanted to get that experience.

Now I’m back to where I was 10 years ago, but this time, the market has changed. No more picking up the phone and making cold calls; we now have Facebook and LinkedIn and blogs and mailing lists and MeetUps. It’s easier now than ever to circulate out there and see what projects are there. And while this does not alleviate the marketing process, it does provide better avenues for searching.

And I’ve changed: I’ve successfully built a team from scratch. I’ve successfully hired and brought people up to speed. I’ve successfully delivered large scale projects, and for the most part when I’ve had enough control to keep things from going haywire, I’ve managed to consistently bring projects in on time and under budget.

So a few hours ago I filed an S Corp app with LegalZoom and created a very basic web site, Glenview Software Corporation, along with ordering business cards. I figure I’ll give the freelance thing a try again, but this time not be afraid of going after the larger projects.

We’ll see where this lands.

If you need a highly qualified software developer with 25 years of professional experience in mobile, embedded, web, desktop and client/server architecture software, please send me an e-mail.

Meanwhile I’m going to go back to what I do best: create damned good software through solving very hard problems. Like that LISP interpreter which I now have running embedded on an iPhone and an iPad which I plan to turn into a symbolic programmable calculator for those archiectures.