Friday, November 12, 2010

Using Time Profiler to Tune Performance



There are two parts of software development on the iPhone we still don't feel comfortable with: gdb and Instruments. We know they are powerful tools but we just can't seem to understand what they are telling us most of the time. Perhaps understanding those two areas of development is a good goal for the next quarter. If we knew those tools we would be able to write better software, faster.

Here is an example of Instruments in action tonight. We
are trying to speed up an application that just seems sluggish. Especially on our older test devices. Our philosophy is to always test on the oldest devices we have. If it runs well on those, it will fly on newer hardware.

We ran the app with just the Time Profiler so we could identify where the app spends its time. Some of the things were obvious to us. However, here was one that wasn't obvious and turns out was a simple fix on our part with a high return.

This particular application streams audio using a set of co
de written by Sound Cloud to interface with their site. We noticed, however that the NSDateFormatter was taking a great deal of time:
4.9% of the time had been spent regenerating a date formatter object. With a little digging we found this set of code:

NSDate *position = [NSDate dateWithTimeIntervalSinceReferenceDate

:stream.playPosition / 1000.0];

NSDate *songEnd = [NSDate dateWithTimeIntervalSinceReferenceDate:[currentArticle.duration intValue] / 1000.0];

NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;

[formatter setDateFormat:@"mm:ss.S"];

playPositionLabel.text = [NSString stringWithFormat:@"%@ / %@", [formatter st

ringFromDate: position], [formatter stringFromDate:songEnd]];

scrubberSlider.maximumValue = (float)[currentArticle.duration intValue];

scrubberSlider.value = (float)stream.playPosition;

We were alloc/initting an NSDateFormatter every 2/10's of a second, then using it to display some numbers and then throwing it away. It seemed harmless enough but clearly the whole alloc/init was more overhead that we realized. So, we simply moved the variable out and declared it in the header file as a regular variable

NSDateFormatter *formatter;

and then just set it up in the viewDidLoad method

formatter = [[NSDateFormatter alloc] init] ;

[formatter setDateFormat:@"mm:ss.S"];

Now it should be alloc/initted just once and then used each time. Sure enough we ran again and now we had to actually search for the references to the NSDateFormatter since they were so far down the list.

Yay!

Thursday, November 4, 2010

Writing Apps for the iPad

The recent PBS app for the iPad got a lot of press and we, like many others, downloaded it. So far it hasn't quite been what we expected from the reviews. Today we were greeted with this error.

A PBS application that cannot play videos isn't going to be very useful to us. Sadly, as people are rushing to stake out their portion of the iPad/iPhone space we are seeing things like this more often.

We can imagine that PBS, like lots of Enterprises before them, saw the new application as a great thing but didn't understand that regular monitoring and updating of their new system would be needed. Or else, they went outside of their normal workflow processes to get the initial application populated but then did not set up ongoing processes to ensure that future media was ready for this new channel.

Monday, October 11, 2010

Updates to our Git Repos

Many thanks to Ken Edwards for an update he submitted for our Audio Streamer plugin. Ken's iPhone application 3 Down News was the inspiration for our plugin and we are really excited to see that he's incorporating our code into his most recent version of the app. Look for an update from the App Store soon. Also, if you're more of a Grey Cup than a Lombardi Trophy person, Ken's app is a great way to stay up to date on what's happening.

We've also updated the OOZWebView based on some work we've been doing for another client. We've added a UIActivityIndicator to the view so that there is a little spinner when the pages are loading. If you don't like the spinner and would rather have a Network Activity Indicator (the little spinner to the right of the network signal strength indicator) you can easily change the OOZWebView.m file and everywhere we have
[activityIndicator startAnimating];
you can replace that with
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;

and everywhere we have
[activityIndicator stopAnimating];
you can replace that with
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = NO;

Yes, you are leaving an unused UIActivityIndicator item in your code but it's pretty small and probably won't break anything. However, if you want to be sure that you have clean code, you need to remove the references to activityIndicator in the OOZWebView.h and OOZWebView.m also remove the UIActivityIndicator from OOZWebView.xib

Monday, September 13, 2010

Modifying XCode to Run the TapLynx Sample

With the release of the new iPhone SDK 4.1 Apple has eliminated the 2.2.x build options but has also oddly eliminated the ability to build in 4.0. I've never seen this before, but that is the way. Now, the default build of TapLynx doesn't work without a few (thankfully simple) changes.

