Today’s bitch: Source Of Truth

One of the minor side projects I’m working on has an interesting problem. It turns out that under some circumstance, when a setting within the application is changed, the setting doesn’t “take”–meaning somehow the setting isn’t stored in a location which is then picked up and used by other parts of the application.

And this gets to one of my bitches about a lot of software I’ve had to maintain, and that is the concept (or lack thereof) of Source Of Truth.

The idea behind the Source Of Truth is simple: what class, library, method, global variable, or object is the definitive owner of a piece of data, such as a setting, a preference or the text that a user is editing?

And while this seems quite simple in theory, it can be quite difficult in practice, especially when software is maintained across multiple different teams or across multiple different programmers–because it is just sooooo temping to put some bit of data somewhere that’s easy to track.


Now sometimes the source of truth for a value is actually on a remote server. This does not give you a license to just make a call from one part of your code, stash the value away, then use (and edit) the value from there–because it means someone else may come along and follow the pattern, and now suddenly you have two copies of the value, and no assurances those two values are in sync.

Better instead to create a common class which can cache the value as appropriate. And that may mean that in order to retrieve the value for the current setting you have to perform an asynchronous call with a callback to finish populating your interface.

In concrete terms, suppose to get the value we need to reach out to a remote server and call the api/setting endpoint. Because we cannot guarantee that we will have the data locally we can create an API call which, if the data is local, calls our callback method–but if not, reaches out to get the current value.

- (void)getSettingWithCallback:(void (^)(NSString *str))callback
{
	if (self.value) {
		callback(self.value);
	} else {
		void (^copyCallback)(NSString *str) = [callback copy];
		dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
			/*
			 *	GET http://eample.com/api/setting for our setting
			 */

			NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://example.com/api/setting"]];
			[req setHTTPMethod:@"GET"];
			NSData *data = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];

			NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
			self.value = str;

			dispatch_async(dispatch_get_main_queue(), ^{
				copyCallback(str);
			});
		});
	}
}

Note there are two things wrong; first, we’re not handling errors gracefully. Second, if we have multiple callers attempting to get the same value, we may wind up with multiple simultaneous network calls to the back end. In that case it may be useful to track if we have an API call in flight, and if so queue up the callback into an array, so that when we get the value we need we can iterate the array and call all the callbacks.

If it turns out that it is possible the value could change on the remote server–because the user could log into a web interface, for example–then it is easy to associate this with a timer, and invalidate the value, forcing us to get the latest and greatest after some idle time has passed.

But the general gist is here: if we want our setting, we simply call

[[APIObject shared] getSettingWithCallback:^(NSString *value) {
        ... do something interesting here...
}

Our save method would both update the back end–remember: our back end is the source of truth. We also store the value locally so we’re not constantly hitting the back end:

- (void)setSetting:(NSString *)value withCompletion:(void (^)(void))callback
{
	self.value = value;

	void (^copyCallback)(void) = [callback copy];
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
		/*
		 *	PUT http://eample.com/api/setting to save our setting
		 */

		NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];

		NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://example.com/api/setting"]];
		[req setHTTPMethod:@"PUT"];
		[req setHTTPBody:data];
		[NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];

		dispatch_async(dispatch_get_main_queue(), ^{
			copyCallback();
		});
	});
}

Again, the error checking is not here, but it serves to sketch the general idea: send the updated value to the back end in a separate thread, then notify when our update is complete.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s