Pittsburgh Tech Fest: iOS Best Practices slides & code

Pittsburgh Tech Fest was great this year! It was a perfect opportunity to learn about some different technologies and techniques.  I’d like to give a special thanks to Dave and Eric for doing an awesome job organizing the event and the speakers.

For those that are interested, below are the links to the talk I did on iOS Best Practices: Avoid the Bloat and feel free to comment or ask any questions on this post!

Code before refactoring: https://github.com/JAgostoni/iOS-Best-Practices/tree/master/UglyApp
Code after refactoring: https://github.com/JAgostoni/iOS-Best-Practices/tree/master/NotSoUglyApp
Code as presented at Pittsburgh Tech Fest: https://github.com/JAgostoni/iOS-Best-Practices/tree/master/PghTechFest

PowerPoint Slides: iOS Best Practices – Pittsburgh Tech Fest
PDF Slides: iOS Best Practices (PDF) – Pittsburgh Tech Fest

Thanks again to those that attended my talk!

Advertisements

Test Flight: Simplified Beta Deployments for iOS Apps

One of the more challenging things in creating iOS applications is the whole provisioning and deployment process.  This becomes especially true during beta testing of your application using Ad-Hoc distribution.

The process is something like this:

  1. Gather all the UDIDs from your test subjects
  2. Enter these into the iOS Provisioning Portal
  3. Create a distribution certificate (if you haven’t already)
  4. Create an Ad-Hoc provisioning profile
  5. Download the profile and certificate
  6. Configure and build your app signing with these credentials

Then, your testers need to get the application and install it.  Generally like this:

  1. Email the testers the provisioning profile and app bundle
  2. The tester need to drag both to iTunes (or Organizer) and then tether and sync

This makes it a little cumbersome for most testers and difficult for non-tech savvy users.  One could post the app bundle to a website with a manifest and then create a launch page (like an internal app store) and then notify the users.  This is where Test Flight (www.testflightapp.com) comes in … managing the web-delivery of your application in a really easy-to-use process.

Remembering the above, now I do this:

  1. (optional) Ask your test users to register their devices
  2. (optional) Create a team out of these testers and download their device IDs
  3. (optional) Upload all of these device IDs at one time to the Provisioning Portal
  4. Follow the same process as above to create a provisioning profile and build
  5. Now upload the IPA bundle to Test Flight and I am done

At this point, the testers can receive a notification of the new build (and subsequent builds) and simply visit Test Flight on their device.  They will be able to install your beta app straight from the browser.  Further, I can report on who has installed the app and who has not.

All this and the website has a nice easy to use design on top of that!

Updated iPad view resizing for keyboard…

There were a couple of glitches in the code I had originally posted in “iPad: resize view to account for keyboard” especially when dealing with a scroll view within a split-view controller, etc.

Originally, I was using the “setContentOffset” method on the UIScrollView:

[scrollView setContentOffset:scrollPoint];

This was problematic depending on the orientation and so forth.  Frankly, I could never get it to behave the way I wanted.  After much debugging I looked at some other methods and found: scrollRectToVisible.  Well … that sounds like just what I need and no longer have to worry about calculating the perfect position for the scroll point.  Here was the updated line:

[scrollView scrollRectToVisible:activeField.frame animated:YES];

There. Much better! See the original post for the rest of the tutorial.

SNLog – Simple logging framework for iOS

[SNLog Project Page Link]

Logging

A simple task that every developer runs into when creating software is proper logging and tracing.  What I have found is the simplest solution for logging is always the best.

Requirements are almost always the same:

  1. Need to log to the console when debugging
  2. Need to write to a file/database for runtime tracing and diagnostics

There are lots of frameworks for just about every platform out there but logging on the iPhone seemed to be lacking so I decided to create my own as an exercise.  Something that would be applicable to my current projects as well as to help out anyone else that might need something like this.

NSLog

NSLog is the de-facto standard for logging during debugging in Xcode but is primarily temporary scaffolding in your code that needs removed prior to deploying the application.  I wanted something as simple to use as NSLog that could be configured to log somewhere more persistent than the console.

SNLog

The plan of attack on this was straightforward:

  1. Minimize the files that need included in the project (just two, the .h and .m)
  2. Make use of it as easy as NSLog
  3. Re-define NSLog to use the framework instead of the built-in function
  4. Use a strategy pattern to allow multiple logging mechanisms (console and file built-in)

In addition, I ran into the following additions while I was working on it:

  1. Don’t let the file logging grow unbounded (especially on a mobile device)
  2. Capturing the log level/importance can allow different mechanisms to capture the level that is required

