Apps As Art

We seem to be on a roll again about this whole “indie is not viable” discussion. Next up, Allen Pike on Supply-Side Blues.

If you haven’t read them yet, I posted a couple other articles on this topic [see indie].

Allen’s post is spot-on with my assessment – the AppStore is becoming much like the music industry. Heck, when I was 16, I wanted to play professional saxophone. I might have actually had the chops to do it. At one point I started considering what my career outlook would have been. I noticed my saxophone teachers all had one thing in common – playing wasn’t their main source of income (or probably not; one guy played broadway shows in LA, but even that was inconsistent as shows come and go all the time). Average salaries were low.

Let’s look at another music angle. I have a family member with a reasonably good shot at making it big in the industry, but even then the sacrifice is large for an unknown, and certainly unrealized, payoff. Pick your poison – take a “real job” and do the things you love when you have time in the evenings, or go for it and make peanuts until (if) you make it big.

That’s why this whole thing is bananas. Apps are becoming art in much the way we approach music. And here’s the kicker – as indies, we LOVE to emphasize the care we put into the craft – paying attention to every detail, making it perfect. Tell me that doesn’t sound like something you’ve read on Etsy!

Friends, the gold rush is over, and we’re in this strange, mature, creative industry now. Customers expect loads of great things for free, we do it because we love it and have no expectation of making it big, and the only real money for most of us is taking that other job that actually pays. You know, that one converting CSV files for the sales team.

But you know what – it’s not going to stop me. I will keep on keepin’ on.

Tagged , ,

The AppStore Ice Cream Truck

Charles Perry (no relation), wrote this follow-up to Brent Simmons’ Love post (app store, indies, etc).

Here’s the question I posted to Charles – Does it change if the ice cream truck is the only one you’re allowed to sell on, yet the entire menu is only the top-sellers?

Sure – don’t blame the delivery truck, but there is significant impact when your product is effectively hidden from view. (How many times have you searched for the exact app title to be presented with dozens of unrelated results?).

Advertising is the obvious answer, and I think that has been the answer for years. I struggle with doing it right. How, where, and how much are all questions I don’t know the answers to. When bootstrapping something and your team is small, it’s a tough sell to spend money on things that may not have a direct correlation to income. BUT how else will people find out about our apps?

We (indies – I count myself as such, though I’m not making a living off it), need to figure this business out. Apple isn’t going to come to our rescue. They’re just the BigBoxRetail of apps. We need to do a better job getting the word out. More traditional advertising channels? Sure. Is there any reasonable way for us as a community to go about educating the average Joe and Jane that AppStore.app isn’t the only (or best) way to find apps? Actually, I wonder if they even care. See my previous post – perhaps the craft and quality movement will creep into digital.

Stay tuned. I hear the ice cream truck coming.

In the meantime a shameless plug: Have you heard about my latest project, Pocket Coach Pro? If you’re interested in getting fit (and I’m not talking about turning into a Hollywood six-pack… just about getting in shape and staying in shape – for your own health), then this app might be the one for you. Go, sign up, and you’ll be on my no-spam mailing list where I will send occasional development updates, take sign ups for beta testing, etc.

Tagged , ,

Indie Love

Is going indie on iOS viable? Nope. Don’t bother.

First, by Brent Simmons: http://inessential.com/2015/06/30/love

Then, Gruber follows: http://daringfireball.net/linked/2015/06/30/the-love-era

Hit the nail on the head. I have an app currently in development that might have a reasonable chance of doing OK, but I can’t see making my entire living off of it. Sounds great, huh? Here’s the thing – this is a concept I believe in (health), coupled with something I love to do – develop apps that I conceived. To try out-competing megacorp is foolish, but nothing says you can’t have a slice of the pie AND have fun doing it.

One thing I’m reminded of is how I sense parallels to the music industry (or any creative art, for that matter). We do what we do for the love of doing it. Few artists can make a full-time living off the art, but I don’t see that stopping them. Do it because you love it and because some people appreciate it. A few make it big, but not making it shouldn’t matter. That’s where Brent’s most poignant remark comes, “If there’s a way out of despair, it’s in changing our expectations.” True of everything in life, right? It’s often how you respond to circumstances rather than the circumstances you experience that matters.

