Yet Another iPhone 4 Review

Ok, I’m adding to the general cacophony of about 1,000 other reviews on the iPhone 4. This is most definitely drowned out by antennagate, I suppose, but i just wanted to offer a brief take on my experience with the phone.

First off, a big disclaimer: I’ve become a huge fan of Apple’s products over the past few years. My friend Henry would call me a fanboy, but that’s his take. :) From my end, I just look at it as choosing the best product available regardless of manufacturer.

I’ve been using an iPhone 3G for the past 2 years. It served me well, and I was impressed with it when I first bought it. The software/OS experience and integration was second to none. It survived many falls. The only casualty it actually sustained was a faceplant in my driveway. This was remedied by a screen replacement. Over time, the shine has worn thin with the 3GS due to some awesome offerings from the emerging Android system.The iPhone 4 purchase was my first new cell phone in 2 years.

What I Love

Speed!

From my experience, the boost in speed due to the upgraded processor and RAM has made the overall experience with the phone better than ever. Granted, I have been using a 3G for 2 years, and 3GS owners might not notice the same dramatic upgrade in their speed. For me, speed is a feature, and was a most welcome upgrade over my 3G.

Retina Display

The density and clarity of the screen is eye popping. Everything seems to just jump out of the display at your eye. When I compare this to my old 3G display, the difference is dramatic. The place I notice this the most is with text. It’s much easier to read small type on web pages now.

Multitasking

The Apple implementation of multitasking took some flak for not being true multitasking meaning that a full version of each app doesn’t reside in memory when it loses focus. Moreover, the developer of the app has to design it to take advantage of specific hooks in the iOS API to allow the app to save its state when it regains focus. Also, the app developer has to take advantage of background service APIs to allow the app to continue to process work in the background if that’s the desired behavior. This includes playing music, downloading files, syncing data, etc. The difference between apps that take advantage of this functionality and those that don’t are obvious. The ones that don’t use the iOS 4 multitasking APIs simply restart every time they regain focus on the screen. Overall, most of the higher quality apps have released updates to incorporate the multitasking APIs, and the implementation is very snappy.

Battery Life

I typically run my phone with the wi-fi and bluetooth radios enabled for most of the day. Even with multitasking thrown into that mix, I am usually ok to use the phone all day without concern of the battery dying. This is a win, in my opinion, and further proof that multitasking was implemented in a very battery conscious manner.

Photos/Video

The addition of tap to focus on video makes the iPhone 4 a complete replacement for my flip video camera. The clarity and resolution of images taken with the camera is excellent. I have this photo set as my lock screen:

IMG_0001.jpg

Whenever I look at it on the phone, I’m struck by the quality of this photo, which I was able to take from my cell phone, and not a point and shoot camera.

What I Don’t Like

The Antenna Issue

I know the antenna problems been beaten to death. I can reproduce the dropping of bars on the phone by covering the spot where the antennas are spaced on the bottom left corner of the phone. I haven’t been able to drop calls this way or cause web pages to pause. There was a pretty dramatic example of this here. For such a well designed phone, this seems to be a show stopper that shouldn’t have made it past quality testing.

Bluetooth Reception

I use bluetooth all day at work to listen to music/podcasts. I also use it at home when doing chores, like dishes or other things around the kitchen. Prior to iOS4, my reception was good, rarely dropping the signal. I’m not sure if the problem is the hardware or the new OS, but the audio signal gets very choppy and/or drops out altogether if I am walking around, even when the phone is within 5 or 10 feet from me. The 4.0.1 update last week has seemed to alleviate some of these issues. The jury is still out on this one, as I haven’t had a lot of time to test it out yet.

Overall, I think Apple got it right with the iPhone 4, adding excellent features to a proven platform. I’m impressed with the added functionality and new hardware, and I hope they continue to innovate on this platform.

To end, I saw a link to this amusing song. Apple payed this during the press conference they held last week. This sums up my feelings as well:

Post to Twitter Post to Digg

Tags: ,

Scott Sigler’s New Book – Ancestor

I’ve been a fan of Scott Sigler for some time now. He writes some amazing stories along the same vein as Stephen King. In addition to publishing his work, he podcasts all his novels, in addition to other content (short stories, updates, etc) for free.

Please check him out. If you like what you hear, support him!

Here’s a trailer for his latest book, Ancestor:

