Categories in Objective-C

   Posted by: Jason Shapiro

Typically, when a developer wants to extend or modify the behavior of a class in an Object Oriented language, inheritance is used.  While Objective-C does support inheritance, it also offers an alternative syntax: "Categories."

The easiest way to conceptualize a category is to think of it as a short cut to add extra methods to an existing class.  This is the key difference between inheritance and categories: inheritance allows you to override methods, add new methods, and add new instance variables / properties; a category is more limited: you cannot create new instance variables / properties... only methods are permitted.

Let's compare and contrast the two with a simple code example.  Let's say I want to add a method to the NSArray class that returns all of the text in an array, as uppercase Strings.  I could try extending NSArray:

   1: @interface StringArrayUpperCaser : NSArray
   2: -(NSString *)stringAtIndexToUpperCase:(NSInteger)index;
   3: @end
   1: #import "StringArrayUpperCaser.h"
   2:  
   3: @implementation StringArrayUpperCaser
   4: -(NSString *)stringAtIndexToUpperCase:(NSInteger)index
   5: {
   6:   NSString *theString = [self objectAtIndex:index];
   7:   return [theString uppercaseString];
   8: }

Unfortunately, I'd find out pretty quickly that I'd need to do a bit more work.  There are certain methods that are required to be overridden in all subclasses of NSArray (not to mention other overrides that would be required to make convenience methods such as "arrayWithObjects" to work properly).

Given that all I wanted to do was add one method to NSArray, this is starting to look like a lot more work than I had bargained for.  So let's try an alternative solution: creating a category.  This process is relatively straight forward.  You'll create an interface and implementation, just as you would with a normal class, however you put the name of your category in parenthesis (to the right of the class name you are enhancing):

   1: @interface NSArray (StringToUpperCase)
   2: -(NSString *)stringAtIndexToUpperCase:(NSInteger)index;
   3: @end
   1: #import "NSArray+StringToUpperCase.h"
   2:  
   3: @implementation NSArray (StringToUpperCase)
   4: -(NSString *)stringAtIndexToUpperCase:(NSInteger)index
   5: {
   6:   NSString *theString = [self objectAtIndex:index];
   7:   return [theString uppercaseString];
   8: }
   9: @end

The convention for naming the physical files is typically <Class Name>+<Category Name>.  In this case, we'd name the files NSArray+StringToUpperCase.h and NSArray+StringToUpperCase.m

Now, when I want to use this new method, I import the category and simply call it on a pointer for an NSArray object:

   1: #import "NSArray+StringToUpperCase.h"
   2:  
   3: int main (int argc, const char * argv[])
   4: {
   5:   @autoreleasepool 
   6:   {
   7:     NSArray *myArray = [NSArray arrayWithObjects:@"this", @"is", @"a", @"test.", nil];
   8:     for( NSInteger i = 0; i < [myArray count]; i++ )
   9:     {
  10:       NSLog(@"%@", [myArray stringAtIndexToUpperCase:i]);
  11:     } 
  12:   }
  13:     return 0;
  14: }

results

You'll note that although we named our categories in the interface / implementation ("StringToUpperCase"), we did not have to reference the category name when using the new method.  All we had to do was create an NSArray object and send the new message.

If you happen to see the following warning ("... may not respond to..."), double check to make sure you imported the category's header file.

category-warning

Besides easing sub-classing requirements, Categories can help with organizations: large classes can be separated into different files, based on functionality.  For example, you may have a SystemManager class that does 'read', 'create', and 'manipulate' functionality.  You could have the core methods declared in the SystemManager class, and create categories for each of the separate specific functionalities: SystemManager (read), SystemManager (create), SystemManager (manipulate).  You could also use categories to separate your deprecated methods from the rest of your class: SystemManager (deprecated).

In summary, Categories are a handy way for quickly extending or modifying the behavior of a class, while also offering benefits in terms of logical organization.

Adding "Select All" functionality to Facebook Invites

   Posted by: Jason Shapiro

Disclaimer: At the time this article was written, this code was verified to work on Chrome 15.x, Firefox 5.x, and IE 8.x). Facebook, however, could change their markup at any time, rendering unexpected results in any browser.  Use at your own risk.

