Showing posts with label design. Show all posts
Showing posts with label design. Show all posts

Thursday, August 27, 2015

Constraints Give Focus

The more constraints one imposes, the more one frees one's self. And the arbitrariness of the constraint serves only to obtain precision of execution.
Igor Stravinsky

I've spoken of this before, as a creative person, the hardest job can be to know when to stop/when to ship.

Building an app, creating a work of art, planning the weekend, deciding what to make for dinner; all of these things can get out of hand if you don't limit yourself. You need a plan and you need to set a bar for:
  • What is the bare minimum needed to achieve your end result? The Minimum Viable Product.
  • What is the desired feeling you want your consumers to experience? Utility? Joy? Full?
  • What are you desired goals with this product? Ship an app? Showcase your skills?
All of these things need to be considered but many times we want to deliver everything with a dime store budget. This is seldom possible.

My wife, from her experience in product management, was the first to introduce me to the concept that you can have a product Cheap, Fast, or Good. Pick Two! This is called the Project Management Triangle.

As an indie with a full time job and a family, I run into this problem all the time. I feel I am asking too much to pick two when having one with limited time is hard enough.

Here's how many of my projects go:
  • Brilliant Idea!
  • Crave out a block of time to put in a mad dash to get a prototype working.
  • Start to design a MVP.
  • Start to implement the MVP.
  • Run into corners I never foresaw.
  • Start slogging through the corners, rounding everything out.
  • Invest too much time into after hours indie work.
  • Crash, burn, burn.
  • Put project on back burner.
For every project I have completed, there is a mountain of projects I have abandoned.

Is this bad?

Only if you let it get to you. I look at that mountain as wisdom.

Here is what I take away from my "failures", if you want to call them that:
  • Experience
    • To know when I am adding more features than I can complete.
    • To know which ideas will take too much time.
  • Knowledge
    • With new techniques, skills, APIs, etc.
    • Few jobs offer training. As a contractor, I must think of my own training. These side projects help me to expand my skills and train.
  • Resolves
    • To find better ideas to solve, build, and deliver.
  • Efficiency
    • The more projects I work on the better I become at solving problems.
    • The more I projects I deliver the better I get at delivering solutions.
Constraints add focus.

Figure out how to use those constraints to improve your projects.

Learn from your mountain of failures and learn where you can add constraints to increase your pool of successes.

Friday, August 21, 2015

UX Testing, Where Bad Designs Go To Die


Bad Design Be Gone


I made some UX decisions during design that I thought would work out. They didn't.

Back to the drawing board.

While testing the app over the past few days (walking the dog, running, driving) and I realized some things did not flow well. So I took Tuesday night to reevaluate the design and work out how to solve some of the flow problems.

Two things I did:
  • I reviewed multiple fitness apps to understand how they handle some of the UX I was having problems with.
  • I showed the app to more people to gather feedback about the UX.
Both of those things brought to light multiple areas that could be improved.

So, I redesigned a couple views, added some missing views, and jotted down some great feature ideas I received. The features I captured for a post V1 release.

Missing Interstitial View


In particular, a problem area I had was the Paddling/Workout workflow. Here is what I had:

  1. User taps PADDLE on the main view.
  2. Transition to Prepare To Launch view.
  3. User chooses if they want to track location and use a paired Microsoft Band.
  4. User taps Play button to start paddling workout.
  5. Transition to PADDLE view, the main workout view.
  6. User decides to stop workout, they tap Pause button.
  7. Transition to Paddle End view.
It was steps 6-7 that had a problem. Here are what the views looked like.
Missing a Pause/Stop Confirmation View

The main problem with the above design is that I am trying to do too much in the PADDLE_End view. I am trying to cover several tasks:
  • CONTINUE: You paused by mistake, did you want to go back?
  • DONE: End the workout.
  • Upload to Strava: Did you want to upload the workout?
  • Send to the Health App: Did you want to store your workout in the iOS Health App?
  • SEND SESSION TO PADDLE MATE: (Too long) Are you a tester and want to submit samples?
Here is what I have done so far to fix a major flaw.

I added an interstitial view between 6&7 which is a Pause view. This will let me move the CONTINUE from PADDLE_End back to the main view.

I think this offloads the decision to end the workout to this new PADDLE_Pause interstitial view. Now the user is not thinking about should they continue or end on the next screen, lower the cognitive work on the PADDLE_End view.


New Paddle Pause with interstitial Pause/Stop Confirmation view added before Paddle End view

