An Objective-C wrapper for AudioServicesPlaySystemSound

In Tallymander 2.0 I use a handful of sound effects to provide user feedback. I thought that the amount of code I had to write every time I wanted to play back a sound was a bit cumbersome so I wrote this wrapper around the AudioToolbox C functions I was using.

DCSoundServices.h:

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
 
@interface DCSoundServices : NSObject {
 
}
 
+ (void)playSoundWithName:(NSString *)fileName type:(NSString *)fileExtension;
+ (void)vibrateDevice;
 
@end

DCSoundServices.m:

#import "DCSoundServices.h"
 
@implementation DCSoundServices
 
+ (void)playSoundWithName:(NSString *)fileName type:(NSString *)fileExtension
{
 
	CFStringRef cfFileName = (CFStringRef) fileName;
	CFStringRef cfFileExtension = (CFStringRef) fileExtension;
 
	CFBundleRef mainBundle;
	mainBundle = CFBundleGetMainBundle ();
 
	CFURLRef soundURLRef  = CFBundleCopyResourceURL (mainBundle, cfFileName, cfFileExtension, NULL);
 
	SystemSoundID soundID;
 
	AudioServicesCreateSystemSoundID (soundURLRef, &soundID);
 
 
	AudioServicesPlaySystemSound (soundID);
 
	CFRelease(soundURLRef);
 
}
 