One of the pains of maintaining a Facebook page becomes evident when you want to send invitations to all of your friends.   Instead of providing a "Select All" button, you are required to click on every checkbox for every person that you would like to have added to the list. 

invitepic

For pages that have 1,000 or more friends, this process is extremely cumbersome. 

The solution is to use JavaScript to select all of the names for you!

In order to do this, we need to:

  1. Find out the name that is being used for each checkbox element (hopefully they have a consistent name for all checkboxes... otherwise we'll need to use more general functions to traverse the DOM).
  2. Create some JavaScript code that selects all of the checkboxes (by typing in some executable JavaScript into the Location/Address Bar).

Sure, this may seem like as much work as clicking each checkbox, but once we've determined what JavaScript will "select all" invitees, we can simply cut and paste this code for all future invites... at least until Facebook changes their markup.

 

Step 1: Determine the Checkbox Name

There are lots of tools that let you inspect the DOM of a web page.  In this case, I'm using the built in inspector that comes free with the Chrome browser.  I simply right click on the checkbox and select "Inspect Element."

elementname

We're in luck!  After inspecting several checkboxes, I was able to see that there is a consistent name being used: "checkableitems[]."  We'll use this name with the DOM API to get a collection of all the checkboxes on the page.

 

Step 2:  Create our "Select All" JavaScript

Our code is going to be very simple... retrieve a collection of all the checkboxes on this page, iterate through that collection and programmatically add a checkmark. 

To retrieve the collection of checkboxes, we'll use a DOM method on the "document" object: "getElementsByName()":

   1:  var invitees = document.getElementsByName("checkableitems[]");


Next we need to iterate through each of the objects in this collection.  For this we'll use the "For" loop.   I had originally used a "ForEach" loop in this example, but switched it to this for compatibility with IE.

   1:  for( i = 0; i < invitees.length; i++ )
   2:  {
   3:      // ...
   4:  }
 

The object we're referencing from the collection, an HTMLInputElement, includes a "checked" property.  Normally, setting this to a value of "true" (resulting in a checkmark), would be sufficient...

   1:  invitees[i].checked = true;


... and although that will cause each person to become checked, my testing has shown that it doesn't affect which invitees have actually been selected.  So, let's try another option... perhaps there is some event handling that is kicked off when a check box is clicked.  Rather than just calling "click" on the element, let's double check that the type of element we're clicking is a checkbox (I'd hate to accidentally click a button that says "remove friends").

   1:  if( invitees[i].type == "checkbox" )
   2:  {
   3:      invitees[i].click();
   4:  }


Putting this all together:

   1:  var invitees = document.getElementsByName( "checkableitems[]" );
   2:  for( i = 0; i < invitees.length; i++ )
   3:  {
   4:      if( invitees[i].type == "checkbox" )
   5:      {
   6:          invitees[i].click();
   7:      }
   8:  }


Let's tighten this up with shorter variable names and no unnecessary white space (something I wouldn't normally advocate; verbose and meaningful code is preferable to that which is compact and obscure.  Nevertheless, the goal here is to have a single line of code that we can cut and paste into the location/address bar):

   1: a=document.getElementsByName("checkableitems[]");for(i=0;i<a.length;i++){if(a[i].type=="checkbox")a[i].click();}

 

The Solution:

Facebook doesn't load all of your friends at once.  Instead, it loads more people as you continue to scroll down the list of names.  So the first thing you need to do is open up the "Invite Friends" window, and drag the scroll bar down until you reach the end of your list.  You'll notice the bar sometimes jump a bit as it continues to load names into the window.

scroll

Now to execute the code written above, we simply need to prepend it with the word "javascript:" (note the colon), so the browser executes it as code, rather than treat it like a website url.  I also added a "void(0);" at the end to prevent IE & Firefox from reloading the page (a trick I learned from http://stackoverflow.com/questions/2918776/javascript-in-an-address-bar).

Thus, the actual code to cut and paste into your "Location/Address" bar is:

javascript:a=document.getElementsByName("checkableitems[]");for(i=0;i<a.length;i++){if(a[i].type=="checkbox")a[i].click();}void(0);

Note:  Some browsers will not let you and cut and paste the word "javascript:" into the location/address bar.  Double check to make sure your code starts with "javascript:" before clicking enter (if it's not there, just type it in).  Also, some versions of Firefox won't let you run any javascript commands in the location/address bar.  In that case, under Tools, find the "Scratch Pad", and execute the JavaScript there.

endresult
It may take from several seconds up to a couple of mins to select everyone... 
the longer your list of friends, the longer it'll take to add the checkmarks.

That's it!  From here on out (until Facebook changes their markup), all that we'll have to do is create an invite, scroll down to the end of our list, cut/paste that single line of code, and all of our friends will be selected!

New Changes to Oracle's Java Certification Paths

   Posted by: Jason Shapiro

One of the major changes that has happened in the Java world, since Oracle's acquisition of Sun, is in regard to the official certification program.  Following Oracle's certificate taxonomies, Java now has four levels: Associate, Professional, Master, and Expert

The main differences between these categories are:

  • The Associate level establishes competency in basic skills which are considered prerequisite knowledge.
  • The Professional level verifies the holder possesses the expected skill set to be able to effectively develop and use the specified technologies.
  • The Master level is considered the highest level of skills for a specific technology path.
  • The Expert level is used to show expertise in a specific technology that isn't covered in the Associate -> Master certification paths.

The two most popular certifications (Java Programmer and Web Component Developer) fall into the "Professional" category.  A complete list can be found at: the Oracle Middleware Certification page. 

One of the biggest controversies, and sources of confusion, is in regard to a new training requirement.  Beginning October 1st, 2011, anyone who wants to complete the Oracle Certified Master, Java SE Developer and/or the Oracle Certified Master, Java EE 5 Enterprise Architect will be required to complete one training course.  To be clear, the most commonly completed certification programs - Oracle Certified Professional, Java Programmer and Oracle Certified Professional, Java EE 5 Web Component DeveloperDO NOT currently include this training requirement.

I recently communicated with Oracle to receive some clarification on the new rules and changes (my sincere gratitude to Oracle for taking the time to answer these questions):

1) There has been a great deal of confusion regarding the new course attendance requirements for the Java Architect and Java Developer certifications. Could you clarify the requirement (i.e. which specific certifications are affected by this rule - is it just the Oracle Certified Master, Java SE 6 Developer & Oracle Certified Master, Java EE 5 Enterprise Architect? Will any Professional or Expert level courses have the same requirement?)

"ONE course is required. Candidates can choose from one of the courses in each list on http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=449. The certifications that will require training on October 1 are:

Oracle Certified Professional, Oracle Solaris 10 System Administrator
Oracle Certified Expert, Oracle Solaris 10 Security Administrator
Oracle Certified Master, Java SE 6 Developer
Oracle Certified Master, Java EE 5 Enterprise Architect"

2) If someone has completed a portion of the Developer or Architect certification (such as the assignment), prior to October 1st, and finishes up the latter portion after October 1st, will they be exempt from the course attendance requirement?

"The certification path must be completed by October 1, 2011. If candidates submit Assignment and Essay on September 30, it can take up to 4 weeks to grade. If they pass, they are exempt from the training requirement. If they fail, they are required to complete training and resubmit the assignment. We recommend that candidates submit the assignment and complete the essay by August 27 to allow time for grading and also time to resubmit in the event that they fail."

 

3) Other than the piece of paper, what does Oracle see as a benefit? Do certificate holders get first peek at new releases or have preference for attending / presenting at JavaOne?