Let me wonder this – we (Americans, at least) enjoy the big apps from well-known companies. The names sell themselves. Congratulations to them – that’s no easy task. We’re taking another turn. Things like the foodie movement, farm to table, hand-crafted goods, rejection of the mass-produced, etc are surging farther and farther into our society. We have a millennial spirit of idealism that may end up working to our favor. Will “hand-crafted apps” follow? Maybe. I mean, I don’t know if they actually will, but possibly there’s a chance, and I can hope they will.

So, if I may suggest – do it for the love. Any other benefit that comes of it is bonus.

Tagged , ,

iBeacons In Real Life

The State of iBeacons (June 2015)

This is pretty consistent with my experiences. On observation I’d add  about squirrely RSSI values – readings walk around a bit. You’ll do better to calculate a moving average and toss out the occasional 0 reading. Also be mindful of your iBeacon’s power settings – you may need to calculate your own distance from the RSSI value if the device isn’t transmitting at the default power expected by iOS. Even so, distance may not be entirely accurate. Take it with a grain of salt.

Tagged ,

Introducing: Pocket Coach Pro

This project has been near and dear to my heart long before I wrote a single line of code. That said, I think it’s understandable I”m pretty nervous about this pre-announcement. BUT, I also look forward to helping people change their lives for the better. Count this as a semi / unofficial announcement. :)

Several years ago I started getting more serious about my health and began searching for fitness methods that worked. I actually had a lot of trouble finding something that gave me results and was interesting enough to keep doing it. Running wasn’t it. Getting to local hiking spots was too time consuming. Attending the big gym down the road was overwhelming with all those machines (and therefore ineffective). Personal training was way too expensive. What I found wasn’t a set or specific routine or program that gets old after a while (you know those videos you can buy). It also wasn’t a fad celebrity workout.

It took a few years, but things finally started to click. I wanted something that reflected functional fitness to prepare me for “life,” and I needed structure to tell me what I should be doing.  I also wanted a system that was efficient and effective. I’ve been doing this for a few years now, and I can say that it works for a wide range of fitness levels. Most importantly, this system is sustainable for a healthy lifestyle.

Visit Pocket Coach Pro

The app isn’t quite done yet, but we’re getting there. Count this as a first look as we start to get the name out in the wild. Head on over and sign up if you’re at all interested. From time to time I’ll send out sneak peeks, some insider-only info, and I’ll throw in a little bonus for you to make it worth your while. And don’t worry – I don’t do the spam thing. I treat you the way I would also like to be treated – with respect.

I have no set launch date, but it will initially be available for iPhone / iPad, and likely thoughtful integration with Apple Watch.

Tagged , , , , ,

Why I’m Excited about watch OS 2

For those who don’t already know, I have Repetition Workout Counter in the AppStore for iPhone and iPad. The most recent release – version 2.0 – added support for Apple Watch from day one.

I didn’t quite know what to expect out of the Apple Watch. It’s quite an elegant piece of hard ware and software engineering. That is, until you start using third-party apps. The issues I (well, and many others) have is that running the app as an extension on the phone and communicating UI changes over Bluetooth has been problematic. It’s slow. Sometimes it doesn’t connect.

In the case of Repetition, it only does two things – increment a counter, and control the stopwatch function. At times even these are painfully slow – and I’m only sending minimal payloads (most often a single key:value pair) over the air to the Watch.

Now, enter watchOS 2, announced yesterday at WWDC. This is what we developers have been waiting for – the ability to run apps natively on the Watch, some UI improvements, access to hardware components, complications, and more.

For apps like Repetition, that means some key advantages over WatchKit:

  • Immediate UI updates. (counter and timer are immediate)
  • Haptic feedback from the taptic engine (say, a little buzz every X minutes)
  • Better heart rate monitoring
  • No need to be near your phone during workouts.
  • Watch face complications with the current reps and timer w/o having to open the app

So, you can see why we’re excited for these enhancements. I really think the watch is perfect for two things – notifications and exercise. With Apple pushing the Health aspect so much, I wouldn’t be surprised if they felt the same way.

Tagged ,

Automating iOS + Watchkit build numbers, with Git, for iTunes Connect

This wasn’t especially difficult, but it was a little tedious to track down.