At first, I left the CONTINUE in but realized I needed to remove it since the decision to CONTINUE/UNPAUSE is on the interstitial view. Now, I think I have a cleaner UX for ending the workout.

Change Design, Test, Repeat


Then, on Wednesday night I completed the integration of Microsoft Band data, CoreLocation data, and workout metrics into my Realm backend. This is cool since I had implemented the code to load the DOCK view, the main view you land on when launching the app, previously when I designed the data model.

Now I have a lot of workouts showing up on the main view as I test the PADDLE workflow.

Then I released I needed a way to delete a workout on the PADDLE_End view when a user (me) wants to delete a session they do not want to track. So, for this I added a DELETE button to the PADDLE_End view.

I do not think this adds to much to the cognitive load since a review of multiple fitness apps those that many use this concept on their end workout view. I also think it fits to have the DELETE | DONE on the top so the user can choose if they want to keep/delete this workout.

They are along the same line visually and hence they are giving you two options along the same line of vision.

Now with Pause interstitial and Delete option
Now it's time to retest and see how this new design's UX feels after repeated use.

Sunday, August 16, 2015

Refactoring Like a Boss

Bringing Order to Chaos

Dark Mode for evening Kayaks
I like to create a lot of sample projects where I can workout features of an app. Though there extra some setup involved, it let's me refine a feature to the least code needed to support the functionality I am want.

For Paddle Mate, I have a project for Charts, one for Barometer tracking, one for Realm, and one for the Stroke Detection algorithm that is the heart of the project.

Today has been a day of bringing those together and things are shaping up as I move parts into place.

Gone is my MVC (Massive View Controller from last weekend) as I have now broken the app down into a clean hierarchy of:

  • Frameworks
  • Source
    • DataModel
    • Managers (For lack of better naming)
    • Utilities
    • Views
    • ViewControllers
  • Tests
  • Products

Design Payoffs

The nights spent reviewing design tips, fonts, colors, and then creating multiple mockups has paid off. I am much happier with the look of the app as I integrate the various features. Having designs to guide me is speeding up development. 

I am occasionally getting slowed down when I find I missed something but refinement is part of the process and learning experience. Now I know I need some additional views to handle the clean starting and stopping of a workout when I need to manage location services, mapping, Microsoft Band connections, pausing, continue, and whatever else I missed.

A big payoff for my workflow has been the purchase of PaintCode. I first heard about PaintCode from friends at Shopify who swore by it. I broke down and bought it few months ago and started to learn how to use it.

Since then I have become more adapt with it and have started to build up a library of icons for my apps. This has helped me speed up development time as I can reuse assets while I work.

Another big help with design assets has been:
  • The Noun Project where I look for inspiration and often use icons from there, with attribution.
  • Entypo+ with it's wealth of SVG pictograms.
Over time I plan to build more of my own icons so that I can build unique assets which I own.

Well, off to bed. Another long day with 15 hours of coding. Today was not a fill the rings kinda day.

Sunday (today) I plan to tackle:
  • Upload workout to a fitness tracking site.
  • Export to Health App Integration.

Friday, August 14, 2015

Designing Paddle Mate

Programming is my strong suit and designing does not come naturally. Though I am inclined to art and have both drawn and painted, I still find app design is something I struggle to get right.

With that in mind, here are some things I am doing as I work to make Paddle Mate useful (and non-ugly) for users in bright sunlight, while kayaking in the evening, and when reviewing past kayaks or showing the app to a friend.

I am doing some reading first off. Here are some things I have read recently on design:

Here was the UI:


Here are my new designs I plan to test for bright sunlight:

Monday, May 18, 2015

App V1: Knowing When To Stop

Fishing while taking a break
from building Heart Relay.
I have to stop at some point. The hardest part of shipping software you design and write yourself is shipping it. Like any creative activity, you have to know when to stop.

I had a "working" prototype 3 weeks ago in about 3 hours. It had next to no UI, was not intuitive, had no settings, persisted nothing, did not handle Bluetooth disconnects, and many other missing bits and pieces.

The have been plugging away 2-3 hours a night when I can so I can ship a V1. Here are some of the items holding up shipping:

  • Completing the list of V1 features for Heart Relay. There are not that many, mostly make it intuitive, performant, and not look like it was designed by a code monkey.
  • In relation to one, I am not happy with some of the icons. I should just ship what I have but I want more consistency in the icons and to replace words with icons (harder to understand maybe but I would like the user to not be hit with a screen of text).
  • Animations
    • I wanted to add some custom animations for both the connecting event (instead of using the default activity indicator spinner) and the Heart Rate measurement (a beating heart).
    • Animations and custom icons take time.
  • Testing:
    • While testing, I discovered that in noisy Bluetooth environments (work) the Bluetooth connection sometimes drops. Due to this I needed to work on reconnect and how to help the user to recover from this.
    • Testing takes time.