"Employers look for ways to distinguish employees and prospective employees who have the solid foundation of skills needed for effective performance. The Oracle Certification Program helps the IT industry make these distinctions by establishing a standard of competence in specific job roles.
See below how thousands of members have testified to the value of the Oracle Certification Program*.

97% said they have benefited from certification.
96% would recommend the program to a professional colleague.
89% said they gained more confidence in their Oracle expertise after becoming certified.
Candidates can also view our salary survey http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=305 (tho this survey does not include Java certification)
There is also a 'Certification Lounge' at shows such as Oracle OpenWorld and JavaOne."

 

4) If you take an approved class and don't pass the exam, are you allowed to retake the class for free? What guarantees/limits/warrantees come with the class?

"We would not expect a course to be a complete preparation for an exam. Oracle recommends a combination of training and experience and we encourage candidates to use multiple resources to help prepare for exams. In the recommended training section of each exam page, we provide the course or courses that will best help a candidate prepare for the exam, but the exam will not map back to this course question for question because the course cannot fully address the hands-on practice/experience component of a candidate's preparation strategy.  Oracle University courses come with a 100% satisfaction guarantee http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=94, but this guarantee is not tied to how closely the course maps to the certification exam or to a candidate's exam result.."

 

Additional information on the new Java Certification rules can be found at: http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=449

