Monday, September 28, 2009

Best Practices That Work

Tonight I got an email from a friend that my site was down.  Sometimes, if Apache has been running for a long time on my vhost then for some reason the process id gets corrupted.  I don't know if that has to do with the VPS or what.  I never had that problem when I had my dedicated server.  It happens so infrequently I rarely even notice.  However, my usual task of just restarting Apache didn't work.  I got an error message that the main site's docroot didn't exist. That was very strange and very disturbing.  I got on the phone with the sysadmins at my hosting company and couldn't really find the culprit and they didn't have the automated backup of the site.

The good news, as I was told, was that they could recreate the directory structure within one minute and get Apache up and running. SWEET!  However, what about my site?!  Though they offer backups for the VPS they are not guaranteed.

I was not panicking because I keep all site changes backed up in subversion on a completed different host.  Once the directory structure was put back in place all I had to do was check the latest copy of my site and I was backup and running.  It was good to see and know that using best practices does ACTUALLY work.  Imagine that.

Posted via email from Cory Wiles Blog

Tuesday, September 22, 2009

Using NSNotificationCenter with UINavigation Controllers

When presenting an app to user with a large list of information, most developers will add in the ability to search the list inside of the table view.  To further increase usability it is a good practice to include different filters.  For example, a scope button that allows the user to filter by name, address, phone number, etc.  Implementing these features is pretty straight forward.  However, one of the filter options that was needed for a particular app that I am working on was to filter by department.

The complexity to this problem came in the fact that the search term couldn't be the department name, but the code.  Obviously, most users don't know the corresponding codes for a given department.  So I needed a way to present a list of "friendly" department names to the user to pick from, and once a given department was chosen then the corresponding department code would be populate the search bar field.

The first attempt was to use a UIPickerView.  Unfortunately, this wasn't the best solution mainly because you can't use key/value pairs with the UIPickerView.  Well...not in a very straight forward manner.  My second solution attempt was to use a modalViewController and then pass the selected object back to the parent controller.  Everything worked out just fine, but the parent object property wasn't being updated.

To solve this problem there are a couple of schools of thought.

  1. Set the parent controller as the delegate and setup a new protocol that the child controller implements.
  2. Use the NSNotificationCenter which uses the Observer Pattern to update another object based upon some action/method.
Most of the time I would say that the second option is overkill since it is designed for a one-to-many object broadcasts, but in this use case it is the preferred approach because "you would otherwise have to pass instances around of delegates just to connect objects". - http://alexvollmer.com/index.php/2009/06/24/cocoas-ways-of-talking/

FINAL SOLUTION:
// Parent UIViewControlller

- (void)viewWillAppear:(BOOL)animated {

  [[NSNotificationCenter defaultCenter] addObserver:self
                                        selector:@selector(refreshSearchBarControllerWithDepartmentCode:)
                                        name:@"DepartmentCodeNotification"
                                        object:nil];
 
  [super viewWillAppear:animated];
}