Post to Twitter Post to Digg

Tags: , ,

Using NSXMLParser

This is my first stab at an iPhone project. I’m writing an app that consumes an RSS feed and parses it to retrieve a few bits of information for display on the screen. This is my first attempt at an implementation. Coming from a .NET background, it is quite a paradigm shift when switching to the heavily convention based world of Cocoa development.

If I was writing this app in .NET, I’d go out looking for the proper class libraries to parse the XML and begin framing out a class to perform all the functions I needed to populate an object to be persisted to the UI.

When researching the best way to do this, I found 2 prevalent methods of parsing XML in Cocoa:

I decided to go with NSXMLParser because it seemed like the path of least friction to get started with. NSXMLParser is a stream based reader. It basically reads the entire XML tree character by character and fires off events via delegate calls when it encounters useful information. I found this model very easy to use. After some trial and error, I’ve got it working well for my current project.

I am parsing the xml feed for Woot.com. The following is an abbreviated snippet of the elements I am interested in. The actual feed has a lot more info in it, but this should give you a feel for the structure:

<channel>
    <copyright />
    <description>Woot! - One Day, One Deal</description>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>Oppositional.Syndication, 1.0.0.0, http://www.oppositionallydefiant.com/</generator>
    <item>
      <description>[Removed for brevity]</description>
      <link>http://www.woot.com/</link>
      <title>Dyson DC24 Blueprint Limited Edition</title>
      <woot:price xmlns:woot="http://www.woot.com/">$279.99</woot:price>
      <woot:soldout xmlns:woot="http://www.woot.com/">False</woot:soldout>
<woot:wootoff xmlns:woot="http://www.woot.com/">False</woot:wootoff>
      <woot:purchaseurl xmlns:woot="http://www.woot.com/">http://www.woot.com/WantOne.aspx?id=e94aa72d-5860-4f9b-bab2-42211de2f4cc</woot:purchaseurl>
      <woot:thumbnailimage xmlns:woot="http://www.woot.com/">http://sale.images.woot.com/Dyson_DC24_Blueprint_Limited_EditionbklThumbnail.jpg</woot:thumbnailimage>
      <woot:substandardimage xmlns:woot="http://www.woot.com/">http://sale.images.woot.com/Dyson_DC24_Blueprint_Limited_EditionxxcShoppingYahoo.jpg</woot:substandardimage>
      <woot:standardimage xmlns:woot="http://www.woot.com/">http://sale.images.woot.com/Dyson_DC24_Blueprint_Limited_Editionm32Standard.jpg</woot:standardimage>
      <woot:detailimage xmlns:woot="http://www.woot.com/">http://sale.images.woot.com/Dyson_DC24_Blueprint_Limited_Edition98lDetail.jpg</woot:detailimage>
      <woot:shipping xmlns:woot="http://www.woot.com/">$5 shipping</woot:shipping>
  </item>
</channel>

Defining the Model Object

The first thing I did was create a model object which would encapsulate the element values from the rss feed that I was interested in. Here is the code for the class:

SaleItem.h

#import <Foundation/Foundation.h>


@interface SaleItem : NSObject {
  NSString *itemTitle;
  NSString *itemPrice;
  NSString *itemThumbnaillUrl;
  NSString *itemSubstandardImageUrl;
  NSString *itemStandardImageUrl;
  NSString *itemDetailImageUrl;
  NSString *itemDescription;
  NSString *itemShippingCost;
  NSString *itemSaleUrl;
 
  BOOL isWootOff;
}

@property (nonatomic, retain) NSString *itemTitle;
@property (nonatomic, retain) NSString *itemPrice;
@property (nonatomic, retain) NSString *itemThumbnaillUrl;
@property (nonatomic, retain) NSString *itemSubstandardImageUrl;
@property (nonatomic, retain) NSString *itemStandardImageUrl;
@property (nonatomic, retain) NSString *itemDetailImageUrl;
@property (nonatomic, retain) NSString *itemDescription;
@property (nonatomic, retain) NSString *itemShippingCost;
@property (nonatomic, retain) NSString *itemSaleUrl;

@property (nonatomic, assign) BOOL isWootOff;

@end

SaleItem.m

#import "SaleItem.h"


@implementation SaleItem