So that’s it … in two simple files I was able to create a simple logging mechanism that is a drop-in replacement for NSLog.  I can keep all those frivolous NSLog statements in my code and choose where to redirect the log.

Check out the Project Page for more details and the download.

iPad: Resize view to account for keyboard (updated)

UPDATE (3/9/2011):

There were a couple of glitches in the original code so I found a nicer way to handle the scrolling as updated below!

Classic problem

In an iOS application the keyboard slides up and hides the view/field that the user is editing.  There are a ton of solutions out there but there are problems with just about all of them when it comes to the iPad.  Even more of an issue is the stock “Splitview” applications that we can implement using other UI views like a UIScrollView.

Requirements

First, here is a list of functions that I wanted to implement:

  • Works with a UISplitViewController
  • Works with a UIScrollView
  • Ensures the field being edited is visible
  • Uses smooth animation, not jumpy
  • Looks like a iPad application should behave
The Apple Way

What better way to implement this than to use Apple’s documentation, right? Well, as described in this document Apple has a great approach.  However, there were several problems with this implementation:

  • It was written for an iPhone
  • It does not take into account the rotation of the device
  • It does not take into account the scroll view’s current position
  • It does not take into account the split view design

However, what I do like about is that it takes advantage of simple code with minimal state management and just needed some tweaks.

Let me break it down.

First, when getting the size of the keyboard the following lines of code do NOT take in to account the rotation of the device:

NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];

The good news is there is an easy way to fix this by just translating the CGRect using the current view’s convertRect method:

kbRect = [self.view convertRect:kbRect toView:nil];

Now the rectangle will be oriented correctly no matter which way the device is turned.  Ok … problem one solved.  Next up, the code didn’t take into account the split view controller design pattern. First, when calculating the CGRect which determined what was the “visible” area of the screen it ignored the toolbar as well as fact that the “detail view” was offset by the “master view” when in landscape view.

Accounting for the toolbar was easy by modifying this code:

CGRect aRect = self.view.frame; aRect.size.height -= kbRect.size.height;

… by taking into account the size of the toolbar:

aRect.size.height -= self.toolbar.frame.size.height;

Now, in order to determine if the currently selected field is visible, it’s coordinates must be translated to the same system the detail view is in.  The stock code gets the active field’s coordinates as follows:

CGPoint fieldOrigin = activeField.frame.origin;

… then as before I need to translate to a comparable system:

fieldOrigin = [self.view convertPoint:fieldOrigin toView:self.view.superview];

… and take into account the current scroll position of the UIScrollView (thanks DK_Donuts) and I end up with:

CGPoint fieldOrigin = activeField.frame.origin;
fieldOrigin.y -= scrollView.contentOffset.y;
fieldOrigin = [self.view convertPoint:fieldOrigin toView:self.view.superview];

Now we use this magical method: scrollRectToVisible and the scroll view takes over for us:

[scrollView scrollRectToVisible:activeField.frame animated:YES];

As far as making the positioning smooth and behaving like an iPad app, I need to save the original offset …

originalOffset = scrollView.contentOffset;

…and then reset the position when the keyboard is hidden.

[scrollView setContentOffset:originalOffset animated:YES];

The animation smooths it all out. Finally … something that works the way I expected!

The Code

Below is the listing of the code as modified to work as I needed it.

1. Add some instance variables

CGPoint originalOffset;
UIView *activeField;

2. Create the keyboard notification handlers

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
	kbRect = [self.view convertRect:kbRect toView:nil];
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;

    CGRect aRect = self.view.frame;
    aRect.size.height -= kbRect.size.height;
    aRect.size.height -= self.toolbar.frame.size.height;
    CGPoint fieldOrigin = activeField.frame.origin;
    fieldOrigin.y -= scrollView.contentOffset.y;
    fieldOrigin = [self.view convertPoint:fieldOrigin toView:self.view.superview];
    originalOffset = scrollView.contentOffset;
    if (!CGRectContainsPoint(aRect, fieldOrigin) ) {
        [scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}

… and …

- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
     UIEdgeInsets contentInsets = UIEdgeInsetsZero;
     scrollView.contentInset = contentInsets;
     scrollView.scrollIndicatorInsets = contentInsets;
     [scrollView setContentOffset:originalOffset animated:YES];
 }

3. Wire up the notifications in viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
    name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
    name:UIKeyboardWillHideNotification object:nil];

4. It is up to you to wire up the activeField ivar depending on your implementation.