I’ve relied on a handy little build phase script (I wish I remember where I found it) for automating build numbers from git commits for iOS apps since Apple gave us access to TestFlight via iTunes Connect. This same script didn’t work for my Apple Watch app as I anticipated, so it took a little bit to work out the kinks for AppStore submission.

It boils down to this

  1. Set the build number = the number of git commits on the branch.
  2. Change the Info.plist build numbers for the iOS app target, the Watch App target, and the Watch Extension target. This is done in a script build phase before the “Copy Bundle Resources” phase.
  3. Reset the Info.plist build numbers after all the build phases have run (another script build phase)

See the gist I posted for the complete script. Make sure to check the directory paths for the correct app name.

Happy building!

Tagged , , , , ,

Jay Fields on Unit Testing

Working Effectively with Unit Tests – Interview with Jay Fields

I rather enjoyed this interview with Jay. It was nice hearing a reasonable voice in the discussion – namely, there’s no need to be over-zealous about any one approach. Even in my own work I’ve seen a tremendous benefit to testing as I write code. It saves a lot of headache and provides some assurance that things still work down the road when code changes. Of course, as always, you need to be confident your tests are thorough enough in the first place.

Tagged

Measuring Perceived Performance

Measuring user perceived latency | Foursquare Engineering Blog.

I’ve found one of the most important components of performance is the perception. Within reason, the actual performance doesn’t matter as much as how people perceive actions to be progressing and completing.

This is about the user’s experience.

Consider the two possible experiences of loading a web page. In one version – the browser waits until all content is loaded. The other version – the browser progressively loads content, as we’re typically used to. Which one will feel faster, less frustrating, and more “productive”?

Are you doing the same in your app? Luke W explains it well in his post about content loading spinners.

Tagged ,

Localizing Your iOS App: Tutorial, Tips, and Tricks

I recently ran through the process localizing a client’s app. It took some time to fully wrap my head around the inner workings, but now in hindsight it seems relatively simple.

The general overview looks something like this:

  1. Identify strings that need localization
  2. Generate .strings files
  3. Export & send to translator
  4. Import translations
  5. Some Gotchas [Edit – this is new since this post was first published; I will add to it from time to time.]

If you would like to follow along at home, you can checkout the project on Github.

Pre-flight Check

Before we get going, it’s important to make sure all our ducks are in a row. You need to ensure your project settings file has Base Localization turned on. Go ahead and add a second language, which will kick off a few other operations you’d have to run manually. Among them is adding the en.lproj and the other language’s lproj directory.

Xcode project settings

In most cases, you want to turn on Base localization for your storyboard and xibs. Do this by opening the storyboard or xib, and checking “Base” in the Localization section of the File Inspector. Make sure English and the second language are both checked and the type is Localizable Strings, not Interface Builder Storyboard. This tells the app to use the base localized storyboard for all the layout, and the strings as the language-specific content source.

Interface builder view

You should have something similar to this

Project file list

Identification

The first step is to identify strings that need to be localized. It shouldn’t be hard – anything user-facing should be translated, except for a few words, such as brand and app names (though you can localize the app name if you wish).

Let’s start with some code.

@IBOutlet weak var introLabel: UILabel!
override func viewDidLoad() {
    super.viewDidLoad()
    introLabel?.text = "Hello, World! It's great to see you.";
}

This is a likely scenario – you’re setting text programmatically. The problem is that it’s only going to be in one language. The first step is to use the NSLocalizedString to provide a key name, and a comment used by the translator.

While you can rely on autocomplete to give you a rather verbose NSLocalizedString function, use this tip to take advantage of default values, leaving you with something equivalent to the Objective-C macro. For starters, use your English string as the key:

introLabel?.text = NSLocalizedString("Hello, World! It's great to see you.", comment: "A friendly greeting presented to the user when first opening the app.");

(We’ll change the key name later). Do this for every string you want translated.

Generate Your Strings

It’s time to run the genstrings command in the terminal – this will take all those NSLocalizedString keys and comments and spit them out in a strings file.

find . -name \*.swift | xargs genstrings -o en.lproj

Note: This will only work if you have the en.lproj directory created as part of our Pre Flight. Go ahead and drag the en.lproj/Localizable.strings file into your project. Put it in the Supporting Files group. Open it up. You should see this,

/* A friendly greeting presented to the user when first opening the app. */
"Hello, World! It's great to see you." = "Hello, World! It's great to see you.";

Caution: From this point on, you should not run genstrings on everything – only on new files that haven’t been processed yet. genstrings will overwrite.

Let’s change the key name in the .swift file and the .strings file:

introLabel?.text = NSLocalizedString("Message_GreetingText", comment: "A friendly greeting presented to the user when first opening the app.");
/* A friendly greeting presented to the user when first opening the app. */
 "Message_GreetingText" = "Hello, World! It's great to see you.";

Run the app. Still works. Now, go through your strings and swap out the keys for something not quite so specific. After all, your text could change, so a descriptive key would be helpful.

Now, change the value in the Localizable.strings file and re-run. Great, right!? You can see it’s working just fine – NSLocalizedString is correctly loading the content from the strings file. Time to move on.

Standard translation Changed the translation string.

Storyboards & xibs

Remember the preflight where some things happened, and maybe you didn’t understand it? Let’s look to storyboard in the Project Navigator. Notice that disclosure arrow to the left? Click it. I’m looking at the Main.storyboard (Base), Main.strings (English), and Main.strings (Spanish). Go ahead and click on the Main.strings (English) file.

Yuck!

Unfortunately, your’e kind of stuck with these ugly key names. They refer to the XML IDs in the interface file. If you look at the strings file in my demo project, you’ll also see accessibilityHint and accessibilityLabel values. It’s important that accessibility information (you should have it) be localized for screen readers.

Change one of the normalTitle values and re-run the app.

Pretty neat, huh?

Tip: If you ever need to add new UI elements to the interface file, work on the Base storyboard, then toggle the development language (e.g., English) From Localizable Strings to Interface Builder Storyboard, and back to Strings. The file will be re-generated. (You’ll be asked to confirm you want to do the conversion at both points).

Tip: Don’t bother trying to change the comments, either, unless you absolutely know you’re done tweaking the interface. It is more helpful to provide translators with screenshots if they are kind enough to take them. Context goes a long way.

Caution: There’s one problem you will run into – UITextViews. For some reason, as of iOS 8.1, an iOS bug prevents UITextView text values from updating. You’ll have to set it separately via NSLocalizedString. It’s a pain.

Export

Time for export. Xcode 6 introduces xliff file support – something that I understand is an industry standard in the translation community. To begin, select the top-level project in the Project Navigator, then go to the Editor menu > Export for localization…

In the dialogue that follows, select “Development Language Only”.

Save the file anywhere (I like to keep it versioned in the project root… on the filesystem, not in the actual Xcode project). I usually give it a name like “Translation Files”. It might take a few moments to process.

When you’re done, you’ll have a handy file called en.xliff (if English is your dev language). This is what you send off to the translator.

Import

The import process is easy. Select the project, then go to Editor > Import Localizations… . Xcode will do the rest.

Testing

Besides testing in your development language, make sure all the strings are displayed properly. You can even do this before a single word has been translated. Try running the app with pseudo localization. Open the scheme settings (Product > Scheme > Edit Scheme), select the Run group, and then click on the Options tab.

Check Localization Debugging (will show non-localized strings in all-caps).

Application Language – I often use Double Length pseudo language.  It makes all your strings twice as long. Sometimes you’ll see some funny formatting, especially if you have string substitutions (e.g. stringWithFormat) as part of your values. That’s ok – it’ll still work.

You can also export your xliff to an online service that will provide you with automatically generated pseudo languages you can try in the app. It’s funky looking, but it gets the job done. They’ll provide you with the appropriate instructions for their service.


Some Gotchas

NSLocalizedString with table

I didn’t catch this at first on a recent project. It’s subtle, yet frustrating to diagnose.

When using NSLocalizedString, and you reference a table, the translation export procedure will create an entry in the xliff file for the tableName.strings, which means on import, the appropriate .strings file will be generated. It’s worth noting.

Look for tableGenerationExample() in the demo ViewController, then look in the en.xliff export file. Scroll down until you find this section,

  <file original="LocalizationDemo/generated_table.strings" source-language="en" datatype="plaintext">

Look familiar?

Tagged , , ,
Follow

Get every new post delivered to your Inbox.

Join 433 other followers