While doing all this new features have come to mind. Here are things I have cut from V1 or are feature creeps that I will consider for V2.
  • Free, Paid, IAP, or Ads?
    • Free: For V1 launch.
    • Paid: For Pro version later with some whiz-bang additions.
    • IAP: Research shows IAP is not worth the work.
    • Ads: After launch.
  • Heart Rate Zone Tracking
    • Going to keep for Pro version later.
  • Heart Rate Zone Alerts
    • Going to keep for Pro version.
    • Will buzz band when you drop out of or rise above your zone target.
  • Heart Rate Export
    • Going to keep for Pro version.
    • Let you export to CSV so later import to whatever app you want.
So, I am still plugging away.

I am debating if I will redesign the app icon. Probably not for V1.

Ok, back to drilling down some reconnection features.

Thursday, February 12, 2015

I Ain't Afraid of No NSError: Adventures in API Design

These are the notes from my Lightning Talk at Xcoders Seattle on February 12, 2015. A Lightning Talk is a 5-10 minute presentation that gets to the heart of an idea, problem, solution, quibble, etc. They are popular at conferences (also called Blitz Talks) where multiple will be held over 60-90 minutes.

The local Xcoders has begun running this as a way to let those without a 60+ minute presentation bring new ideas into the local developer community. I think it's great and if your developer meetup is not using them, then give them a try.

--
Adventures in API Design

My Point

NSError's userInfo dictionary is the right way to return errors from your API. Let me elaborate.

Designing an API

Everything is a trade off. When designing your API there are multiple things to consider:
  • How much of the internal API should I expose?
  • Should I provide logging?
  • How long is too long for a method name?
  • How should I return errors?
Let's focus on errors. Those are easy, right?


Throw an Exception?

We have several options available and if you are coming to Cocoa (Objective-C) programming from other languages then you might think using NSException to throw exceptions is an option, right? 

Wrong!

Apple has made it clear through it's use of NSError that exception throwing is not the answer. Exceptions are things you should fix/not handle in code and continue on.

With Swift they have now codified this desire. Swift has done away with the ability to catch exceptions.

Ways of Using NSError

Ok, so exceptions are definitely not the way. So how can we pass errors back to our users? And how can we do that if we want to provide more info than NSError provides?

A quick review of some popular 3rd Party APIs shows that we have several options for error handling:
  1. Subclass NSError.
  2. Use Categories to extend NSError.
  3. Use NSError and it's userInfo NSDictionary.

Option 1: Subclassing

This Sounds like a great idea if you come from other OO languages. But with Objective-C we run into a corners that can catch us up.  Here are some problems:
  • Friction. 
    • Devs need to handle NSError and YourNSError.
  • Crashes
    • Crashes make 3rd party dev mad!
    • Why crashes? If you have an API call that says it returns YourNSError but you return NSError thinking no problem (or by mistake) then what happens the the 3rd Party Dev calls one of your new methods that you added to YourNSError? Their app will crash since the selector (method) is not available on the NSError you actually returned.
  • Slowed code
    • After a crash due the scenario listed above the 3rd Party Dev must use reflection using NSObject's respondsToSelector: to verify that your returned error objects contain the listed methods. Reflection is slow, why add the overhead to a 3rd Party Devs App?

Option 2: Add a Category

Ok, so subclassing is perhaps not the answer but what about just adding a Category to NSError and adding your new properties and methods to this category?

First, if you do not know that you can add properties to an existing class using a category, then look up Associated Objects. I don't recommend you try this. You warp the very foundations of the classes you are using and cause more harm than good in most cases.

But, given that some popular APIs use just this method. What are the pros to extending NSError like this?
  • Less Up-Front Friction for Devs
    • No messy switching between a subclass and NSError.
What are the cons?
  • Crashes
    • Ok, so you decided to add some new properties using the magic of the Objective-C runtime. You learned about Associated Objects ability to extend a class with a property and found a "back door" around subclassing.
    • The thing about adding a property through object association is that if the API does not set the property then it has not been associated with the object. Again, we have a missing selector and we have caused a crash.
    • Dev no like crash! 
  • Slower code
    • We are back to having to use reflection (respondsToSelector:) to verify your error objects support the properties you listed. Boo!!