Xcode 4: Deploying Your App to Your Own iPhone / iPad / iPod touch

   Posted by: Jason Shapiro

When I first learned how to write an iOS application, I found that the biggest challenge wasn't in learning Objective-C, nor was it in becoming familiar with the Xcode IDE.  Instead, the main difficulty was in getting my app on to my own device!  Unfortunately, many of the books and tutorials assume you'll figure this out on your own... and as I learned, the process was not trivial:

  • I had to create a public/private key pair (via "Keychain"),
  • Generate a certificate request (via "Keychain"),
  • Login to the Provisional Portal at the Apple Developer Site,
  • Create and download a development certificate with the certificate request,
  • Find & register the UDID of my device,
  • Create an "App ID",
  • Create and download a provisional profile which links my developer certificate, UDID, and App ID together,
  • Install the certificate & profile into Xcode...

... all for the simple purpose of testing out my app on my iPhone!

Well, thankfully, Apple has reduced this complexity with the release of Xcode 4.  Here are the steps you need to take, in order to deploy your apps to your own device (for the purpose of developing & testing only... Ad Hoc & iTunes App Store distribution is still a much more involved process):

  1. Register as a Paid Apple Developer.  Only paid accounts can deploy to actual devices.
  2. Open up Xcode 4's Organizer, and select "Provisioning Profiles" in the left side menu:
    left-top-menu
  3. On the bottom of the right side, 'check' the checkbox labeled "Automatic Device Provisioning": 
    provisioningprofiles
  4. Plug your device into your computer.  Select your device in the left side menu, and click the "Use for Development" button: 
    setupfordev
  5. You will be prompted to log in to your Apple Developer account.  Enter your credentials here:
    login
  6. Xcode will likely you tell you that it doesn't recognize the version of iOS.  Allow it to collect the necessary information when prompted: 
    unknownios
  7. After this has finished, you're done!  You should now see your device listed in the top left list of "Schemes" in Xcode: 
    xcode

That's it - no more dealing with keys, certificates, UDIDs, App IDs, etc (at least for testing on your own device).  Also, note that you shouldn't need to repeat these steps for the same device on the same computer.  Once your device has been configured, it should be available for all of your Xcode iOS projects!

Easy Memory Management in Objective-C (iPhone / iPad)

   Posted by: Jason Shapiro

One of the reasons developers try to avoid learning Objective-C is in regard to its memory management (or rather, its lack of automation). 

I'm going to cut right to the chase: in basic, single threaded applications, memory management really isn't all that awful, and with a few simple rules, you can avoid most of the common mistakes made.

Allocating & Deallocating Memory

When an object is created, space needs to be allocated to store it on the heap (an area of memory for objects).  When that object is no longer in use, it should be deallocated from the heap in order to allow other objects to occupy that space.  This act of deallocation runs two great risks:

  • Forgetting to deallocate some memory before the pointer has dropped out of scope causes a "memory leak."  This means that the memory space is still occupied, despite the fact that it is no longer able to be used (or, for that matter, released).
  • Deallocating space before it's finished being used produces bugs which can be very difficult to track down.  If you have multiple pointers which are referencing the same object, you obviously don't want one of the pointers to remove the object while others are still using it! Unfortunately, the moment an exception occurs could be light years away from when the deallocation took place (hence the difficulty in locating the source of the problem).

Retain & Release