+ (void)vibrateDevice
{
	AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
 
@end

Assuming you’ve got a sound file named click.aif in your application bundle, you can play a sound in just one line of code:

[DCSoundServices playSoundWithName:@"click" type:@"aif"]

Or, vibrate the device:

[DCSoundServices vibrateDevice]

Much tidier than having to import AudioToolbox into every UI that ever plays back a sound.


Convert a CSV into a Plist file

Here’s some code from awhile back. It’ll take a CSV and turn it into a Plist file.

The structure works like this:

The first row of your CSV is considered to be the header. The code will use content from the header row to create key names. For every subsequent row, the code will create a dictionary, associating the content of each column with the key names from the header. The dictionaries live in an array which is written out to a Plist file.

This is handy if you’ve got a bunch of spreadsheet-type data you’d like to use as the basis for a static reference in your app. Say, data about the periodic table, airport listings, trivia questions, whatever. You can sift and sort your data in a user-friendly environment like Numbers or Excel, then dump it out to CSV and put this code to work.

It uses cCSVParse to read the CSV file.

//	NSLog(@"Converting %@",pathAsString);
 
	CSVParser *parser = [CSVParser new];
	[parser openFileWithPath:pathAsString];
	NSMutableArray *csvContent = [parser parseFile];
	[parser closeFile];
 
	if (pathAsString != nil)
	{
 
		NSArray *keyArray = [csvContent objectAtIndex:0];
 
		NSMutableArray *plistOutputArray = [NSMutableArray array];
 
		NSInteger i = 0;
 
		for (NSArray *array in csvContent)
		{
 
			NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
 
			NSInteger keyNumber = 0;
 
			for (NSString *string in array)
			{
 
				[dictionary setObject:string forKey:[keyArray objectAtIndex:keyNumber]];
 
				keyNumber++;
 
			}
 
			if (i > 0)
			{
				[plistOutputArray addObject:dictionary];
			}
 
			i++;
 
		}
 
//		NSLog(@"Plist output array %@", plistOutputArray);
 
		NSMutableString *mutableString = [NSMutableString stringWithString:pathAsString];
		[mutableString replaceOccurrencesOfString:@".csv" withString:@".plist" options:nil range:NSMakeRange([mutableString length]-4, 4)];
 
		NSURL *url = [NSURL fileURLWithPath:mutableString];
 
//		NSLog(@"Write to URL %@",url);
 
		[plistOutputArray writeToURL:url atomically:YES];
 
	}

That’s from a simple desktop app I built for the purpose. It takes the path of your CSV, via drag-and-drop, then spits out the Plist beside it.  You can grab the source at Google Code. You can also download this compiled binary if you want to get crackin’ right away.


How to Run a Great iPhone Beta Test

The App Store has over 100,000 apps available for purchase.

That’s a big number.

I can’t tell you the secret to success in the App Store. I haven’t discovered any magic formulas. The one thing I can tell you with 100% certainty is this: if you ship a product with bugs that genuinely piss off your customers or, worse, make them unable to use some portion of your hard work, the road toward success just got much longer and a lot more miserable. Even with “prioritized review” requests, once your buggy app is on the store, the review queue means you could be waiting a long time before your customers can enjoy your bug fix.

You will not sleep well at night.

The good news is that this problem has been solved a thousand times over. A thorough beta test can save you from this terrible outcome and the sleepless nights that come with it. With a bit of diligence, you can ship an app with awesome stability to match your great ideas.

I’ll share how I run my betas. This is about process — the technical side of Ad Hoc distribution is thoroughly documented by Apple and others. The tools I use are simple and free. They work. If you have suggestions for better ones, I want to know about them before my next test! Please share in the comments.

Gather Your Testers

You’re going to need testers. If you use a volunteer testing pool, figure that 80% of testing will be done by 20% of your volunteers. That means to have any hope of gathering meaningful feedback, you’ll need at least 10 volunteers. 15 or 20 would be the ideal minimum.

Next, you’ll need to start early — but not so early that your testers’ enthusiasm cools by the time you ship your first beta build. Try to get the word out between three to six weeks before you’re ready to test. If you have a particularly strong network, you may need less time. If you’re relying more on the kindness of strangers, though, make sure to give yourself plenty of lead time. Use your website, your blog and your Twitter to solicit volunteers but don’t be a pest.

Most importantly: if you have contact with existing customers from previous versions or perhaps other products entirely, send a quick, polite email explaining what you’re going to test and encouraging them to join up. Customers who have emailed you in the past with feedback or feature requests may be especially keen on seeing your latest work.

Understand Your Testing Pool

You need to know things about your testers. What kind of hardware will they be using? What version of iPhone OS? What’s their geographic region? (International testers are crucial for finding your screwups with localized formatting and other geographic issues.) Most importantly, you’re going to need to collect their UUID.

I use Forms for Google Docs to collect most of my feedback. It’s an easy-to-configure way to store structured data. Since you can designate required fields, you can ensure your testers don’t forget to include anything crucial when reporting issues. Best of all, you can embed the forms into existing web pages, allowing you to include them alongside testing guidance, version history or other information that might be useful.

Here’s the form I use to collect beta tester info:

It’s not short. The other benefit to using a form is to ensure you only get testers who are reasonably skilled at providing detailed information. Everyone who makes it past this filter has a decent shot at actually telling you everything you need to know later in the beta.

Betas Worth Testing

If your app doesn’t look somewhat close to the final product, don’t distribute it for testing. If you’re relying on a volunteer testing pool, you want them eager to use your stuff. If your overall UI is incomplete, you’re not ready to test. You’ve got a limited amount of time and attention with your testers. Don’t squander it on early builds you could easily test yourself. The closer you think your app is to shipping, the more useful your testers will be to you.

You do yourself no favors if the last bit of functionality you add affects previously-solid features that now need a whole new round of testing.

Believe me.

Distributing Your Binaries

So you’ve toiled away and you’ve finally got an app you feel is ready to be tested.

Don’t email the binary.

Depending on the size of your app, the attachment may not be properly delivered to the recipient, especially if they’re using Exchange-hosted email. Instead, host your app and the necessary provisioning profile on the web.

I also recommend a simple checkout system. When you’ve got a new build ready for testing, send your testers a link to a one-question form that asks for their email address. On the form confirmation screen, you can provide a link to the binary. This lets you get an idea of who is participating. It also lets you answer an important question: am I getting zero feedback because there are no bugs or because there are no active testers? With a checkout process, you’ll know for sure.

Use clear versioning with every distributed build. You need to be certain you and your users are talking about the same build when they give you feedback. Beta phases, version numbers, dates, whatever you need to make things clear for everyone involved. Make sure that somewhere in your app, your code pulls the version number out of your app’s Info.plist file and displays it in the UI. Here’s some code to get that string:

NSString *currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];