If you try a build and run with the current TapLynx sample code (as of Sept 13, 2010) and XCode with a 4.1 SDK, you will get this error
before you even try to Build and Run you should notice this in the menu bar and know that your build will fail
What XCode is telling you is that it doesn't know what SDK 4.0 is anymore.

To fix this and run your shiny new TapLynx sample you need to tell your project file that it should run with the 4.1 sdk. To do that select the project document and right click (or control click if your mouse has but one button)
or under the "Project" menu. Select "Edit Project Settings"



You should notice in bold letters that "iOS Device 4.0 (missing)" is the value for Base SDK. This is the cause of the error. However, if you just change that to "iOS Simulator 4.0 or "iOS Simulator 4.1" you will might still get the error if the Build Configuration is at anything other than "Debug". So, the fastest way to "Yay!" is to set All Configurations to have a Base SDK of either "iOS Simulator 4.0" or "iOS Device 4.1".

If you choose to set your base SDK to iOS Device then make sure that you set the active SDK to either the simulator or the device depending on how you are testing the app. Either use the Project menu to "Set Active SDK" or use the Overview dropdown to set either "Device" or "Simulator".

Good Luck.


Friday, September 10, 2010

Using YouTube RSS Feeds in TapLynx

If you are using the TapLynx framework to build an application then adding a video feed might very well be a requirement. YouTube.com has an API with gdata (the Google data technology) so that you can turn lists of videos into RSS feeds and TapLynx will work well with them.

The TapLynx support website has a few suggestions for how to work with the gdata feeds. Once you have created a playlist or if you want all of the videos uploaded by a user you can get set up pretty quickly.

I recently wanted to go a little beyond that as I wanted to create some feeds that query YouTube and return whichever videos match my query. The Google data api allows for me to create a url that contains my search parameters and it will return an rss feed for TapLynx. If I wanted to get five videos that pertain to Jimi Hendrix, I could use this URL
http://gdata.youtube.com/feeds/api/videos?q=Jimi%20Hendrix&max-results=5
The "q" parameter is where I can put in my search terms. The "max-results" parameter is how I can constrain the number of videos returned. This url will function and it will return me five Hendrix clips from YouTube. There is only one problem. Three of the clips are not encoded for the iPhone and they won't play on the iPhone.

All YouTube videos by default are encoded to play in the Flash player. Many (but not all) videos have alternate encodings that YouTube will use when an iPhone or HTML5 device or other non-Flash device asks to play them. Thankfully there is a format parameter that we can add to our URLs to return only videos that provide the alternate formatting. The default format for all YouTube videos is "5". So the flash version of each video is denoted by a "5". The iPhone would like to have things that are designated with a format of "6". So, using our example above we can just add a formatting parameter and feel secure that all of the videos that are being returned can be played on an iOS device. Our Hendrix example now looks like
http://gdata.youtube.com/feeds/api/videos?q=Jimi%20Hendrix&max-results=5&format=6
and it returns five video clips (three that were in the original list and two that replace the ones that only have a Flash version) all of which will play on an iPhone.

So, if you are trying to generate rss feeds of YouTube videos for an iPhone application hopefully this will help you ensure that all of the videos play.

Saturday, August 28, 2010

Nieman Journalism App uses Tyree Apps' OOZWebView

We are happy to note that Joshua Benton and the Nieman Journalism Lab at Harvard have incorporated the OOZWebView into their iPhone application. It is really exciting to know that something we have made open source is actually helpful and valuable.

Now the whole concept of Open Source becomes clearer to us. This great feeling isn't going to feed the kids, but it's pretty powerful and we've already had a few inquiries about paying gigs based on publicity we've gotten through our Open Source work.

Based on Joshua's work with out plugin we've made some changes and found a bug. The bug occurred when the plugin was used in offline mode and has now been fixed in the github repository for the plugin. One of the things that Joshua added to our codebase was a command to scale the web pages. Originally, when one would load a webpage, the first page would look fine and then any future pages had the potential to look too big. So he suggested that we add a line in the ConnectionDidFinishLoading method (line 158 of the OOZWebView.m file) to allow the page to resize:

[self.webView setScalesPageToFit:YES];

However, when we did add that code it resized our initial page as well. Now the initial page would shrink to fit itself onto the screen rather than staying at the original size. It was a simple matter of setting a viewport command in the meta data of the html controlling the initial page to rectify the situation.