@synthesize itemTitle;
@synthesize itemPrice;
@synthesize itemThumbnaillUrl;
@synthesize itemSubstandardImageUrl;
@synthesize itemStandardImageUrl;
@synthesize itemDetailImageUrl;
@synthesize itemDescription;
@synthesize itemShippingCost;
@synthesize itemSaleUrl;
@synthesize isWootOff;


-(void) dealloc{
  [itemTitle release];
  [itemPrice release];
  [itemThumbnaillUrl release];
  [itemSubstandardImageUrl release];
  [itemStandardImageUrl release];
  [itemDetailImageUrl release];
  [itemDescription release];
  [itemShippingCost release];
  [itemSaleUrl release]
 
  [super dealloc];
}

@end

Defining a Class to Reference Constants

Next, to make my life easier, I defined a class to define some constants. These constants define the string values I will be searching for as I parse the XML.

Constants.h

#import <Foundation/Foundation.h>

extern NSString * const ITEM;
extern NSString * const TITLE;
extern NSString * const PRICE;
extern NSString * const THUMBNAIL_IMAGE;
extern NSString * const SUBSTANDARD_IMAGE;
extern NSString * const STANDARD_IMAGE;
extern NSString * const DETAIL_IMAGE;
extern NSString * const DESCRIPTION;
extern NSString * const IS_WOOTOFF;
extern NSString * const SHIPPING_PRICE;
extern NSString * const SALE_URL;

@interface Constants : NSObject {
 
}

Constants.m

#import "Constants.h"
@implementation Constants
  NSString * const ITEM       = @"item";
  NSString * const TITLE        = @"title";
  NSString * const PRICE        = @"woot:price";
  NSString * const THUMBNAIL_IMAGE  = @"woot:thumbnailimage";
  NSString * const SUBSTANDARD_IMAGE  = @"woot:substandardimage";
  NSString * const STANDARD_IMAGE   = @"woot:standardimage";
  NSString * const DETAIL_IMAGE   = @"woot:detailimage";
  NSString * const DESCRIPTION    = @"description";
  NSString * const IS_WOOTOFF     = @"woot:wootoff";
  NSString * const SHIPPING_PRICE   = @"woot:shipping";
  NSString * const SALE_URL     = @"woot:purchaseurl";
@end

Creating the Worker Class

NSXMLParser works using delegate callbacks to let you hook into events that you are interested in. Most of the examples I found online used delegate methods right inside the viewcontroller to accomplish the parsing. Since I have several views which are going to take advantage of this functionality, I decided to split it out into a worker class. Let’s construct this class (I named it XMLParser) piece by piece:

XMLParser.h

#import <UIKit/UIKit.h>

@class Constants; //Forward declaration of the Constants class
@class SaleItem;  //Forward declaration of the SaleItem model object class

@interface XMLParser : NSObject {
  NSMutableString *currentValue; //This will hold element values as we build them piece by piece
  BOOL itemElementInProgress; // This will determine if we are currently parsing an element
  SaleItem *saleItem; // Instance of our model object
}

@property (nonatomic, retain) NSMutableString *currentValue;
@property (nonatomic, retain) SaleItem *saleItem;
@property BOOL itemElementInProgress;

- (BOOL) parse; // This performs the parsing of the file
- (id)initWithSaleItem:(SaleItem *)aSaleItem; // Custom initializer
- (void) trimString; // Small convenience method to remove whitespace

@end

Building XMLParser.m

Lets start with our basic shell:

#import "Constants.h"
#import "SaleItem.h"
#import "XMLParser.h"

@implementation XMLParser
  // Synthesize our properties
  @synthesize currentValue;
  @synthesize itemElementInProgress;
  @synthesize saleItem;

  // Make sure objects are released
  -(void)dealloc{
    [currentValue release];
    [saleItem release];
    [super dealloc];
  }

@end

Now, let’s create the initializer. Add the following code directly after the property @synthesize calls:

  // Sets the saleItem property to a SaleItem object passed in
  - (id)initWithSaleItem:(SaleItem *)aSaleItem{
    if (self = [super init]) {
      [self setSaleItem:aSaleItem];
      [self setItemElementInProgress:NO];
    }
   
    return self;
  }

I am initializing the class this way so I can populate the SaleItem inside of XMLParser and still have access to it from the calling class (a ViewController) when I’m done parsing.

Before we get into how to instantiate NSXMLParser and use it to parse a file, let’s set up the delegate methods to get the data we need out of the file.