Luckily, with Objective-C, allocating is easy (you simply call "alloc" on the class you wish to create an instance of):

   1:  Customer *cust = [[Customer alloc] init];

Allocating not only creates the object and a place for it in memory, it also adds a single "retain count."  This count is used to determine whether or not an object should be deallocated.  As long as an object has a positive "retain count," it will remain on the heap.  Retain counts can be added explicitly by calling "retain" on a pointer.

   1:  [cust retain];

[Detailed rules on when you should add a retain count are discussed below.]

This "retain count" means that you will never have to deallocate an object yourself.  Instead you "release" an object that you've allocated (or became "an owner of" - more on that below), when you are finished. 

   1:  [cust release];

Every time a "release" message is received, the retain count is decremented.  The memory, however, will not be deallocated until the retain count has reached zero.  This helps reduce errors that would occur by accidentally deallocating an object while it is still "on active duty."

The Golden Rule: Symmetry

If you only remember one thing from this article, let it be this: If you are an owner of an object you MUST release the object when you have finished.  Likewise, if you are not an owner of an object, you MUST NOT release the object.  Therefore what you are looking for is symmetry in your code.  Retain -> Release.  That's it.  All of the other rules and use cases derive from this simple concept.  The next natural questions are:

  • What does it mean to be an owner?
  • When do I want to become an owner?
  • How do I become an owner?

What Does It Mean to be an Owner?

You're the owner of an object when your code has added a retain count to the referenced object.  This retain count could have been added directly (by calling "retain" on the pointer):

   1:  [cust retain];

... or indirectly by calling a "creation" function - more on that below.

When Do I Want to Become an Owner?

You are an owner of any object you create, whether you want to be, or not (again, more on that below).  In addition to that scenario, you'll generally want to become an owner when you are keeping the object beyond the scope of a single function (as an instance variable).  If you aren't an owner of an instance variables that points to an object, you run the risk that the object will be deallocated before you are finished using it.  Therefore, it's important to make sure you add a retain count for all instances variables that point to objects (unless you already added one implicitly, by calling a "creation" function). There are other exceptions & less common situations that you may run into as well, but this is the big one to remember.  

How Do I Become an Owner?

As stated above, you can become an owner of an object by calling the retain function.

You also become an owner by calling a "creation" function.  This is a function that not only returns a pointer to an object, it also adds a retain count on your behalf.

You can recognize a "creation" function by its name: it will start with, or simply be, one of these words: "Alloc," "New," "Copy," or "mutableCopy."  Since a "creation" function adds a retain count on your behalf, you are implicitly the owner of that object, and must eventually call "release."