Update your Info.plist’s version number religiously before distribution. Even better, update it automatically.

Communication

You need to communicate with your testing pool. Keeping them regularly updated, without being spammy, is crucial to keeping them excited about what’s going on.

When you send out a new build, explain what’s new. Give specific testing guidance so that your testers know where they should focus their efforts. New method of rendering cells you strongly suspect has a bug or two? Share your hunches. Built a new import subsystem you’re suspicious might break with legacy data? Provide an example data set or encourage existing users of the old version to give it a whirl. If you’re manipulating data that lives only on the user’s device, tell them to back up their iPhone before running your beta. And tell them why. Don’t be a douche on this: never take for granted the importance of your testers’ application data, especially if you’re lucky enough to have your existing customers helping you. Yes, we all know, betas are risky. Still, the iPhone isn’t the desktop and if you’re testing an upgrade to an existing app, your customers may have data they created before accepting the risks of a beta.

If it has been more than seven to ten days since your last build, send a quick update to the pool. Call out awesome testers and let them know they’re making a difference. Share the score of resolved issues you were able to fix thanks to the pool’s feedback. Most of all, thank them. Their efforts are helping you to not look like an idiot.

Sandboxing Helps

If you’re making an all-new app, you can skip this section. If you’re testing an upgrade to an existing app, read on.

Your early beta builds should use a different app identifier than your shipping app. This way, your beta will be installed as a distinct application alongside your currently shipping version on the user’s device, instead of overwriting it. This lets you ensure that no shenanigans happen to legacy data if you’ve got customers from previous versions in your testing pool. Toward the end of your beta, after much internal testing, you can start distributing builds that overwrite previous shipping versions. You’ll want to ensure that the app properly imports legacy data and preferences and, of course, ensure nothing otherwise disastrous happens when iTunes distributes your updated app to existing customers with existing data.

Gather Useful Feedback

You need to know things about how your application is failing. The forms I use are inspired heavily by what I saw once upon a time during the iPhone OS 2.0 Enterprise AppleSeed beta test. I think it covers the basics. Of course, don’t be afraid to add other questions specific to your application’s circumstances. For bugs, measure how well you can reproduce the bug and its overall severity while capturing the reproduction steps. For feature or enhancement requests, measure the tester’s impression of the request’s importance along with what they’re asking for.

However you gather your feedback, ensure that every single message you send to your testing pool includes brief instructions on how your testers should report their bugs. Never make them hunt around for it.

Ship

You haven’t added or changed anything for a couple of builds. You’ve fixed plenty of bugs and no one has reported regressions. You’ve sent emails to your top-performing testers and they’re giving you a thumbs-up.

Give it one last test yourself. Thirty minutes, minimum, actually doing things your users are likely to do. Is it kosher?

Then ship it.

Then, while you wait for approval from Apple, test it some more, informally, yourself. If somehow a show-stopping bug pops up while you await approval, you can reject your binary and submit a fix before it ruins anyone’s day.


Customers, Never Guests

The trouble with the Hero’s Journey is that there will be trials.

The universal trial, of course, is money and I’m hardly exempt. There’s a sixty day delay between me making money from an iPhone app and Apple actually paying me. That leaves immediate, painful gaps in my cashflow.

The obvious solution to this is consulting — I’m privileged to know how to do a lot of things that are useful to people. Unfortunately, I’m still learning how to market, grow and manage that particular end of my business, so I’m painted into the most dread of corners: retail.

I live by the axiom that no honest man is too good for honest work. So while retail is often the dullest, most imagination free work you can do before hitting manual labor, that’s not the part that I hate most about my seasonal job.

No, the worst of it is this: I have to call my customers “guests.”

This is some of the most odious corporate newspeak bullshit in recent years. It has always irked me. Guest means a specific thing: certainly it implies hospitality, which may explain the intent, but it fails to properly convey the truth of the relationship between the store and the customer. Being the guest of another places the guest in the inferior position and the host in the superior position. While manners may require that hospitality be extended, being termed a guest in the final equation simply means that the customer does not belong there. It suggests they belong somewhere else.

This is the wrong view.

The customer is not a guest of the store. A successful retail experience means that the customer is at home in the store.

Somewhere, somehow, having “customers” became a distasteful condition for large corporations. This is unfortunate and I wish they would cut the crap. The truth is that there is honor in having customers. There is honor in upholding the sanctity of the customer relationship. Being a customer of a business means something very specific that no other English word can capture. Being a customer means being the lifeblood of a business. Being a customer means being the motive force behind a powerful organism that provides products, services, livelihoods and, ultimately, the basic existence of others. Being a customer is being part of a tradition that keeps babies nourished, families housed and people clothed.

That means something. Something potent. Something that must be continually venerated if we’re going to keep moving forward as rational people. Does any of this sound remotely like having a “guest” to you?

I’m proud to have customers. I’m proud to respect their importance to my business and their contribution to the fact that I’m not sleeping outside tonight. That is essential to my work ethic and it will never, ever change.

The end of my seasonal retail job can’t come fast enough. I’m not sure my teeth will survive the grinding required for me to get the word “guest” past my lips on every shift.


Career Advice: Penelope Trunk is a Charlatan

(Or: Physician, heal thyself)

Let’s start with this: I’m an idiot. I’m 24 years old and I don’t know anywhere near as much as I need to. I convince myself otherwise because without the strength of thinking I know at least something, I could never get much done.

That said, I do know this: there are only a few people who you should take advice from. I mean life advice: advice on how to be who you are, how to manage your world, how to grow as a person.

  1. People who have demonstrated an interest in your success and years of loyalty. You’ll be lucky if you get one of these. I hit the lottery, and I have two. You’ll know them with this test: If they asked you to drop everything and save their ass (business, product, family, life) for a month, you’d do it without hesitation.
  2. Your significant other. This is someone who spends a lot of time with you and sees all that you struggle with, all that makes you happy. You’ve been through good and bad and get wistful recalling both. My luck continues: my girlfriend is the wisest counselor I could ever ask for.
  3. Yourself: If you cut the crap and take a long walk alone, you can ask yourself anything and usually get the right answer. Make the time to know your own thoughts: you might be surprised how much is waiting in your own brain.

That’s all. Here are people who should not be trusted for advice:

  1. Some dick with a blog (even me). If you’ve ever read a top-ten post on a blog, you know the content is cranked out to drive pageviews. The author probably slapped the content together in the space of two hours to benefit an audience of thousands. Like with drive-by legal or medical advice, you’re a fool to assume you can get something directly applicable to your case from a one-size-fits-all post.
  2. Parents. Your mileage may vary but parents are often too invested in your safety and security to be able to weigh the benefits of those risky life decisions with huge payoffs and incredible experiences. If your parents are batshit insane (thankfully not my case, but I have seen this) that investment may yield terrifyingly bad advice. Even if the advice you get is reasonable, there’s plenty we don’t need to tell our own parents.
  3. Your social circle. Excluding a choice best friend or two, your social circle can’t tell you anything useful about how to run your life. Groups breed conformity and breaking from that might be consciously or even subconsciously discouraged.
  4. Penelope Trunk. (cf. #1)

Penelope Trunk wants to tell you how to run your career. She presumes to be an expert on this subject. She’s not.

Once upon a time, as a young man desperate for growth and success, a blog specifically like hers, geared toward shameless career ambition, seemed like crack. Loyal readership taught me otherwise. Penelope Trunk is someone barely in control of her own life. That she is honest and open about her flaws is endearing but doesn’t change the fact that she cannot provide viable career advice based on personal experience. She’s proudly a trainwreck and while that may be great for her blog’s readership, would you trust a fitness trainer who doesn’t exercise and can’t stick to a healthy diet? Mental health counseling from a patient in a psychiatric ward? Computer advice from someone who uses Windows 98? Come on. I may be an idiot but at least my bullshit detector works.

Only when Penelope Trunk is viewed as a cautionary tale will you find viable lessons for your own career. I would never claim to be qualified to advise you on how to run your life. Nonetheless, if you take the things Trunk has done with her life and imagine the opposite, you may find valuable guidance.

Read on for these lessons.

Continue Reading…


Next

Prev