By setting the viewport, we are telling the iPhone how to scale the content of the website. Apple has an excellent developer article about viewports and Safari and the iPhone. However, if you're the impatient type, try sticking this code into your html:

<meta name="viewport" content="width=320, user-scalable=yes">

This is a meta command so it needs to go into the "head" section of your html page.

Tuesday, August 3, 2010

UIPopoverController and SplitView Matt Gemmell Saves the Day

Until now Apple provided a SplitView controller for your iPad that worked fine. However, they used it in a slightly different way in some of their own applications (like Settings).

If you wanted to implement similar features you had to roll your own controller and then worry that you've screwed something up and introduced a bug that won't appear until well after your application ships.

In steps Matt Gemmell who has produced an open source splitview controller for the iPad that offers some really cool user interface enhancements. His twitter library seems to pop up in everyone's applications. Perhaps this controller code will wind its way around as well.

Wednesday, July 28, 2010

Wordpress Shortcodes

Haven't done much work with Wordpress before, but a few of our current contracts have required us to dig in pretty deeply. Wordpress is impressive in how it can hide the guts from the users and authors while letting us get at the back end to tweak and make updates.

One of the features we thought was missing was some way to do simple text replacement for pieces of text or html that repeat in a lot of places. We spent our time Googling for things like wordpress macro, wordpress text replace, auto-complete and others. We found lots of plug-ins but none were really what we wanted or were not certified to work with the versions of Wordpress our clients are using.

Then we stumbled across the proper vocabulary word: shortcode! Wordpress has a whole api and everything. Basically though, a shortcode is just a php function that returns some value when called. Another little function call to add_shortcode placed in the functions.php file is all that is required to set them up.

The best part is that for most Wordpress themes, the functions.php file can be edited from the administrator interface. So, we don't need access to our client's server or ftp. This will make maintenance much easier in the future.

Here is a simple example:
First we have the function we want
function TA_address(){
return '<a href = "http://www.tyreeapps.com">Visit our website for good times</a>';
}

Then just below that in the functions.php file we add our shortcode to Wordpress' master list

add_shortcode('TAwebsite','TA_address');


Now we can enter our new shortcode [TAwebsite] into our Wordpress pages wherever we want to have our website address. We can enter this from the regular blog post or page editor. We don't even have to switch over to the html view. And perhaps the best part of the whole deal...we can change the string that the shortcode returns in one place and it instantly changes across our whole website.

So, if there is a piece of code you need on EVERY page of your site, edit the template file. If there is a piece of code you need at the same place on every page, look at the widgets and menus. If there is a piece of code that you need a lot but not everywhere, then a shortcode might be just the ticket.

It is important to note that if your shortcode is named the same as another shortcode bad things will happen. Best practice is to prepend your shortcodes with some identifier (like "TA" for mine) that is unlikely to be used by other people. The more we use open source stuff, the more we're realizing the importance of getting to the habit of adding an identifier to ALL functions and other broadly scoped objects.
Shortcodes can do much more than just text replacement. Here is a nice list of some more advanced usages.

Quality Control


We know that paying attention to each detail is difficult. Missing a mistake is a downside of the Apple App Store process. This mistake will take the owners about 5 seconds to fix, but will take a recompile, resubmit, update cycle to get deployed....weeks.

It's unfortunate that this is one of the very first images that any new user of the application will see after they download.

When we see things like this, it makes us EVEN MORE paranoid about polishing every last detail of our own applications. There's a fine line between polishing and goldplating though. It's tough not to cross it.

Tuesday, July 27, 2010

Adapting AudioStreamer for TapLynx

Matt Gallagher has made a cool library that does audio streaming. We have adapted his library for use with the TapLynx framework and released it on github. You can get your own copy of the library at github.
The TapLynx framework lets someone with limited programming experience produce professional applications for use in the Apple App store. A TapLynx based application can be controlled by changing settings in a single .plist file. This file can be local to the application or hosted on a remote web server.

The AudioStreamer adaptation is mostly controlled from settings in the NGConfig.plist that controls the rest of a TapLynx application. Modifications to the UI can be made by editing a single .xib file (TAStreamingPlayer.xib).
If the plugin is fed a single mp3 feed it will open a basic player and let the user listen to the stream. Per the design of the underlying AudioStreamer library, the soundfile can be either a continuous stream or of a finite duration.

