I Am Not The Android Expert You Seek

I do not own an Android device. I have spent less than 2 hours in my entire life playing with an Android device. I have never written an app for Android. Heck, I have never even written a single line of Java code in a shipping app. So, for the love of god, please stop asking me if I can look into problems related to Android and Android apps. I am the last person in the world you want to ask to help you with Android related problems.

Disclaimer: I have nothing against Android. It’s just not an area of interest to me.

Posted in work.

Enjoy Less and Love More

To some my life seems like a non-stop vacation. I’m a ski bum, a slacker, someone who rarely works. I’m always off on some adventure, snowboarding in the winter months, hiking and kayaking in the summer months. And from one point of view this is true. Over the last 12 months I have spent a great deal of my time snowboarding, hiking and kayaking. But I’ve also been focused on writing code and building apps.

Writing code and building software is a passion of mine. It’s the one thing I’m most passionate about. I’ve been writing code for more than 30 years, and I cannot image doing anything else with my life.

Over the last year I’ve been enjoying my passion more than ever. I’ve written more code in the last year than I did in the previous 3 years combined. I realized just this morning I have worked on and contributed to 22 different apps in the last 12 months, of which 15 are available in the App Store today. That’s a lot of time behind the keyboard.

Business is booming and life is good. So what changed? How is it I can live the ski bum, slacker life and still get a ton of work done? How is it I’m able to work on more apps today compared to a few years ago and still have time for adventure and family? The answer for me was to cut out those things from my life that I enjoy but do not love. (I already cut out those things I hate years ago.)

I stopped writing. I stopped organizing meet ups events. I stopped speaking at conferences. And while I haven’t completely stopped attending conferences and meet up events, I’ve cut way back. In the last 12 months I’ve attended one CocoaHeads event, one conference, and I socialized in San Francisco for only a 3 days during WWDC. This is a big change from just a few years ago when I was writing more words than code, attending and often times speaking at a conference every other month on average, and attending and organizing multiple social gatherings and meet ups each month.

I’ve also made changes to my daily routines. I don’t spend as much time reading news and following the latest happenings on social sites like App.net, Facebook and Twitter. I still make posts to these sites, but I limit the amount of time I spend reading what others have to say. And I’m not as active as I once was in chat rooms and irc. Cutting back on these distractions gives me more time each day to focus on things I love doing.

Some friends and family members have pointed out that I seem happier since moving to Stowe, and they are right. I am happier these days, but it’s not just because I moved to Stowe. It’s because I’m doing more of what I love, and I accomplished this by doing less of what I only enjoy.

By not doing the things I enjoy, I’ve been able to spend more time doing the things I love.

Posted in general, life, work.

Wait, Justin and I Agree?

My friend Justin wrote a post explaining why he thinks you should use Interface Builder with Auto Layout. It was in response to my post about why I don’t use Interface Builder these days thanks to Auto Layout. Justin’s post is good with plenty of valid points. Justin and I also agree iOS and Mac developers should be using Auto Layout. But he said something that got me wondering…does Justin agree with me more than he realizes. Regarding writing layout constraints in code, Justin says:

It’s totally possible to do this, and there are situations where I do it as well. Small views with a few basic constraints are usually quicker and easier to write without a Xib. Insanely complex views such as the TED video player I wrote and maintain are also too heavy for a straight Xib implementation.

I found that almost all the apps I have worked on in recent years consists of views that fall into the category of either being small views or complex views. And even “insanely complex views” can likely be broken down into smaller views using the composite view pattern. In other words, in my experience many views can be broken down into smaller, more manageable views that end up needing only a few basic constraints. And even Justin agrees that “small views with a few basic constraints are usually quicker and easier to write without a Xib.” So it’s my style, my approach to solving UI problems that makes it quicker and easier for me to write UI with layout constraints in code rather than using IB.

Does this mean I’m right and Justin is wrong? No. Does it mean Justin’s right and I’m wrong? No.

In programming there are multiple ways to accomplish a task, and one approach isn’t always necessarily better or more right than another approach. The approach a programmer takes to solve a problem is often based on past experience and the solution often reflects the personal style of the programmer.

Over the years my style has changed and evolved to a point where I break down complex problems into smaller, more manageable tasks, and I use this style, or approach if you will, when implementing a UI. These days view controller containment and composite views work well for me. I like to write light weight view controllers, and I use other objects to serve as the data source and delegate to my view controllers. And more recently I have found I don’t need to use Interface Builder to be a productive programmer.

Does this mean you should abandon IB as well? Certainly not. Does this mean I hate IB and I will never use it again? Most certainly not. When a task I need to accomplish is quicker and easier for me to do in IB, then I will most certainly use IB. Just like Justin will sometimes write layout constraints in code, I will sometimes use IB to put together a view.