It is generally assumed that if these words do not appear in the name of the function, and you haven't called "retain" on the pointer, you are not an owner of the object.  Therefore you should NOT release the object when you are finished using it.

   1:  NSString *myStr1 = [[NSString alloc] initWithString:@"Intertech" ];
       // An owner of the NSString (alloc) -- you must eventually call "release."
   2:  NSString *myStr2 = [[NSString stringWithString:@"Intertech"]; 
// NOT an owner of the NSString. -- you should NOT call "release."

Autorelease

When a function has a return value of a pointer to an object that it creates, the retain count needs to be handled in a special way.   For example, look at the following code... What's wrong with it?

   1:  -(Customer *) customer
   2:  {
   3:      Customer *cust = [[Customer alloc] init];
   4:      return cust;
   5:  }

We've broken the golden rule!  We've created an object with "alloc" (therefore adding a retain count), but we haven't released it before the "cust" pointer has fallen out of scope!  So how about this example... any better?

   1:  -(Customer *) customer
   2:  {
   3:      Customer *cust = [[Customer alloc] init];
   4:      [cust release];
   5:      return cust;
   6:  }

Well... now we have symmetry (retain -> release), however it's possible that the Customer object will be deallocated before the caller is able to use and/or retain it!

To get around this problem, Objective-C gives us a special kind of release called "Autorelease."  This function provides a deferred release, meaning we have given up ownership, but the object won't be released until a later time.  It does this by adding the release to an "autorelease pool."  You'll notice that this pool is automatically created and "drained" in your main() function by Xcode:

   1:  int main (int argc, const char * argv[])
   2:  {
   3:      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   4:   
   5:  // ...
   6:   
   7:     [pool drain];
   8:     return 0;
   9:  }

Therefore, you typically don't need to manage the pool yourself.  Instead, simply call "autorelease" when you have created an object in a function (unless your function is a "creation" function, which means the name should start with the one of the "creation" names: "Alloc," "New," "Copy," or "mutableCopy."  In that case, the caller would expect you to leave the retain count on their behalf).

   1:  -(Customer *) customer
   2:  {
   3:      Customer *cust = [[Customer alloc] init];
   4:      return [cust autorelease];
   5:  }

 

The Dealloc Function

When your object has been released enough times that the "retain count" has hit zero, the OS will deallocate your object from memory.  However, most objects that you create will be composed of pointers to other objects.  Consider the following interface:

   1:  @interface Customer : NSObject {}
   2:  @property (nonatomic, retain) FullName *custName;
   3:  @property (nonatomic, retain) Address *custAddr;
   4:  @end

When the customer is deallocated, what happens to the FullName and Address objects?  We wouldn't want to have the OS blindly deallocate these two objects, in case they are still in use by other blocks of code.  Instead, we should be given the opportunity to remove our retain count on these properties (if appropriate) before the Customer object is deallocated.  This is done through a special function called "dealloc."  It is your responsibility to create this function and release all retained instance variables (a call to the parent dealloc should be made at the very end of this function):

   1:  -(void) dealloc
   2:  {
   3:      [custName release];
   4:      [custAddr release];
   5:      [super dealloc];
   6:  }

Putting it together

Here is an example that contains several memory management problems.  See if you can find them all:

   1:  #import "FullName.h"
   2:  @interface Customer : NSObject {
   3:      FullName *_custName;
   4:  }
   5:  - (Customer *) customer;
   6:  - (FullName *) custName;
   7:  - (void) setCustName: (FullName *) newValue;
   8:   
   9:  @end
  10:   
  11:  @implementation Customer
  12:   
  13:  - (Customer *) customer
  14:  {
  15:      Customer *cust = [[Customer alloc] init];
  16:      return cust;
  17:  }
  18:   
  19:  - (FullName *) custName
  20:  {
  21:      [_custName retain];
  22:      return _custName;
  23:  }
  24:   
  25:  - (void) setCustName: (FullName *) newValue
  26:  {
  27:      _custName = newValue;
  28:  }
  29:   
  30:  - (void) dealloc
  31:  {
  32:      [super dealloc];
  33:  }
  34:   
  35:  @end

Problem #1: The Customer function on lines 13 - 17 has broken symmetry.  It creates an object with "alloc" but does not "autorelease" it.  Remember, when a function creates and returns an object, it should call autorelease, rather than release (for details, review the "Autorelease" section above).

Corrected:

   1:  - (Customer *) customer
   2:  {
   3:      Customer *cust = [[Customer alloc] init];
   4:      return [cust autorelease];
   5:  }

Problem #2: The custName function on lines 19 - 23 adds a retain count, but doesn't release it.  In fact, there is no real reason to add a retain count here, since we haven't created a method that uses one of the four "creation" names ("add," "new," "copy," and "mutableCopy"). 

Corrected:

   1:  - (FullName *) custName
   2:  {
   3:      return _custName;
   4:  }

Problem #3:  The setCustName: function on lines 25 - 28 are storing an instance variable, but are not retaining the reference.  Without a retain, it is possible the object will be deallocated before we are finished using it.  In this case, we need to release the old value and retain the new value.  Don't worry about whether or not there is already an old value set for this instance variable.  Unlike other languages (such as the NullPointerException in Java), Objective-C will quietly ignore the "release" call if nothing is there.

Corrected:

   1:  - (void) setCustName: (FullName *) newValue
   2:  {
   3:      [_custName release];
   4:      _custName = [newValue retain];
   5:  }

Problem #4:  We forgot to release our instance variable in the dealloc method!

Corrected:

   1:  - (void) dealloc
   2:  {
   3:      [_custName release];
   4:      [super dealloc];
   5:  }

P.S. - Use Your Tools!

While following the rules described above will drastically reduce the amount of memory issues you experience in your code, you'll probably still miss a few due to exceptional situations and common mistakes.  Thankfully, the iOS SDK includes several tools to help you identify the over and under releasing of objects.  In Xcode you can use the "Analyze" build option to see if it detects any memory errors.  Also, Instruments provides a "Leaks" template (identifies under releasing) and a "Zombies" template (identifies over releasing).  I'll explore these tools in a future post!

Intertech's Mobile DevCon - iOS Follow-up

   Posted by: Jason Shapiro

I want to thank everyone for attending yesterday's Mobile DevCon!  It was really great to meet so many of you and to learn about the different types of mobile projects you are / will be working on.

There are two points that I want to follow-up on from my iOS seminar.  The first is regarding the free developer account from Apple.  I had stated that if you sign up with Apple, you'll be able to download Xcode for free.  During the break, an attendee mentioned that he wasn't able to download Xcode 4 (though Xcode 3 was available).  Instead, there were two options: upgrade to a paid account or pay for Xcode 4 via the Mac App Store.  Another person who heard our conversation told me that he had a free account and was able to download Xcode 4.  Looking at various blogs online, I saw the same "you can't get this" / "yes you can" debates.

So, this morning I followed up with Apple to find out the specifics: With the free account you are able to download Xcode, but only "GA" versions.  Xcode 4 is still considered "beta," so you do need to have a paid account to download the latest version for free.  Another option is to purchase the software from the Mac App Store: http://itunes.apple.com/us/app/xcode/id422352214?mt=12  There is no ETA on when Xcode 4 will be considered "GA."

The second point is a correction I want to make in regard to the iOS Simulator.  I had a minor brain freeze when a question came in as I was configuring the code demo:  "Is there a simulator for the iPad?"  For whatever reason, my brain took that as "Is there another simulator for the iPod (touch)", so I said: "No."  The correct answer, of course, is that there is a simulator for the iPad.  You can access this through the schemes:

iOSsimulator1

or through the simulator itself (Device -> iPad):

iOSsimulator2

Don't forget that you can download all of the Mobile DevCon slides at: http://bit.ly/mobiledevcon and my sample iOS code project at http://bit.ly/mobiledevconioscode - (be sure to read the "ReadMe.rtf" file for instructions on adding GData to the project!)

Find Us
Contact Us 651-288-7000 1-800-866-9884
Home | Training | Curriculum | Course Finder | Schedule | Enroll | Twin Cities Java User Group | Consulting | Foundation | Jobs | About Us | Our Story | Press Room | Instructors | President | Map & Directions | Sitemap

Java Training | JSF / Struts / Spring / Hibernate Training | Java Power Tools Training | .NET 4.0 & Visual Studio 2010 Training | Microsoft Web Development Training | Prism / MVVM / MEF Training | .NET 3.5 and Visual Studio 2008 Training | .NET 2.0 and Visual Studio 2003 Training | Cloud Computing Training | Ajax / Web Services / XML Training | Groovy and Grails Training | SQL Server 2012 Training | SQL Server 2008 Training | SQL Server 2005 Training | Mobile Development Training | SharePoint 2010 Training | SharePoint 2007 Training | Agile, Process, Analysis & Design Training | Arch/Design Patterns Training | Microsoft Official Curriculum Training | Web Development Training | Ruby Training | Rational Application Developer (RAD) Training | WebSphere Application Server Training | WebSphere Portal Training | WebLogic Training | Boot Camp Training | Project Management Training | C / C++ Training | Metro / WinRT / Windows 8 Development Training | Retired

Intertech delivers training on-site and virtually serving cities including Phoenix, AZ | San Francisco, CA | Los Angeles, CA | San Diego, CA | San Jose, CA | Washington, DC | Chicago, IL | Orlando, FL | Boston, MA | Duluth, MN | Minneapolis St. Paul, MN | Rochester, MN | Raleigh-Durham, NC | New York, NY | Philadelphia, PA | Austin, TX | Dallas, TX | Houston, TX | Seattle, WA.