If the plugin is
fed an array of streams, it will present the user with a picker view so that they can choose among the streams.

Instructions for loading the plugin and installing it within your TapLynx application can be found in the readme that is in the github.com repository. There is also a sample plist file to show how the array of streams needs to be formatted.

This has been a fun project for us and we will post a few of the interesting things we learned while working on it over the next few days. Please enjoy the library and use it in your projects. Tyree Apps would love attribution either on your website or your blog. We are also happy to engage in projects to customize the plugin or to create other custom plugins for hire.

Tuesday, June 8, 2010

The Rule Holds True

As exhibited a few times yesterday: the likelihood that a piece of technology will fail is proportional to the number of people watching your demonstration.

At WWDC, though, everyone in the room is aware of the rule, so we were all very supportive of the speakers.

Saw a few really cool things. I saw a 3D demo of a horse game that I will have to buy for my own daughter when it is released. I'm getting inspired to have some serious coding time.

Monday, June 7, 2010

At WWDC

We are here in SF at WWDC. The hotel is a funky, quirky thing a few blocks from the convention center. Monday seems to be keynote day. We will have the big Job's keynote in the morning and then keynotes from some of the other big groups in the afternoon.

We are looking for a few things this week:
- UI help on Massage Helper so we can get it deployed
- Core Data information to help us with some of the contracts we currently have
- Video/Audio information to get our head wrapped around how to do streaming
- Better fundamental understanding of XCode and the testing and performance tools

Swag this year was an interesting track jacket that will be just the thing to take a chill off. I was expecting an iPad case, so now I need to go buy one of those.

Saturday, May 15, 2010

Making the Blog Mobile

Getting ready for WWDC in a few weeks. It's been a longtime dream of
mine to have a reason to go to this conference and to go to this
conference. In preparation, I'm linking my blog to my phone. I smile,
because I bet I'll be the only person with a Nokia E71 at the
conference. Maybe I'll put an Apple sticker on it or something.

Thursday, April 15, 2010

World is Flat Moment

We were talking to a potential customer yesterday in Argentina about doing some programming work and we've gotten a new customer recently in Switzerland.

With things like Sykpe, email, timezones and IM thinking about working 9 to 5 seems so quaint.

Wednesday, April 14, 2010

UIPopoverController and SplitView with iPad