Different situations call for different solutions. There isn’t just one way to solve a problem, and programmers shouldn’t do something just because one person says this is the way it should be done. Programmers should explore the different ways to accomplish a task, and decide what works best for them and what tools and approaches make them the best, most productive programmer they can be. And don’t get hung up on doing a task the same way each time. Challenge yourself by find new ways of accomplishing the same task. In doing this, you might find you agree much more with the person that you thought you disagreed with on the onset.

Posted in ios, programming.

Still Enjoying the Snow

Top of Nosedive I refuse to accept that winter is over. For instance, today I hiked up Mt. Mansfields to do a snowboard run on Nosedive. And based on the crowd, I wasn’t the only one who thought today was a good day for a hike.

Here are some pictures from today’s hike.

I Stopped Using NIBs Thanks to Auto Layout

The subject to this post might suggest I don’t like Auto Layout, but on the contrary I really like Auto Layout. I didn’t always like Auto Layout. For the longest time Auto Layout was a major pain in my ass, but it turns out it was Interface Builder that was causing me the majority of headaches when I used Auto Layout. So I stopped using Auto Layout in IB and I started using it in code only.

It took me a while to get comfortable with Auto Layout in code, but once things clicked, I found it easy to get the layout I wanted. In fact, I have found Auto Layout saves me time when laying out a UI, and I’ve become so comfortable with Auto Layout that now I become annoyed if I have to work on a project that does not use Auto Layout.

Because I use Auto Layout in code I realized overtime I was using Interface Builder only to define the UI elements that made up the view. I would define all the Auto Layout constraints in code. This got me thinking, why do I need to define the UI elements in IB? So I did a small project with no NIBs or storyboards, and I loved it. I felt more productive, and I was banging my head against the wall a hell of a lot less.

(Credit also goes to Matt Massicotte for encouraging me to try a project with no NIB or storboard files.)

I’ve now done a number of iOS projects where I don’t use a single NIB or storyboard, and I’m convinced this is the right approach for me. I see everything regarding a view and its subviews explicitly defined in source code, which makes it easier to see what’s going on and to make changes. No more bouncing between inspectors to figure out what is causing a problem or to understand how a view is rendered. I see it all in the source code defined in a single .m file.

So how do I construct my views using only source code? I have a base class that I call WPSCompositeView in my WPSKit, a set of classes and categories I use to build apps. WPSCompositeView has a class method named +addToSuperview: that creates the instance of the view, which must be a subclass of WPSCompositeView, then calls -loadView on the instance of the view. I place all the code needed to create the subviews of the view in the -loadView implementation. The constraints for the view are implemented in the -updateConstraints that is called by iOS when needed. That’s it.

Here’s an example of a composer view for a new app I’m working on:

And the code for creating this view is as follows:

- (void)loadView
{
  UIView *containerView = self;

  WPSTextView *textView = [[WPSTextView alloc] init];
  [textView setTranslatesAutoresizingMaskIntoConstraints:NO];
  [textView setAlwaysBounceVertical:YES];
  [textView setInputAccessoryView:[self toolbar]];
  [textView setFont:[UIFont systemFontOfSize:17.0]];
  [textView setPlaceholderText:NSLocalizedString(@"What's up?", @"Placeholder text.")];
  [self setTextView:textView];

  UIButton *locationButton = [UIButton buttonWithType:UIButtonTypeCustom];
  [locationButton setTranslatesAutoresizingMaskIntoConstraints:NO];
  [locationButton setImage:[UIImage wps_imageNamed:@"location" withMaskColor:[UIColor lightGrayColor]] forState:UIControlStateNormal];
  [locationButton setImage:[UIImage wps_imageNamed:@"location" withMaskColor:[UIColor wps_iOSDefaultBlue]] forState:UIControlStateSelected];
  [locationButton setSelected:NO];
  [locationButton addTarget:self action:@selector(_locationButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
  [self setLocationButton:locationButton];

  UILabel *locationLabel = [[UILabel alloc] init];
  [locationLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
  [locationLabel setTextColor:[UIColor lightGrayColor]];
  [locationLabel setTextAlignment:NSTextAlignmentLeft];
  [locationLabel setText:NSLocalizedString(@"Location Disabled", @"Location text.")];
  [self setLocationLabel:locationLabel];

  UILabel *characterCountLabel = [[UILabel alloc] init];
  [characterCountLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
  [characterCountLabel setTextColor:[UIColor lightGrayColor]];
  [characterCountLabel setTextAlignment:NSTextAlignmentRight];
  [self setCharacterCountLabel:characterCountLabel];

  [containerView addSubview:textView];
  [containerView addSubview:locationButton];
  [containerView addSubview:locationLabel];
  [containerView addSubview:characterCountLabel];

  [textView wps_setDidChange:^(UITextView *aTextView) {
    NSString *countString = nil;
    NSUInteger length = [[aTextView text] length];
    if (length > 0) {
      countString = [NSString stringWithFormat:@"%lu", (unsigned long)length];
    }
    [characterCountLabel setText:countString];
  }];
}

- (void)updateConstraints
{
  WPSTextView *textView = [self textView];
  UILabel *characterCountLabel = [self characterCountLabel];
  UIButton *locationButton = [self locationButton];
  UILabel *locationLabel = [self locationLabel];

  [locationButton wps_constrainToHeight:38.0f];
  [locationButton wps_constrainToWidth:38.0f];

  NSDictionary *bindings = NSDictionaryOfVariableBindings(textView, characterCountLabel, locationButton, locationLabel);
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[textView]-4-[characterCountLabel]-0-|" options:0 metrics:nil views:bindings]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[textView]-0-|" options:0 metrics:nil views:bindings]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-8-[locationButton]-[locationLabel]-[characterCountLabel]-8-|" options:0 metrics:nil views:bindings]];
  [self addConstraint:[NSLayoutConstraint constraintWithItem:locationLabel attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:characterCountLabel attribute:NSLayoutAttributeBaseline multiplier:1.0 constant:0.0]];
  [self addConstraint:[NSLayoutConstraint constraintWithItem:locationButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:locationLabel attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];

  [super updateConstraints];
}

- (UIToolbar *)toolbar
{
  if (_toolbar == nil) {
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, screenBounds.size.width, 44)];
    [toolbar setTintAdjustmentMode:UIViewTintAdjustmentModeNormal];


    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    BOOL adnOn = [defaults cp_isAdnOn];
    BOOL facebookOn = [defaults cp_isFacebookOn];
    BOOL twitterOn = [defaults cp_isTwitterOn];

    UIColor *offColor = [UIColor lightGrayColor];
    UIColor *adnColor = adnOn ? [UIColor blackColor] : offColor;
    UIColor *facebookColor = facebookOn ? [UIColor wps_facebookBlue] : offColor;
    UIColor *twitterColor = twitterOn ? [UIColor wps_twitterBlue] : offColor;

    UIBarButtonItem *cameraButton = [self buttonWithImageName:@"camera" color:[UIColor wps_iOSDefaultBlue] action:@selector(_cameraButtonTapped:)];
    [self setCameraButton:cameraButton];

    UIBarButtonItem *adnButton = [self buttonWithImageName:@"adn" color:adnColor action:@selector(_adnButtonTapped:)];
    [adnButton setTag:adnOn];
    [self setAdnOn:adnOn];
    [self setAdnButton:adnButton];

    UIBarButtonItem *facebookButton = [self buttonWithImageName:@"facebook" color:facebookColor action:@selector(_facebookButtonTapped:)];
    [facebookButton setTag:facebookOn];
    [self setFacebookOn:facebookOn];
    [self setFacebookButton:facebookButton];

    UIBarButtonItem *twitterButton = [self buttonWithImageName:@"twitter" color:twitterColor action:@selector(_twitterButtonTapped:)];
    [twitterButton setTag:twitterOn];
    [self setTwitterOn:twitterOn];
    [self setTwitterButton:twitterButton];

    UIBarButtonItem *draftsButton = [self buttonWithImageName:@"drafts" color:[UIColor wps_iOSDefaultBlue] action:@selector(_draftsButtonTapped:)];
    [self setDraftsButton:draftsButton];

    UIBarButtonItem *flexibleButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    [toolbar setItems:@[adnButton, facebookButton, twitterButton, flexibleButton, draftsButton, cameraButton] animated:NO];

    _toolbar = toolbar;
  }
  return  _toolbar;
}

The only things not created or managed but this view are the navigation bar and collection view of photos. The navigation bar is provided by the UINavigationController used by the view controller that created this composer view. Also, the view controller uses controller containment to include the collection view of photos, which is actually managed by another view controller and custom view class.

Oh, I almost forgot to mention…I create the composite view in the loadView method implemented in the view controller. For example:

- (void)loadView
{
  [super loadView];
  UIView *contentView = [self view];

  CPComposeView *composeView = [CPComposeView addToSuperview:contentView];
  [self setComposeView:composeView];
}

After I create the view I might add more layout constraints to position and size the subview as needed within the main content view. I do that in the view controller’s -loadView when needed.

So that’s it. That’s why I have stopped using NIB and storyboard files in my iOS projects. This approach might not work for others, but it certainly works for me.

Posted in ios, programming.

WTF Ruby Gems

I’m working with a client to update one of their iOS apps. The Xcode project uses CocoaPods for dependency management. I’m not a fan of CocoaPods, but I grin and bear it when working with existing client projects.

Yesterday I needed to update a class, one .h file and one .m, from an open source project, so I ran pod update on the project. This told me I needed to update my install of CocoaPods, so I did. This in turn updated a ton of other Ruby Gems on my system. It was annoying having to go through the update process when all I needed was two files that I could have copied from github in less time, but no harm done, right? WRONG!!!

Today I go to publish a new blog post. I use Jekyll to convert my site to static HTML. Guess what? Yesterday’s CocoaPods update has now broken my Jekyll install, and I cannot publish the blog post I just finished writing. WTF!?

ALL I WANTED TO DO WAS GET THE LATEST FREAKIN’ VERSION OF TWO FILES, A .h AND .m, AND NOW I HAVE TO WASTE TIME TRYING TO FIX WHAT WAS NOT BROKEN UNTIL YESTERDAY JUST SO I CAN PUBLISH A BLOG POST.

This is why I don’t like using things like CocoaPods, plug-ins, and other simular widgets.

Posted in rant, tech.

Family Influence

Over on ADN, Manton Reece posted a link to The Distance, a new online magazine about “long lasting businesses and the people behind them”. Manton’s post included this quote:

Starting a business is the easy part. Staying in business for a while is harder.

This got me thinking about my own business, which has been around for more than ten years. More importantly it got me thinking about the family influences in my life that unbeknownst to me, until recently, led to me working for myself. I have often shared the story about how the idea to start White Peak Software came about, at a swim-up bar during my honeymoon, but I’ve never really talked about the family influences that helped lead me down the path of becoming a business owner. The reason for this is simple. It never occurred to me until recently.

My grandfather (my dad’s dad) was a successful business owner running his own lumber company for many years. My dad owned a general maintenance company. My aunt’s husband (my dad’s sister’s husband) opened James Middleton Jewelers back in 1955, and the store is still in business today run by my cousin Vickie.

Entrepreneurship was, and still is, common on my mom’s side of the family as well. My mom and her sisters opened and ran their own hair salon for a short period of time. My mom’s oldest sister’s husband ran his own accounting firm that is still in business today and still run by family members. My mom’s youngest sister, Beth and her husband Jay, owned Gary’s Ice Cream and Deli, the place of my very first “real” job, and later Mikey’s. My sister Renay’s husband has his own pressure washing company, and my grandmother, Dot, made and sold crafts.

I grew up surrounded by family members pursuing their own dreams and making their own destiny. Some were, and still are, successful in their business endeavors while others were not so lucky. But even the unlucky ones tried. They took the chance at running their own business. And all of this has had a huge impact on me even though I did not realized it until recently.

Thank you to all my family members who have influenced me directly and indirectly with their entrepreneur spirit. White Peak Software would not be here, and I would be living the life I live today if not for your influence.

Posted in family, work.

Still No Regrets

I pierced my ear when I was 16 or 17 years old. I did it myself on the way to school. I stopped by my mom’s place to pick up my little sister. While my mom was busy getting my sister ready, I grabbed two ice cubes from the frig, and I used them to numb my ear lobe. Then I took a piercing ear ring, the kind with a pointy end, and shoved it through my ear lobe. It made a pop sound as it pushed through the backside of my ear lobe. I felt nothing. It was easy peasy.

I don’t remember if it was later than day or the next when my dad found out. Needless to say, he was not happy. In fact he was very angry with me. As he shared his disappointment with me, he told me having my ear pierced was just a fad and that I would soon regret it. Well, 30 plus years later I’m still wearing earrings and I still have no regrets.

Posted in family, random.

Not Organizing Events

When I lived in NYC I organized and ran events for computer geeks like me. It was a lot of fun, and a great way to meet new people. Then I moved to Salem, Mass, a suburb of Boston, and again I organized and ran events in Salem and Boston. And again it was a lot of fun and a great way to meet new people.

I now live in Stowe, Vermont, and as of right now I have zero desire to organize and run events for like-minded computer nerds. After being so involved organizing meetups and such for over 10 years, it’s nice to take a break. That said, I sometimes wish someone had a tech-focused garthering in Stowe. I wonder how long before I end up organizing something.

Posted in general, social.

Cross Posting

I decided to go back to cross posting between ADN, Twitter, and Facebook. I’m not as active as I once was on any one of these social networks, so I figured cross posting won’t cause too much noise in the streams of followers.

Posted in general, social.