- (void) refreshSearchBarControllerWithDepartmentCode:(NSNotification *)notification {

  NSString *key;
  NSString *trimmedSearchString;

  for (key in [notification userInfo]) {
    trimmedSearchString = [[[notification userInfo] valueForKey:key] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  }
 
  self.searchDisplayController.searchBar.text = trimmedSearchString;

  [self.searchDisplayController.searchBar becomeFirstResponder];
  [self.searchDisplayController.searchResultsTableView reloadData];
}

- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {

  if (selectedScope == DEPARTMENT_SCOPE_TITLE_INDEX && [[self.searchDisplayController.searchBar text] length] <= 2) {
   
    DepartmentsViewController *dvController = [[DepartmentsViewController alloc] initWithNibName:@"DepartmentsView" bundle:[NSBundle mainBundle]];

    [self presentModalViewController:dvController animated:YES];

    [dvController release];
   
  } else if ([[self.searchDisplayController.searchBar text] length] >= 2) {
   
    NSString *trimmedSearchString = [[self.searchDisplayController.searchBar text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    [self getEmployees:trimmedSearchString method:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:selectedScope]];

    [self.searchDisplayController.searchResultsTableView reloadData];
  }
}

//Child UIViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

  NSString *code        = [self.tableView cellForRowAtIndexPath:indexPath].detailTextLabel.text;
  NSString *description = [self.tableView cellForRowAtIndexPath:indexPath].textLabel.text;
 
  NSDictionary *searchDepartmentCode = [NSDictionary dictionaryWithObjectsAndKeys: code, description, nil];

  [[NSNotificationCenter defaultCenter] postNotificationName:@"DepartmentCodeNotification"
                                                      object:nil
                                                    userInfo:searchDepartmentCode];

  [self dismissModalViewControllerAnimated:YES];
}

END RESULT:
After a user selects the appropriate department, the child controller is dismissed and the search bar text is updated with appropriate department code.

Posted via email from Cory Wiles Blog

Thursday, September 10, 2009

Microsoft and Enhanced Experience

I read a very interesting article yesterday that ranked the usability between Apple's website and Microsoft's website.  Granted, I am a little biased toward Apple, but I do feel that the author did a great job in their comparison. *spoiler: Apple won.  To add to the critique the Microsoft website does something that is a HUGE pet peeve of mine.

If a company claims to be in the "web" business then they need to create websites that look the same in all browsers.  Microsoft has a HORRIBLE reputation on releasing browsers that are not standards compliant, but are constantly giving non-IE users who visit their websites a second rate experience.  Case in point...their own website.  If you go their website with anything other than Internet Explorer, the visitor is presented with this ANNOYING alert in the upper left hand corner that is asks if you want to "Upgrade your Internet experience".  After few seconds it does minimize, but if you don't click the "Don't show me this EVER again link" the next page you navigate to will show it.

The fact is that I do want to upgrade my internet experience, but not by installing Silverlight, or IE7 or IE8 for that matter.  All of the before mentioned pieces of software are horrible, lack innovation, and do nothing but dumb down the web.  I want to upgrade my internet experience by not being harassed by Microsoft.

Posted via email from Cory Wiles Blog

Tuesday, September 8, 2009

Automate Adding Twitter Followers with Zend Framework

Adding followers to a user one at a time is sometimes a VERY lengthy process.  Last night the question was posed to me if there was a way to automate adding followers from users who follow someone else.

Example scenario:
John Doe follows Jane Doe
John Doe wants to follow at 600 followers of Jane Doe

Unfortunately, the Zend_Service_Twitter class doesn't offer any functionality to retrieve a list of followers from another user, but I was able to extend the class and add the functionality.

The particular user that I chose to test has roughly 6,000 users.  My client didn't want all 6,000, but the first 200.  Within 10 minutes the new custom class was written,  the script ran, and now my client was now following 200 new people of like mind. :)

I plan on submitting the feature request to the Zend Framework gurus to have the ability to find followers from other users so that it can be apart of the main service.

Until then here is the class and script:

require_once 'Zend/Service/Twitter.php';

class CW_Twitter extends Zend_Service_Twitter {

  public function __construct($username, $password) {
    parent::__construct($username, $password);
  }

  public function getOtherUserFriends($name) {
        $this->_init();
        $path = '/followers/ids/' . $name . '.json';
        $response = $this->restGet($path);
        return $response->getBody();
  }

}

$twitter = new CW_Twitter('username', 'password');
$response = $twitter->getOtherUserFriends('friendusername');

$arrayObject = new ArrayObject(Zend_Json::decode($response));
$limitIterator = new LimitIterator($arrayObject->getIterator(), 0, 200);

    foreach ($limitIterator as $value):
      $twitter->friendship->create($limitIterator->current());
    endforeach;

Posted via email from Cory Wiles Blog