Option 3: It's So Not OO

A common complaint I hear about Apple APIs is the reliance on NSDictionary. NSError is one of those culprits with it's userInfo. It does not feel OO so many poo-poo it as an aberration.

So what? It's how it's done and it has good reason. So why use a pure NSError and pass back additional details in userInfo?
  • Less Friction
    • No learning a new NSError class/category.
  • No Reflection = Faster Code
    • No reflection needed to validate your error objects support your listed properties or methods.
  • Reduced Crash Vector
    • If the user makes a call to [userInfo objectForKey:] on a non-existing key, then instead of a crash they will receive nil which in Objective-C is a no-op (meaning no crash). This is unlike a call to a non-existing property/method (when misusing a Category or subclass) which will crash with an NSInvalidArgumentException with "unrecognized selector sent to instance".
  • For the high runner case you don't need more than NSErrors (domain, code, and localizedDescription).
  • For the low runner cases you can extend NSError with additional details in the userInfo dictionary by adding new keys, publishing them in your API docs, and then let the app developer decide if they want that added detail.
  • If the dev doesn't really care, they can just print the entire userInfo and get all the details of your error.

Avoid friction.
Don't slow down code.
Avoid skip crashes. 

The right way to expose your API errors is with NSError.

References


For further reading, start here:

Thursday, December 24, 2009

Designing HockeyTweet

It's been a long road, longer than I thought it would take but then aren't many things? How did HockeyTweet begin?

The main design goal of HockeyTweet was to create a fast Tweet creation interface for Hockey, specifically the NHL. This was born from the desire to allow people to quickly tweet their thoughts about the game without missing the action.

This desire stems from my youth, watching Hockey with my Dad. When there was a play we did not agree with we shouted out our protests. When there was an awesome save, there were shouts of joy.

So how do you bring this experience to the iPhone?

First I thought about how to speed up tweet entry. To do that let's save the user time by giving them the list of teams, players, penalties, and games. What did I need to consider for each?

Teams
  • Full name or abbreviation? Abbreviation wins out by far, we are only working with 140 characters in a tweet afterall.
  • Do we need to update this? Not if we limit our scope to the NHL (teams are pretty static, name changes are not common.)
Players
  • Full name or abbreviated? After some testing and consideration for length, I went with full last name and first initial.
  • Numbers? Extraneous info, if you are a not a big fan you know the star's names but not necessarily their numbers. No numbers.
  • Do we need to update this? Yes, team rosters change often due to injuries, trades, retirement, new players, etc. This will need to updated regularly - need to consider this in how to perform updates.
Penalties
  • Full name of abbreviated? For clarity and since I expect some of the long ones will not be used often I went with full name for each. Where the penalty had a common name that was shorter I used that.
  • Do these change often? No, no need to build in updating for penalties.
Game Schedule
  • How to handle showing a game? Use abbreviated team name with vs.
  • Games change often so this definitely needs to be updated on a regular basis.
  • Do we need time of the game? No, this app is for tweeting about the game while it is happening. Also, want to consider the day before and after since people may want to tweet about an upcoming game or yesterdays game. Time can be dropped.
  • Ok, we are showing yesterday, today and tomorrow. How are we showing that? I went with the 3 letter month and the date only.
    • Why no year? Don't need it, we are only showing current games, not previous years.
    • Numeric month would be shorter, why not use numerical month? You save 1 character for less clarity. Then you have to decide on a format MM/DD or DD/MM. I feel the 3 letter month provides more clarity, another decision done.

Handling Updates

During my design I identified that players and schedules had to be updated regularly. How to handle that? This was something new for me since I had to figure out how to do regular updates to my app without impacting the user experience in a negative way.

After some research I settled on web services as the solution. I first designed a web service with Perl SOAP::XML. What did I learn besides new tech (I had never programmed a web service before so this was all new)?
  • I know Perl well so writing the prototype web service took about 1.5 hours. Good references through The Perl Bookshelf and Google certainly helped.
  • Integrating the web service using asynchronous URL requests with asynchronous XML parsing was a mess. There was a lot of code to handle what was a simply a problem of go get some data and populate this array for display in the UIPicker. Trust me, the first pass of this code was ugly and hairy.
  • Where would I host this? My home system was out of bounds, I did not want to re-enter the world of IT and server maintenance to host my own web service. A hosted solution would work but that would cost money for an app that would likely have a limited customer base (iPhone Fans who are Hockey fans, that watch the NHL, and use Twitter). The total earnings for the app would probably not cover the cost of one year of hosting, so I needed something free.
  • How will this web service scale? Not sure, probably not well without lots or testing and more research to make sure I wrote a good web service.
  • How would I cache the web service data? In memory? On disk? If on disk how would I do that on the hosting service? What about Perl version? Which would the hosting service support? Could I install my own custom Perl modules? Many questions here to answer.
At the same time, hosting became an issue I had to solve. There were a lot of questions to answer for what appears on the surface to be a simple app. I had lots of tech to piece together so what next? Research!

Google App Engine

I started researching hosting solutions that offered the ability to run my own Perl services. All the hosting services seemed more expensive than I wanted to pay. Through the searching I kept coming back to Google App Engine. The pricing model is generous and I use lots of Google services which I find sometimes limited but get the job done 90% of the time.

How does Google App Engine work when hosting from it? My summary would be, unless you write something that is very successful, it's free. Once it starts to cost money (nothing for me yet) you should be already monetizing your product. If you are not making enough money to pay the generous hosting with Google App Engine then why are you offering the product?

Google App Engine had a kink though, I had to convert the already written (though prototype) service in Python or Java. Python was just poking it's head up at my day job so I figured why not go that route. At this time I enlisted a friend who was interested in the project. He signed on to rewrite the prototype Perl SOAP::XML web service using Google's App Engine and in Python. This relieved me of some work so I could concentrate on the app.

JSON 2.2 Framework for iPhone

Around the same time, I came across JSON 2.2 Framework for iPhone. Let me just say, that this is an awesome framework. It let me remove a lot of the hairy XML parsing code from my app and replace it with a clean implementation that effectively lets me pass Python Dictionaries of arbitrary complexity from the Python web service to HockeyTweet where the JSON string is cast to a Cocoa NSDictionary. The ease of using this library can not be said enough.

If you need to write a web service to work with the iPhone, look at Google App Engine, use Python, and the JSON 2.2 Framework for iPhone.

Ok, so time passed, my partner came and went; he had other pressing projects to work on. I finally finished version 1.0 of the app near the end of November. I submitted the app about November 28, 2009. Then it was time to wait.

iPhone Tech Talk Toronto


A week later I was off to Toronto for the iPhone Tech Talk World Tour. This is a one day conference where Apple Evangelists run 1 hour sessions (courses/tech previews/tech tricks) and provide services such as UX (User Experience) reviews. It is effectively a pep rally + course + feedback + networking session with Apple's face to the development community.

It was AWESOME!

The UX Review and The Redesign

At the Tech Talk, as soon as I heard that there would be UX reviews I signed up for one. The review went well. I received valuable feedback from the Apple team which resulted in 10 new features they felt would polish the app and give it a Wow factor.

So began The Redesign. The list of features were:
  1. Improve the Info button (it was not very responsive)
  2. Change the text buttons to icons
  3. Add Arenas to the pre-loaded UIPickers
  4. Add a UIPicker for pre-loaded Hockey Phrases to give the app a more positive spin (the app had a negative spin with just the Penalties that could be combined with Players or Games)
  5. Make the Hockey Phrases user customizable
  6. Change the colour of the Add button from white to the same colour as the other buttons on the main view (the one different button made the view look unfinished)
  7. Indicate using colour (red) to the user when they typed a Tweet over the 140 character limit for a Tweet
  8. Make the fact that a Tweet was successfully sent clearer
  9. The buttons on the flipside view (Settings) could be retouched to make the app look finished
  10. (My own) Add pre-loaded Team rosters and Schedules so that if the user bought the app and ran it without an internet connection, there was data in the app from day one. The original design was to get the first load of data from the web service.
I worked almost every night for a week and I was ready to launch again.


Not so Fast

But first, I got feedback that the icons could use more polish. Eck! I hired a graphic designer online and he went to work on new icons. I received 5 of the 7 icons and had good ideas on the other two which I implemented in the tight deadline I gave him.



Phew! Now it was time to ship.

I uploaded again and on Monday HockeyTweet hit the App Store.

What would I do differently?
  1. Hire graphic help earlier in the process. Work with them to turn my paper/sketch prototypes into the UI which I code on the back end.
  2. Work longer on pinning down the design and the requirements before I start. I let feature creep push the app in a direction I had to reverse early in the process.
  3. Solicit more early UI design feedback.