I usually put delegate methods at the bottom of the file, right after a #pragma mark definition to delineate this group of methods from the rest of the code. Hop down to the bottom of XMLParser.m, and place this code just prior to @end:

#pragma mark NSXMLParser Delegate Calls
// This method gets called every time NSXMLParser encounters a new element
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
   namespaceURI:(NSString *)namespaceURI
  qualifiedName:(NSString *)qualifiedName
     attributes:(NSDictionary *)attributeDict{
    // If element is named "item", set bool to true so we know
    // we are inside the element in other methods. This is needed
    // because "item" contains most of the data we need
    if ([elementName isEqualToString:ITEM]) {
      [self setItemElementInProgress:YES];
    }
  }

// This method gets called for every character NSXMLParser finds.
  -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    // If currentValue doesn't exist, initialize and allocate
    if (!currentValue) {
      currentValue = [[NSMutableString alloc] init];
    }
   
    // Append the current character value to the running string
    // that is being parsed
    [currentValue appendString:string];
  }

The final delegate method is fired when NSXMLParser reaches the end of an element. Here is the place we can be sure we have gotten the full value of the element, and can now persist that data to our model object for use. right after the foundCharacters method, add this method:

// This method is called whenever NSXMLParser reaches the end of an element
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
                   namespaceURI:(NSString *)namespaceURI
                  qualifiedName:(NSString *)qName{
  [self trimString];  // Remove whitespace with a convenience method
 
  if ([elementName isEqualToString:ITEM]) {
    [self setItemElementInProgress:NO];  // If we are currently on the "item" element, then do not save value
  }


  // If we are processing an element inside of "item",
  // then determine if we need to save the value 
  if (self.itemElementInProgress) {
    // Now check the element id against the ones we are
    // interested in (using the Constants class values).
    // If we find one, set the corresponding property on the
    // SaleItem object
    if ([elementName isEqualToString:TITLE]) {
      [[self saleItem] setItemTitle:currentValue];
    }
       
    if ([elementName isEqualToString:PRICE]) {
      [[self saleItem] setItemPrice:currentValue];
    }
   
    if ([elementName isEqualToString:THUMBNAIL_IMAGE]) {
      [[self saleItem] setItemThumbnaillUrl:currentValue];
    }
   
    if ([elementName isEqualToString:SUBSTANDARD_IMAGE]) {
      [[self saleItem] setItemSubstandardImageUrl:currentValue];
    }
   
    if ([elementName isEqualToString:STANDARD_IMAGE]) {
      [[self saleItem] setItemStandardImageUrl:currentValue];
    }
   
    if ([elementName isEqualToString:DETAIL_IMAGE]) {
      [[self saleItem] setItemDetailImageUrl:currentValue];
    }

    if ([elementName isEqualToString:DESCRIPTION]) {
      [[self saleItem] setItemDescription:currentValue];
    }
   
    if ([elementName isEqualToString:SHIPPING_PRICE]) {
      [[self saleItem] setItemShippingCost:currentValue];
    }
   
    if ([elementName isEqualToString:SALE_URL]) {
      [[self saleItem] setItemSaleUrl:currentValue];
    }
  }
 
  [currentValue release];
  currentValue = nil;
}

The Parse Method

Finally, the whole process is started by the parse method:
    - (BOOL)parse{
    // Create and initialize an NSURL with the RSS feed address and use it to instantiate NSXMLParser
    NSURL *url = [[NSURL alloc] initWithString:@"http://www.woot.com/salerss.aspx"];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];

    // Tell NSXMLParser that this class is its delegate
    [parser setDelegate:self];
   
    // Kick off file parsing
    [parser parse];
   
    // Clean up
    [url release];
   
    [parser setDelegate:nil];
    [parser release];
    return YES;
  }

Final Notes

Some final thoughts on the methodology I am using:

  • I want to get rid of the if statements and use Key-Value coding to set property values. I haven’t gotten the chance to refactor this
  • I am very new to Cocoa, and any suggestions/comments are welcome and encouraged!

Post to Twitter Post to Digg

Tags: ,

StackOverflow DevDays DC Review

This took a while to write (taking care of 2 babies and having a horribly tight deadline on the current project will do that to you). I attended the DevDays conference in DC on 10/26. Overall, the conference was a great value for the price. Aside from 1 presentation, I gained a lot of knowledge from every speaker on the stage. The audience seemed very engaged, and the content was relevant and up to date. And as an side, thank God we didn’t have any drag and drop Microsoft standard demo nonsense! Here’s a breakdown of each presentation and what I found useful:

Opening [Joel Spolsky]

The conference opened with a humorous video starring Joel and Jeff, and led into a talk by Joel on user experience and delivering value in application design. Joel gave a very good speech on the tradeoffs between simplicity in design and adding features. He highlighted some statistics showing how feature additions correlate to applications sales. I took a lot from this speech, and found it enlightening.

iPhone [Dan Pilone]

I found this session useful, mainly because I am currently learning the Cocoa framework to begin iPhone development. Dan is currently developing iPhone apps (and shipping!). It is always good to hear the experiences/best practices from someone who is actually working in the environment. Some of the topics he touched on were:

  • Designing an app (Objective-C, XCode, Interface Builder)
  • Marketing
  • App Store Approval

He highlighted some of the common pitfalls, such as not properly planning/marketing an app. This was one of my favorite sessions of the day.

ASP.NET MVC (Scott Allen)

This was another session I was very interested to hear, because I am currently doing ASP.NET MVC development. Scott took the audience from a basis of no knowledge of the library to a functional app while trying to cover all of the terminology and basics. He was using the 2.0 version of the library, which was also great to see. I have not had the opportunity to use 2.0 yet, and it was great to see some of the new features (especially integrated client and server validation!).

Python (Bruce Eckel)

This talk was good mainly for the history Bruce brings to the table. He was able to give a little bit of inside knowledge on the design decisions made for the C++ and Java languages.

jQuery (Jonathon D Worth)

This was a great session. Jonathon focused mainly on getting started on the library. There were a lot of ‘wow’ moments where he showed 1 to 2 lines of jQuery replacing up to 10 lines of plain JavaScript. I have been using jQuery for over a year, and I still learned some new things about jQuery. Overall, I think this was my favorite session.

 

In wrap up, this conference was well worth the $99. There was a lot of value here. The topics that were covered were hot in the mainstream right now. As a primarily .NET developer it was beneficial to be exposed to new languages and technologies. If the StackOverflow team decides to do DevDays again next year, I’m definitely on board.

Post to Twitter Post to Digg

Tags:

jQuery Plugin – NotifyBar

I just took a stab at writing my first jQuery plugin.
I’ve been using jQuery for about a year now, and it has saved me countless
hours of development and frustration. I finally ran across a pattern I found myself
repeating over and over with no plugin available that behaved exactly the way I wanted it to,
either through default behavior or by passing options in to set it correctly.

I frequently find myself needing to notify the user of some important piece of information.
I don’t want to show a modal dialog in all of these situations, because I consider that a
full stop type of experience. This allows the developer to grab the attention of the user with
a notification that can be dismissed or left at the top while the user continues
to interact with the page. Again, this is my first stab at this, so any feedback is welcome!

Description

This plugin allows you to pass a message to a hidden div at the top of your page.
When activated, it slides down to provide a message to the user.

Custom Options Include:

  • Div Background Color
  • Text Notification Color
  • Notification Text
  • Close Text

Usage

Html

Place this HTML at the top of the page where you want to add NotifyBar:

<div id="notifyBar" style="display: none">
    <span class="notifyText"> </span>
    <span class="closeText"> </span>
</div>

jQuery

Make sure you have a reference to the jQuery library:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>

NotifyBar

Add a reference to the NotifyBar plugin:

<script src="js/jquery.notifyBar.js" type="text/javascript"></script>

Call NotifyBar

Call NotifyBar to show a notification:

<script type="text/javascript">
    $(document).ready(function() {
    $('#showNotification').click(function() {
        $('#notifyBar').showNotifyBar();
    });
});
</script>

Options

Here are the available options:

  • notifyText: Text inside the div that will be displayed for the notification
  • closeText: Text to be displayed on the right as an anchor that triggers the NotifyBar to be closed
  • backgroundColor: Background color value of the NotifyBar
  • textColor: Color of the text displayed (both the notification and close text)

Here are the defaults if an option is not set:

  • notifyText: [nothing]
  • closeText: “X”
  • backgroundColor:  ‘#FE7A15′
  • textColor: ‘#FFFFFF’

Download notifyBar

Post to Twitter Post to Digg

Tags: