MGTwitterEngine and Locations

So Twitter just updated their API to disallow the use of authentication through the REST API. They were nice enough to announce that in an email 2 days after they turned it off so that meant that I had to learn all about OAuth overnight and scramble to find something that could take over the Twitter functionality of the LogYourRun iPhone app.

Luckily most of the leg work in this are has been done by Matt Gemmell and his Twitter engine. Some slight modifications are required in order to get it running on the iPhone and working with OAuth.

With these libraries dropped in the new Twitter authentication system is fairly easy to implement. The main problem is that the MGTwitterEngine does not support the Twitter location API. Since this is an important piece of TweetMyDistance I modified the MGTwitterEngine.m and added a function for tweeting with location. The new method takes latitude and longitude as well as the tweet and if this is a reply to a previous tweet. As you can see from my tweet it works great. The code below should replace the code in MGTwitterEngine and don’t forget to also update your .h file.

- (NSString *)sendUpdate:(NSString *)status
{
    return [self sendUpdate:status inReplyTo:0];
}

- (NSString *)sendUpdate:(NSString *)status inReplyTo:(unsigned long)updateID
{

	return [self sendUpdate: status inReplyTo: updateID withLatitude: 0.0 andLongitude: 0.0];

}

- (NSString *)sendUpdate:(NSString *)status
			   inReplyTo:(unsigned long)updateID
			withLatitude:(double) lat
			andLongitude: (double) lng
{
	if (!status) {
        return nil;
    }

    NSString *path = [NSString stringWithFormat:@"statuses/update.%@", API_FORMAT];

    NSString *trimmedText = status;
    if ([trimmedText length] > MAX_MESSAGE_LENGTH) {
        trimmedText = [trimmedText substringToIndex:MAX_MESSAGE_LENGTH];
    }

    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    [params setObject:trimmedText forKey:@"status"];
    if (updateID > 0) {
        [params setObject:[NSString stringWithFormat:@"%u", updateID] forKey:@"in_reply_to_status_id"];
    }
    if (lat != 0.0 && lng != 0.0) {
		// lat=%1.6f&long=%1.6f&display_coordinates=true
        [params setObject:[NSString stringWithFormat:@"%1.6f", lat] forKey:@"lat"];
        [params setObject:[NSString stringWithFormat:@"%1.6f", lng] forKey:@"long"];
        [params setObject:@"true" forKey:@"display_coordinates"];
    }
    NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];

	DLog(@" twitterbody: %@", body);

    return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
                        queryParameters:params body:body
                            requestType:MGTwitterUpdateSendRequest
                           responseType:MGTwitterStatus];
}
  • Share/Bookmark

LogYourRun Version 2.8 for iPhone

To make full use of the new features of the new iOS4, I have updated the LogYourRun iPhone application to support running the app in the background. Background GPS is supported on the new iPhone 4 as well as the 3GS. Running the app in the background means that you will no longer have to worry about leaving the app on the screen and what will happen when you get a text or a phone call while running. Also, locking the screen will greatly improve the battery life and let you run longer without worrying about the battery draining. In recent tests on a 3GS iPhone I was able to run the program in the background for 1 hour after which the battery was down 12%. This is a 2x improvement over the battery consumption if the app is run with the screen on.

While running in the background the app turns off the pedometer – so you will only be getting GPS information. But the option for automatically tweeting every mile or so will still work as long as there is a data connection available.

As you run the application badge (number in red circle in top left corner of the app) will show your distance so you will not forget that the application is collecting data. Also the location services icon will be displayed in your status bar. Once the iOS4 NDA is lifted I will post some pictures of the app in action.

Additional features in 2.8 include:

* In case of emergency (ICE) contact information – enter name and phone number and you will be able to easily call your ICE person straight from the app.
* Power save button. Get most of the benefits of running the app in the background even if your phone does not support background apps. This button stops the screen from updating and turns off the pedometer for some extra power savings.

For more information please see:

http://www.logyourrun.com/iphone/

  • Share/Bookmark

Upgrading 1st gen MacBook Air with SSDHD

Like most computers, over time my first generation MacBook Air was starting to no-longer perform as well as when I got it. Partly this is a perception issue since I do most of my development on an dual quad core MacPro, but partly this was also due to a failing hard drive and the general accumulation of software and background processes. Since I was going to get a new HD I thought that I might as well get a solid state drive. These drives have no moving parts so should not be as prone to failure – but more importantly these drives are orders of magnitude faster than spinning disk HDs.

Transferring file system from internal HD to runcore SSD

I got my drive from runcore – it comes with a nice kit for copying the contents of your old drive to the new drive – but the screwdriver that they send with the kit is not use able for opening the laptop so make sure you have a good screwdriver handy.  Before installation I did a speed test using the timer on the iPod touch to test the speed of opening iTunes, Word, Excel and Xcode.  Installation was pretty easy but not something you should attempt if you are non-technical.  Before final installation I did a time machine backup to ensure all my data was safe.

Internals of the MacBook Air

After successful installation (for some reason the desktop background picture did not transfer) I did my speed tests again.  iTunes went from 20 seconds to load to less than 3 seconds.  This drive is fast as lightning!!!  As a matter of fact the MacBook Air now opens applications faster than my 8 core MacPro!  Data crunching such as building in Xcode is still faster on the MacPro – but it is now acceptable on the MacBook Air.  I am very impressed with the SSD and would definitely recommend this option to anyone getting a new MAcBook Air or as a replacement for people with older MacBook Airs – it has re-energized my old computer and now allows me to develop on the road!

