iOS Beta Testers Wanted!

I'm putting the last few VoiceOver accessibility labels in my iOS app (RoadTrip) and then it's basically done. I want to take a stab at putting some custom textures in and I'm sure I'll find some new bugs once I turn off VoiceOver and go back to the iPad version but the end of the project is visible from here. If I know you in real life, you have a iOS 5.0 device available, and you're willing to do some beta testing for me I'd appreciate it. Get in touch with me! I'm hoping to release a beta version by the weekend.

Quick tip about localizing currency on iOS

[UPDATE:2014-02-19 I've modified this sample code. This worked in 2011, but not in 2014. I think the issue is that ARC used to make a copy in the call that creates the NSMutableCharacterSet but now it does not and you have to specify mutableCopy or you don't get a NSMutableCharacterSet. Where I once wrote:
NSMutableCharacterSet* tempSet = [NSCharacterSet whitespaceCharacterSet];
You should in fact use:
NSMutableCharacterSet* tempSet = [[NSCharacterSet whitespaceCharacterSet] mutableCopy];
I apologize for the confusion.

Let's say you have a text field in your iOS app and you want to take currency values in said field. NSNumberFormatter is your friend and can happily turn a string like "$42.23" into the floating point number 42.23. It will also turn the floating point number 42.23 back in "$42.23". Even better with the German locale active it will turn 42.23 into "$42,23 €" and vice-versa (note the comma as well as the euro symbol). Sweet! However, here's a minor glitch. Sweden's currency "symbol" is in fact a string – "kr". While your formatter will turn the float 42.23 into "42,23 kr" it cannot turn "42,23 kr" back into the floating point value. What to do? What I decided to do was strip whitespace and currency symbols from the string before trying to convert the string into a float. Like so:

NSCharacterSet* costTrimmingCharacters = nil;
//OK, some settings have currency symbols that can't survive a 2-way trip. For example, Sweden's currency "symbol" is "kr"
//So what we're going to do is trim whitespace and currency symbols from the string. Also then we don't have to use the currencyFormatter and can
//simply use the decimalFormatter (since it will strip $ from US locales, for example.)
NSMutableCharacterSet* tempSet = [[NSCharacterSet whitespaceCharacterSet] mutableCopy];
[tempSet addCharactersInString:[[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleCurrencySymbol]];
costTrimmingCharacters = [tempSet copy];
//Note in a real app you should store costTrimmingCharacters somewhere and only build it the first time you need it.

NSNumberFormatter* currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setLocale:[NSLocale autoupdatingCurrentLocale];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString* aggravatingString = [NSString stringFromString:@"42,23 kr"] NSString* goodString = [aggravatingString stringByTrimmingCharactersInSet:costTrimmingCharacters]; NSNumber* goodValue = [currencyFormatter numberFromString:goodString]


Full disclosure: I didn't test that exact snippet, it's frankensteined from a few pieces of code out of RoadTrip that would have just obscured the point. So there may be a syntax error somewhere due to a typo but the idea should be clear. Last two little suggestions:
  1. I use a related character set in the field delegate's shouldChangeCharactersInRange: replacementString: method so that I don't even accept illegal characters and that way you can't even enter "kr" unless you're in the Sweden locale.
  2. In the delegate's textFieldDidEndEditing: method once I've converted the string into a float I go right ahead and convert it back into a string and assign it back into the field. That means if they entered 42.23 they'll see a nice $42.23 or 42,23 € or whatever their locale specifies.

Cleaning up a D&D Character File

A couple of months ago Wizards of the Coast "upgraded" the Character Builder program from the downloadable .NET application for Windows to a Silverlight program that can run on a Mac. In theory I support this. However, one crazy thing happened: the new program doesn't send text to a printer and instead sends big images. This means if you print to a PDF file two things happen: the file size is obscene (12 or more Mb for a four page file), and the text is blurry as all get out.

After some struggling with this I had an epiphany: if WotC wants to give me images go ahead and work with that, process the images, and get a smaller, sharper file. After some messing about I came up with a process that works well for me. If you have a Mac and you want to play along here's an Automator app. You'll also need a scriptable image editor - I use Acorn from Flying Meat software. (You might ask why not Pixelmator. The answer is Pixelmator seems crashy when run from script. I have a version of this that uses Pixelmator but I have to run it 5-6 times to get a successful output.) Here's what you do:

  1. Run the Character Builder and hit the Print button.
  2. When the print dialog opens use the PDF button in the lower left and choose "Save PDF to folder as TIFF". Pick someplace (I use the Desktop) and let it run. You now have a TIFF file for each page in the character file. These are fat and they are blurry but we'll fix that next.
  3. Select all of the pages and drop them on the Automator App.
  4. The Automator app uses Applescript to tell Acorn to open all of the TIFF files, run an Unsharp Mask on them, and save them again.
  5. The Automator app then creates a new PDF from the Images. This will be named Character.pdf and it will be on your Desktop. You can now delete the TIFF files and enjoy a readable PDF that can put on an iPad (for example.)

To be clear, this is still a work-around. The Character Builder ought to output text and then you could just use the print to PDF button normally. This workaround isn't great - there's still some definite halo effects on the text and the files are still pretty fat. My sorcerer (Gurdrian) was a 471 Kb PDF file with the old builder and my current version is 6.1Meg - over an order of magnitude larger, while looking worse. This is sucky, but it's less sucky than the 15 Mb version simply printing from the app to a PDF create

Anyway, if this helps somebody out there, here's my Automator app. No warranty, etc, blah, blah but it does the trick for me. If you try it and have an issue drop me a line and I'll see what I do to help.

Character Builder Cleanup App

I still don't like Automator

I've recently seen some interesting videos about using Automator to do more stuff, so I thought I'd give it another try. (See my first post about Automator for some background.) Turns out that you can pass multiple variables into an action, if you are very, very careful. In general it's easy - if you use two "Get Value of Variable" actions chained together you get an array with the two arguments. Unless you don't. Going back to our example I need to provide two arguments: one is the URL of an image and one is a text string describing the map. The Automator script that I currently use saves the filename into a text file, then asks for the description which it saves into a separate text file. Then it fires off a Ruby script that processes the filename into the URL for the image and makes the necessary changes to the web server. It works, but it's a bit slow and it looks ridiculous. OK, so you're with me so far right? We have a file that the user selected (via a 'Ask for Finder Items' action), and a description that the user provided (via a 'Ask for Text' action). So let's store each of those in a variable and get them back to back. It works! We have two arguments in the data stream. We can pass that to a 'Run Shell Script' action and $1 will be our filename, and $2 will be the description. I work with that for a bit and it's all great, so I go ahead and start plugging those changes into the "real" script. Except it blows up when I try to retrieve the description variable. After a lot of messing around I realized the types of the variables is important. The 'Ask for Finder Items' gives you back an file alias. (If you inspect the result it will say 'Alias' in blue and then the filename.) If you manipulate that (say by a 'Rename Finder Items' action) it changes from an alias to the actual file. If you get an alias variable and then a text variable you will be fine. If you get a file then attempt to get a text variable the second get fails. There's a fix for this. You can run a 'Store Disk Item References' action and it will turn the file back into an alias. That seems wonky, but sure whatever. Next problem. My action *MOVED* the image file from my local drive to the server. So the original file doesn't actually exist by the time you get the variables together. This will also fail, and cause a screwup, even if you have an alias. After some thought I decided to copy the file and then delete the file on my local drive later. Turns out you can delete the "alias" and there's no error, but the original file is there. You have to do the opposite of that 'Store Disk Item References'. Get the variable, run a 'Retrieve Disk Item References', THEN run the 'Move Finder Items to Trash'. Goofy, but I can hang with it. OK, so now we're set. I've got a script action that is getting the two arguments, I can write the necessary sed mojo (sed is a UNIX command that lets you manipulate text) to convert the filename over to the URL and we're cooking with gas. It all works! Fantastic! Now, there are a couple of actions where Automator seems really slow: both of those Disk Item references take a while and I'm now using Preview to convert the RAW file from my camera to a JPG (which means I don't have to change the filetype on my camera every week.) The convert is instant but Automator takes several seconds before moving to the next step. I wonder if making it into a stand-alone "application" would help? I try that. The application doesn't work. I open the workflow (the one that just worked mind you) and run it. It crashes. After a lot of playing around I confirm what it looked like originally. I can take a running workflow, add a new "Run Shell script" command and paste in the commands I want, and that workflow will run. Save it and run it again and it still runs. Close and reopen Automator and the Run Shell Script action now errors out! I have no idea what that's about. When I last complained about Automator I felt like I didn't really understand how it worked. Now, after several hours I understand how it's supposed to work, and it just plain doesn't all the times. The distinction between a file and an alias to a file is subtle and it's not documented. The automator documentation calls both of them a Files/Folders object. The 'Rename Finder Items' action claims its input is a 'Files/Folders' and its result is a 'Files/Folders'. Which is true enough, but the change from an alias to a real file changes what you can do with variables later. This second problem with the external script command not working the same on a file load as it does when the command is created is just bullshit. So, I really need to get rid of Automator. It has a lot of promise, and if I was just automating something simpler it would be OK. But in a way I preferred the way I thought it worked (where it seemed very limited but it worked) to what it actually does (where some things don't work right at all and there are undocumented types that you need to care very much about.)
Read more

I don't like Automator

(Yes, D&D players this is ongoing comedy from the attempt to update Gametable. Quiet!) I've recently being using Automator and I have to say, I don't care for it. It works pretty good until you hit some sort of hard limit. Here's the part it does well: I can run the action, browse files and select an image. Automator has an image editor (I use Pixelmator) resize the image, do some quick & dirty auto-leveling, resize the image to a suitable-for-the-web size, and then upload it to online storage. I'll admit all this is pretty fantastic, although I suspect I could do it with Applescript. The problem is after that I have to use a web page, enter the filename of the image and enter a text string description. If I screw that up, even up to the point of typing '.jpg' instead of '.JPG' then the web software screws up. It can derail momentum of our D&D combat. The answer is obvious: the Automator action needs to update the web application as well. It's pretty easy for me to write a new bit of code so there's a URL that takes the information - all I need to do is construct a URL. Oh. You see, Automator has variables, but it doesn't have anything like *operations*. And you basically only have one argument passing from one Automator action to another. To have *TWO* arguments you have to save one into a text file and pull it back when you need it. If I have two arguments "foo" and "bar" and I want to create a url that looks like http://www.foo.com/special/url/?&name=foo&description=bar, well that's not a very Automator sort of thing. I messed with it for a while and got as far as getting Automator to spit out a single text string that looks like (in C terms) "foo\nbar" (in English that's foo, (carriage return) bar.) I finally decided to punt: I'm going to write a Ruby script that grabs that arguments and builds and sends the URL. Long-term I think I'll see if i can write an Applescript that presents the UI and does all the work. If not I could throw it together pretty quickly as a real Mac application, but c'mon.
Read more