[Update: This is the most popular post on this blog by about a factor of 100. If you wouldn't mind leaving a comment as to what you were looking for as you arrived I would appreciate it. I am happy to modify and expand this tutorial to make it more useful. -wt]

I have been working a lot on the Massage Helper application recently and think that I'm almost done. Something that I really struggled with was ensuring that popover views were getting dismissed correctly. Apple's guidelines require that only one UIPopoverController is visible at any time. For most popovers this is a relatively straightforward process. We just need to remember to set a variable in our view to hold the currently visible popover and each time we want to display a new popover we need to dismiss any visible popover's first.

In these examples, I'm using the template that Apple provides when we start a project as a split-view project. If you want to follow along, simply create a new project in XCode of type SplitView and then just build and go. In landscape mode, you will see the split view and in portrait mode, the smaller of the views will become a popover. It is this popover that we are talking about. The sample template has a RootViewController (the smaller view that becomes the popover) and a DetailViewController (the bigger view that is always visible).

The sample template has a property associated with the DetailViewController called "popoverController". Whenever a popover is displayed, this property gets to point to it. If the popoverController property is set to nil then no popover is being displayed. We have added, a little function called "managePopovers" that checks the property and if a popover is being displayed is gets dismissed. We will call managePopovers each time we are about to display a new popover. This code is lifted from the "setDetailItem:" function of the DetailViewController.m. Here are the examples.

managePopovers

if (popoverController != nil) {

[popoverController dismissPopoverAnimated:YES];

}


Displaying one of my popovers

-(IBAction)updateSessionDate:(id)sender{

DateChangeViewController* myDateChange = [[DateChangeViewController alloc] init];

UIPopoverController *tempPopover = [[UIPopoverController alloc] initWithContentViewController:myDateChange];


[self managePopovers];

self.popoverController = tempPopover;

tempPopover.delegate = self;

[myDateChange release];

[tempPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

}


The DateChangeViewController referenced above does all of its setup without a .nib. The key to managing the popovers is that we call managePopovers and then set the popoverController property of the view equal to the popover we are about to display.

Sidenote: One other thing I discovered is that Popovers don't seem to like .nib files very much. When I would try to create a view from a .nib and use it in a popover, I tended to get errors about key value compliance. I haven't tried to explore if it's me or the computer, I just started to create any view that needs to be in a popover programatically.

So, simple. However, there is one popover that doesn't quite work well and that is the popover associated with the split view itself (in the Apple template it is the popover of the list of locations). The SplitView template that Apple provides only cares about the one popover it needs to function as a demonstration. So, when the split view sets itself up it sets the popoverController property but then it doesn't set the popoverController property on subsequent times the SplitView popover displays. Popovers will dismiss themselves whenever the user taps outside of their bounds, so if you only have one popover, you never need to dismiss it. This is what the Apple template relies on. The issue with that is that tapping on a UIBarButtonItem to display another popover doesn't trigger the event to dismiss a popover. So, using the code above we can get the Apple supplied popover to dismiss the first time we display our DateChange popover, but the next time we display the Apple supplied popover we can't get it to dismiss.

Thankfully, the key to fixing this is to implement one of the methods that Apple provides for SplitView controllers and their delegates. There are actually 3 methods a SplitView controller can support, but the Apple template only uses two of them. The willPresentViewController method gives us the opportunity to set the popoverController property each time the SplitView controller's popover is about to display.

Here is my implementation of the method (this is in the DetailViewController.m):

- (void)splitViewController:(UISplitViewController*)svc popoverController:(UIPopoverController*)pc willPresentViewController:(UIViewController *)aViewController{

[self managePopovers];

self.popoverController = pc;

}

Now, every time the popover associated with the SplitView itself displays, the method is called and the popoverController property gets set.

This was a maddening part of working with the UIPopoverController class, so I hope that you stumble across this little hint and that it saves you some time. If anyone at Apple is reading, it would be great if you added the willPresentViewController code to the SplitView template and then commented it out like you do for the UITableView and other templates.

Monday, April 5, 2010

Tutorial on Mercurial

I'm still using Mercurial and feel like it's making more sense every day. I came across Joel Spolsky's recent article and tutorials on Mercurial and they have helped. I am relearning the command line and vim. Joel's company has a GUI for Mercurial which looks interesting, but I'm not in the market for that yet. Maybe when the projects get bigger.

So far my biggest improvements in how I use Mercurial have been tweaking the .hgignore file and distributing the branches across my desktop and laptop.

I would like to start using version control on my word processing documents next.

Tuesday, March 30, 2010

Renewing My Developer Certificate

We've been deep in development and sleep deprivation for our first iPad app. It just added to the stress level when we noticed that the Development and Distribution certificates will expire next week. Apple's documentation is very complete about how to create the initial certificate but less clear on what to do about a renewal. I think it shows how new the whole iPhone OS process is.

The best step-by-step I've found so far is at Alex Winston's blog so I've posted it here. With all of these certificates expiring and renewing, I wonder if there will be apps that drop from the app store. Carter's Coin Flip could use a little less competition. A search for "coin flip" turns up 66 apps. We don't appear on the initial page of six, but we are in row six (of 17) when someone clicks the "Show All" link.

So far, we don't think that there are any Massage Therapist Notebook type applications, so hopefully that will be more of a blue ocean for us.

Saturday, March 20, 2010

Using Static HTML Pages in TapLynx

As I've been experimenting more with adding plug-ins to TapLynx I started to think about how some of the other iPhone libraries work. In those libraries, they are basically wrapping javascript and HTML pages with some iPhone classes so that the application will compile into a binary that can be sold on the Apple App store. Today we're going to make a little form that will sit in our TapLynx application and then post itself up to a .php file on our server for rendering.

If you do not have TapLynx and want to see the files that I'm using you can navigate to the html form from this link and you can grab the .php file that processes the POST from this link (form_process.php). The initial form will take two text fields as input and the .php processing form will echo the values of those fields back to the user.

An interesting thing to note as you design your pages, TapLynx uses a custom web controller so you cannot take advantage of the mobile Safari controls. You will need to give the person a way to get back to the launching page or else TapLynx will get stuck.

As with many things in TapLynx, the instructions for adding the HTML file are well documented in the supporting wiki. For this example, we make a configuration change to the NGConfig in our TapLynx project so that we can show the controller and have it display a file called form_test.html which we have copied into our XCode project. Our NGConfig looks like this:


The form_test.html file is a pretty simple html file and the code for the file looks like this:


Finally, here is the code for the .php file that is processing the data. Notice that I have included a little javascript link tag to take the person back to the initial form.


Any other issues? Well, the initial page needs to be local to the system. This is sort of contrary to a big part of TapLynx where you can have the configuration file remote and the rss feeds remote so you can make changes without resubmitting the application for approval by Apple. Also, my .php processing form is not on the phone, it's on my server. You can have your file link to other files on the phone (they will all be in the same directory on the phone regardless of if you have them in different folders in XCode). However, I can't think of a real reason in the context of a TapLynx application where I would want to do this. The .php file won't execute on the phone because the phone is just a browser.

So, if you're working on a TapLynx application and you want a few little things to be custom and you know some html and javascript, you can get the job done and extend your application and still not have to learn Objective-C (which is kind of the point, right?). I'm thinking of making a little glossary and dictionary for one of my applications and this is going to be just the thing.

Converting My Developer Account

I have started the process to convert my personal account to a business account over at Apple. Originally, I had wanted to create a separate business account but there is not a clean way to move the applications I already have published to the new account. The whole App Store process seems oddly manual. However it's been getting better over the last few months. I'm generally pleased with how the back end of the App Store runs. For the conversion, they keep asking me to fax documents rather than asking me to send PDF's. It's hard to find a fax machine. So, for last night's request, I tried out a free version of faxzero.com's service. I should research some other services for the next time, but I was in a hurry.

During this process, I'm shut out of my developer portals over at Apple. The timing is making me nervous as Apple sent out an announcement yesterday (Mar 19) that any iPad projects submitted by March 27 would be considered for the Grand Opening of the iPad store. Having a project in that store would be a great marketing boost for this little company.

Tuesday, March 16, 2010

Version 1.7 of CCF Approved and Thoughts About iPad

That was record time. By Monday afternoon it was approved. Maybe with updates it's easier for Apple. I also notice that the app is "approved for the iPad". This development I don't care for so much. I've been working on an iPad version but trying to make it actually be different on the iPad.

For the iPad version I want to do more with the statistics mode and let people download that data into a spreadsheet, so that they can really do their math homework with the tool. The niche target audience for that part of CCF is only possible in this era: I'm targeting students in secondary schools and colleges who are in the one or two week unit of their math course focusing on probability and who are doing the bonus question that asks them what happens if they flip a coin 1000 times. I'm also thinking that we can put in some information about each of the coins but that is more so I can torture Carter with tasks (doing the research) that will make him a better person.

Not every cool application for the iPhone is going to translate well to the iPad I predict.

Saturday, March 13, 2010

Carter's Coin Flip Version 1.7 Submitted to the App Store

In version 1.7 of CCF we changed from using the random function we were using to using arc4random. I think that in a number of cases, the random seed was not working correctly but I couldn't prove it to my satisfaction. So, I just chucked the whole thing and used arc4random which seeds itself.

Also I got an actual German speaker to help me change the German words for heads and tails. This whole translation part is easy to do on the technical side and much harder on the language side. For example. I originally used Vorderseite and R├╝ckseite for Heads and Tails since I found those terms in a German coin collecting site. Lots of effort, but I later found out that those words are really just "front" and "back". When a German speaker is flipping a coin the call will be either Kopf or Zahl. I'm happy to make the change. As I translate into more and more languages I don't speak, I think it's a matter of time before my screenshots show up on a site like Engrish Funny or something.

Spanish is going to be a little more of a challenge as I'm told that each country uses words that are specific to the actual images on their coins.

I'm translating this application as I add new coins for a few reasons. The first is because it's a fun way for Carter and me to learn about new countries and their coins. The second is to honor the people in each country who have purchased the application. The last is because Apple has a nifty little deal going (for them) in that they don't pay you until you net about $250 worth of sales for any one region (for me at 70 cents a copy this might take a while). If they are smart, they are collecting interest on the money while they wait for me and all of the other developers to meet the minimum. So, the handful of purchases in Great Britian for example will probably never be paid to me. The rest of the EU is lumped together (I think), so I'm trying to translate into Spanish, French, German, Italian, etc. in order to try to get enough sales in that area to actually get paid for any of them.

A Cyclist Who Writes Software

An issue I have with having been a cyclist before I started writing software for the iPhone is that all of the objects that deal with graphics utilize the Core Graphics framework and their names start with CG. That's a problem because all of the great cycling races have a General Classification. The guy in the yellow jersey at the Tour de France is the leader of the General Classification or GC.

When I compile things I can almost guarantee at this point that I will get some errors because I'm trying to talk to GCAffineTransform or GCColor. The fingers and the brain just can't seem to get on the same page.

Friday, March 12, 2010

Source Control in OS X I'm Using Mercurial

I'm starting to use Mercurial for my source control and so far I'm liking it. I had been trying to get Subversion to work within Xcode but I could never get it to behave like I wanted. Apple has placed the nice SCM menu in Xcode so I assumed that the whole thing would be seamless. Perhaps I was using the system incorrectly, but I read the documents and still items would get dropped from my repository and I got odd error messages.

The big change for me with Mercurial, is that I'm using it with the command line outside of Xcode. Maybe Subversion would work better if I also used it this way. Unfortunately for Subversion, the experience with it integrated into Xcode has colored my perception of how good it is.

Mercurial hasn't been perfect. I can't get the .hgignore file to work the way I think it is supposed to yet. I really like the fact that Mercurial has its own web server so that I can look at things somewhat graphically when I want and that I can clone repositories across the net.

Thursday, March 4, 2010

Three20 Framework

I am looking at making some more plug-ins for the TapLynx framework. At issue for me is that I've been leveraging the Three20 framework which is an extensive set of view controllers and functions for the iPhone and iPad. I am worried that after someone adds the TapLynx library (3.9M) and the Three20 library (4.3M) that they are too close to the 10M limit that the App store imposes.

Saturday, February 13, 2010

Implementing Tap and Hold on the iPhone

In the Apple Developer guide there is some good sample code for implementing the tap and double tap. We've written about that in a previous blog post so won't go over it again here. For something we're doing now, we want to show a little tool palette when the user taps down and holds their finger on the screen. The way we're doing this for now is just to rearrange the code that we use in other applications for tap and double tap.

In the - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event event we call our method to display the palette with a delay of .3. That line looks like this

[self performSelector:@selector(showtheTools:) withObject:buttons[i] afterDelay:0.3];


In our - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event event we first call a cancel of the delay and then perform an action for single tap. The code to cancel the delayed event is

[NSObject cancelPreviousPerformRequestsWithTarget:self];


Our thinking here is that once the tool palette is displayed there is no PerformRequest to cancel so nothing is done. In our touchesEnded even we have one more thing. If the tool palette is displayed, it sets an instance variable. touchesEnded checks that variable and only performs the single tap code if that variable indicates that the palette was not displayed.

UITouch *touch = [touches anyObject];

[NSObject cancelPreviousPerformRequestsWithTarget:self];

if (paletteIsDisplayed == NO) {

if(([touch view] == buttons[i])){

self.touchedButton:buttons[i];

}

}



One of these days, I wish Apple would publish a library of the best practice patterns for handling basic touch gestures. I know that a gesture library is a feature in other O/S and I would love to see it here.

Thursday, February 4, 2010

iWeb and SEO two different worlds

I am trying to use iWeb, I really am. I just get really frustrated with it sometimes. So, apologies to anyone who is currently taken aback by the amateur look to my navigation. I'm having to recode the nav bar and then push it into iWeb. I am using the approach taken over at All About Web for fixing the iWeb nav bar.

Sometimes iWeb is like that person you used to date: really pretty but such high maintenance.

So now, after I make my pretty pages in iWeb, I have to load each page into TextWrangler to add my keywords and google tracking code. Then ftp everything over by hand. I had been using the RAGE iWeb SEO tool to add keywords but it died during its most recent update and I just haven't had a chance to see if it was the tool that died or something with my computer.

Anyway, as my old affair with FrontPage devolved into Notepad and the command line, my recent fling with iWeb seems to be slowly morphing into vi/TextWrangler/Terminal. These kind of programs (iWeb and FrontPage) have a real place in the computer world as they are helping move the whole everyone-is-a-publisher democratization of content. However, when they try to "help" me with my pages, they often don't.

Saturday, January 30, 2010

Citizen Reporter Plug-in for TapLynx

[Update 3/16/2010: I'm sorry, I had the comment notifications going to a bad email address, so I missed the comments that were made. I've corrected the problem and will now make some changes and updates to the plug in]

In addition to being a really easy framework to use to make an iPhone app, TapLynx lets you extend the framework by adding your own view controllers. I've created a little view controller that lets a TapLynx app offer the user a quick way to snap a photo and then email it to someone without ever leaving the TapLynx based application.

If you ever want to create an RSS based application you should definitely check out the TapLynx framework. The framework is designed to be used by people with limited coding experience and is controlled by making configuration changes to a .plist file. If you have a list of RSS feeds and a couple of images, you can have an app compiled and ready to submit to the App store in under an hour. Really.

The Citizen Reporter plug in simply presents the user with a screen that looks like the one in this screen shot.
I made the view in Interface Builder so that you can have a GUI way to change the look and the language. Please feel free to use this or to encourage me
to make changes and fixes. Also, if you have questions on the code, I'm happy to answer and explain what I've done. You can modify the colors and such on the .xib to fit your scheme better, I don't think it will break anything unless you delete one of the buttons or the little thumbnail I added.

TapLynx can work with any view controller as long as the view controller is more or less self contained and as long as the view controller uses an init method like this one:

#pragma mark Initialize for TapLynx

-(id)initWithTabInfo:(NSDictionary *)tabInfo topLevelTab:(NSDictionary *)topLevelTab {

return [self initWithNibName:nil bundle:nil];

}

You hook the view controller to the main TapLynx project by adding a regular tab entry to the NGConfig.plist. Here is a screen shot of how I added a view controller called "CitizenReporterReporterViewController" and a little camera.png file to use on the tab.


The ViewController itself is just calling the image picker and the email composer that are already built into the iPhone OS 3. The view controller is already configured to sit within
TapLynx as one of the tabs. When the user goes to that tab, it asks
them to first get a picture from their camera or photo album and then
email the picture to an address you choose with an option for them to
add a note. It leverages the build in Image Picker and Mail Composer
controllers in iPhone OS3. I know that it would be more professional to actually have some server side thing to get the picture, but I figured this is a good first step. Besides, there are millions of server configurations and TapLynx is aimed a people who don't necessarily have servers to store the photos and messages.

There is one place in the code where you need to make some changes. I went ahead and moved the strings you will need for the "to", "subject" and "body" parts of the email into some string constants. At the top of the CitizenReporterViewController.m file you will want to change the string to be something more meaningful (and the email address to be the email address where you want the pictures to be sent).

#pragma mark Constants

static NSString * const kSendTo = @"theaddress@mycompany.com";

static NSString * const kSubject = @"Subject of the Email";

static NSString * const kBody = @"Body of the Email";


Find the code shown above and make your changes. It is important to ensure that the at sign and the quote marks stay where they are and that you just change what's in the middle.

The zip Archive contains 3 files, you need to add the CitizenReporterViewController.h, .m and .xib to your project. Additionally, you will need to configure the NGConfig in your project
to have a new tab with a Title, ShortTitle, TabImageName and a customViewControllerClass entry. The customViewControllerClass should be a String and it's value should be CitizenReporterViewController. The files can be in any part of the XCode project file structure, but Classes, Other Resources, Resources are probably the most common. I wouldn't put them in Frameworks or somewhere like that. The archive is stored at the Tyree.apps website and you can get it from this link.

Thursday, January 28, 2010

Carter's Coin Flip Version 1.6 Submitted to the App Store

Carter's Coin Flip version 1.6 includes a Belgian 5 Franc coin, a French 1 Franc coin and a Canadian 25 cent coin. My wife says that we should look for a Canadian coin she called a "Looney" since that is more Canadian somehow. I was working with a Canadian two dollar coin but it was too worn and I couldn't get a good photograph of it. I've also translated the interface into French and written some French language copy for the France App store. I have to thank Nicole Washington for helping me make sure the French isn't just a transliteration. The Belgian coin is in her honor.

I'm also planning to record a spot for the Today in iPhone podcast. They are having a contest where developers can talk about their apps for 60 seconds. This seems like a good venue for TyreeApps.com since we're a family affair and not as polished as some of those other companies. I've never recorded a commercial so I'm obsessing a little too much about what to say and whether I have a voice for podcasting. I think I should just suck it up and do it.

Thursday, January 21, 2010

TapLynx Plug In

It's amazing how much work gets done when you can't sleep.

I've been working on a plug-in for the TapLynx framework. It's almost done. I'll post code snippets and a link to the files as soon as it's finished.