I did a test where I put 5 files int my dropbox folder and opened them on both the MacPro and the MacBook Air at the same time.  The documents opened: Excel, Word, Preview, Xcode, and iTunes.  As you can see from the video the MacBook Air was only slightly slower at opening these documents and party this was because Excel needed to do some importing/data crunching before opening the document.  iTunes started almost at the same time on the two computers as did Xcode.

The files used in the speed test.

  • Share/Bookmark

Converting App from iPhone to Universal

With the iPad selling like hot cakes it seems like a good idea to get on the bandwagon and make some apps for the iPad.  The LogYourRun app is not particularly well suited for running on the iPad – I doubt that people will be strapping their iPads to their arms and run around the park.  However, the heart rate app seems like something that people would enjoy using even on iPads.  Since I did not want to have to maintain two separate apps I decided to make the app universal.  This was actually quite an easy process and took only about 10-12 hours (could have been faster if I had known what I was doing – which is why I wanted to share what I learned).

First step is to make sure you copy your application folder so you will be able to go back to the original application if you mess up.  Then with your newly copied application upgrade your target to work on the iPad in addition to the iPhone.  Click on your target and go into the menu – under Project you will find an entry that says “upgrade current target for iPad”.  Select the “One Universal application” option.  This will generate a new group which contains your new iPad version of the MainWindow.xib file – MainWindow-iPad.xib.  You can use this new nib to load all your iPad specific views.

At this point I went through all my existing nibs and created iPad versions (can be done in the Interface Builder) and saved them as XxxViewController-iPad.xib in the Resources-iPad folder.  For iPad nibs you have to make sure that you have versions of the nib that look decent and are usable in both landscape and horizontal orientations.  This can be tricky if you have a lot of UI elements but is very important since iPad apps have to support all orientations.  Make sure you anchor your UI elements to the right edges and that they scale to look nice.

The heart rate app uses a tab bar.  It took me the longest time to figure out how to get this to rotate.  The reason for this was that I had not read Apple’s documentation.  Turns out that all the view controllers that the tab bar links to have to return YES for all shouldAutorotateToInterfaceOrientation: orientations.  This makes sense conceptually.  In addition you need to set your view controllers to automatically resize when rotation occurs.  Once these are set you will be able to see what the rotation looks like in the iPad simulator (hopefully great).

Loading the iPad specific nibs using the view controller is a great way to get these to show up on the iPad only – but there are some cases where you will need to load a view controller programatically (in my case when the user clicks on the help button it brings up the help VC).  To make sure that you load the right nib you can check if the app is running on the iPad.  The following code is what I used:

- (BOOL) isIPad {
#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 30200)
  if ([[UIDevice currentDevice] respondsToSelector: @selector(userInterfaceIdiom)])
    return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
#endif

  return NO;
}

This code will check if the iDevice responds to the userInterfaceIdiom selector and if it does it checks if the device is an iPad.  If it is I load the iPad version of the helpVC if not then I load the iPhone version of the helpVC.  Make sure you also put the respondToRotation in these VCs since they will need to respond predictably also.

Finally you will need to create a default image that can be presented on the iPhone.  In your info.plist you can set the “Launch image (iPad)” to the base name of the iPad graphic (e.g. Default-iPad.png).  You can then create two images named Default-iPad-Landscape and Default-iPad-Portrait and add them to your project.  The iPad will pick whichever of the default images that fits with the rotation of the device.

When uploading your app – build the app in OS3.2.  The app will run on both iPhone and iPad from one binary. You will also have to upload screenshots for the iPad version for display in the iPad app store and create two new icons (50×50 and 72×72).  Once you have all that together your app is ready for submission and you can start praying that it will be a speedy path to acceptance.

  • Share/Bookmark

Elevation for routes

I have been playing around with the GPS functionality of the iPhone for a while now and I think I have optimized the collection of in-plane GPS data as best as possible given the limitations of the hardware.  The iPhone was not created to be a garmin forerunner and this especially shines through when you try to get elevation data out of the iPhone GPS.  This became painfully obvious after I built the functionality for collecting and displaying elevation data from the GPS.  Even after creating sophisticated methods for filtering the GPS data and averaging the data and weighing the data by the vertical accuracy the graphs of the running data had no correlation with reality.  At first this was disappointing – I had created this great code for collecting, saving, and displaying the elevation data but clearly if I released an app which was going to show unrealistic elevation data – the app would get killed by bad reviews.

So I looked around for other places to get elevation data.  Turns out that the space station has used lasers to measure elevation all over the surface of the earth and that Google has an API which allows you to query that data.  This means much better resolution on the elevation data than a GPS (even a garmin) could ever provide and I can show elevation data for both current routes as well as routes that have been saved which have no elevation data saved with them.  The only issue is that a data connection is required in order to get at this data but the amount of data being sent back and forth is so small that the elevation data shows up almost instantly even on an EDGE connection.  Being a good citizen I only have the app request elevation data if the user is interested in seeing this type of data – this keeps data usage down and keeps load off Google’s servers.

The video below shows how the elevation data is displayed within the app.

  • Share/Bookmark