Kevin McMahon2023-11-04T20:36:53-05:00https://www.kevfoo.comKevin McMahonkevin@kevfoo.comMaking Sure OpenSea Can Read Your Contract-level Metadata2022-05-25T00:00:00-05:00https://www.kevfoo.com/2022/05/opensea-contracturi<p><em>Leaving this here for my future self and for others as this question pops up on the OpenSea discord quite a bit.</em></p>
<p><em>tl;dr</em> – Don’t return an IPFS URI (<code class="language-plaintext highlighter-rouge">ipfs://{cid})</code> or IPFS HTTP gateway URL (<code class="language-plaintext highlighter-rouge">https://ipfs.io/ipfs/{cid}</code> or <code class="language-plaintext highlighter-rouge">https://gateway.pinata.cloud/ipfs/{cid}</code>) via the <code class="language-plaintext highlighter-rouge">contractURI()</code> function. Use a traditional HTTP web API or base64 encode the metadata JSON and return it from the function if you want OpenSea to be able to read the contract-level metadata and display it on your collection’s page.</p>
<p>OpenSea allows smart contract developers to include <a href="https://docs.opensea.io/docs/contract-level-metadata">storefront-level metadata</a> by implementing a function named <code class="language-plaintext highlighter-rouge">contractURI()</code> and returning a URI pointing to the metadata. OpenSea expects metadata formatted as JSON that consists of a few specific attributes they recognize. This information is pre-populated into the NFT’s collections page associated with the smart contract on OpenSea.</p>
<p>For individual non-fungible tokens (NFT) like ERC721’s and ERC1155’s, calling the <code class="language-plaintext highlighter-rouge">tokenURI()</code> function with a tokenId value as a parameter returns a URI that points at the token’s metadata. The <code class="language-plaintext highlighter-rouge">tokenURI()</code> function typically returns URI that point to traditional endpoints like web servers or cloud storage using conventional URL protocols (<code class="language-plaintext highlighter-rouge">https://</code>) or, more commonly in web3, to files stored on IPFS via the IPFS URL protocol (<code class="language-plaintext highlighter-rouge">ipfs://</code>). URIs returned from this function can point directly to IPFS or via IPFS HTTP gateways without problems.</p>
<p>It is unclear from the docs, but the responses from the token URI function can vary more than the contract URI function. I have experimented with valid URI variations, all pointing to the same JSON, and had no success with having the contract URI function return either IPFS or IPFS public gateway URIs. Providing the same types of URI from the token URI functions has worked as expected. What I have found to work are the following:</p>
<ol>
<li>
<p>Return the metadata JSON as a base64 encoded string. Do not forget to prepend the string with <code class="language-plaintext highlighter-rouge">data:application/json;base64,{yourBase64EncodedStringHere}</code>.</p>
</li>
<li>
<p>Return a URL pointing to a conventional REST API endpoint that returns the metadata JSON.</p>
</li>
</ol>
<p>I cannot explain why the API requests to a REST API work as expected or why the requests to IPFS or an IPFS public gateway do not. I plan to file this as a ticket with OpenSea, and hopefully, if they’re unable to address the issue outright, they can at least update the docs to be more explicit about what can and cannot be returned from the contract URI function.</p>
Authenticating to Heroku via CLI with MFA Turned On2021-12-09T00:00:00-06:00https://www.kevfoo.com/2021/12/Heroku-CLI-with-MFA<p><em>Leaving this here for my future self.</em></p>
<p>Heroku has turned off interactive login and pushes folks to authenticate via a browser when authenticating to use the CLI app. This works fine if you don’t have MFA turned on (you should though!) and the flow completes without issue. If you do have MFA turned on, you’ll get a blank screen and a cryptic message (“IP address mismatch”) when attempting to complete the login.</p>
<p>To authenticate from the CLI you can follow <a href="https://help.heroku.com/AVOHZTFH/heroku-cli-login-interactive-option-no-longer-supported-with-salesforce-mfa">this note</a> from the Heroku Developer Docs and put your newly generated API key in a .netrc file or set and environment variable (<code class="language-plaintext highlighter-rouge">HEROKU_API_KEY</code>) or just enter the API key to the password prompt when interacting with the CLI.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ ~ heroku auth:login -i
heroku: Enter your login credentials
Email : [YOUR_EMAIL_HERE]
Password : [THE_API_KEY_YOU_GENERATED]
Logged in as [YOUR_EMAIL_HERE]
</code></pre></div></div>
Carvey Replacement Hinges2020-10-01T00:00:00-05:00https://www.kevfoo.com/2020/10/Carvey-Replacement-Hinges<p>The internet is almost as impressive as my ability to break things. This is a story about how I broke a CNC machine and was able to not only find the exact replacement parts but order them for next day delivery within about 20 minutes.</p>
<h2 id="tldr">tl;dr</h2>
<p>The Inventables Carvey model I am using uses <em>Southco E6-10-301-20 Series Adjustable Torque Position Control Hinge with Holes</em>.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/10/southco-e6-10-301-20-hinge.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/10/southco-e6-10-301-20-hinge.jpg" /><img src="https://cache.kevfoo.com/2020/10/southco-e6-10-301-20-hinge.jpg" title="" alt="" /></picture></p>
<p><a href="https://files.southco.com/static/Literature/e6-at.en.pdf">Southco E6-10-301-20 Series Adjustable Torque Position Control Hinge with Holes (PDF)</a></p>
<p><a href="https://www.amazon.com/gp/product/B00GM5G89E">Amazon Link</a></p>
<h2 id="what-happened">What Happened</h2>
<p>Recently a friend let me borrow a <a href="http://web.archive.org/web/20201001153120/https://www.inventables.com/technologies/carvey">Carvey</a> that was taking up space at his place and, not 10 mins after getting it powered up, I broke it. To make a long story short, I racked the lid when attempting to pick it up, which caused both hinges to fail catastrophically. The hydraulic lift, which props the lid open and provides a soft close, put a tremendous amount of pressure on the device’s plastic hinges. Once the first hinge failed, the second failed immediately. It felt like my heart was going to be next.</p>
<p>After calling the very understanding owner and apologizing for destroying the hinges, we discussed the steps necessary for repair. Inventables support is great. I’m sure I could’ve got replacement hinges from them. But that would take phone calls, support tickets, and waiting for them to ship one-off parts. The parts are just hinges. If we could get replacements, it is a simple process to affix them to the case. Maybe we can find replacements ourselves.</p>
<p>So the search began. After coming up empty via the search engine route, <a href="https://www.mcmaster.com">McMaster-Carr</a> was the next stop. It turns out there are tons of different hinges. McMaster-Carr apparently has <em>all</em> of them. We were however able to find <a href="https://www.mcmaster.com/adjustable-friction-hinges/">something</a> that looked promising. With the specs of the McMaster-Carr hinge now in hand, I got my calipers out. The sizes matched (give or take a ten-thousandth of an inch). We felt good about having a viable replacement option, but the question lingered if we could do better.</p>
<p>After a closer inspection of the broken hinges, I noticed that they had Southco branding. A quick search of their site led us to the <a href="http://web.archive.org/web/20201001152036/https://files.southco.com/static/Literature/e6-at.en.pdf">spec sheet</a> of the Southco E6-10-301-20. That was it, and of course, <a href="http://web.archive.org/web/20201001152835/https://www.amazon.com/gp/product/B00GM5G89E?pldnSite=1">Amazon</a> has it stocked and ready to ship.</p>
<p>The fact that I was able to identify these hinges, source them from a supplier, order a small number of them, and have them shipped to my door in two days is amazing. That I was able to do it within 20 minutes after I broke them blows my mind—what a world.</p>
Favorites from August 20202020-09-30T00:00:00-05:00https://www.kevfoo.com/2020/09/Favorites-from-September-2020<p>A collection of some of my favorite links, images, tweets and quotes from September 2020. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/09/it-has-no-use-but-it-has-value.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2020/09/it-has-no-use-but-it-has-value.png" /><img src="https://cache.kevfoo.com/2020/09/it-has-no-use-but-it-has-value.png" title="" alt="" /></picture></p>
<p><em>via <a href="https://www.instagram.com/p/CFKgqBklTlS/?utm_source=ig_web_copy_link">wendy macnaughton on Instagram: “Art.”</a></em></p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://the-toast.net/2014/07/11/how-to-buy-a-car/">How to Buy a Car Without Interacting With a Human - The Toast</a></li>
<li><a href="https://www.youtube.com/watch?v=3GK_3KgZios">How To Steal Like Wes Anderson - The Grand Budapest Hotel - YouTube</a></li>
<li><a href="https://www.type-together.com/typographic-matchmaking">Typographic matchmaking - TypeTogether</a></li>
</ul>
<h2 id="quotes">Quotes</h2>
<ul>
<li><a href="https://twitter.com/thesamball/status/1301485871447838721">The four most expensive words in the english language are, “This time it’s different.” – Sir John Templeton</a></li>
<li>“Nothing in life is to be feared, it is only to be understood. Now is the time to understand more, so that we may fear less.” – Shane Parrish & Rhiannon Beaubien, The Great Mental Models Volume 2</li>
</ul>
Favorites from August 20202020-08-31T00:00:00-05:00https://www.kevfoo.com/2020/08/Favorites-from-August-2020<p>A collection of some of my favorite links, images, tweets and quotes from August 2020. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/08/lego-dimensions-car-show.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/08/lego-dimensions-car-show.jpg" /><img src="https://cache.kevfoo.com/2020/08/lego-dimensions-car-show.jpg" title="Lego Dimensions Car Show" alt="Nostalgia is a helluva drug." /></picture></p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/08/this-machine-kills-fascists.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/08/this-machine-kills-fascists.jpg" /><img src="https://cache.kevfoo.com/2020/08/this-machine-kills-fascists.jpg" title="This machine kills fascists" alt="This blue box is the last thing in the world that I thought our democracy would hinge on." /></picture></p>
<h2 id="reading">Reading</h2>
<ul>
<li><a href="http://web.archive.org/web/20200804184514/https://www.theatlantic.com/magazine/archive/2020/09/coronavirus-american-failure/614191/">How the Pandemic Defeated America</a></li>
<li><a href="https://www.nytimes.com/2020/08/31/opinion/cdc-testing-coronavirus.html">It Has Come to This: Ignore the C.D.C.</a></li>
</ul>
<h2 id="admiring">Admiring</h2>
<ul>
<li><a href="https://www.poojapittie.com/2020">Art by Pooja Pittie</a></li>
</ul>
<h2 id="watching">Watching</h2>
<ul>
<li><a href="https://vimeo.com/446927270">2020: An Isolation Odyssey</a> via <a href="https://kottke.org/20/08/2020-an-isolation-odyssey">@kottke</a></li>
</ul>
<h2 id="links">Links</h2>
<ul>
<li>
<p><a href="https://www.designedbycave.co.uk/2020/LEGO-Interface-UX/">The UX of LEGO Interface Panels – George Cave</a></p>
</li>
<li>
<p><a href="https://coolors.co/">Coolors - The super fast color schemes generator!</a></p>
</li>
<li>
<p><a href="https://www.thisiscolossal.com/2020/08/victor-solomon-kintsugi-court/">Opulent Kintsugi Installation</a> via <a href="https://www.thisiscolossal.com">@colossal</a></p>
</li>
<li>
<p><a href="https://a.singlediv.com/">A Single Div: a CSS drawing project by Lynn Fisher</a></p>
</li>
<li>
<p><a href="https://kottke.org/20/08/ai-claudius">AI Claudius</a></p>
</li>
</ul>
<h2 id="cooking">Cooking</h2>
<ul>
<li><a href="https://twitter.com/seriouseats/status/1294076271308177411">Treat your summer tomatoes right by mastering these essential techniques.</a></li>
</ul>
<h2 id="ordering">Ordering</h2>
<ul>
<li><a href="https://publishing.andrewsmcmeel.com/calendar/tricks-to-appear-smart-in-meetings-2021-day-to-day-calendar/">Tricks to Appear Smart in Meetings 2021 Day-to-Day Calendar</a></li>
</ul>
<h2 id="ideas">Ideas</h2>
<blockquote>
<p>“A bad system will beat a good person every time.” W. Edwards Demming</p>
</blockquote>
<p>via <a href="https://twitter.com/marshallk/status/1291900360865406982">@marshallk</a></p>
<blockquote>
<p>“The optimal strategy might be executing a suboptimal plan at a fast pace. Strategy evolves as lessons are learned—and the person who moves faster, learns faster. Learning is a marathon and perfection is a weighted vest.” – <a href="https://twitter.com/JamesClear/status/1290661258002407428">@JamesClear</a></p>
</blockquote>
Favorites from July 20202020-07-31T00:00:00-05:00https://www.kevfoo.com/2020/07/Favorites-from-July-2020<p>A collection of some of my favorite links, images, tweets and quotes from July. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/07/coronacoaster.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/07/coronacoaster.jpg" /><img src="https://cache.kevfoo.com/2020/07/coronacoaster.jpg" title="Coronacoaster (noun)" alt="The worst ride of 2020." /></picture></p>
<h2 id="reading">Reading</h2>
<ul>
<li>
<p><a href="https://books-on-books.com/2020/06/27/books-on-books-collection-the-last-word-on-the-ampersand/">Books On Books Collection - The Last Word on the Ampersand</a></p>
</li>
<li>
<p><a href="https://www.vulture.com/article/jerry-saltz-the-last-supper.html?utm_source=tw">Jerry Saltz: My Quarantine Obsession: These Two Last Suppers</a></p>
</li>
</ul>
<h2 id="links">Links</h2>
<ul>
<li>
<p><a href="https://gobyexample.com/">Go by Example</a></p>
</li>
<li>
<p><a href="https://ncase.me/loopy/">LOOPY: a tool for thinking in systems</a></p>
</li>
<li>
<p><a href="https://dataconomy.com/2015/06/the-four-essentials-vs-for-a-big-data-analytics-platform/">The Four Essential V’s for a Big Data Analytics Platform</a></p>
</li>
<li>
<p><a href="https://www.websters1913.com/">Webster’s 1913</a></p>
</li>
<li>
<p><a href="https://emilyoster.substack.com/p/resource-rundown-for-schools-and">Resource Rundown (for Schools & Parents) - ParentData</a></p>
</li>
<li>
<p><a href="https://www.supremo.co.uk/typeterms/">Type Terms</a></p>
</li>
</ul>
<h2 id="ideas">Ideas</h2>
<ul>
<li>
<p><a href="https://www.heavybit.com/library/video/executive-communication/">Executive Communication w/ Harrison Metal’s Michael Dearing</a></p>
</li>
<li>
<p><a href="https://twitter.com/helenanders26/status/1278269080697335808">Java is to JavaScript, as Ham is to Hamburger. Never fails to get that ‘lightbulb’ moment.</a></p>
</li>
<li>
<p><a href="https://austinkleon.com/2017/11/24/have-you-tried-making-yourself-a-more-interesting-person/">Have you tried making yourself a more interesting person? - Austin Kleon</a></p>
</li>
<li>
<p><a href="https://twitter.com/jsjoeio/status/1281256827037134849">Effective learning: Twenty rules of formulating knowledge — Thread by @jsjoeio</a></p>
</li>
<li>
<p><a href="https://ravivarmans.com/self-development/the-difference-between-reversible-and-irreversible-decisions/">The Difference Between Reversible and Irreversible Decisions – Ravivarman</a></p>
</li>
</ul>
<blockquote>
<p>Our lives are based on symbols and mental models that we use to understand and internalize the external world. But are our mental models “accurate working models”?
— David Deutsch, <a href="https://twitter.com/ruthmalan/status/1287766072083517440">via @ruthmalan</a></p>
</blockquote>
<h2 id="filed-for-later-use">Filed for Later Use</h2>
<p>JavaScript tip via <a href="https://twitter.com/sulco">Tomek Sułkowski</a>: “npkill” is a nifty little tool that shows all <code class="language-plaintext highlighter-rouge">node_modules</code> in your system - and allows to remove the selected ones.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx npkill
</code></pre></div></div>
Chicago Alphabet2020-07-30T00:00:00-05:00https://www.kevfoo.com/2020/07/chicago-alphabet<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/07/chicago-alphabet.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2020/07/chicago-alphabet.png" /><img src="https://cache.kevfoo.com/2020/07/chicago-alphabet.png" title="Chicago Alphabet" alt="The city of big type." /></picture></p>
<p><a href="https://twitter.com/53viroqua/status/1286801447275896835">Chicago Alphabet</a> via <a href="https://twitter.com/53viroqua">@53viroqua</a></p>
Pierian Spring2020-07-23T00:00:00-05:00https://www.kevfoo.com/2020/07/pierian-spring<blockquote>
<p>“A little Learning is a dang’rous Thing;<br />
Drink deep, or taste not the Pierian Spring:<br />
There shallow Draughts intoxicate the Brain,<br />
And drinking largely sobers us again.”</p>
<p>— Alexander Pope, <a href="https://en.wikisource.org/wiki/An_Essay_on_Criticism">An Essay on Criticism</a></p>
</blockquote>
Favorites from June 20202020-06-30T00:00:00-05:00https://www.kevfoo.com/2020/06/Favorites-from-June-2020<p>A collection of some of my favorite links, images, tweets and quotes from June. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/06/image.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2020/06/image.png" /><img src="https://cache.kevfoo.com/2020/06/image.png" title="" alt="" /></picture></p>
<p><em><a href="https://twitter.com/IanSanders/status/1268797120183754753">via @IanSanders</a></em></p>
<h2 id="links">Links</h2>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/06/an-undulating-brick-facade-3.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/06/an-undulating-brick-facade-3.jpg" /><img src="https://cache.kevfoo.com/2020/06/an-undulating-brick-facade-3.jpg" title="" alt="" /></picture></p>
<ul>
<li><a href="https://themindcircle.com/an-undulating-brick-facade-germany/">This Undulating Brick Façade Imitates The Movement Of Draped Fabric</a></li>
<li><a href="https://carbon.now.sh/">Carbon — Create and share beautiful images of your source code. Start typing or drop a file into the text area to get started.</a></li>
<li><a href="https://www.redblobgames.com/grids/hexagons/#map-storage">Hexagonal Grids - Map storage in axial coordinates</a></li>
<li><a href="https://daringfireball.net/linked/2020/06/12/catalina-extra-fonts">Daring Fireball: Excellent MacOS Catalina Fonts You Probably Didn’t Know You Had Access To</a></li>
</ul>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/06/hemingway.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/06/hemingway.jpg" /><img src="https://cache.kevfoo.com/2020/06/hemingway.jpg" title="Hemingway." alt="What if Dracula had Gun Hands?" /></picture></p>
<ul>
<li><a href="https://twitter.com/pixelatedboat/status/1271442840996900864">Hemingway. via @pixelatedboat</a></li>
</ul>
<h2 id="ideas">Ideas</h2>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/06/knots-1.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/06/knots-1.jpg" /><img src="https://cache.kevfoo.com/2020/06/knots-1.jpg" title="Windy Chien's Knot Wall" alt="chop wood, carry water" /></picture></p>
<ul>
<li><a href="https://www.thisiscolossal.com/2020/06/windy-chien-circuit-boards/">Rope Twists into Massive, Fibrous Circuit Boards by Artist Windy Chien</a></li>
</ul>
<p>What I particularly like about the Rope Twists is the origin story. Windy Chien set out to learn a new knot everyday. The discipline of getting up and doing that every single day led to the permissive creativity which produced the art.</p>
Favorites from May 20202020-05-31T00:00:00-05:00https://www.kevfoo.com/2020/05/Favorites-from-May-2020<p>A collection of some of my favorite links, images, tweets and quotes from May. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/05/may-murder-hornets.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/05/may-murder-hornets.jpg" /><img src="https://cache.kevfoo.com/2020/05/may-murder-hornets.jpg" title="May, in a nutshell." alt="It can always get worse." /></picture></p>
<h2 id="links">Links</h2>
<ul>
<li><a href="http://computers-are-fast.github.io/">Do You Know How Much Your Computer Can Do in a Second?</a></li>
<li><a href="https://robservatory.com/speed-up-your-mac-via-hidden-prefs/">Speed up your Mac via hidden preferences</a></li>
<li><a href="https://blog.coryfoy.com/mapping-mondays/">(Wardley) Mapping Mondays</a></li>
</ul>
Favorites from April 20202020-04-30T00:00:00-05:00https://www.kevfoo.com/2020/04/Favorites-from-April-2020<p>A collection of some of my favorite links, images, tweets and quotes from April. These aren’t comprehensive but do capture some of the things I found interesting, thought-provoking, or cool while surviving but not thriving during the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/04/dunning-kruger.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/04/dunning-kruger.jpg" /><img src="https://cache.kevfoo.com/2020/04/dunning-kruger.jpg" title="...it's time for Dunning Kruger" alt="When you know more than doctors who've spent their entire careers studying infectious diseases, it's time for Dunning-Kruger." /></picture></p>
<p><em>via</em> <a href="https://twitter.com/theturner/status/1252336852108931072">@theturner</a></p>
<h2 id="quotes">Quotes</h2>
<p>“The great ones do not set up offices, charge fees, give lectures, or write books. Wisdom is silent, and the most effective propaganda for truth is the force of personal example. The great ones attract disciples, lesser figures whose mission is to preach and to teach. These are gospelers who, unequal to the highest task, spend their lives in converting others. The great ones are indifferent, in the profoundest sense. They don’t ask you to believe: they electrify you by their behavior. They are the awakeners. What you do with your petty life is of no concern to them. What you do with your life is only of concern to you, they seem to say. In short, their only purpose here on earth is to inspire. And what more can one ask of a human being than that?” – <a href="https://www.nitch.com/posts/1652259674657208320">Henry Miller</a></p>
<p><a href="https://www.goodreads.com/quotes/5069838-be-scared-you-can-t-help-that-but-don-t-be-afraid-ain-t">“Be scared. You can’t help that. But don’t be afraid.”</a> – William Faulkner, <em>The Bear</em></p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.patatap.com/">Play with your keyboard.</a></li>
<li><a href="https://scottberkun.com/2020/its-time-to-learn/">It’s time to learn.</a></li>
<li><a href="https://medium.com/@rakyll/things-i-wished-more-developers-knew-about-databases-2d0178464f78">Things I Wished More Developers Knew About Databases</a></li>
<li><a href="https://www.betterworldbooks.com/go/donate">Donate Books</a></li>
<li>Art Institute of Chicago (soft) launched <a href="https://www.artic.edu/open-access/">an API</a> to access their collection…</li>
<li>…and you can get one of my favorites there on <a href="https://www.threadless.com/search?q=great%20wave&sort=popular&style=face-mask&departments=accessories">a mask</a>.</li>
</ul>
<p><em>Bring on the May flowers!</em></p>
2020 in a picture2020-04-29T00:00:00-05:00https://www.kevfoo.com/2020/04/2020-in-a-picture<p>I can’t think of a better way to capture what my 2020 has been like so far than this.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/04/jordan-shocked-face.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/04/jordan-shocked-face.jpg" /><img src="https://cache.kevfoo.com/2020/04/jordan-shocked-face.jpg" title="" alt="Yep. Pretty much." /></picture></p>
Favorites from March 20202020-03-31T00:00:00-05:00https://www.kevfoo.com/2020/03/Favorites-from-March-2020<p>A collection of some of my favorite links, images, tweets and quotes from March. These aren’t comprehensive but do capture some of the things I found interesting, thought provoking, or cool while trying to survive the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/03/sidewalk-chalk.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/03/sidewalk-chalk.jpg" /><img src="https://cache.kevfoo.com/2020/03/sidewalk-chalk.jpg" title="The greatest pleasure of life is love. -- Euripides" alt="The greatest pleasure of life is love. -- Euripides" /></picture></p>
<h2 id="listening">Listening</h2>
<ul>
<li><a href="https://brain.fm">Brain.fm</a></li>
<li><a href="https://store.nin.com/products/ghosts-digital-download">NIN Ghosts V & VI</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>“Half of strategy consulting is breaking one question apart into three and the other half is turning three questions into one.” – <a href="https://twitter.com/benedictevans/status/1235675848264409088">Ben Evans</a></li>
</ul>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="https://www.seriouseats.com/coronavirus-cooking-guide">The Coronavirus Cooking Guide — Serious Eats</a></li>
<li><a href="https://www.slideas.app/">Slideas Markdown Presentation Editor for Mac</a> – Cool markdown to slides tool to compete with Deckset</li>
<li><a href="https://adamschwartz.co/magic-of-css/">Magic of CSS — Adam Schwartz</a></li>
<li><a href="https://austinkleon.com/2020/03/04/how-to-make-a-zine-from-a-single-sheet-of-paper/">How to make a zine from a single sheet of paper — Austin Kleon</a></li>
<li><a href="http://library.nyam.org/colorourcollections/2020-participating-institutions/participating-institutions/">Download Free Coloring Books from 113 Museums</a> <em>via</em> <a href="https://twitter.com/openculture/status/1240660846851706881">Open Culture</a></li>
<li><a href="https://www.travelandleisure.com/attractions/museums-galleries/museums-with-virtual-tours">12 Museums From Around the World That You Can Visit Virtually</a></li>
</ul>
Links for Parents with Kids on a (Sudden) Staycation2020-03-15T00:00:00-05:00https://www.kevfoo.com/2020/03/links-for-parents-with-kids<p>Like most families we found out last week that our school and daycare were closing. For the better part of the weekend I’ve been scrambling to prepare and figure out what our game plan is. Our school district sent home e-learning resources, lesson plans, and enrichment activities. This will help but, as a parent, your bag of tricks can never be too deep.</p>
<p>I’ve started collecting ideas, activities and information for our kids and figured I’d share. Below is a collection of what I’ve aggregated so far. I’ll continue to edit these and encourage anyone to share what they’ve found useful or helpful <a href="https://gist.github.com/kevinmcmahon/928745dbeba9073b105edc2d4d9d5014">via this Github Gist</a>.</p>
<h2 id="activities">Activities</h2>
<ul>
<li><a href="http://public.pbs.org/PBSKIDSDaily">PBS KIDS DAILY (Newsletter)</a></li>
<li><a href="http://view.commonsense-email.org/?qs=5651c92ff8ba28babcecae7fa77d679486a89fce790dd11ff8f3244eba846ef30daaa5804b7b13cb1219e64cb6521881960bd3f2d0374b553d45d9bd45b66eca51e9ca19af15d3ad689bceac427e0ade4ac66f8764cc24f3">Resources for Families and Educators Facing Coronavirus Uncertainty</a></li>
<li><a href="https://www.youtube.com/channel/UC1XLSK-wYXG5qWqkHeBhj9w">Jen the Entomologist Pre-school Science - Tiny Science Channel</a></li>
</ul>
<h2 id="digital-field-trips">Digital Field Trips</h2>
<ul>
<li><a href="https://www.travelandleisure.com/attractions/museums-galleries/museums-with-virtual-tours">12 Museums From Around the World That You Can Visit Virtually</a></li>
</ul>
<h2 id="creative">Creative</h2>
<ul>
<li><a href="https://austinkleon.com/2020/03/04/how-to-make-a-zine-from-a-single-sheet-of-paper/">How to make a zine from a single sheet of paper - Austin Kleon</a></li>
<li><a href="https://austinkleon.com/2020/03/24/drawing-with-kids/">Austin Kleon’s Drawing with Kids Recommendations</a></li>
<li><a href="https://www.youtube.com/watch?v=vheR1cd58oc&list=PL14hRqd0PELGbKihHuTqx_pbvCLqGbOkF">Lunch Doodles with Mo Willems</a></li>
<li><a href="https://www.instagram.com/wendymac/">Wendy MacNaughton’s Daily Draw Together Sessions on Instagram Live</a></li>
</ul>
<h2 id="talking-to-kids">Talking to Kids</h2>
<ul>
<li><a href="https://www.pbs.org/parents/thrive/how-you-and-your-kids-can-de-stress-during-coronavirus">How You and Your Kids Can De-Stress During Coronavirus</a></li>
<li><a href="https://www.youtube.com/watch?v=4ex4H6n_s7w">What is social distancing? (Video)</a></li>
<li><a href="https://sites.google.com/district65.net/socialemotionallibrary/home">Miss Johnson’s Social Emotional Online Library</a></li>
</ul>
<h2 id="home-schooling">Home Schooling</h2>
<ul>
<li><a href="https://www.linkedin.com/pulse/schooling-home-advice-from-homeschooling-family-eric-johnson/">Schooling at home advice from a homeschooling family.</a></li>
<li><a href="https://docs.google.com/spreadsheets/d/1RRv9cENXMp1frTxMmGv3HrNkag6e2RqRZirpHSRzy44/htmlview?fbclid=IwAR0genzlpAzaPgv9tmgLRQVU4sFZgAKBFiCzCipL0gfxTZIj6NG9rvbW8IE&sle=true#gid=0">Amazing Educational Resources — Google Docs</a></li>
<li>
<p><a href="https://classroommagazines.scholastic.com/support/learnathome.html">Scholastic Learn at Home</a></p>
<p><em>Last Updated 3/24</em></p>
</li>
</ul>
Why You're Good2020-03-01T00:00:00-06:00https://www.kevfoo.com/2020/03/more-important-why-youre-good<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/03/more-important-why-youre-good.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2020/03/more-important-why-youre-good.png" /><img src="https://cache.kevfoo.com/2020/03/more-important-why-youre-good.png" title="Why You're Good" alt="It's more important to know why you're good than that you're good." /></picture></p>
Favorites from February 20202020-02-29T00:00:00-06:00https://www.kevfoo.com/2020/02/Favorites-from-February-2020<p>A collection of some of my favorite links, images, tweets and quotes from February. These aren’t comprehensive but do capture some of the things I found interesting, thought provoking, or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/02/empty-when-full.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/02/empty-when-full.jpg" /><img src="https://cache.kevfoo.com/2020/02/empty-when-full.jpg" title="Empty When Full" alt="Existential trash." /></picture></p>
<h2 id="filed-for-later-use">Filed for later use</h2>
<ul>
<li>If you hit a SSL error on a page in Chrome, type <code class="language-plaintext highlighter-rouge">thisisunsafe</code> to ignore it. ( <em><a href="https://twitter.com/tapbot_paul/status/1226379496539836416">via @tapbot_paul</a></em> )</li>
</ul>
<h2 id="reading">Reading</h2>
<ul>
<li><a href="https://www.goodreads.com/book/show/24490392-the-15-commitments-of-conscious-leadership">The 15 Commitments of Conscious Leadership</a> nudged via the <a href="https://fs.blog/jim-dethmer/">Farnam Street podcast</a>.</li>
<li><a href="https://leanpub.com/wardley-maps">Wardley Maps</a> ebook that you can pick your price on with all proceeds going to charity.</li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>“The least obvious part of the system, its function or purpose, is often the most crucial determinant of the system’s behavior.” (Donella H. Meadows, Thinking in Systems)</li>
<li>“Ignorance is the softest pillow on which a man can rest his head.” – Michel de Montaigne <em>via <a href="https://twitter.com/EthicsInBricks/status/1229378893980807168">Ethics in Bricks</a></em></li>
</ul>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li>
<p>Microsoft has a nice catalog of <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/">Cloud Design Patterns</a> arranged by category</p>
</li>
<li>
<p><a href="https://towardsdatascience.com/all-machine-learning-models-explained-in-6-minutes-9fe30ff6776a?gi=49299812266c">All Machine Learning Models Explained in 6 Minutes</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=gmc4wEL2aPQ">Mind in Motion: How Action Shapes Thought – Stanford Seminar – Barbara Tversky</a></p>
</li>
<li>
<p>If you’re fresh out of things to worry about, checkout this <a href="https://www.thisiscolossal.com/2020/02/metaballstudio-asteroid-animation/">comparison of the size of asteroids in the solar system to New York City</a></p>
</li>
</ul>
Favorites from January 20202020-01-31T00:00:00-06:00https://www.kevfoo.com/2020/01/Favorites-from-January-2020<p>A collection of some of my favorite links, images, tweets and quotes from January (which seemed like it lasted 7 years). While these aren’t a comprehensive look at or summary of January 2020, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2020/01/be-brave.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2020/01/be-brave.jpg" /><img src="https://cache.kevfoo.com/2020/01/be-brave.jpg" title="Be Brave Enough" alt="Be brave enough to suck at something new." /></picture></p>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p><a href="https://www.oreilly.com/library/view/building-evolutionary-architectures/9781491986356/">“The more <em>reusable</em> something is, the less <em>usable</em> it is.” — Evolutionary Architecture by Neal Ford, Rebecca Parsons and Patrick Kua</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/10368067-poke-the-box">“We reward those who draw maps, not those who follow them.” — Poke the Box, Seth Godin</a></p>
</li>
</ul>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li>
<p><a href="https://www.infoq.com/presentations/microservices-failure-modes/">Managing Failure Modes in Microservice Architectures</a></p>
</li>
<li>
<p><a href="https://klinger.io/post/71640845938/dont-drown-in-email-how-to-use-gmail-more">Don’t drown in email! How to use Gmail more efficiently. - Startup Lessons Learned</a></p>
</li>
<li>
<p><a href="https://abidlabs.github.io/simulated-customer/">The Simulated Customer Game: Handling Objections</a></p>
</li>
<li>
<p><a href="https://www.libraryextension.com/">Library Extension - See book availability from your local library</a></p>
</li>
<li>
<p><a href="https://blog.thepete.net/blog/2019/12/09/delivering-on-an-architecture-strategy/">Delivering on an architecture strategy - Pete Hodgson</a></p>
</li>
<li>
<p><a href="https://medium.com/@copyconstruct/effective-mental-models-for-code-and-systems-7c55918f1b3e">Effective Mental Models for Code and Systems</a></p>
</li>
</ul>
Favorites from December 20192019-12-31T00:00:00-06:00https://www.kevfoo.com/2019/12/Favorites-from-December-2019<p>A collection of some of my favorite links, images, tweets and quotes from December. While these aren’t a comprehensive look at or summary of December 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/12/you-got-this.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/12/you-got-this.png" /><img src="https://cache.kevfoo.com/2019/12/you-got-this.png" title="Danger Sign" alt="" /></picture></p>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p><a href="https://twitter.com/CodeWisdom/status/1201495486793949184">“If software cannot be maintained, then it will be rewritten” – Dave Cheney</a></p>
</li>
<li>
<p><a href="https://twitter.com/CompSciFact/status/1201970659682861060">“Hardware eventually fails. Software eventually works.” – Michael Hartung</a></p>
</li>
<li>
<p><a href="https://twitter.com/helenanders26/status/1203946156310462466">“If it’s out of your hands, it deserves freedom from your mind too.” - Ivan Nuru</a></p>
</li>
</ul>
Favorites from November 20192019-11-30T00:00:00-06:00https://www.kevfoo.com/2019/11/Favorites-from-November-2019<p>A collection of some of my favorite links, images, tweets and quotes from November. While these aren’t a comprehensive look at or summary of November 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<p><a href="https://twitter.com/farnamstreet/status/1193155066494177282">“Sheep don’t bring their owners grass to prove to them how much they’ve eaten, they digest it inwardly and outwardly bring forth milk and wool. So don’t make a show of your philosophical learning to the uninitiated, show them by your actions what you have absorbed.” - Epictetus</a> via <a href="https://twitter.com/farnamstreet">Farnam Street</a></p>
<h2 id="favorite-links">Favorite Links</h2>
<p><a href="https://vimeo.com/128934608">Constraints that Enable Innovation</a> via <a href="https://twitter.com/ruthmalan/status/1201157076384436227">Ruth Malan</a></p>
<p><a href="https://www.artofmanliness.com/articles/learn-new-skills-quickly/">How to learn new skills quickly</a></p>
<p><a href="https://www.gwern.net/Complement">Commoditize Your Complement - Gwern.net</a></p>
<p><a href="https://www.defmacro.org/2019/11/25/craftsman-exec.html">Craftsman, executive — a tale of two modes</a></p>
<h2 id="filed-for-later-slideware">Filed for later slideware</h2>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/11/danger-sign.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/11/danger-sign.png" /><img src="https://cache.kevfoo.com/2019/11/danger-sign.png" title="Danger Sign" alt="" /></picture></p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/11/bad-decisions.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/11/bad-decisions.png" /><img src="https://cache.kevfoo.com/2019/11/bad-decisions.png" title="Bad Decisions" alt="Can't stop making bad decisions." /></picture></p>
Entropy2019-11-10T00:00:00-06:00https://www.kevfoo.com/2019/11/Entropy<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/11/entropy.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2019/11/entropy.png" /><img src="https://cache.kevfoo.com/2019/11/entropy.png" title="Entropy" alt="Entropy makes it necessary for leaders to constantly work on maintaining an organization’s purpose, form, and methods even if there are no changes in strategy or competition." /></picture></p>
Favorites from October 20192019-10-31T00:00:00-05:00https://www.kevfoo.com/2019/10/Favorites-from-October-2019<p>A collection of some of my favorite links, images, tweets and quotes from October. While these aren’t a comprehensive look at or summary of October 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/10/vonnegut-quote.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2019/10/vonnegut-quote.jpg" /><img src="https://cache.kevfoo.com/2019/10/vonnegut-quote.jpg" title="If this isn't nice, I don't know what is." alt="If this isn't nice, I don't know what is." /></picture></p>
<p><a href="https://austinkleon.com/2019/10/04/notice-when-you-are-happy/">Vonnegut via Austin Kleon</a></p>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<h3 id="teach-your-children-well">Teach your children well</h3>
<blockquote>
<p>“Instead of buying your children all the things you never had, you should teach them all the things you were never taught. Material wears out but knowledge stays.”
— <a href="https://www.swiss-miss.com/2019/10/teach-them-all-the-things.html">Bruce Lee</a> via Swiss Miss</p>
</blockquote>
<h3 id="second-order-effects">Second order effects</h3>
<blockquote>
<p>“Always remember that to argue, and win, is to break down the reality of the person you are arguing against. It is painful to lose your reality, so be kind, even if you are right.”
— <a href="https://jamesclear.com/why-facts-dont-change-minds">Haruki Murakami</a> via James Clear</p>
</blockquote>
<h2 id="favorite-links">Favorite Links</h2>
<p><a href="https://sivers.org/d22">Cut out everything that’s not surprising</a></p>
<p><a href="https://blog.stephsmith.io/learning-to-write-with-confidence/">Writing is Thinking: Learning to Write with Confidence</a></p>
Favorites from September 20192019-09-30T00:00:00-05:00https://www.kevfoo.com/2019/09/Favorites-from-September-2019<p>A collection of some of my favorite links, images, tweets and quotes from September. While these aren’t a comprehensive look at or summary of September 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/09/four-horsemen-procrastination.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2019/09/four-horsemen-procrastination.jpg" /><img src="https://cache.kevfoo.com/2019/09/four-horsemen-procrastination.jpg" title="Napping. Snacks. Social Media. Cleaning." alt="Napping. Snacks. Social Media. Cleaning." /></picture></p>
<p><a href="https://www.cartooncollections.com/cartoon?searchID=WD600017&ANDkeyword=four+horsemen&NOTkeyword=&TITLEkeyword=&categories=&artists=&collection=&colorOption1=colour&colorOption2=blackWhite&orientationOption1=portrait&orientationOption2=landscape&cp=0&limit=24">by Ellis Rosen</a></p>
<h1 id="favorite-quotes">Favorite Quotes</h1>
<p><strong>How (Not) To Grow Old</strong></p>
<blockquote>
<p>“The best way to overcome it [the fear of death]—so at least it seems to me—is to make your interests gradually wider and more impersonal, until bit by bit the walls of the ego recede, and your life becomes increasingly merged in the universal life. An individual human existence should be like a river: small at first, narrowly contained within its banks, and rushing passionately past rocks and over waterfalls. Gradually the river grows wider, the banks recede, the waters flow more quietly, and in the end, without any visible break, they become merged in the sea, and painlessly lose their individual being. The man who, in old age, can see his life in this way, will not suffer from the fear of death, since the things he cares for will continue. And if, with the decay of vitality, weariness increases, the thought of rest will not be unwelcome. I should wish to die while still at work, knowing that others will carry on what I can no longer do and content in the thought that what was possible has been done.”
— <a href="http://www.openculture.com/2018/12/bertrand-russells-advice-for-how-not-to-grow-old.html">Bertrand Russell</a></p>
</blockquote>
<p><strong>Growing Up vs. Growing Old</strong></p>
<blockquote>
<p>“You put so damned much value on youth, it seems to me you confused growing up with growing old.”
— <a href="https://twitter.com/RyanHoliday/status/1172525366096928768">Hemingway to Fitzgerald</a></p>
</blockquote>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="https://twitter.com/EdwardTufte/status/1170697758078111745">Edward Tufte on Twitter: “A+ How to demolish a study in 20 words! (Have always used this list style in writing referee reports, whether favorable or unfavorable, put my time into reading the manuscript not writing long-winded review.)”</a></li>
<li><a href="https://www.animalz.co/blog/bottom-line-up-front/">BLUF: The Military Standard That Can Make Your Writing More Powerful</a></li>
<li><a href="https://twitter.com/zachtratar/status/1174354895673016320">Zach Tratar on Twitter: “One thing we do well at @stripe is write memos to collaborate on problems and potential solutions. These tend to follow the SCQA format for clarity. S = Situation C = Complication Q = Questions A = Answers Start with basics, say what’s hard, provoke thought, and then solve!”</a></li>
</ul>
Favorites from August 20192019-08-31T00:00:00-05:00https://www.kevfoo.com/2019/08/Favorites-from-August-2019<p>A collection of some of my favorite links, images, tweets and quotes from August. While these aren’t a comprehensive look at or summary of August 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/08/mister-rodgers.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2019/08/mister-rodgers.jpg" /><img src="https://cache.kevfoo.com/2019/08/mister-rodgers.jpg" title="Mister Rodgers" alt="It feels good to have made something." /></picture></p>
<p><a href="https://twitter.com/41Strange/status/1158498749410242560">“Mister Rogers… “</a></p>
<h1 id="favorite-papers">Favorite Papers</h1>
<ul>
<li><a href="http://tuvalu.santafe.edu/~simon/it.pdf">Information Theory for Intelligent People</a></li>
<li><a href="https://media.defense.gov/2018/Oct/09/2002049591/-1/-1/0/DIB_DETECTING_AGILE_BS_2018.10.05.PDF">Detecting Agile BS</a> – Surprisingly good guidance for folks looking to buy software solutions. I am trying to use this content to help educate our clients about making good choices when setting out to build software.</li>
</ul>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="https://dmarby.se/blog/lorem-picsum/">Lorem Picsum, death by a million pixel-gigabits</a></li>
<li><a href="https://simpleoptout.com/">Deep links to opt-out of data sharing by 60+ companies – Simple Opt Out</a></li>
<li><a href="https://lorinhochstein.wordpress.com/2019/08/22/experts-arent-good-at-building-shared-understanding/">Experts aren’t good at building shared understanding</a></li>
<li><a href="https://speakerdeck.com/buritica/increasing-engineering-tempo-at-splice">Increasing Engineering Tempo</a></li>
<li><a href="https://twitter.com/HerrDoktorFunk/status/1164939786316058624">Clément Chastagnol on Twitter: “This is bonkers! It was still science-fiction 5 years ago 🤯Clone a voice in 5 seconds to generate arbitrary speech in real-timehttps://t.co/3wQ6Qn8Ts2”</a></li>
<li><a href="https://career.andreabadgley.blog/2019/08/05/from-worry-lists-to-pipelines-4-steps-we-used-to-design-processes-so-we-can-scale/">From worry lists to pipelines: 4 steps we used to design processes so we can scale – Andrea Badgley: Support Driven</a></li>
</ul>
<h1 id="favorite-quotes">Favorite Quotes</h1>
<ul>
<li><a href="https://twitter.com/dcancel/status/1160268938359574530">David Cancel on Twitter: “Expect to be terrible the first time you try anything. And the second and third time. If you want to get good at anything it will require a bruised ego. Keep showing up if you want it.”</a></li>
</ul>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/08/amy-oleary-narrative2016.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2019/08/amy-oleary-narrative2016.jpg" /><img src="https://cache.kevfoo.com/2019/08/amy-oleary-narrative2016.jpg" title="For stories to matter, they must give us a map to how the world works." alt="For stories to matter, they must give us a map to how the world works." /></picture></p>
<p><a href="https://twitter.com/itsjina/status/716273347735236611">via @itsjina</a></p>
<ul>
<li>
<p><a href="https://twitter.com/ShaneAParrish/status/1164006456938684416">Shane Parrish on Twitter: “It’s easier to believe that something else is holding us back from what we want than to face the reality that we hold ourselves back.”</a></p>
</li>
<li>
<p><a href="https://twitter.com/polotek/status/1165100507355963393">Marco Rogers on Twitter: “The role of management is changing. I don’t know how long this has been in progress, but the pressure on managers in tech has gone up sharply in recent years. Seemingly spurred on by the movement towards increasing diversity, equity, and inclusion in the workplace.”</a></p>
</li>
<li>
<p><a href="https://twitter.com/bcantrill/status/1164725111171104768">Bryan Cantrill on Twitter: “Mistakes are expected, respected, inspected, and corrected.” – words to live by from my 7th grade son’s math teacher</a></p>
</li>
</ul>
Favorites from July 20192019-07-31T00:00:00-05:00https://www.kevfoo.com/2019/07/Favorites-from-July-2019<p>A collection of some of my favorite links, images, tweets and quotes from July. While these aren’t a comprehensive look at or summary of July 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/07/crayola-cyberpunk-crayons.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/07/crayola-cyberpunk-crayons.png" /><img src="https://cache.kevfoo.com/2019/07/crayola-cyberpunk-crayons.png" title="Crayola Cyberpunk Crayons" alt="Crayola made the leap into a cyberpunk future with the release of these internet themed crayons." /></picture></p>
<p><a href="https://twitter.com/DavidM2000AD/status/886515653368315904">hedgerow devil on Twitter: “In 1997 Crayola made the leap into a cyberpunk future with the release of these internet themed crayons. Web Surfin’ Blue is my colour.… https://t.co/P9ab9q8eSG”</a></p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="https://colors.eva.design/">Eva Design System: Deep learning color generator</a></li>
<li><a href="http://web.archive.org/web/20200203061643/http://confreaks.tv/videos/rubyconf2017-keynote-growing-old">Growing Old - Chad Fowler’s Ruby Conference 2017 Keynote</a></li>
<li><a href="https://www.thisiscolossal.com/2019/07/negative-space-new/">Negative Space: The Vast Emotional Landscape of a Father-Son Relationship Packed into an Animated Short</a> via <a href="https://thisiscolossal.com">Colossal</a></li>
<li><a href="http://donellameadows.org/archives/leverage-points-places-to-intervene-in-a-system/">Leverage Points: Places to Intervene in a System - The Donella Meadows Project</a></li>
<li><a href="https://kottke.org/19/07/the-great-wave-by-katsushika-hokusai">The Great Wave by Katsushika Hokusai</a></li>
<li><a href="https://jvns.ca/blog/brag-documents/">Get your work recognized: write a brag document - Julia Evans</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p><a href="https://twitter.com/ValaAfshar/status/1146084429116452864">Vala Afshar on Twitter: “.@Netflix did not kill blockbuster. Ridiculous late fees did. @Uber did not kill taxi business. Limited access and fare control did. @Amazon did not kill other retailers. Poor customer service did. Not being customer-centric is the biggest threat to any business. —@betobrea… https://t.co/CvUwdIXcvh”</a></p>
</li>
<li>
<p><a href="https://twitter.com/RyanHoliday/status/1147158298380181504">Ryan Holiday on Twitter: ““By leadership we mean the art of getting someone else to do something that you want done because he wants to do it.” Dwight D. Eisenhower”</a></p>
</li>
<li>
<p><a href="https://twitter.com/RyanHoliday/status/1147241256520691712">Ryan Holiday on Twitter: ““If a man knows not which port he sails, no wind is favorable.” – Seneca”</a></p>
</li>
<li>
<p><a href="https://twitter.com/dan_abramov/status/1148590323972677632">Dan Abramov on Twitter: “Tired: how many lines of code is this function Wired: how many different concepts did you need to unwrap in your head to understand what this function is doing”</a></p>
</li>
<li>
<p><a href="https://twitter.com/jessitron/status/1123310331957145601">“When we rush development, skip tests and refactoring, we get “Escalating Risk.” Please give up the “technical debt” description; it gives business people a very wrong impression of the tradeoffs.”</a></p>
</li>
<li>
<p><a href="https://twitter.com/HeidiStevens13/status/1152358901813325824">Heidi Stevens on Twitter: “I spent today with a man whose mom saved the July 21, 1969 Chicago Daily News for the moon landing coverage. (Column coming tomorrow.) It also contained this op-ed. 50 years ago. Unbelievable.… https://t.co/XcOuybhNEr”</a></p>
</li>
<li>
<p><a href="https://twitter.com/KarenCivil/status/1153321607940792320">Karen Civil on Twitter: “The dream is free, the hustle is sold separately.”</a></p>
</li>
</ul>
<h2 id="favorite-thoughts">Favorite Thoughts</h2>
<ul>
<li><a href="https://tom.preston-werner.com/2010/08/23/readme-driven-development.html">Readme Driven Development</a></li>
</ul>
Favorites from June 20192019-06-30T00:00:00-05:00https://www.kevfoo.com/2019/06/Favorites-from-June-2019<p>A collection of some of my favorite links, images, tweets and quotes from June. While these aren’t a comprehensive look at or summary of June 2019, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/06/painters-as-turtles.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/06/painters-as-turtles.png" /><img src="https://cache.kevfoo.com/2019/06/painters-as-turtles.png" title="Painters as Turtles" alt="painters-as-turtles" /></picture></p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li>
<p><a href="https://www.youtube.com/playlist?list=PLDA2BC5E785D495AB">The American Revolution with Joanne B. Freeman</a> – Getting ready for the 4th. The first video of the top 5 tips for studying the revolution was particular interesting.</p>
</li>
<li>
<table>
<tbody>
<tr>
<td>[Animated Knots by Grog</td>
<td>Learn how to tie knots with step-by-step animation](https://www.animatedknots.com/) – Refer back to this more than a few times a year.</td>
</tr>
</tbody>
</table>
</li>
<li>
<table>
<tbody>
<tr>
<td>[Undervalued Software Engineering Skills: Writing Well</td>
<td>Gergely Orosz](https://blog.pragmaticengineer.com/on-writing-well/)</td>
</tr>
</tbody>
</table>
</li>
<li>
<table>
<tbody>
<tr>
<td>[What does debugging a program look like?</td>
<td>Julia Evans](https://jvns.ca/blog/2019/06/23/a-few-debugging-resources/)</td>
</tr>
</tbody>
</table>
</li>
<li>
<table>
<tbody>
<tr>
<td>[“These are the two axes of professional design and engineering. Did you produce within the constraints? Did you deliver measurable results? That’s it.” – Constraints and measurement</td>
<td>Seth’s Blog](https://seths.blog/2019/06/constraints-and-measurement/)</td>
</tr>
</tbody>
</table>
</li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p><a href="https://twitter.com/johnmaeda/status/1140088643249496066">John Maeda on Twitter: “The right brain speaks the truth; the left brain makes the truth. Integrity is when you speak+make the <em>whole</em> truth.… https://t.co/iPniqOOlk0”</a></p>
</li>
<li>
<p><a href="https://twitter.com/dril/status/1140096866169679873">wint on Twitter: “beanie babies are just bit coins in real life”</a></p>
</li>
<li>
<p><a href="https://twitter.com/austinkleon/status/1140305770552221696">Austin Kleon on Twitter: ““Your children are fine without advice & suggestions.”— sign at the #nycplayground on Governor’s Island https://t.co/RQuQmKwNZ2”</a></p>
</li>
<li>
<p><a href="https://twitter.com/farnamstreet/status/1144750988072497152">Shane Parrish on Twitter: “Every choice you make is a step toward or away from the person you want to become. No single choice will get you where you want to go. Only repeated steps over time in the same direction will move you forward.”</a></p>
</li>
<li>
<p>“A gem cannot be polished without friction, nor a man perfected without trials.” – Seneca</p>
</li>
</ul>
Google I/O 20192019-05-19T00:00:00-05:00https://www.kevfoo.com/2019/05/google-io-2019<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2019/05/io-2019.webp" /><source type="image/png" srcset="https://cache.kevfoo.com/2019/05/io-2019.png" /><img src="https://cache.kevfoo.com/2019/05/io-2019.png" title="Google I/O 2019 Logo" alt="io-2019" /></picture></p>
<p>This year’s Google I/O kicked off amid calls for increased scrutiny and more regulation of the entire tech industry. Given the recent <a href="https://www.forbes.com/sites/davidphelan/2019/04/12/amazon-confirms-staff-listen-to-alexa-conversations-heres-all-you-need-to-know/#7cb00caf5d9d">trust</a>, <a href="https://www.nytimes.com/2019/04/24/technology/facebook-ftc-fine-privacy.html">privacy</a>, and <a href="https://daringfireball.net/linked/2019/04/18/facebook-instagram-shocker">security</a> issues, tensions between the public and the tech industry are rising. Action to stem a public backlash against the tech industry is overdue.</p>
<p>In <a href="http://web.archive.org/web/20200121003947/http://goodbadstrategy.com/about-the-book/">Good Strategy/Bad Strategy</a>, Richard Rumelt cites three elements that every good strategy: a diagnosis, guiding policies and coherent actions. The diagnosis characterizes and explains the nature of the challenge. Guiding policies establish an approach for dealing with the challenge. Coherent actions serve to execute the guiding policies. This underlying structure is what Rumelt calls the kernel of a strategy. In the keynote, a new strategy or kernel seemed to emerge from Google — one that sought to counter the wave of bad press the technologies that underpin Google’s core businesses received.</p>
<p>I have captured the essence of Google’s new strategy below in the kernel structure. It maps neatly into what Rumelt was addressing.</p>
<h2 id="diagnosis">Diagnosis</h2>
<p>Google has been learning about everyone for over 20 years. They’re the king of data collection and analytics. They are the top ad network, analytics services, and data ingestion network on the planet. It is Google’s dominance in these areas that make people uncomfortable. Given the recent spate of security and privacy violations, it is understandable. The public is leery about trusting one company with all this information. Can one company secure it, respect the user’s wishes, and not be creepy?</p>
<p>The recent problems at Facebook and Amazon have ensnared Google. Disdain is growing for machine learning and artificial intelligence. Their core businesses built on these technologies are coming under scrutiny from regulators. More discussion is needed about the positive aspects of the technologies.</p>
<h2 id="guiding-policies">Guiding Policies</h2>
<ul>
<li>Be transparent and clear about what and why Google is collecting data.</li>
<li>Respect the user’s privacy and security.</li>
<li>Make it easier to understand what is happening with their data.</li>
<li>Provide tools to control or limit the amount of data sharing that is occurring.</li>
<li>Show the tangible benefits that these technologies can make.</li>
</ul>
<h2 id="coherent-actions">Coherent Actions</h2>
<ul>
<li><a href="https://federated.withgoogle.com/">Federated (on-device) learning</a> limits the amount of raw data shared with Google while still contributing to the improvement of the global data model.</li>
<li>Incognito mode for locations, search, etc.</li>
<li>Accessibility features like <a href="https://www.android.com/accessibility/live-transcribe/">Live Transcribe</a> and <a href="https://blog.google/outreach-initiatives/accessibility/impaired-speech-recognition/">Project Euphoria</a></li>
<li><a href="https://ai.google">AI for Social Good</a></li>
<li><a href="https://ai.google/principles/">Google’s AI Principles</a></li>
</ul>
<p>Google distilled their new strategy down to a single theme: <em>be helpful</em>. It was interesting to see the talk part of their strategy get rolled out. Talking about a more helpful Google attempts to change the narrative, but talk is cheap. In the coming months and years, we will see if Google’s strategy gets embodied in their actions.</p>
Building Apps and Teams for the Enterprise2016-04-20T00:00:00-05:00https://www.kevfoo.com/2016/04/building-apps-and-teams-for-the-enterprise<p>Here are the <a href="https://speakerdeck.com/kevinmcmahon/building-apps-and-teams-for-the-enterprise">slides</a> from my Android Listener talk on building apps and teams in the enterprise.</p>
<iframe title="Building Apps and Teams for the Enterprise" class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/f24cdcd701dd4dde979135c363a2a687?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 790px; height: 444px;"></iframe>
RecyclerView Talk at AndroidListener Chicago2014-11-19T00:00:00-06:00https://www.kevfoo.com/2014/11/RecyclerView-AndroidListener-Talk<p>Here are the slides from my <a href="http://www.meetup.com/AndroidListener-Chicago/events/205043582/">AndroidListener</a> talk on the RecyclerView.</p>
<iframe title="RecyclerView Talk at AndroidListener Chicago" class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/0e0a5e90528f013294f64e2c5650262a?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 790px; height: 444px;"></iframe>
Real World Android Development Links2013-08-15T00:00:00-05:00https://www.kevfoo.com/2013/08/real-world-android-development-links<p>Here are some links to the artifacts from the Real World Android Development talks.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://speakerdeck.com/kevinmcmahon/real-world-android-development">Slides</a></li>
<li><a href="http://www.appdevwiki.com">AppDevWiki</a></li>
</ul>
<h2 id="open-source-libraries">Open Source Libraries</h2>
<ul>
<li><a href="http://actionbarsherlock.com/">Action Bar Sherlock</a></li>
<li><a href="http://square.github.io/dagger/">Dagger</a></li>
<li><a href="http://square.github.io/retrofit/">Retrofit</a></li>
<li><a href="http://square.github.io/picasso/">Picasso</a></li>
<li><a href="http://square.github.io/otto/">Otto</a></li>
<li><a href="https://github.com/square/okhttp">OkHttp</a></li>
<li><a href="http://viewpagerindicator.com/">ViewPagerIndicator</a></li>
<li><a href="https://github.com/cyrilmottier/Polaris2">Polaris</a></li>
<li><a href="https://github.com/keyboardsurfer/Crouton">Crouton</a></li>
<li><a href="https://github.com/google/gson">GSON</a></li>
<li><a href="https://github.com/commonsguy">Commonsware</a></li>
</ul>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://plus.google.com/s/%23AndroidDev">#AndroidDev on Google+</a></li>
<li><a href="http://www.youtube.com/user/androiddevelopers">Android Developers Youtube Channel</a></li>
<li><a href="http://androiddevweekly.com/">AndroidDevWeekly.com Newsletter</a></li>
<li><a href="http://androidweekly.net/">AndroidWeekly.net Newsletter</a></li>
</ul>
Real World Android Development Talk2013-08-08T00:00:00-05:00https://www.kevfoo.com/2013/08/Read-World-Android-Development<p>Last night I had the pleasure of presenting to the <a href="http://www.meetup.com/GDG-Google-Developer-Group-Schaumburg/events/129695972/">GDG Schaumburg</a> group about my experiences helping clients define and create Android apps. The talk expands the focus from just discussing the technological aspects of building Android apps and covers the important components (people and process) needed to successfully launch an app. I wanted to do something adifferent than just an Android toolbelt talk and, judging from the post-talk feedback and discussion, it resonated with the crowd.</p>
<iframe title="Real World Android Development Talk" class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/1a8f38a0e2110130040f0e1a274ba0c1?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 790px; height: 593px;"></iframe>
<p><em>*UPDATE*</em> Make sure to check out the <a href="https://www.kevfoo.com/2013/08/real-world-android-development-links/">links</a> also.</p>
Android Crash Course Updated2013-07-26T00:00:00-05:00https://www.kevfoo.com/2013/07/Android-Crash-Course-Update<p>I’ve refreshed the <a href="http://kevfoo.com/androidcrashcourse/">Android Crash Course</a>(<a href="https://cache.kevfoo.com/acc/AndroidCrashCourse.pdf">PDF version</a>) as we’re currently in the process of ramping up a developer at work. It is still a work in progress and, as always, I’d love to get feedback or suggestions on this and how it could be more helpful. Thanks.</p>
Google I/O 2013 Recap2013-05-21T00:00:00-05:00https://www.kevfoo.com/2013/05/Google-IO-2013-Recap<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2013/05/google_io_640_480.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2013/05/google_io_640_480.jpg" /><img src="https://cache.kevfoo.com/2013/05/google_io_640_480.jpg" title="Google I/O 2013 Logo" alt="" /></picture></p>
<p>Last week I had the pleasure of attending this year’s Google I/O. Before I got too far removed from the conference, I thought I would try to capture my impressions and thoughts about last week.</p>
<h2 id="keynote">Keynote</h2>
<p>The keynote clearly wasn’t as tight as WWDC keynotes and frankly not as cohesive as past I/O conferences. Lots of bits were spilled last week in the aftermath, and I will spare you a retread of the common complaints. I do think though that a majority of the problems stemmed from the lack of a unifying theme or major tent-pole announcement. It is tough to build a substantive keynote around the smaller, more strategic moves Google announced. That being said, I cannot argue with the complaints about the “blocking and tackling” problems Google had during the keynote, especially at a conference this big.</p>
<h2 id="android-announcements">Android Announcements</h2>
<p>The keynote did not include an announcement incrementing the Android API level. Many took that as Google wavering on their commitment to the platform. Watching in-person, I had the opposite impression, and the subsequent Android sessions during the week reaffirmed that.</p>
<p>The Google Play Services and the r13 Android Support Library UI enhancements showed us how Google plans to curb the fragmentation issues which have plagued the platform. By packaging these enhancements in libraries that are compatible back to Froyo and immediately available for integration, Google enabled developers to get these new features and services into the hands of users quickly. This eliminates the dependency Google had previously on handset manufactures to integrate new Android releases on their current and older devices.</p>
<p>Many have questioned the long term viability of the Android platform for Google as companies like Amazon and Samsung seek ways to circumvent or minimize Google’s influence on their products. The approach that Google adopted has provided a way forward that serves their needs as well as developers. That is a big deal.</p>
<h2 id="comparison-to-wwdc">Comparison to WWDC</h2>
<p>The content and variety of sessions at both conferences are excellent. On the whole, WWDC’s sessions appeared to be more polished and rehearsed when compared to the I/O sessions. I found the I/O sessions tended to be a little more light-hearted and included more variety in the session structure (fireside chats, interactive labs), which added to the enjoyment of the sessions and kept them from becoming monotonous. Both conferences provide a tremendous amount of access to the engineering teams directly working on the frameworks and services developers interact with daily. These opportunities for interaction are easily the most valuable parts of each conference. While the Apple labs at WWDC predominantly allowed for one-on-one interaction with their engineers, Google made their engineers available in an Office Hours setting that had the engineers holding court for small groups around white boarded areas in the conference hall. If you had particular questions, the WWDC labs could not be beaten for access and personal attention. If you were interested in picking up some tips and inside information about your favorite Google developer topics, then the I/O office hours are great.</p>
<p>My biggest gripes about I/O are mainly with the logistics and the infrastructure at the conference. The most popular talks were being held in rooms that could have quickly been filled if they were four times the size. The bigger tracks like Android and Chrome would have benefitted from closer proximity. Instead, they were spread over two floors. That would’ve made bouncing between sessions easier and eased up the congestion around the escalators and the other traffic hotspots on the levels. The session rooms themselves were stuffy and hot during the talks and especially so in the popular sessions, which typically had crowds sitting along the walls and in the aisles. While the range of these complaints spans from the minor (too crowded at times) to significant (uncomfortably hot rooms) annoyances, they still detract from an otherwise excellent conference experience. The best thing about these issues is that they are fixable. Hopefully, Google will make some changes to eliminate these issues from future conferences.</p>
<h2 id="external-conference-activities">External Conference Activities</h2>
<p>The best parts of conferences are social interactions, and Google I/O was no different. I kicked off the week at the <a href="https://web.archive.org/web/20130322032745/http://meatup.io/">Meatup.io</a>, made my way out to Twitter for their Mobile Showcase lightning talks, and capped the week off by going to a meetup-style discussion on Android testing at a local startup. It was great to meet so many great developers and engineers at these events, and it was nice to be able to thank so many of the project creators and maintainers in person for their contributions to Android’s open-source community.</p>
<h2 id="summary">Summary</h2>
<p>I had a fantastic time at Google I/O. I’m still chewing on and trying to process all the great content, conversations, and code that I was exposed to last week. I highly recommend Google I/O, and if you are fortunate to get an opportunity to attend a future event, I strongly urge you to go. Spending a week with 6,000 passionate and excited developers is a fantastic way to immerse yourself in learning and explore new technologies and services.</p>
<h2 id="favorite-sessions">Favorite Sessions</h2>
<h3 id="android">Android</h3>
<ul>
<li><a href="http://www.youtube.com/watch?v=yhv8l9F44qo">Volley: Easy, Fast Networking for Android</a></li>
<li><a href="http://www.youtube.com/watch?v=XpqyiBR0lJ4">Structure in Android App Design</a></li>
<li><a href="http://www.youtube.com/watch?v=qlrKh-L4bqU">Taking Advantage of Android Platform Features</a></li>
<li><a href="https://www.youtube.com/watch?v=LCJAgPkpmR0">The New Android SDK Build System</a></li>
</ul>
<h3 id="non-android">Non Android</h3>
<ul>
<li><a href="http://youtu.be/r-VL7NKJqcs">The Freebase APIs: Tapping into Google’s Knowledge Graph</a></li>
<li>Gamestorming</li>
<li><a href="http://www.youtube.com/watch?v=z2exxj4COhU">Cognitive Science and Design</a></li>
</ul>
That Conference 20132013-05-09T00:00:00-05:00https://www.kevfoo.com/2013/05/That-Conference-2013<p>I am excited and honored to be speaking at <a href="https://thatconference.com/">That Conference</a> again this August in the Wisconsin Dells. That Conference will have multiple tracks for mobile, web, and cloud developers as well as an interesting <em>other</em> track this year.</p>
<p>I’ll be sharing some of the hard-won Android tips and best-practices that I have collected over the past few years while building and shipping apps for clients. Hope to see you there in August!</p>
Setting Up Motorola ET1 for Android Development2012-10-12T00:00:00-05:00https://www.kevfoo.com/2012/10/setting-up-motorola-et1-for-android-development<p>Getting ADB to recognize the <a href="http://www.motorola.com/Business/US-EN/Business+Product+and+Services/Tablets/ET1+Enterprise+Tablet_US-EN">Motorola ET1</a> took longer than I’d like so I’m posting this with the hope that it’ll help others who find themselves in a similar spot.</p>
<p>Like most Android-based devices, developers need to enable USB debugging on the device by toggling a setting in the developer section of the settings app. However, with the ET1, developers also need to add the device’s vendor id (0x05E0) to the <em>~/.android/adb_usb.ini</em> file or else the device won’t be detected or visible to ADB.</p>
<p>Once the vendor id has been added, kill and restart the ADB server. After ADB starts back up, the ET1 should now be listed in the ADB device list and able to be deployed to.</p>
Favorites from August 20122012-08-31T00:00:00-05:00https://www.kevfoo.com/2012/08/Favorites-from-August-2012<p>A collection of some of my favorite links, tweets and quotes from August. While these aren’t a comprehensive look at or summary of August 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="http://www.360cities.net/image/curiosity-rover-martian-solar-day-2#172.65,10.74,61.5">Mars Curiosity Panorama Photo</a></li>
<li><a href="http://www.theatlantic.com/technology/archive/2012/07/the-speed-of-sound-is-too-slow-for-olympic-athletes/260413/#.UBlbWYJWZ5c.twitter">The Speed of Sound is Too Slow For Olympic Athletes</a></li>
<li><a href="http://animatedpizzagifs.com/">Animated Pizza Gifs</a></li>
<li><a href="http://www.economist.com/blogs/democracyinamerica/2012/08/infrastructure?fsrc=scn/tw/te/bl/comingapart">Infrastructure - Coming apart at the seams</a></li>
<li><a href="http://blog.grammarly.com/">Grammarly</a></li>
<li><a href="https://web.archive.org/web/20120923145120/http://www.theverge.com/gaming/2012/8/21/3258225/nintendo-power-magazine-to-cease-publication-after-24-years">RIP Nintendo Power</a></li>
<li><a href="http://kottke.org/12/08/feynman-diagram-sculptures-by-edward-tufte">Feynman diagram sculptures by Edward Tufte</a></li>
<li><a href="http://reason.com/archives/2012/08/20/the-wrong-side-absolutely-must-not-win">The wrong side must absolutely not win</a></li>
<li><a href="http://therapboard.com/">The Rap Board</a></li>
<li><a href="https://web.archive.org/web/20130925203046/http://www.jest.com/video/186274/if-movies-had-crappy-fonts">If Movies Had Crappy Fonts</a></li>
<li><a href="https://blog.mapbox.com/mapping-mars-with-open-planetary-data-8a7a44dc42bc">Mapping Mars with Open Planetary Data</a></li>
<li><a href="https://vimeo.com/43497548">Tony Fadell: On Setting Constraints, Ignoring Experts & Embracing Self-Doubt</a> <a href="https://vimeo.com/43497548">Video</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>When you say “I seen,” I assume you won’t finish that sentence with “the inside of a book.” – <a href="https://24.media.tumblr.com/tumblr_m8hi2xcquD1r5j6uso1_500.png">someecards</a></li>
</ul>
Favorites from July 20122012-07-31T00:00:00-05:00https://www.kevfoo.com/2012/07/Favorites-from-July-2012<p>A collection of some of my favorite links, tweets and quotes from July. While these aren’t a comprehensive look at or summary of July 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="http://www.youtube.com/watch?v=AXUiyK1olZ8">Amazing Tiny Wings 2 Trailer</a></li>
<li><a href="http://web.archive.org/web/20120623232018/http://drunks-and-lampposts.com/2012/06/13/graphing-the-history-of-philosophy/">Graphing the History of Philosopy</a></li>
<li><a href="http://web.archive.org/web/20120723142725/http://www.getkempt.com/the-code/the-phone-stack.php">The Phone Stack</a></li>
<li><a href="http://www.businessweek.com/articles/2012-07-05/just-shazam-it">Just Shazam It</a></li>
<li><a href="http://web.archive.org/web/20120713170420/http://www.openculture.com/2012/07/alan_watts_and_his_zen_wisdom_animated_by_creators_of_south_park.html">Alan Watts and His Zen Wisdom Animated by Creators of South Park</a></li>
<li><a href="http://allentucker.com/pay-too-much/">Pay too much</a></li>
<li><a href="http://www.thisiscolossal.com/2012/07/animal-sculptures-made-from-reclaimed-household-objects/">Animal sculptures made from reclaimed household objects</a></li>
<li><a href="http://www.pushing-pixels.org/2012/07/25/the-usability-of-radial-menus.html">The Usability of Radial Menus</a></li>
</ul>
<h1 id="favorite-quotes">Favorite Quotes</h1>
<ul>
<li>“No SciFi author foresaw this: we are building clans around languages we speak to the Machine.” – <a href="https://twitter.com/mraleph/status/225587192427327488">@mraleph</a></li>
<li>“It’s important to take a moment to reflect on how fortunate we are that those alien spaceships were compatible with Jeff Goldblum’s laptop.” – <em><a href="https://twitter.com/gavinspeiller/status/220526998437638147">@gavinspeiller</a> on July 4th</em></li>
</ul>
Nexus 7 Quick Thoughts2012-07-17T00:00:00-05:00https://www.kevfoo.com/2012/07/Nexus-7-Quick-Thoughts<p>A few quick initial impressions of the <a href="https://play.google.com/store/devices/details?id=nexus_7_8gb">Nexus 7</a>.</p>
<ol>
<li>A legitimate Android tablet has finally been released. It is the best
Android tablet on the market.</li>
<li>Jelly Bean is definitely “buttery”.</li>
<li>Touch is very responsive in Jelly Bean. <em>Big</em> improvement over
previous Android devices/versions.</li>
<li>The Kindle Fire is comically bad and outdated compared to it.</li>
<li>The build quality is ok. Doesn’t feel plastic-y like the Samsung
products. Not a fan of the faux leather back.</li>
<li>Feels like a big phone.</li>
<li>Seems like resolution/size will require better fluidity from apps.
Devs will definitely have to account for more screen real estate.</li>
<li>Not sold on the 7” form factor but I think I’ll be in the minority.</li>
</ol>
<p>Overall I’m pretty impressed with the Nexus 7. Seems like the software
finally caught up with the hardware and, judging by the buzz around the
product, the affordable price will make this a popular choice for those
consumers looking for a tablet.</p>
Core Location : The Missing Details2012-07-05T00:00:00-05:00https://www.kevfoo.com/2012/07/Core-Location-The-Missing-Details<p>My primary objective at WWDC this year was to find answers to Core Location questions I had and develop some intuition about how the framework works underneath the hood. Armed with some questions and a small sample app illustrating some of the bugs I found, I set out for the one Core Location lab on the calendar during the week. These “office hour” style labs offer developers an opportunity to talk directly with the Apple engineers who work on these frameworks. After getting paired up with one of the more senior Core Location guys, I was blown away by how valuable these sessions can be. I was able to resolve several issues and had a bunch of questions answered while also picking up a few tips and tricks to use in future apps. The amount of knowledge and insight shared during my relatively short session was invaluable.</p>
<p>While I can’t share everything I learned in the lab due to the NDA covering iOS 6 updates and features, I did collect non-iOS 6 specific concepts that I can share with you. Documentation for the information does not exist even though it helps paint a fuller picture of how Core Location works. During my time in the lab, two things became clear: variations in environment and hardware significantly impact location services quality and that Apple engineers are still actively iterating and improving Core Location. I didn’t get a straight answer as to why they are reluctant to share this information, but I would not be surprised if these types of things were a factor.</p>
<p>Finally, the contents below are the product of notes scribbled on paper during my lab session and a brain dump immediately following. I am reasonably confident that what I captured in my notes is legit and valid information, but it is entirely likely that I misheard, misinterpreted, or misunderstood something — caveat emptor.</p>
<h2 id="region-monitoring-information">Region Monitoring Information</h2>
<ul>
<li>
<p>The <strong>minimum region radius size is 100m</strong>. Core Location will allow you to set a radius value smaller than 100m for a CLRegion, but internally the framework will treat the region as if its radius was 100m.</p>
</li>
<li>
<p>The <strong>maximum number of regions able to be monitored at one time is 20</strong>. Nowhere is this limit documented. If you try to register for more than 20 regions, you will get an error indicating that you can’t add any more to monitor. One approach to get around this cap is to load and unload them based on your current location. You can use significant location change notifications to evaluate when old regions need to be removed and updated with those closest to the user’s current location. Keep in mind that modifying the ones you are tracking does take power, so it is something that you want to do judiciously.</p>
</li>
<li>
<p><strong>Regions fall into three categories</strong>. Depending on what category the registered region falls into affects the time it takes for Core Location to trigger the delegate callbacks.</p>
<ul>
<li>Fine Region (<strong>0 - 150m</strong>)
<ul>
<li>With a floor of 100m, this category’s range is effectively 100-150m.</li>
<li>For regions, this size <strong>performance is heavily dependent on the location-related hardware</strong> in the devices. The iPhone 4S, unsurprisingly, has the best hardware. Some iPads and all iPod Touches don’t have cellular radios. Consider all these types of scenarios during development.</li>
<li>The amount of time that it takes Core Location to detect and call the appropriate delegate method is roughly <strong>2-3 minutes</strong> on average after crossing the region boundary.</li>
<li>Some developers have figured out independently that smaller regions would see quicker callbacks and would cluster smaller regions to cover one large area to improve region crossing notifications. This approach was frowned upon by the Apple engineer. This type of solution uses more resources/power than monitoring just the single large region, but it is a viable alternative if you need the quickest possible updates. Apple is aware of this, and they are continually trying to minimize the time that elapses until Core Location invokes the delegate methods.</li>
</ul>
</li>
<li>“Normal” Region (<strong>150m - 7km</strong>)
<ul>
<li>Core Location <strong>can take as long as 15 minutes</strong> to invoke the delegate methods, but it is usually less.</li>
<li><strong>Response time improves significantly with increased user interaction</strong> with the device. Core Location tries to minimize power consumption, so it doesn’t accelerate battery drain and tries to piggyback on other activities to check location. A device that is actively browsing the web or sending text messages is more likely to have Core Location checking for updates than a device that is idle in standby mode.</li>
</ul>
</li>
<li>“Huge” Region (<strong>7km+</strong>)
<ul>
<li>This category, referred to as the “1/3 of the earth” region, was mentioned and confirmed to exist. There was no additional information given.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Regions are not registered for instantaneously</strong>. If you register for a region and then immediately enter/exit the region, then it won’t get registered properly. The recommended suggested wait time was 10s, which is helpful to know when testing.</p>
</li>
<li>
<p>Bugs prevented the start/stop watching region callbacks from being invoked in iOS 5. The callbacks do not work correctly in the simulator and only work correctly on some hardware devices. These bugs appear fixed in iOS 6.</p>
</li>
</ul>
<h2 id="other-core-location-tidbits">Other Core Location Tidbits</h2>
<ul>
<li>
<p><strong>Significant location change</strong> (SLC) updates will not fire unless <strong>5 minutes</strong> elapsed, and a change of <strong>500m</strong> occurred since the last update.</p>
</li>
<li>If you <strong>stop and then start registering for SLC notifications, there are no guarantees that it will fire immediately</strong>. Enough people thought this worked or tried this that Apple changed how Core Location behaves, and now a cached value (if Core Location has one) will be returned on the subsequent firing.</li>
<li>
<p>You can tell the source of your location information by observing the blue location dot used by MapKit. A <strong>pulsing dot means GPS</strong> (<a href="https://cache.kevfoo.com/2012/07/bluedot_gps.png">example</a>) and a <strong>static dot means cell/wifi</strong> (<a href="https://cache.kevfoo.com/2012/07/bluedot_cellwifi.png">example</a>). This is not documented anywhere officially, and apparently, a lot of Apple engineers don’t recognize/notice the difference.</p>
</li>
<li>
<p><strong>Core Location quality/performance varies substantially based on where you are</strong>. In a high-density area like a city, the device will download smaller payloads and use fewer resources than if you’re in a sparsely populated area.</p>
</li>
<li>
<p><strong>Core Location piggybacks on other user interactions</strong> to do most of its updating. The more opportunities it has to check/update location, the better the response time.</p>
</li>
<li>The mocked location data provided to the <strong>simulator always appears to be coming from GPS</strong>. You can’t simulate the behavior that the cellular or wifi sources provide or affect what happens when the GPS source is sketchy. In prior iOS releases, region monitoring relied heavily (exclusively?) on the location data provided by cell towers to operate. Still, improvements to both the algorithms and simulator should mitigate any of these issues.</li>
</ul>
Chicago Alt.Net Amazon Web Services2012-07-04T00:00:00-05:00https://www.kevfoo.com/2012/07/chicago-alt-net-amazon-web-services-talk<p>In June I had the pleasure to speak at the <a href="http://chicagoalt.net">Chicago Alt.Net</a> meetup about <a href="http://aws.amazon.com">Amazon Web Services</a>. The talk covered the basics of cloud services, provided a high-level overview of how Amazon’s web services work and showed how easy it was to integrate AWS into a project via the .Net SDK that Amazon provides. For some of the attendees it was their first technical look at the platform. I hope that they found the talk informative and piqued their interest enough to explore the platform further.</p>
<script async="" class="speakerdeck-embed" data-id="4ff41794a4fa25001f004724" data-ratio="1.3333333333333333" src="//speakerdeck.com/assets/embed.js"></script>
Favorites from June 20122012-06-30T00:00:00-05:00https://www.kevfoo.com/2012/06/Favorites-from-June-2012<p>A collection of some of my favorite links, tweets and quotes from June. While these aren’t a comprehensive look at or summary of June 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="http://web.archive.org/web/20191116073101/http://www.lettersofnote.com/2011/10/dejobbed-bewifed-and-much-childrenised.html">Dejobbed, bewifed and much childrenised</a></li>
<li><a href="http://designtaxi.com/news/352664/Simplistic-Illustrations-Of-Negative-Space/">Simplistic Illustrations of Negative Space</a></li>
<li><a href="http://web.archive.org/web/20120630023937/http://www.fastcoexist.com/1679873/a-cheap-accurate-cancer-sensor-created-by-a-15-year-old">A cheap and accurate cancer sensor created by a 15 year old.</a></li>
<li><a href="http://kottke.org/12/06/frida-kahlo-rocking-daft-punk">Frida Kahlo Rocking Daft Punk</a></li>
<li><a href="http://www.happyplace.com/16428/newspaper-describes-nickelback-concert-and-hilarious-alternatives-funny">Newspaper artfully describes upcoming Nickelback concert and hilarious alternatives to it.</a></li>
<li><a href="http://www.quickmeme.com/meme/3prrs3/">Scumbag Steve describes the Ricketts Family</a></li>
<li><a href="http://coderwall.com/p/euwpig?i=3&p=1&t=git">A better git log</a></li>
<li><a href="http://visual.ly/10-artists-10-years-color-palettes">10 Artists, 10 Years : Color Palettes</a></li>
<li><a href="http://grist.org/list/gza-and-neil-degrasse-tyson-team-up-on-a-hip-hop-record-about-science/">GZA and Neil de Grasse Tyson to team up on a hip-hop science album</a></li>
<li><a href="http://web.archive.org/web/20120629032613/http://www.visionmobile.com/product/developer-economics-2012/">Developer Economics 2012 : The New Mobile App Economy</a></li>
<li><a href="http://web.archive.org/web/20200829132714/https://lettersofnote.com/2012/06/19/my-mother-declared-my-bedroom-a-disaster-area/">My mother declared my bedroom a disaster area.</a></li>
<li><a href="http://www.boston.com/bigpicture/2012/06/national_geographic_traveler_m.html">2011 National Geographic Traveler Photo Contest Winners</a></li>
<li><a href="http://www.thisiscolossal.com/2012/06/digitally-assembled-paintings-by-russ-mills/">Digitally Assembled Paintings</a></li>
<li><a href="http://twitpic.com/a1miep">Great moments in tech support history</a></li>
<li><a href="http://www.buzzfeed.com/gavon/the-internet-responds-to-supreme-court-ruling-on-o?sub=1644827_399363">Chief Justice John Roberts #YOLO</a></li>
<li><a href="http://people.csail.mit.edu/mrub/vidmag/">Eulerian Video Magnification framework that can visualize the human pulse.</a></li>
<li><a href="http://www.niemanlab.org/2012/06/theres-no-such-thing-as-an-objective-filter/">There’s no such thing as an objective filter: Why designing algorithms that tell us the news is hard</a></li>
</ul>
<h1 id="favorite-quotes">Favorite Quotes</h1>
<ul>
<li>“<a href="http://news.ycombinator.com/item?id=4178401">EC2 comes with a free Chaos Monkey service. It’s called EC2.</a>” – <em>pjscott via Hacker News</em></li>
<li>“Never attribute to stupidity that which is adequately explained by process.” – <a href="https://twitter.com/ftrain/status/213341347883651072">Paul Ford</a></li>
<li>“We are cups, constantly and quietly being filled. The trick is, knowing how to tip ourselves over and let the beautiful stuff out.” – Ray Bradbury <em>RIP</em></li>
</ul>
Android Crash Course2012-06-22T00:00:00-05:00https://www.kevfoo.com/2012/06/Android-Crash-Course<p>One the main goals of my current client engagement was to help a team of talented developers come up to speed and become proficient with the Android platform. A key component of the approach I employed was to hold a weekly “tech talk” session that focused on basic Android topics. The topics ranged from how do views work to understanding the development tools available and were intended to fill in some of the fundamentals of the platform as the team was starting to develop the project. The scope and the depth of the sessions varied depending on the topic and the relative familiarity that the team already had with it, but ultimately they focused on building solid fundamentals.</p>
<p>After being asked recently by another developer for some tips on getting up to speed with Android, I thought it might be useful to aggregate and cleanup the notes I had from the tech talks with the hope that they might prove useful for someone looking for a crash course in Android development.</p>
<p>The outline is still very rough and definitely needs further refinement, but in the spirit of releasing early and often, I’m making it available today <a href="http://kevfoo.com/androidcrashcourse/">here</a> and in <a href="https://cache.kevfoo.com/acc/AndroidCrashCourse.pdf">pdf form</a>. I’d love to get feedback or suggestions on this and how it could be more helpful so please feel free to reach out.</p>
Favorites from May 20122012-05-31T00:00:00-05:00https://www.kevfoo.com/2012/05/Favorites-from-May-2012<p>A collection of some of my favorite links, tweets and quotes from May. While these aren’t a comprehensive look at or summary of May 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2012/05/pinata.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2012/05/pinata.jpg" /><img src="https://cache.kevfoo.com/2012/05/pinata.jpg" title="" alt="" /></picture></p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="http://sigpwned.com/2012/05/04/complication-is-what-happens-when/">Complication is What Happens When You Try to Solve a Problem You Don’t Understand</a></li>
<li><a href="http://www.theparisreview.org/interviews/4825/the-art-of-fiction-no-21-ernest-hemingway">Ernest Hemingway, The Art of Fiction No. 21</a></li>
<li><a href="http://web.archive.org/web/20200301074621/http://www.lettersofnote.com/2009/11/youre-nothing-but-pimp.html">Frank Sinatra’s letter to Mike Royko</a></li>
<li><a href="https://web.archive.org/web/20120511083336/http://www.thisiscolossal.com/2012/04/a-bathroom-situated-atop-a-15-story-elevator-shaft/">Bathroom on top of a 15 story elevator shaft</a></li>
<li><a href="https://www.thisiscolossal.com/2012/05/pinata-anatomy/">Pinata Anatomy</a></li>
<li><a href="http://www.theatlantic.com/politics/archive/2012/05/democracy-is-for-amateurs-why-we-need-more-citizen-citizens/256818/">Democracy is for amateurs</a></li>
<li><a href="https://web.archive.org/web/20120521210010/http://www.smithsonianmag.com/arts-culture/Where-Did-the-Taco-Come-From.html?c=y&story=fullstory">Where did the taco come from?</a></li>
<li><a href="http://www.thisiscolossal.com/2012/05/high-speed-liquid-flowers-photographed-by-jack-long/">High speed liquid flowers</a></li>
<li><a href="http://open.spotify.com/track/59yzZJSFso9CMcIzrEvhtL">Golden Age</a></li>
<li><a href="http://cdixon.org/2012/05/26/the-experience-economy/">The Experience Economy</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p>“The world is a book and those who do not travel only read one page.” – <a href="http://icanread.tumblr.com/post/23548675140">St. Augustine</a></p>
</li>
<li>
<p>“Lighting one candle with another candle– spring evening” – <a href="https://kindle.amazon.com/post/PB4VB0UTRF5G">Haiku from Buson</a></p>
</li>
</ul>
That Conference - Summer Camp for Geeks2012-05-22T00:00:00-05:00https://www.kevfoo.com/2012/05/That-Conference-Summer-Camp-for-Geeks<p><a href="https://www.thatconference.com/?s=badge"><img alt="That Conference" border="0" src="https://www.thatconference.com/Images/SiteBadges/440w.jpg?s=badge" width="440" height="300" /></a></p>
<p>I am excited and honored to be speaking at <a href="https://thatconference.com">That Conference</a> this August in the Wisconsin Dells. That Conference is going to have tracks for mobile, web and the cloud and the sessions look outstanding. I’ll be doing a session on Core Location which, according to the iOS 6 rumors, may be a hot topic at WWDC this year. Hope to see you there!</p>
Geofencing in iOS - Chicago Code Camp 20122012-05-20T00:00:00-05:00https://www.kevfoo.com/2012/05/Geofencing-in-iOS-Talk<p>Yesterday I had the pleasure of doing a talk exploring Core Location at the 4th annual <a href="http://www.chicagocodecamp.com">Chicago Code Camp</a>. Really enjoyed the audience participation and hopefully everyone left with a better understanding of Core Location.</p>
<p><a href="https://github.com/kevinmcmahon/LocationDemo">Code on Github</a></p>
<script async="" class="speakerdeck-embed" data-id="4fb7ab70d1170e00220208b0" data-ratio="1.299492385786802" src="//speakerdeck.com/assets/embed.js"></script>
Favorites from April 20122012-04-30T00:00:00-05:00https://www.kevfoo.com/2012/04/Favorites-from-April-2012<p>A collection of some of my favorite links, tweets and quotes from April. While these aren’t a comprehensive look at or summary of April 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="http://startupsthisishowdesignworks.com/">Startups, this is how design works.</a></li>
<li><a href="http://blog.getprismatic.com/blog/2012/4/18/content-focused-design-type-edition.html">Content Focused Design : Type Edition</a></li>
<li><a href="http://www.vitsoe.com/en/gb/about/dieterrams/gooddesign">Dieter Rams: ten priniciples of good design</a></li>
<li><a href="http://www.nytimes.com/2012/04/05/opinion/kristof-arsenic-in-our-chicken.html?_r=1&pagewanted=all">Arsenic in Our Chicken?</a></li>
<li><a href="http://textsfromhillaryclinton.tumblr.com/">Texts from Hillary</a></li>
<li><a href="http://www.swiss-miss.com/2012/04/helvettaca.html">Helvettaca - Swiss Miss</a></li>
<li><a href="http://theoatmeal.com/comics/facebook_likes">Facebook Likes - The Oatmeal</a></li>
<li><a href="http://www.thisiscolossal.com/2012/04/figurative-willow-branch-sculpture-by-olga-ziemska/">Figurative Willow Branch Sculpture - Colossal</a></li>
<li><a href="http://www.architizer.com/en_us/blog/dyn/41732/nasa-determines-average-color-of-the-universe/">Nasa determins the average color of the universe</a></li>
<li><a href="http://design.org/blog/difference-between-ux-and-ui-subtleties-explained-cereal">The Difference Between UX and UI: Subtleties Explained in Cereal</a></li>
<li><a href="http://twistedsifter.com/2012/04/adding-monsters-to-thrift-store-paintings/">Adding monsters to thrift store paintings.</a></li>
</ul>
<h1 id="favorite-quotes">Favorite Quotes</h1>
<ul>
<li><a href="http://www.fastcodesign.com/1669503/user-experience-is-the-heart-of-any-company-how-do-you-make-it-top-priority">“If you start with “useful” as a first principle, then you automatically place customer need and experience first.”</a> – Mary Ellen Muckerman</li>
</ul>
Favorites from March 20122012-03-31T00:00:00-05:00https://www.kevfoo.com/2012/03/Favorites-from-March-2012<p>A collection of some of my favorite links, tweets and quotes from March. While these aren’t a comprehensive look at or summary of March 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="http://boingboing.net/2012/03/02/individual-dolphins-identify-t.html">Individual dolphins identify themselves to new dolphins they meet</a></li>
<li><a href="http://worrydream.com/oatmeal/unexpectedly.png">Your youth has ended unexpectedly.</a></li>
<li><a href="http://nowsourcing.com/blog/wp-content/uploads/2012/01/louisville-painter.html">The Psychology of Color</a></li>
<li><a href="http://hiddentruths.northwestern.edu/home.html">Hidden Truths: The Chicago City Cemetery and Lincoln Park</a></li>
<li><a href="http://blog.blprnt.com/blog/blprnt/tedxvancouver-the-weight-of-data">The Weight of Data - Nice talk about bringing a human element to data.</a></li>
<li><a href="http://www.dezeen.com/2012/03/07/recreational-island-house-by-2by4-architects/">Recreation Island House</a></li>
<li><a href="http://www.pbs.org/wgbh/nova/physics/fabric-of-cosmos.html">The Fabric of the Cosmos</a></li>
<li><a href="http://propublica.github.com/stateface/">StateFace</a></li>
<li><a href="https://coolhunting.com/design/100-ideas-that-changed-graphic-design/">100 Ideas That Changed Graphic Design</a></li>
<li><a href="http://maps.stamen.com/#watercolor/12/37.7706/-122.3782">Stamen Maps - Amazing map tiles</a></li>
<li><a href="http://web.archive.org/web/20120327181427/http://www.thehecklerstore.com/keep-calm-caray-on-t-shirt/">Keep Calm, Caray on.</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<ul>
<li>
<p>“Papyrus is a classy comic sans.” – @rands</p>
</li>
<li>
<p>“Time is a created thing. To say “I don’t have time” is to say “I don’t want to”” - Lao Tzu</p>
</li>
</ul>
Favorites from February 20122012-02-29T00:00:00-06:00https://www.kevfoo.com/2012/02/Favorites-from-February-2012<p>A collection of some of my favorite links, tweets and quotes from February. While these aren’t a comprehensive look at or summary of February 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h2 id="favorite-links">Favorite Links</h2>
<ul>
<li><a href="http://www.listsofnote.com/2012/02/wartime-golf-rules.html">War Time Golf Rules</a></li>
<li><a href="http://www.roughtype.com/archives/2012/01/why_publishers.php">Why Publishers Should Give Away - eBooks</a></li>
<li><a href="http://t.co/pHkrvHWb">Mapresent, a new mapping presentation tool for the iPad</a></li>
<li><a href="http://t.co/ZiVYpFqd">Yale Discovers a Fungus That Eats Plastic</a></li>
<li><a href="http://www.youtube.com/watch?v=cGMCmbLq2qs&feature=youtu.be">Edible Balloon from Alinea</a></li>
<li><a href="http://t.co/Deyz4qfr">How Target Figured Out A Teen Girl Was Pregnant Before Her Father Did</a></li>
<li><a href="http://t.co/UJoaScer">L.E.D. Surfer</a></li>
<li><a href="http://www.shorpy.com/node/12442">Mardi Gras 1907</a></li>
<li><a href="http://chicagopast.com/">Chicago Past photoblog</a></li>
<li><a href="http://www.dezeen.com/2012/02/23/starbucks-coffee-at-dazaifu-tenman-gu-by-kengo-kuma-and-associates/">An amazing Starbucks store on the approach to a Shinto shrine in Dazaifu, - Japan</a></li>
<li><a href="http://blog.unitedheroes.net/archives/p/4396/">Welcome to the Internet - Advice to a 13 year old.</a></li>
</ul>
<h2 id="favorite-quotes">Favorite Quotes</h2>
<blockquote>
<p>“Chaos was the law of nature, order was the dream of man.” – Henry Adams</p>
</blockquote>
<blockquote>
<p>“All men will benefit, if we can invoke the wonders of science instead of its terrors.” – <a href="https://www.jfklibrary.org/archives/other-resources/john-f-kennedy-press-conferences/news-conference-25">JFK</a></p>
</blockquote>
Favorites from January 20122012-01-31T00:00:00-06:00https://www.kevfoo.com/2012/01/Favorites-from-January-2012<p>A collection of some of my favorite links, tweets and quotes from January. While these aren’t a comprehensive look at or summary of January 2012, they do capture the things that I found interesting, thought provoking, and/or cool over the past month.</p>
<h1 id="favorite-links">Favorite Links</h1>
<ul>
<li><a href="http://www.youtube.com/watch?&v=2WmlUXsjSv8">Wilco, Nick Lowe & Mavis Staples rehearse The Weight</a></li>
<li><a href="http://www.ted.com/talks/sheena_iyengar_choosing_what_to_choose.html">TED Talk: How To Make Choosing Easier</a></li>
<li><a href="http://www.youtube.com/watch?v=e-P5RGdjICo">× as the visual symbol for multiply</a></li>
<li><a href="http://www.youtube.com/watch?v=PNpVURAgG5g">Wes Anderson from above</a></li>
<li><a href="http://www.flickr.com/photos/gsfc/6760135001">Blue Marble: NASA’s Incredible New High Resolution Photograph of Earth</a></li>
<li><a href="http://i3.kym-cdn.com/photos/images/newsfeed/000/234/765/b7e.jpg">MEME PIC: I have no idea what I am doing</a></li>
<li><a href="http://t.co/hQzUMEj3">Amazing sleuth work done to determine Ice Cube’s ‘Good Day’</a></li>
<li><a href="http://www.fastcompany.com/1810522/mits-math-breakthrough-could-transform-your-phone-tablet-pc-tv-mri-scans">New FFT calculation could be 10x faster</a></li>
<li><a href="http://www.youtube.com/watch?v=D4k3bX0ydJg">Now <em>THIS</em> is a firework</a></li>
<li><a href="http://maxogden.com/#blog/gut-hosted-open-data-filets">Gut: HTTP Unix pipes for Open Data</a></li>
<li><a href="http://typographica.org/2012/features/our-favorite-typefaces-of-2011/">Typographica’s Favorite Typefaces for 2011</a></li>
<li><a href="http://www.buzzfeed.com/mattcherette/beavis-and-butthead-in-real-life">Beavis and Butthead, as sculpted by a special FX makeup artist.</a></li>
</ul>
<h1 id="favorite-tweets">Favorite Tweets</h1>
<ul>
<li><a href="https://twitter.com/#!/dansinker/status/161997962803810305">@dansinker</a>:”The first rule of John Kerry fight club…” contains at least 75 subclauses.”</li>
<li><a href="https://twitter.com/#!/citizenparker/status/164060055862382592">@citizenparker</a>: MUTABILITY! From hell’s heart I stab at thee (until another operation inadvertently changes my state to “not stabbing”)</li>
</ul>
<h1 id="quotes">Quotes</h1>
<ul>
<li><a href="https://plus.google.com/u/0/107980702132412632948/posts/ikswxSskaUc">“Data is the new raw material of the 21st century”</a> - Tim Berners Lee & Nigel Shadbolt</li>
</ul>
Importing CSV data with location information into PostGIS2012-01-09T00:00:00-06:00https://www.kevfoo.com/2012/01/Importing-CSV-to-PostGIS<p>Sometimes location data isn’t neatly packaged in a SHP file, and it requires some massaging before it can be used in spatial queries. In this post I’ll take a look at importing latitude and longitude data from a CSV file into our PostGIS database and transforming those coordinates into geometry data that can be spatially queried against.</p>
<h1 id="step-0---initial-starting-point">Step 0 - Initial Starting Point</h1>
<ul>
<li>
<p>Get a CSV dataset. For this example I’ll be using the <a href="http://data.cityofchicago.org/Historic-Preservation/Individual-Landmarks/tdab-kixi">Individual Landmarks</a> dataset exported as CSV. You can get a copy of the CSV file that I am using for the example <a href="https://cache.kevfoo.com/data/Individual_Landmarks.csv">here</a>.
We already have the latitude and longitude and the extra location field isn’t something that we’ll use so I removed the location column prior to exporting the dataset. You can hide the column by selecting the arrows on the column heading in the table (<a href="https://cache.kevfoo.com/2012/01/hide_column_via_header.png">shown here</a>) or via the manage screen and deselecting the location column from the list of available columns (<a href="https://cache.kevfoo.com/2012/01/hide_column_via_manage.png">shown here</a>).</p>
</li>
<li>
<p>A PostgreSQL database with PostGIS installed.</p>
</li>
</ul>
<h1 id="step-1---create-the-table-in-the-database">Step 1 - Create the table in the database</h1>
<p>Use the following SQL to create a table to hold the landmark data.</p>
<script src="https://gist.github.com/1584721.js?file=gistfile1.sql"></script>
<h1 id="step-2---copy-the-csv-data-into-the-database">Step 2 - Copy the CSV data into the database</h1>
<p>To run the copy command, login to the database and execute the following command :</p>
<script src="https://gist.github.com/1584983.js?file=gistfile1.sh"></script>
<p>There are a couple of things to note with the copy command:</p>
<ol>
<li>The first argument passed to the copy command specifies the tablename (<em>landmarks</em>) and the columns which the CSV files map to. Our table includes a column <em>gid</em> that is an auto-incremented id which is used as a primary key as well as <em>the_geom</em> geometry column. The CSV does not include either of these columns in the data we are copying. If we don’t specify the specific columns that the fields in the CSV map to, errors will occur when attempting to copy the data.</li>
<li>The DELIMITERS argument specifies your delimiter.</li>
<li>The CSV argument at the end lets PostgreSQL know that the file is CSV.</li>
<li>The HEADER argument lets PostgreSQL know that the file includes the headers on the first line.</li>
</ol>
<p>Here is a look at our data after the import. Note that the_geom has no data. We’ll take care of that in the next step.</p>
<p><img src="https://cache.kevfoo.com/2012/01/imported_landmark_data.png" alt="A look at the newly imported landmark data sans geometry." /></p>
<h1 id="step-3---translate-latitude-and-longitude-into-point-geometry">Step 3 - Translate latitude and longitude into POINT geometry</h1>
<p>Now that we have the data imported, we need to convert the latitude and longitude columns to a PostGIS POINT geometry. The following SQL will update <em>the_geom</em> column in the landmarks table with a POINT geometry created from the row’s latitude and longitude columns. In order to create the POINT, the ST_GeomFromText method is used. This method allows us to create a POINT, which is a PostGIS geometry type, textually with the latitude and longitude values being specified in the construction of the type.</p>
<script src="https://gist.github.com/1584998.js?file=gistfile1.sql"></script>
<p>Here is a look at the data with the the_geom column populated with POINT data.</p>
<p><img src="https://cache.kevfoo.com/2012/01/landmark_data_with_geometry.png" alt="Geometry populated landmarks table" /></p>
<h1 id="step-4---run-some-queries">Step 4 - Run some queries</h1>
<p>Now that all the POINT geometries have been created for our landmark rows we can write some spatial queries against our data. Here are just a few of the types of queries we can run.</p>
<p>Using the ward geometry we have from a <a href="https://www.kevfoo.com/2011/12/Importing-Shapefiles-to-PostGIS-Consuming-Chicago-Open-Data/">previous example</a>, we can run a query that returns all the landmarks in the 43rd Ward.</p>
<script src="https://gist.github.com/1585171.js?file=gistfile1.sql"></script>
<p>This query returns the 5 closest landmarks to a given latitude and longitude :</p>
<script src="https://gist.github.com/1585140.js?file=gistfile1.sql"></script>
Favorite Links and Quotes of 20112012-01-02T00:00:00-06:00https://www.kevfoo.com/2012/01/Favorite-Links-and-Quotes-2011<p>A quest to find the name and url of a <a href="http://www.thewirecutter.com">gadget review site</a> that I had tweeted about a few months ago led me to the <a href="http://web.archive.org/web/20111228060934/http://www.twimemachine.com/">TwimeMachine</a> web app. TwimeMachine lets you read your old tweets and, while I was looking for the gadget website, I got a chance to take a stroll down memory lane and revisit some of my favorite links and quotes from the past year. I pulled out some of my favorites and have shared them below. While these lists aren’t comprehensive summaries of 2011, they do capture the things that I found interesting or thought-provoking over the past year.</p>
<p><picture><source type="image/webp" srcset="https://cache.kevfoo.com/2012/01/woody1.webp" /><source type="image/jpeg" srcset="https://cache.kevfoo.com/2012/01/woody1.jpg" /><img src="https://cache.kevfoo.com/2012/01/woody1.jpg" title="Woody Gutherie's `New Years Rulin's`" alt="Work More and Better" /></picture></p>
<h2 id="my-favorite-links-from-2011">My Favorite Links from 2011</h2>
<ul>
<li>
<p><a href="https://kottke.org/11/06/no-more-fish-in-the-sea">No more fish in the sea</a></p>
</li>
<li>
<p><a href="http://snarkmarket.com/2011/7108">“The correct use of a semicolon is a big red flag for me”</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=7chpllnU-To&feature=player_embedded">Canada’s new money is pretty amazing, eh?</a></p>
</li>
<li>
<p><a href="http://web.archive.org/web/20111016113546/pinchzoom.com/posts/oh-dear-how-to-seo-for-the-itunes-app-store/">How to SEO for the iTunes App Store</a></p>
</li>
<li>
<p><a href="http://youtu.be/Q1ZeXnmDZMQ">The Tragedy of Suburbia</a></p>
</li>
<li>
<p><a href="https://kottke.org/11/07/dean-martins-burger-recipe">Dean Martin makes a burger.</a></p>
</li>
<li>
<p><a href="https://open.spotify.com/album/48nTyXHuop42LKFhu4xiuE?highlight=spotify:track:45tZi79ORrYsJzNkOdknkX">Vitamin String Quartet – The Suburbs</a></p>
</li>
<li>
<p><a href="https://edevolution.wordpress.com/">Imaginative, provocative video from students on the need for (r)evolution in teaching and learning</a></p>
</li>
<li>
<p><a href="https://web.archive.org/web/20111217083757/www.nosh.com/404">404</a></p>
</li>
<li>
<p><a href="http://bit.ly/poIjmp">10 Unmissable TED Videos For Designers</a></p>
</li>
<li>
<p><a href="https://www.quora.com/Brogramming/How-does-a-programmer-become-a-brogrammer?q=brogra">How does one become a brogrammer?</a></p>
</li>
<li>
<p><a href="http://t.co/NncsHbq">Preschool: The Best Job-Training Program</a></p>
</li>
<li>
<p><a href="https://cache.kevfoo.com/2012/01/internet_high_five.jpg">Internet High Five</a></p>
</li>
<li>
<p><a href="http://t.co/XxOz0H7r">Shifting the Suburban Paradigm</a></p>
</li>
<li>
<p><a href="https://www.nytimes.com/wirecutter/">The Wirecutter</a></p>
</li>
<li>
<p><a href="http://web.archive.org/web/20200919094442/https://lettersofnote.com/2011/07/04/persist/">Persist - Letters of Note</a></p>
</li>
<li>
<p>German designer David Hanauer creates <a href="http://t.co/KmGuvewI">dizzying carpets from aerial Google Earth images</a>.x</p>
</li>
<li>
<p><a href="https://www.edwardtufte.com/tufte/dapp/">Tufte’s Data Analysis for Politics and Policy</a></p>
</li>
<li>
<p><a href="http://web.archive.org/web/20111218230519/https://yourhead.tumblr.com/post/3320228508/apples-three-laws-of-developers">Apple’s Three Laws of Developers</a></p>
</li>
<li>
<p><a href="http://t.co/BSalHGD4">“A Brief Rant on the Future of Interaction Design”</a></p>
</li>
<li>
<p><a href="https://foreignpolicy.com/2011/10/28/the-shadow-superpower/">The Shadow Superpower</a></p>
</li>
<li>
<p><a href="https://web.archive.org/web/20201026215131/http://chicagotype.com/">Chicago Type</a></p>
</li>
<li>
<p><a href="https://web.archive.org/web/20111212173329/https://www.thisiscolossal.com/2011/12/weapons-of-mass-creation/">Weapons of Mass Creation</a></p>
</li>
<li>
<p><a href="https://web.archive.org/web/20130307085222/http://iamadriansbrain.posterous.com/andy-swan-ok-but-there-are-two-rules">“OK, but there are two rules: 1. You can’t change the recipe. 2. You can’t do anything that anyone else has done before.”</a></p>
</li>
<li>
<p><a href="http://sivers.org/restrictions-will-set-you-free">Restrictions will set you free.</a></p>
</li>
<li>
<p><a href="http://www.cnn.com/2011/OPINION/02/13/greene.gracious.gesture/index.html">4 Star General, 5 Star Grace</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=lS0b4QCpFGc">Possibilians vs Agnostics by Dave Eagleman.</a></p>
</li>
<li>
<p><a href="http://sirkenrobinson.com/ideas-for-modern-living-passion/">Sir Ken Robinson on passion</a></p>
</li>
<li>
<p><a href="http://t.co/Kzw3vsc">How to write faster</a></p>
</li>
<li>
<p><a href="http://webtypography.net/">The Elements of Typography Style Applied to the Web</a></p>
</li>
<li>
<p><a href="http://web.archive.org/web/20111217052338/www.fontshop.com/blog/newsletters/april10c/indexEMAIL.html">Combining Type with Helvetica</a></p>
</li>
</ul>
<h2 id="my-favorite-quotes-from-2011">My Favorite Quotes from 2011</h2>
<ul>
<li>
<p>“I’m impressed with the people from Chicago. Hollywood is hype. New York is talk. Chicago is work.” -Michael Douglas</p>
</li>
<li>
<p>“The arts are not a way to make a living. They are a very human way of making life more bearable.” – Kurt Vonnegut</p>
</li>
<li>
<p>“Another flaw in the human character is that everybody wants to build and nobody wants to do maintenance.” – Kurt Vonnegut</p>
</li>
<li>
<p>@zachklein: Just told to me: “Schools are the coal-fired power plants of education.”</p>
</li>
</ul>
<h2 id="my-favorite-funny-tweets-from-2011">My Favorite Funny Tweets from 2011</h2>
<ul>
<li>
<p>@jonlech “Next year HTML5 will replace native apps” is the new “Next year will be the year of Linux on the desktop”.</p>
</li>
<li>
<p>@hubs: What doesn’t kill you makes you smaller. – Super Mario</p>
</li>
<li>
<p>@dcurtis: The Container Store is like a strip club for people with OCD.</p>
</li>
<li>
<p>@larrybraverman: GUYS IF EGYPT CAN TAKE DOWN MUBARAK WE CAN TAKE DOWN TICKETMASTER</p>
</li>
</ul>
2011 by the Numbers2012-01-01T00:00:00-06:00https://www.kevfoo.com/2012/01/2011-by-the-numbers<h2 id="apps-shipped--7">Apps Shipped : 7</h2>
<p>4 Android apps with one in the can set for release in a few weeks. 3 iOS apps. Not 100% sure what I can and can’t disclose due to client agreements, but I am very proud and happy with the apps that I helped produce last year. Lots more mobile work on the agenda for 2012.</p>
<h2 id="high-school-programming-courses-taught-1">High School Programming Courses Taught: 1</h2>
<p>In early 2011 I taught Android development to high schoolers participating in a pilot program for the 21st Century Youth Project. It was an amazing experience and I hope to work with them again in the future as the program matures.</p>
<h2 id="speaking-engagements--4">Speaking Engagements : 4</h2>
<p>I had the honor of being selected to speak at <a href="http://web.archive.org/web/20110602221446/http://windycitygo.org/videos2011/#5">WindyCityGo</a> and Chicago Code Camp. I also spoke to user groups in Rockford and Milwaukee.</p>
<h2 id="jobs-changed--1">Jobs Changed : 1</h2>
<p>In July I joined Redpoint Technologies and became a consultant for the first time in my career. While consulting isn’t for everyone, I have enjoyed the diversity of the engagements and clients.</p>
<h2 id="side-hustles--numerous">Side Hustles : Numerous</h2>
<p>Along with maintaining a few sets of <a href="http://www.github.com/kevinmcmahon">MonoTouch bindings</a>, I put together a <a href="http://web.archive.org/web/20130515104459/http://appdevwiki.com/wiki/show/HomePage">wiki for mobile app developers</a> and started working with <a href="http://web.archive.org/web/20140517102558/http://hoodiechicago.com/">some of the data</a> that the City of Chicago is publishing. I was able to help out some friends working on some stealth startups by offering technical advice and mentoring and have a few more tricks up my sleeve for the upcoming year.</p>
<h2 id="trips--7">Trips : 7</h2>
<p>In addition to usual trips to Minneapolis to visit the in-laws and the guys trip out to Phoenix for the PGA event, I was able to squeeze in a “business” trip to Las Vegas, a vacation to southern California, and attend an amazing wedding in Greece.</p>
<h2 id="lucky-guy-1">Lucky Guy: 1</h2>
<p>In reflecting back over the last year I am left feeling a tremendous amount of gratitude and appreciation for the amazing people, places and opportunities that I encountered over the past year. I am truly blessed.</p>
Importing Shapefiles to PostGIS - Consuming Chicago Open Data2011-12-29T00:00:00-06:00https://www.kevfoo.com/2011/12/Importing-Shapefiles-to-PostGIS-Consuming-Chicago-Open-Data<p>The most common spatial format that geographic data is distributed in is ESRI shapefiles. The geographic datasets provided at the City of Chicago’s <a href="http://data.cityofchicago.org">open data portal</a> are no different. In this post, I’m going to walk through converting an ESRI shapefile into SQL, importing that SQL into a PostGIS enabled PostgreSQL database, and querying the geographic data.</p>
<h1 id="prerequisites">Prerequisites</h1>
<p>Before we get started you will need to download and/or install the following data and software:</p>
<ol>
<li><a href="http://postgis.org/">PostGIS/PostgreSQL database</a></li>
<li><a href="http://www.gdal.org/index.html">The GDAL libraries</a></li>
<li>A dataset from the <a href="http://data.cityofchicago.org">City of Chicago Data Portal</a>. For this example I am going to use the <a href="https://data.cityofchicago.org/Facilities-Geographic-Boundaries/Boundaries-Wards-2015-/sp34-6z76">Ward data</a> provided by the city.</li>
<li><a href="http://www.pgadmin.org/download/">pgAdminIII</a> - A PostgreSQL admin and management tool that is optional but <em>highly</em> recommended.</li>
</ol>
<h1 id="step-1-shp-file-to-sql-file">Step 1: SHP file to SQL file</h1>
<p>The data published by the city uses a spacial reference system (SRS) called <em><a href="http://spatialreference.org/ref/esri/102671/">NAD 1983 StatePlane Illinois East FIPS 1201 Feet</a></em> to store data. This SRS falls into the <em>State Plane</em> category which is typically great for measurement and maps covering a limited geographic area like a state or city. For most contexts this is perfect but, if you want to use this spatial data with mapping layers or geocoding services from Google or Bing, it will cause problems. Mapping providers like Google and Microsoft Bing expect data to be stored using the WGS 84 (EPSG code 4326) SRS. This means that if we want to use latitude and longitude coordinates from these services or utilize their mapping layers we will have to convert our data from State Plane to WGS 84. Thankfully the GDAL tools provide a utility to transform the data.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ogr2ogr -f "ESRI Shapefile" -s_srs "EPSG:102671" -t_srs "EPSG:4326" Wards4326.shp Wards.shp
</code></pre></div></div>
<p>Once the shapefile has been converted from State Plane to WGS 84, we can then use the shp2pgsql tool to generate the SQL statements necessary to add the geography data to our database.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shp2pgsql -s 4326 -I -c -W UTF-8 Wards4326.shp wards > wards.sql
</code></pre></div></div>
<p>Here is a sample of one of the insert statements generated by the shp2pgsql with geometry column (the_geom) abbreviated:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="nv">"wards"</span> <span class="p">(</span><span class="nv">"data_admin"</span><span class="p">,</span><span class="nv">"perimeter"</span><span class="p">,</span><span class="nv">"ward"</span><span class="p">,</span><span class="nv">"alderman"</span><span class="p">,</span><span class="nv">"class"</span><span class="p">,</span><span class="nv">"ward_phone"</span><span class="p">,</span><span class="nv">"hall_phone"</span><span class="p">,</span><span class="nv">"hall_offic"</span><span class="p">,</span><span class="nv">"address"</span><span class="p">,</span><span class="nv">"edit_date1"</span><span class="p">,</span><span class="nv">"shape_area"</span><span class="p">,</span><span class="nv">"shape_len"</span><span class="p">,</span><span class="n">the_geom</span><span class="p">)</span> <span class="k">VALUES</span> <span class="p">(</span><span class="s1">'111078823.37393899'</span><span class="p">,</span><span class="s1">'98206.78600274'</span><span class="p">,</span><span class="s1">'20'</span><span class="p">,</span><span class="s1">'WILLIE COCHRAN'</span><span class="p">,</span><span class="s1">'1'</span><span class="p">,</span><span class="s1">'773-955-5610'</span><span class="p">,</span><span class="s1">'312-744-6840'</span><span class="p">,</span><span class="s1">'121 N LASALLE ST, RM 300 OFFICE 19, 60602'</span><span class="p">,</span><span class="s1">'6357 S COTTAGE GROVE'</span><span class="p">,</span><span class="s1">'20030527'</span><span class="p">,</span><span class="s1">'111200062.771999999'</span><span class="p">,</span><span class="s1">'98166.05290559999'</span><span class="p">,</span><span class="s1">'0106000020E6100...'</span></code></pre></figure>
<h1 id="step-2-insert-data-into-db">Step 2: Insert Data into DB</h1>
<p>Now that the dataset has been transformed and the SQL has been generated, it is time to add it to the database. You can do so by running the following command. (NOTE: you will have to swap in your own database name and user for the $DB_NAME and $DB_USER arguments).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psql -d $DB_NAME $DB_USER < wards.sql
</code></pre></div></div>
<h1 id="step-3-write-queries">Step 3: Write Queries</h1>
<p>With the data entered into the database we can now write queries against it. Here is a query that returns information about the ward that contains the point specified by latitude and longitude arguments.</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">SELECT</span> <span class="n">ward</span><span class="p">,</span> <span class="n">alderman</span>
<span class="k">FROM</span> <span class="n">wards</span>
<span class="k">WHERE</span> <span class="n">ST_Contains</span><span class="p">(</span><span class="n">the_geom</span><span class="p">,</span> <span class="n">ST_GeomFromText</span><span class="p">(</span><span class="s1">'POINT(41.927064 87.645658)'</span><span class="p">,</span> <span class="mi">4326</span><span class="p">));</span></code></pre></figure>
<p>The WHERE clause is calling the spatial relationship function ST_Contains. Spatial relationship functions in PostGIS take two input geometries and return either a boolean or another geometry. In our case, ST_Contains returns a boolean indicating if the point geometry passed as the second argument is contained in the geometry stored in the the_geom column of the wards table. The point geometry is constructed from another spatial function ST_GeomFromText. This function takes a textual representation of the geometry along with a spatial reference id (SRID) specifying the SRS that the geometry should be created in. In our case, we are using latitude and longitude values obtained from Google so we are creating the point geometry in the EPSG 4326 SRS.</p>
<h1 id="additional-resources">Additional Resources</h1>
<p>I found the following links useful as I started learning about PostGIS.</p>
<ul>
<li><a href="http://www.bostongis.com/PrinterFriendly.aspx?content_name=postgis_tut01">Boston GIS’s - Getting Started With PostGIS: An almost Idiot’s Guide</a></li>
<li><a href="http://www.kyngchaos.com/software/postgres">Kyng Chaos’ PostgreSQL, PostGIS, and pgRouting installers for Mac OS X</a></li>
<li><a href="http://www.postgis.us/page_buy_book">PostGIS in Action</a> book</li>
</ul>
Announcing the App Dev Wiki2011-10-08T00:00:00-05:00https://www.kevfoo.com/2011/10/app-dev-wiki<p><a href="http://www.appdevwiki.com/?utm_source=blog&utm_medium=post&utm_campaign=wiki">AppDevWiki</a></p>
<p>Earlier this week I put together a wiki to centralize the links, libraries and miscellania that I have been collecting regarding mobile development. I did this mainly for personal use and to provide a reference for coworkers and colleagues getting into mobile dev but hopefully others will find it useful. Check it out and feel free to add your favorite tools, libraries and links to it.</p>
MonoTouch Bindings2011-09-14T00:00:00-05:00https://www.kevfoo.com/2011/09/monotouch_bindings<p>Creating bindings for native, Objective-C based libraries can be somewhat confusing when first starting out. To help smooth the learning curve for those looking to create bindings (and hopefully share them) I have put together some links and an overview of the steps that I use.</p>
<p>As MonoTouch development is picking up steam again after the Novell/Xamarin deal I’ve noticed more and more people looking for help and information about just how to consume native libraries. Most of this information and the utility that auto-generates the code was gleaned from the #monotouch IRC channel, looking at other MonoTouch bindings and experimentation.</p>
<p>The core of this post was written in May 2011 when I was helping out a few folks with some binding issues. The general gist of the process still applies but somethings may have changed. Please let me know via comments or email if I am missing anything or if there are some corrections needed.</p>
<h2 id="useful-links">Useful Links</h2>
<ul>
<li><a href="http://web.archive.org/web/20111003133034/http://ios.xamarin.com/Documentation/Binding_New_Objective-C_Types">Binding Objective-C Types - MonoTouch Docs</a></li>
<li><a href="https://github.com/kevinmcmahon/monotouch-localytics">An Example Project</a></li>
</ul>
<h3 id="binding-steps">Binding Steps</h3>
<h4 id="step-1-create-the-binding-code">Step 1: Create the Binding Code</h4>
<p>The binding that you are going to call from your C#/MonoTouch code you will need to create a C# version You can manually write the bindings by hand or use a utility to help automate the process. There is a tool you can compile from the source found in this <a href="https://gist.github.com/512356">gist</a> that can help save some time. You will need to compile the tool using mcs and invoke it to generate the bindings like so :
mono ObjCParser.exe <FILENAME>.h
You can pass multiple header files associated with the Objective-C library that you are trying to bind. The tool generates three different .cs files which contain the C# bindings you can use. You will need to update the namespaces and the using statements as the files generated by the utility are using the MonoMac namespaces.</FILENAME></p>
<p>The tool isn’t perfect and there are known issues. Cleaning this tool up is on my todo list but extremely low since I know the quirks and it is just faster for me to generate and fix it up than to polish it.</p>
<p>These are the biggest issues that those using the tool should be aware of:</p>
<ol>
<li>Methods are included correctly in their corresponding C# classes but properties aren’t. You’ll need to grab the properties and stick them in the right classes.</li>
<li>If a method specified in a .h file spans multiple lines the tool can’t recognize that. When you go to compile you’ll get some errors.</li>
<li>Delegates can be tricky. When creating delegate mappings need to update and figure out how to handle the delegates. Due to the brute force nature of the generator tool, the delegates are typed as IntPtr’s when in reality they should be typed so it requires so hand updating to straighten the delegates out.</li>
<li>NSURLRequest in Objective-CObjective-C is NSUrlRequest in MonoTouch. There are a few places where the Objective-C classes don’t map exactly into MonoTouch classes due to naming convention issues.</li>
</ol>
<h4 id="step-2-make-the-dll">Step 2: Make the DLL</h4>
<p>Generate the bindings by invoking btouch and providing the relevant arguments for your files. The invocation looks something like this:<br />
/Developer/MonoTouch/usr/bin/btouch -out=<YOURNAMEHERE>.dll <YOUR_BINDING_CODE>.cs</YOUR_BINDING_CODE></YOURNAMEHERE></p>
<h4 id="step-3-reference-the-dll">Step 3: Reference the DLL</h4>
<p>Include a reference to the newly generated dll in your MonoTouch project. You will also need to include the static library in the project. For some reason it isn’t enough to just link to the path. You actually <em>need</em> to have the .a file included in the .csproj file. You can set the build action to None.</p>
<h4 id="step-4-update-the-additional-arguments">Step 4: Update the Additional Arguments.</h4>
<p>Finally, the MonoTouch project settings need to be updated so the Objective-C library is linked against during compile time. Update the Extra Arguments field in the iPhone Build settings under the Project Options.</p>
<p>You can read more in the official docs under the “Linking the Dependencies” section <a href="http://web.archive.org/web/20111003133034/http://ios.xamarin.com/Documentation/Binding_New_Objective-C_Types">here</a></p>
<p>These are the basic steps. It isn’t hard but it isn’t exactly straight forward. My best recommendation is to take a look at my fork of the <a href="https://github.com/kevinmcmahon/monotouch-localytics">Localytics project on github</a> and try and reproduce the bindings on your own.</p>
<h3 id="notes">Notes</h3>
<ul>
<li>
<p>There is a linker bug that Apple introduced in XCode 3.x that causes problems at compile time when trying to link in static libraries with THUMB enabled. You can run a command via the shell to check the dumped output. Make sure to look at line 5, second column. The two byte address is the give away that the library is THUMB) but I don’t have the command off the top of my head. The best way to handle this is to get the full Objective-C source and uncheck the THUMB code option. If you are still using XCode 3.2 then you <em>HAVE</em> to use a static library that has been compiled without THUMB code. The linker bug apparently has been fixed in XCode 4 and you should be able to link against both THUMB and non-THUMB libraries.</p>
</li>
<li>
<p>You also might want to look into making a fat static library. I do this for most of the bindings I publish so that there is only one static library to link against and you don’t have to hassle with using one library which has been compiled for use with the simulator and one that has been compiled for the device. Checkout this <a href="http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4">Stack Overflow question</a> for more details on how to make a fat static library.</p>
</li>
</ul>
<p><em>Updated Decemeber 28, 2011</em> : Added link to fat static library instructions.
<em>Updated August 3, 2020</em> : This information is widely out of date and not current. Culled deadlinks but left this post for posterity.</p>
Mono for Android talk at WindyCityGo2011-05-08T00:00:00-05:00https://www.kevfoo.com/2011/05/windy-city-go-mono-for-android<p>Here is the video and the slides from the <a href="http://web.archive.org/web/20110511232020/http://mono-android.net/">Mono for Android</a> talk I gave last month at <a href="http://web.archive.org/web/20110503224553/http://windycitygo.org/">WindyCityGo</a>.</p>
<iframe src="http://player.vimeo.com/video/22402266?title=0&byline=0&portrait=0&color=007ECE" frameborder="0"></iframe>
<p><a href="http://vimeo.com/22402266">.NET on Android? MonoDroid Does by Kevin McMahon</a> from <a href="http://vimeo.com/chicagoruby">ChicagoRuby</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<div style="width:425px" id="__ss_7578318"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/klmcmahon/mono-for-android" title="Mono for Android?">Mono for Android?</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/7578318" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe> <div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/klmcmahon">Kevin McMahon</a> </div> </div>
Quick Primer: Cocoa and Cocoa Touch for .Net Developers2011-05-04T00:00:00-05:00https://www.kevfoo.com/2011/05/quick-primer-cocoa-and-cocoa-touch<h2 id="cocoa-and-cocoa-touch">Cocoa and Cocoa Touch</h2>
<p>At their core Cocoa and Cocoa Touch are composed of two frameworks. The Foundation Kit provides the core, non-UI classes and, depending on whether you’re developing apps for OS X or iOS, AppKit or UIKit respectively provides the core UI classes. For data the Core Data framework is available.</p>
<h2 id="foundation">Foundation</h2>
<p>The Foundation framework provides the core, non-UI classes that all Cocoa and Cocoa Touch applications are built upon. Basic object behavior, memory management, object mutability, and notifications are all defined among the 80 classes that comprise the framework.</p>
<p>This is the framework that provides the root class NSObject. Much like System.Object, NSObject is the class that all other classes are derived from. NSObject provides the nuts and bolts which enable object-oriented functionality and behaviors in Objective-C. You will also find in Foundation classes like NSString, NSArray, and NSThread or data types, collections, and operating system abstractions respectively.</p>
<h2 id="appkit-os-x--uikit-ios">AppKit (OS X) / UIKit (iOS)</h2>
<p>AppKit for Cocoa and its sister framework UIKit for Cocoa Touch provide the windows, dialogs, buttons, menus, scrollers, textfields and all the other UI elements available to developers. The AppKit framework is used for desktop applications and the UIKit for iOS applications.</p>
<p>The Window, View, and Application classes provided in the AppKit and UIKit framework define the core functionality of app. Each class plays a crucial role in displaying the user interface and responding to the various user events generated while the program is executing. These three classes, constitute what is considered to be the core program framework since all apps derive their functionality and behaviors from them.</p>
<p>While both frameworks share many of the same types and perform similar tasks, having separate frameworks does provide some benefit. Having two, specific frameworks makes it easier to provide device implementations for UI controls when called for (e.g. handling motion or touch gestures) but remain conceptually similar when it comes to behavior (e.g. window, view, and application class relationship), and it eliminates an unnecessary dependency that would be created by lumping desktop and iOS specific code together into a single framework.</p>
<p>It is easy to tell the difference between Cocoa and Cocoa Touch classes. Classes that are found in Cocoa are prefixed with an NS (e.g. NSWindow, NSView), and Cocoa Touch classes are prefixed with UI (e.g. UIWindow, UIView).</p>
<h2 id="delegates">Delegates</h2>
<p>The Cocoa framework leverages the delegate pattern to allow an object’s behavior to be modified without having to resort to subclassing. Unlike C#, which includes a delegate language element, the concept of delegates come to Cocoa purely via the implementation of the delegate design pattern and not a mix language features and pattern.</p>
<p>A delegate in Cocoa is an object that implements some or all of a protocol (interface in Objective-C speak) and is assigned to another object to handle events. Classes using delegates typically define a contract via a protocol which provides the level of indirection needed to decouple the caller object and the delegate object. It is common practice in Cocoa to define all the methods in a delegate protocol as optional. This allows the selective implementation of event handlers for the events needing custom behaviors.</p>
<h2 id="notifications">Notifications</h2>
<p>Notifications in Cocoa provide a mechanism for decoupled message passing. Communication between objects is done by passing messages to and from objects. In order for the communication to take place, the sender must know about the receiver, and this knowledge of the other object introduces tight coupling between the two. Notifications remove the coupling by providing a one-to-many broadcast mechanism which allows objects to register with a notification center as notifications posters, observers, or both. When a notification is posted by an object, the notification center acts as a dispatcher and invokes any callback methods registered with the notification center for that particular notification.</p>
<p>Notifications sound very similar to delegates, but they differ in three key areas. First, there is no limit to the number of objects that can receive notifications. In a delegate scenario, only the delegate object associated with the host object can respond to events. Second, an object can register for any number of notification from the notification center and not just the methods defined by the delegate protocol. Finally, an object posting a notification does not require another object to invoke methods. It can simply fire the notification and not worry about whether there is an observer on the other end to respond.</p>
<h2 id="model-view-controller">Model-View-Controller</h2>
<p>You will find many different design patterns being employed in the Cocoa framework but none are as pervasive as the Model-View-Controller (MVC) pattern. In this pattern there are three possible stereotypes that an object can take on. Model objects encapsulate data and behaviors; view objects handle display and user interaction; and controller objects bind the data and the objects together.</p>
<p>The benefits realized from the pattern come from the separation of concerns imposed on applications. Interactions between layers occur via well-defined interfaces, which provides the abstraction necessary to limit the impact of code changes, and the code is easier to reuse since the interfaces and API are well-defined.</p>
<p>It is important to note that Cocoa does bleed the line between some of the layers in the framework. The concept of the view-controller is prevalent in both Cocoa and Cocoa Touch, and there are some instances in Cocoa where there are model-controllers. When these roles are combined into a single class, the controller takes on the responsibilities that come with owning the second role and still provides the bridge to the third role. So in the case of a UITableViewController, the controller is responsible not only for its traditional role of tying the model with the view but also for how that data looks in the table which is typically the view’s role.</p>
<h2 id="links">Links</h2>
<ul>
<li>
<p><a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals">Cocoa Fundamentals Guide from Apple</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/13704460-cocoa-programing-fundamentals-with-dvd?ac=1&from_search=true&qid=KfBi89Rnfm&rank=1">Cocoa Programming Developer’s Handbook</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/3048265-cocoa-design-patterns">Cocoa Design Patterns</a></p>
</li>
</ul>
Monotouch: Facebook Binding Updates2010-12-12T00:00:00-06:00https://www.kevfoo.com/2010/12/monotouch-facebook-binding-updates<p>UPDATE (12/30/2011) : Facebook Connect bindings are being maintained by the <a href="https://github.com/mono/monotouch-bindings">Mono Project </a></p>
<p>UPDATE (8/3/2020) : This information is widely out of date and not current. Facebook Xamarin bindings were moved to <a href="https://github.com/mono/monotouch-bindings/tree/main/facebookios">facebookios</a>.</p>
<p>I pushed some updates to the <a href="https://github.com/kevinmcmahon/monotouch-facebook/">monotouch-facebook</a> project recently to eliminate potential linking problems that could come up. Apple introduced a bug in their linker that causes problems when linking non-Thumb MonoTouch code with Thumb code. The default settings for the <a href="https://github.com/facebook/facebook-ios-sdk">Facebook iOS SDK</a> project will produce a static library with Thumb code so I forked the project and turned off Thumb code generation.</p>
<p>I also made the fat static Facebook library a little fatter by linking the simulator version of the library (i386 architecture) in along with the iPhone and iPad library (armv6 and armv7 architectures). This fatter library doesn’t require managing and maintaining two versions of the library for the different architectures.</p>
<p>The bindings have been refreshed due to the Facebook iOS SDK being updated recently. You can see the changes reflected in the example app. The biggest change introduced by the update is the app’s Info.plist now needs to be modified to register a custom URL Scheme that will enable the example app to be called back after the user authenticates with Facebook. You can see what the Info.plist should look like in the picture below. You will need to replace <em>*<<<YOURAPPIDHERE>>>*</em> with your real app id.</p>
<p><img src="https://cache.kevfoo.com/2010/12/mtfb_plist.png" alt="" /></p>
<p>I hope to get this info up on the monotouch-facebook project’s wiki page soon. If you find any issues please open them on the project’s issue tracker. Enjoy.</p>
MonoTouch: Dropbox via DropNet, JSON.Net and RestSharp2010-11-19T00:00:00-06:00https://www.kevfoo.com/2010/11/monotouch-dropbox-via-dropnet-json-net-and-restsharp<p><em>MonoTouch-ified code on github: <a href="https://github.com/DropNet/DropNet">DropNet</a></em>, <em><a href="https://github.com/kevinmcmahon/RestSharp">RestSharp</a></em>, <em><a href="https://github.com/chrisntr/Newtonsoft.Json">JSON.Net</a></em></p>
<p>Recently I wanted to add Dropbox access to a MonoTouch app that I am working on and came across the <a href="https://github.com/DropNet/DropNet">DropNet</a> project. DropNet abstracts away all the plumbing involved to interact with Dropbox via their REST-based API and provides an easy-to-use C# API.</p>
<p>In order to be able to use DropNet in a MonoTouch app, a few of the libraries that DropNet depends on, RestSharp and JSON.Net, had to be ported so that they too would build and run on MonoTouch. Thankfully, the process of getting the DropNet dependencies ported didn’t require a lot of work. Chris Hardy had already done the <a href="https://github.com/chrisntr/Newtonsoft.Json">JSON.Net</a> port and John Sheehan, the maintainer of <a href="http://www.restsharp.org/">RestSharp</a>, had already begun the process of porting RestSharp.</p>
<p>After making a few changes to get RestSharp to compile and adding a MonoTouch-specific project to DropNet, I was able to access my Dropbox via my MonoTouch app. Very cool and very easy thanks to the open source contributions made by the C# and MonoTouch communities. I hope to post a DropNet demo app soon. In the mean time, you can grab the libraries I used from these repositories: <a href="https://github.com/DropNet/DropNet">DropNet</a>, <a href="https://github.com/kevinmcmahon/RestSharp">RestSharp</a>, and <a href="https://github.com/chrisntr/Newtonsoft.Json">JSON.Net</a>.</p>
<p>If you’re interested in just DropNet for MonoTouch, pre-compiled binaries of RestSharp and JSON.Net are already included in the repository. Enjoy!</p>
Chicago Alt.Net MonoDroid Talk2010-11-11T00:00:00-06:00https://www.kevfoo.com/2010/11/chicago-alt-net-monodroid-talk<div id="__ss_5722575" style="width: 425px"><object id="__sse5722575" height="355" width="425"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=monodroid101-101109223452-phpapp02&stripped_title=net-monodroid-does&userName=klmcmahon" /><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><embed name="__sse5722575" allowfullscreen="true" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=monodroid101-101109223452-phpapp02&stripped_title=net-monodroid-does&userName=klmcmahon" allowscriptaccess="always" type="application/x-shockwave-flash" height="355" width="425" /></embed></object> <div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px">
View this <a href="http://www.slideshare.net/klmcmahon/net-monodroid-does">presentation</a> and <a href="http://www.slideshare.net/klmcmahon">others</a> on Slideshare.</div> </div>
<p>Wednesday I had the pleasure of introducing MonoDroid to the developers at the <a href="http://chicagoalt.net">Chicago Alt.Net</a> meetup. We had a great turn out at the meeting and there was a lot of interest both the Android platform and the MonoDroid tools. The talk consisted of the slides above and some code examples (both <a href="https://github.com/kevinmcmahon/MonoDroid101">available on github</a>) and was intended to show off the awesome work that the MonoDroid team at Novell has done in bringing C# to Android.</p>
<p>If anyone is interested in learning more about Android and developing apps in C# for Android, I strongly recommend you head over to the <a href="http://web.archive.org/web/20101102010150/http://monodroid.net/">project website</a> and sign-up for a preview invite. The MonoDroid team is inviting more developers to participate in the preview with each new release so now is as good a time as any to get signed up.</p>
Ubuntu Tip : Broadcom 43xx Wireless Setup2010-11-03T00:00:00-05:00https://www.kevfoo.com/2010/11/ubuntu-tip-broadcom-43xx-wireless-setup<p>Recently I installed Ubuntu on my windows machine using the latest <a href="http://www.ubuntu.com/desktop/get-ubuntu/windows-installer">Wubi</a> installer and ran into an issue getting my Dell XPS M1530 laptop’s wireless adapter up and operational. My particular flavor of the M1530 uses the <em>Dell Wireless 1505 Draft 802.11n WLAN Mini-Card</em> wireless adapter. This adapter is built on the the Broadcom 43xx series of chips (in my case the Broadcom 4328) and unfortunately these devices don’t work with Ubuntu 10.10 out-of-the-box.</p>
<p>To fix this, grab an ethernet cable and get on the your network the wired way. Pop open a terminal window and run:</p>
<pre>~$ sudo apt-get install bcmwl-kernel-source<br />~$ sudo modprobe -r b43 ssb wl<br />~$ sudo modprobe wl</pre>
<p>This should get the wireless adapter up and running and now you can use <a href="http://ubuntuforums.org/showthread.php?t=1425727">these instructions</a> from the Ubuntu forums to connect to a wireless access point.</p>
<p>Hope this helps.</p>
Monotouch: Flurry Demo App2010-10-12T00:00:00-05:00https://www.kevfoo.com/2010/10/monotouch-flurry-demo-app<p><em>Github repository can be found here: <a href="http://github.com/kevinmcmahon/FlurryTestApp">Flurry Test App</a>.</em></p>
<p>Recently a request was made for a working example of a Monotouch app that uses the <a href="https://kevfoo.com/2010/08/monotouch-flurry-analytics-bindings/">Flurry bindings</a>, so I put one together. The sample, while trivial, shows the basics required to incorporate the Flurry SDK Objective-C libraries and their bindings into a Monotouch app and provide an example call pattern that shows how to use the Flurry API.</p>
<p>There are a couple of things to note about this demo app. You’ll need to register with Flurry and get a unique application key to use with the test app. In the example code, you will see that I have some comments about providing a real debug and release application key. A best practice is to have two separate keys so you can separate your production analytics from your test analytics. When testing your app, the app uses the testing application key. The release application key would replace the test key when submitting to the App Store.</p>
<p>The latest Flurry SDK works with iOS version 4.1. If you are looking to have your app run on devices running iOS 3.x, you can either add linker flags as additional arguments or use an older version (2.6) of the Flurry SDK. I plan on updating the demo example to include the linker flags needed to get 3.x support with the latest Flurry SDK.</p>
<p>Finally, I have included a line of code in the example that appears extraneous and out of place. This line ensures Monotouch does not remove an implicit dependency that Flurry has. Monotouch aggressively prunes unused code from assemblies to shrink the size of apps. This trivial demo app does not make explicit use of anything from a part of the CoreGraphics library that Flurry needs (something which almost all non-trivial iOS apps will do by necessity) so it gets excised during compilation.</p>
<p>I hope this helps get people started. Flurry analytics are straightforward to use and provides valuable data and insights. Please let me know if you have any questions or comments on the example.</p>
Book Review: Tapworthy2010-08-27T00:00:00-05:00https://www.kevfoo.com/2010/08/book-review-tapworthy-2<h2 id="the-book">The Book</h2>
<p><a href="https://www.goodreads.com/book/show/7709542-tapworthy">Tapworthy: Designing Great iPhone Apps</a> by Josh Clark</p>
<blockquote>"Designing a tapworthy app means designing for an economy of time, attention, and space."</blockquote>
<h2 id="the-review">The Review</h2>
<p>Tapworthy is an indispensable resource for developers trying to make quality mobile applications for all platforms. While the book uses iPhone apps and user interface elements to illustrate the concepts discussed, many of the observations made about the mechanics, human interactions, and the psychology of what makes great applications are applicable to all mobile applications.The book does a good job of exploring the user interface elements found in iOS apps and provides a good summary of how and why you would use the elements in your designs. The coverage of this material is quite good but pales in comparison to the exploration of what goes into making a tapworthy app. It is in this exploration of what makes a tapworthy app that I got my two main takeaways from the book.The first is to be ruthless when cutting scope and narrowing the focus of your app. To help developers and designers do this, a series of questions are provided and discussed.</p>
<p>It is amazing how quickly issues with your design arise when your ideas are subjected to simple questions like:</p>
<ul>
<li>What does your app do and <em>why</em>?</li>
<li>What specific problem does your app uniquely solve for users?</li>
<li>What makes this app mobile?</li>
<li>What mobile context are you designing for?</li>
<li>Why would you use this app when you are away from your computer?</li>
</ul>
<p>These questions are straightforward and may seem somewhat obvious, but they are an easy way to vet ideas. If you can’t quickly provide a compelling answers for these questions, then you might not have a best selling app idea. Conversely, the questions can be used to identify weaknesses that might need more attention and potentially turn a good idea into a great one.The second takeaway was to recognize the three mobile contexts: microtasking, local, and boredom. To make a good app, you need to tightly wrap the answers to the 5Ws (who is the user, what are they doing, why are they doing it on a mobile, where are they, and when are they doing it) around one or more of these. Something common among the most successful apps is how easy it is to identify the answers to the 5W questions plus which mobile context it applies to. When the scope of an app is perfectly tailored to a specific scenario or use case, users will find that every tap has a payoff and accomplishing tasks within the app will seem effortless. Efficiency becomes a feature.</p>
<h2 id="the-summary">The Summary</h2>
<p>There is so much to like about this book. Tapworthy forces us to ask the right questions in designing mobile apps and provides invaluable tips and insights about maximizing your app’s user experience. If you are serious about making good apps, do yourself a favor and pick this book up. You won’t regret it.</p>
Monotouch: Facebook iOS SDK Bindings2010-08-18T00:00:00-05:00https://www.kevfoo.com/2010/08/monotouch-facebook-ios-sdk-bindings<p>UPDATE (12/30/2011) : Facebook Connect bindings are being maintained by the <a href="https://github.com/mono/monotouch-bindings/">Mono Project</a>
UPDATE (8/3/2020) : This information is widely out of date and not current. Facebook Xamarin bindings were moved to <a href="https://github.com/mono/monotouch-bindings/tree/main/facebookios">facebookios</a>.</p>
<p><em>The MonoTouch libraries can be <a href="http://github.com/kevinmcmahon/monotouch-facebook">found on github</a>.</em></p>
<p>It’s late and I am tired so I’ll be brief. I just pushed the binding of official <a href="http://github.com/facebook/facebook-ios-sdk">Facebook iOS SDK</a> to github. In addition to the bindings, the DemoApp, which was included along side the SDK, has been ported to MonoTouch. The code is straightforward and does a good job of getting you up to speed with the important elements of the SDK.</p>
<p>If you want to run the demo application, you will have to create a Facebook application on Facebook and grab the app id. You need the app id to pass back to Facebook during the login process and you can find out where to put it by looking through the comments of the DemoAppViewController class.</p>
<p>Please let me know if you have any issues with this library. Enjoy.</p>
Monotouch: Flurry Analytics Bindings2010-08-06T00:00:00-05:00https://www.kevfoo.com/2010/08/monotouch-flurry-analytics-bindings<p><em>The MonoTouch libraries can be found on my <a href="http://github.com/kevinmcmahon/monotouch-libs">fork of monotuch-libs on GitHub</a></em></p>
<p><em><strong>For an example of how to build and use the binding check out this <a href="https://github.com/kevinmcmahon/FlurryTestApp">example project on github</a>.</strong></em></p>
<p>For my <a href="http://www.tribeguides.com">latest app</a> I wanted to collect some analytics to see which screens and content included in the app are the most useful to users. In evaluating my mobile analytics options, Flurry’s free of charge service and easy to use API made the decision to go with them over Google Analytics and Mixpanel an easy one.Integrating Flurry analytics into your app is pretty easy but there is a small amount of setup to be done prior. You will need to grab the iPhone SDK from Flurry and also register your application with them to get an API key. The API key is unique to your application and provides Flurry a way to know who is sending events and notifications back to them to log. The SDK includes a couple header files and the Objective-C libraries you’ll need to include and link against in your MonoTouch projects. Once the Flurry libraries and the <a href="http://github.com/kevinmcmahon/monotouch-libs">MonoTouch bindings</a> have been obtained and an API key has been generated, we can integrate them into MonoTouch application and start collecting data.Initiating data collection is as simple as calling the StartSession method with the application’s API key. That is all it takes to start collecting basic information about your app like the amount of times it has been launched, the number of different users using it, and other bits of info like hardware version and service provider.Here is some code showing how to turn analytics on.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MonoTouch.Foundation</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MonoTouch.UIKit</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Flurry</span><span class="p">;</span> <span class="c1">// include the MonoTouch binding lib</span>
<span class="k">namespace</span> <span class="nn">Foo</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">Register</span> <span class="p">(</span><span class="s">"AppDelegate"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AppDelegate</span> <span class="p">:</span> <span class="n">UIApplicationDelegate</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">FlurryApiKey</span> <span class="p">=</span><span class="s">"..."</span><span class="p">;</span>
<span class="k">public</span> <span class="k">override</span> <span class="kt">bool</span> <span class="nf">FinishedLaunching</span> <span class="p">(</span><span class="n">UIApplication</span> <span class="n">app</span><span class="p">,</span> <span class="n">NSDictionary</span> <span class="n">options</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 1 liner to kick the session off</span>
<span class="n">Flurry</span><span class="p">.</span><span class="n">FlurryAPI</span><span class="p">.</span><span class="nf">StartSession</span><span class="p">(</span><span class="n">FlurryApiKey</span><span class="p">);</span>
<span class="c1">// Do you app thing here...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>If more detailed analytics are desired, there are additional methods available to help you glean whatever meaningful information you need from your application. The example below shows how you can automatically track the changes in navigation within a tab bar. When you pass a UINavigationController or a UITabBarController to the CurrentPageViews method, each navigation action as you move from one view or tab to another gets tracked. This is ideal for seeing which parts of your app users are interacting with the most.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MonoTouch.Foundation</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MonoTouch.UIKit</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Flurry</span><span class="p">;</span> <span class="c1">// include the MonoTouch binding lib</span>
<span class="k">namespace</span> <span class="nn">Foo</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">Register</span> <span class="p">(</span><span class="s">"AppDelegate"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AppDelegate</span> <span class="p">:</span> <span class="n">UIApplicationDelegate</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">FlurryApiKey</span> <span class="p">=</span><span class="s">"..."</span><span class="p">;</span>
<span class="k">public</span> <span class="k">override</span> <span class="kt">bool</span> <span class="nf">FinishedLaunching</span> <span class="p">(</span><span class="n">UIApplication</span> <span class="n">app</span><span class="p">,</span> <span class="n">NSDictionary</span> <span class="n">options</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 1 liner to kick the session off</span>
<span class="n">FlurryAPI</span><span class="p">.</span><span class="nf">StartSession</span><span class="p">(</span><span class="n">FlurryApiKey</span><span class="p">);</span>
<span class="c1">// create a navigatable controller to watch...</span>
<span class="n">UITabBarController</span> <span class="n">tabBarController</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">UITabBarController</span><span class="p">();</span>
<span class="c1">// ...and pass it to CurrentPageViews.</span>
<span class="n">FlurryAPI</span><span class="p">.</span><span class="nf">CurrentPageViews</span><span class="p">(</span><span class="n">tabBarController</span><span class="p">);</span>
<span class="c1">// Do you app thing here...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>I am touching on just a few things that you can do with Flurry. The SDK docs describe a number of other things you can do like creating and logging custom events and tracking how much time is spent performing activities. I’d recommend checking them out after you download the libraries.I’ve just started collecting analytics during beta testing and things appear to working smoothly.</p>
<p>If you find any issues with the MonoTouch bindings please let me know. Enjoy!</p>
Book Review: Professional Android 2 Application Development2010-05-26T00:00:00-05:00https://www.kevfoo.com/2010/05/book-review-professional-android-2-application-development<p><a href="https://cache.kevfoo.com/2010/05/pa2ad.jpg"><img src="https://cache.kevfoo.com/2010/05/pa2ad_thumb.jpg" alt="pa2ad" title="pa2ad" /></a></p>
<p>Reto Meier’s <a href="https://www.goodreads.com/book/show/7807709-professional-android-2-application-development">Professional Android 2 Application Development</a> does a great job of educating the reader on what is necessary for developing Android applications. Core application fundamentals and advanced Android features are presented to the reader via a series of real-life application examples. Readers who work the examples and leverage the breadth of coverage provided on the Android 2 framework will be well on their way towards establishing an excellent foundation for building compelling Android applications.</p>
<p>After giving the customary history lesson and tour of the toolsets and development environment, the first half of the book deals with the components that make up an application. Chapters are devoted to activities and life cycle, UI views, data and file access, and key Android concepts like intents (message-passing mechanisms) and content providers (ability to expose data to other apps).</p>
<p>Once the fundamentals have been covered, the remainder of the book is spent exploring the advanced and optional features available in the SDK. Android-centric concepts like background services, notifications and widgets are given the same in depth treatment given to the fundamentals so you get a lot of the “why” along with the how. Features and functionality that naturally lend them to being abstracted away via an API like media playback, GPS and Bluetooth are given adequate coverage of what is available via the framework and, where pertinent, the Android-specific behaviors are highlighted.</p>
<p>While the book is divided into fundamentals and advanced topics, continuity is maintained throughout via the example applications used to emphasize the concepts presented. By building the to-do list, compass, and earthquake monitoring service applications while reading through the book, passive learning is transformed into active, directed learning. Unlike some programming books I’ve read in the past, these examples do real work, accomplish specific real-life tasks, and provide some utility once completed. Readers are not just extending these applications one feature at a time, they are also adding one key concept to their proverbial tool belt while doing it.</p>
<p>The speed at which the Android platform is being evolved and improved upon is staggering. Since April 2009 there has been 4 major releases of the Android OS with a 5th release rumored for this fall. While the breadth of coverage provided for the APIs and framework is nice while it is still relevant, the real strength of this book comes building an understanding of the core Android fundamentals. Readers looking for a solid introduction to Android development need to look no further than <a href="https://www.goodreads.com/book/show/7807709-professional-android-2-application-development">Professional Android 2 Application Development</a>.</p>
Retargeting Visual Studio project files with PowerShell2010-05-06T00:00:00-05:00https://www.kevfoo.com/2010/05/retargeting-visual-studio-project-files-with-powershell<p>I originally set out to write this post a little over a year ago. Back then I threw together a script to retarget all the project files from .Net 2.0 to .Net 3.5 for my previous company and recently I found myself having to a similar upgrade from .Net 3.5 to 4.0. I ended up using the same script again so I figured I’d go ahead and publish it.</p>
<p>The upgrade process done by Visual Studio on your current projects and solutions will only migrate the file format to the newest schema and will not retarget the framework to the latest version. That is where the script I’ve included below comes in. It will retarget all the C# project files found under the path provided to version 4.0 of the .Net framework.</p>
<p>C# project files are XML based and navigating the DOM with Linq to XML is a cinch but there are a couple small but important steps that the script needed to include. First, you need to append the namespace to the individual element names or else the elements will not be able to be found. Second, when saving the modified XDocument, a XmlWriterSetting instance needs to be instantiated and the OmitXmlDeclaration property set to true. Setting this property to true will make sure the XML that we save will be considered a valid project file by Visual Studio.</p>
<p>I’ve included the full script below as well as created a gist that can be found <a href="http://gist.github.com/391885">here</a>. It is important to note that this script will edit all the csproj files found under the directory specified in the path variable. Make sure you backup these files or have them under source control prior to running the script. Enjoy.</p>
<p><em>UPDATE: Added change suggested in comments by Jeffery Snover</em></p>
<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="p">[</span><span class="n">Reflection</span><span class="p">.</span><span class="n">Assembly</span><span class="p">]::</span><span class="nf">LoadWithPartialName</span><span class="p">(</span><span class="s">"System.Xml.Linq"</span><span class="p">)</span> <span class="p">|</span> <span class="n">Out</span><span class="p">-</span><span class="n">Null</span><span class="err">#</span><span class="n">specify</span> <span class="n">the</span> <span class="n">root</span> <span class="n">of</span> <span class="n">your</span> <span class="n">source</span> <span class="n">tree</span> <span class="n">below</span><span class="err">$</span><span class="n">path</span> <span class="p">=</span> <span class="s">"C:\Code\chatsworth"</span><span class="err">$</span><span class="n">ns</span> <span class="p">=</span> <span class="s">"http://schemas.microsoft.com/developer/msbuild/2003"</span><span class="err">$</span><span class="n">xname</span> <span class="p">=</span> <span class="p">[</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XName</span><span class="p">]::</span><span class="nf">Get</span><span class="p">(</span><span class="s">"PropertyGroup"</span><span class="p">,</span><span class="err">$</span><span class="n">ns</span><span class="p">)</span><span class="err">$</span><span class="n">tfname</span> <span class="p">=</span> <span class="p">[</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XName</span><span class="p">]::</span><span class="nf">Get</span><span class="p">(</span><span class="s">"TargetFrameworkVersion"</span><span class="p">,</span><span class="err">$</span><span class="n">ns</span><span class="p">)</span> <span class="err">$</span><span class="n">xws</span> <span class="p">=</span> <span class="n">New</span><span class="p">-</span><span class="n">Object</span> <span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">XmlWriterSettings</span><span class="err">$</span><span class="n">xws</span><span class="p">.</span><span class="n">OmitXmlDeclaration</span> <span class="p">=</span> <span class="err">$</span><span class="k">true</span><span class="err">$</span><span class="n">xws</span><span class="p">.</span><span class="n">Indent</span> <span class="p">=</span> <span class="err">$</span><span class="n">truefunction</span> <span class="nf">updatefx</span><span class="p">(</span><span class="err">$</span><span class="n">filename</span><span class="p">){</span> <span class="err">#</span><span class="n">Write</span><span class="p">-</span><span class="n">Host</span> <span class="err">$</span><span class="n">filename</span> <span class="err">$</span><span class="n">xml</span> <span class="p">=</span> <span class="p">[</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">Linq</span><span class="p">.</span><span class="n">XDocument</span><span class="p">]::</span><span class="nf">Load</span><span class="p">(</span><span class="err">$</span><span class="n">filename</span><span class="p">)</span> <span class="err">$</span><span class="n">result</span> <span class="p">=</span> <span class="err">$</span><span class="n">xml</span><span class="p">.</span><span class="nf">Descendants</span><span class="p">(</span><span class="err">$</span><span class="n">xname</span><span class="p">)</span> <span class="k">foreach</span> <span class="p">(</span><span class="err">$</span><span class="n">i</span> <span class="k">in</span> <span class="err">$</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span> <span class="err">$</span><span class="n">fxelem</span> <span class="p">=</span> <span class="err">$</span><span class="n">i</span><span class="p">.</span><span class="nf">Element</span><span class="p">(</span><span class="err">$</span><span class="n">tfname</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="err">$</span><span class="n">fxelem</span><span class="p">)</span> <span class="p">{</span> <span class="err">$</span><span class="n">i</span><span class="p">.</span><span class="nf">SetElementValue</span><span class="p">(</span><span class="err">$</span><span class="n">tfname</span><span class="p">,</span><span class="s">"v4.0"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="err">$</span><span class="n">xw</span> <span class="p">=</span> <span class="p">[</span><span class="n">System</span><span class="p">.</span><span class="n">Xml</span><span class="p">.</span><span class="n">XmlWriter</span><span class="p">]::</span><span class="nf">Create</span><span class="p">(</span><span class="err">$</span><span class="n">filename</span><span class="p">,</span> <span class="err">$</span><span class="n">xws</span><span class="p">)</span> <span class="err">$</span><span class="n">xml</span><span class="p">.</span><span class="nf">Save</span><span class="p">(</span><span class="err">$</span><span class="n">xw</span><span class="p">)</span> <span class="err">$</span><span class="n">xw</span><span class="p">.</span><span class="nf">Close</span><span class="p">()}</span><span class="err">$</span><span class="n">csprojs</span> <span class="p">=</span> <span class="n">Get</span><span class="p">-</span><span class="n">ChildItem</span> <span class="err">$</span><span class="n">path</span> <span class="p">*.</span><span class="n">csproj</span> <span class="p">-</span><span class="nf">Recurseforeach</span> <span class="p">(</span><span class="err">$</span><span class="n">file</span> <span class="k">in</span> <span class="err">$</span><span class="n">csprojs</span><span class="p">){</span> <span class="n">updatefx</span> <span class="err">$</span><span class="n">file</span><span class="p">.</span><span class="n">FullName</span><span class="p">}</span></code></pre></figure>
6 Thoughts about Evernote's App Design2010-04-14T00:00:00-05:00https://www.kevfoo.com/2010/04/6-thoughts-about-evernotes-app-design<p>Evernote is one of my favorite and most frequently used applications. The ubiquity of the service via all the devices in my life (laptop, desktop, iPhone, Nexus One, web, and iPad) and the ease in which I can capture and recall notes made adopting it into my daily workflow extremely easy. By taking a closer look at the clients, it became clear that the user experience, and the design decisions behind them, wasn’t a happy accident.</p>
<p>Lately I have been thinking about user design and experience as I prep my app for the App Store. Here are six things that stood out most about the Evernote iPhone app and the take-away ideas I got from looking closer at the app.</p>
<h3 id="1-the-mobile-apps-immediately-present-4-distinct-actions-for-note-acquisition">1. The mobile apps immediately present 4 distinct actions for note acquisition.</h3>
<p><a href="https://cache.kevfoo.com/2010/03/IMG_0448.png"><img src="https://cache.kevfoo.com/2010/03/IMG_0448_thumb.png" alt="IMG_0448" title="IMG_0448" /></a></p>
<p>Evernote breaks down into two key activities: note acquisition and note retrieval. Given the detached nature of the mobile device and the sluggishness still experienced with cellular networks, the note browsing and searching experience is not ideal. The constraints of the device and the unpredictable nature of the network, I believe, led to a focus on what a mobile device is good for (note capturing) and make that the key action in the mobile versions of its application.</p>
<p>Having the app launch into the note acquisition screen implicitly signals to the user that this is the type of activity that you should be doing on the device. It seems counterintuitive to restrict features to simply not port them directly from the desktop or web offering. But by limiting the scope of the application, it actually maximizes the experience for the user. In Evernote’s case they cannot completely focus on note capture at the expense of cutting out the other actions. These additional activities are included, but they are relegated to the tab bar on the iPhone and the sub menus on Android devices.</p>
<h3 id="2-smart-layout-and-design-can-make-sub-optimal-experience-tolerable">2. Smart layout and design can make sub-optimal experience tolerable</h3>
<p><a href="https://cache.kevfoo.com/2010/03/IMG_0450.png"><img src="https://cache.kevfoo.com/2010/03/IMG_0450_thumb.png" alt="IMG_0450" title="IMG_0450" /></a> <a href="https://cache.kevfoo.com/2010/04/IMG_0466.png"><img src="https://cache.kevfoo.com/2010/04/IMG_0466_thumb.png" alt="IMG_0466" title="IMG_0466" /></a></p>
<p>Given the limited real estate available on mobile devices, it is unsurprising that note browsing is a sub-optimal experience. That being said, I think that Evernote does an excellent job making due with what little space it has. Visually we are given two options, depending on the orientation of the phone, in which we can browse our catalog of notes. While holding the phone in portrait mode, each note’s metadata is visible and allows the user to see titles and tags in addition to a thumbnail image of the note. While holding the phone in landscape mode, the note browser is transformed into a tiled-thumbnail view which shows more notes in the visible frame. If you can recognize the note by the rough appearance or layout of the text, you can pretty rapidly find what you are looking for.</p>
<p>Most people hold the phone in portrait mode so it follows that the default view is the easier of the two to quickly grasp. The two views provide the user with choices and trade offs: lower information density with a greater amount of detail or higher information density with less detail. By giving the user options, Evernote overcomes the inherent limitations of the mobile device and improves the user experience.</p>
<h3 id="3-search-function-is-a-key-but-clunky-feature-in-the-mobile-app">3. Search function is a key but clunky feature in the mobile app.</h3>
<p><a href="https://cache.kevfoo.com/2010/03/IMG_0449.png"><img src="https://cache.kevfoo.com/2010/03/IMG_0449_thumb.png" alt="IMG_0449" title="IMG_0449" /></a><a href="https://cache.kevfoo.com/2010/03/IMG_0452.png"><img src="https://cache.kevfoo.com/2010/03/IMG_0452_thumb.png" alt="IMG_0452" title="IMG_0452" /></a></p>
<p>Evernote has outstanding OCR software that makes everything searchable. Given that search is the killer feature of Evernote, it is surprising that search on mobile devices seems like a second-class citizen. The typical standard search box is visible on most of the non-note capturing screens, and the text search works as expected. Search does have some enhanced features like map view and search near here offered as options to help refine your search, but I am not a fan of either. The actual search queries appear to be executed on the server side and, as a result, the processing time can vary depending on your network connection. In order to do a location based search the app has to make network requests for the search results and get the map coordinates, and this typically translates to a noticeable wait when these types of searches are performed. I feel like search could cut out the location-based search filter entirely and be no worse off.</p>
<h3 id="4-sync-button-is-a-kitchen-drawer-tab-on-the-iphone-app">4. Sync Button is a “kitchen drawer” tab on the iPhone App</h3>
<p>The sync tab is mislabeled, and the label masks all the other functions that are exposed on that tab. Contextually, account information and device-specific settings aren’t readily associated to sync even though the sync process in Evernote synchronizes everything associated to your account. The action of synchronizing your notes could be integrated into the note screen, and the settings, account information, and about information could all be separate tabs managed by the tab controller already being utilized in the iPhone application. In fairness the move to limit the tabs is consistent with the less-is-more type of attitude, and the combination of the multiple actions into the single tab is most likely a compromise to simplify the interface.</p>
<h3 id="5-the-tips-ux-on-the-iphone-is-well-executed-and-non-obtrusive">5. The tips UX on the iPhone is well executed and non-obtrusive</h3>
<p><a href="https://cache.kevfoo.com/2010/04/evernotetips.jpg"><img src="https://cache.kevfoo.com/2010/04/evernotetips_thumb.jpg" alt="evernotetips" title="evernotetips" /></a></p>
<p>The tips screen, integrated into the main view of the Evernote iPhone app, is an excellent way of sharing information about features and activities without being pushy and obtrusive. With a smooth animation that peels back the main view to show the tips view, the user can get a quick suggestion on a feature and then be back capturing or browsing notes. This is a small but well executed design element that adds to the experience.</p>
<h3 id="6-sharp-branding">6. Sharp branding</h3>
<p><a href="https://cache.kevfoo.com/2010/03/IMG_0447.png"><img src="https://cache.kevfoo.com/2010/03/IMG_0447_thumb.png" alt="IMG_0447" title="IMG_0447" /></a></p>
<p>As far as the aesthetics go, I like the Evernote logo. A combination of an elephant and piece of paper plays off the “memory like an elephant” saying and the note-based nature of the application. The logo, paired nicely with a soft green color and a nice font, is the focal point of the splash screen welcoming you into the application. The other platforms do not utilize a splash screen since the application load times aren’t as great as the iPhone, but the same basic logo/font/color scheme is consistent in the icons used on all platforms.</p>
<h3 id="lessons-learned-from-the-evernote-app">Lessons learned from the Evernote app:</h3>
<ul>
<li>Instead of frustrating users with clunky interfaces and hacks to enable certain features of your service or app, only port features or activities that enhance the service and take advantage of the features of mobile devices rather than expose the limitations of them.</li>
<li>Focus on the activities that can capitalize on the advantages of a mobile device instead of fighting the platform.</li>
<li>Optimize for those specific use cases that make sense that you want to capitalize on.</li>
<li>Be ruthless in what you cut out or be stingy in what you put into the mobile version</li>
<li>Be consistent in your branding, color scheme, and feel of your applications.</li>
<li>Good design can always speak louder than any type of instruction. Help users fall into the pit of success.</li>
</ul>
<p><em>You can get the iPhone app in the <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=281796108&mt=8">iTunes App Store</a></em></p>
Monotouch Quickie: Network Indicator Tip2010-04-05T00:00:00-05:00https://www.kevfoo.com/2010/04/monotouch-quickie-network-indicator-tip<p>I’ve used this simple technique below many times in WinForms apps to toggle the wait cursor (hourglass) during long running operations and it is applicable for the situations that call for toggling the network activity indicator in Monotouch.When used with a using statement, the indicator is turned on when the object is constructed and turned off when the code within the using statement has finished executing and the object’s Dispose method is called.</p>
<div class="wlWriterEditableSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:63442f2e-f2c9-436f-ae14-0a99c8efc4e1" style="margin: 0px; display: inline; float: none; padding: 0px;"><pre style="background-color: #ffffff; white-space: pre-wrap; word-wrap: break-word; overflow: auto; font-family: Consolas; font-size: 11.25;"><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> NetworkIndicator : IDisposable{ </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> NetworkIndicator () { UIApplication.SharedApplication.NetworkActivityIndicatorVisible </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">true</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Dispose () { UIApplication.SharedApplication.NetworkActivityIndicatorVisible </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">false</span><span style="color: #000000;">;</span><span style="color: #000000;"> GC.SuppressFinalize(this); }}</span></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div>
<p>Usage:<div class="wlWriterEditableSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:b22771bd-ad3c-4b68-a5ee-b2dcd3af7d42" style="margin: 0px; display: inline; float: none; padding: 0px;"><pre style="background-color: #ffffff; white-space: pre-wrap; word-wrap: break-word; overflow: auto;"><span style="color: #0000ff;">using</span><span style="color: #000000;">(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> NetworkIndicator()){ </span><span style="color: #008000;">//</span><span style="color: #008000;">Make network call here</span><span style="color: #008000;"></span><span style="color: #000000;">}</span></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div></p>
<p>You wouldn’t want to wrap this around a UIWebView (add the toggling of the indicator to the LoadStarted and LoadFinished events) but anytime your app is making calls out the network you can wrap the using statement around the method invocation and get the user friendly network activity notification.</p>
Thoughts on the Day of Mobile conference2010-03-08T00:00:00-06:00https://www.kevfoo.com/2010/03/thoughts-on-the-day-of-mobile-conference<p>Here is a collection of thoughts that I had about the <a href="http://web.archive.org/web/20100303223551/http://www.techinthemiddle.com/DayOfMobile/">Day of Mobile</a> conference held at IIT last Saturday. This was the inaugural conference put together by the <a href="http://web.archive.org/web/20100309125041/http://www.techinthemiddle.com/">Tech in the Middle</a> guys, and I thought that they did a great job. I look forward to seeing what they do next.</p>
<h2 id="overall-themes">Overall Themes</h2>
<p>Two main themes that emerged during the sessions: the future of mobile is web-based apps rather than device specific apps, and the road to mobile riches probably doesn’t run through an app store.</p>
<h2 id="multi-platform-development-is-hard">Multi-platform development is hard</h2>
<p>Certainly scenarios exist where multi-platform apps make sense, but it is imperative that the risk/reward ratio be in your favor to have a chance to be successful. Don’t underestimate the amount of effort and headaches that developing and supporting multi-platform apps will create. Going the mobile web app route has it’s limitations, but it is the closest thing to “write once, run anywhere” that mobile has.</p>
<h2 id="cool-tools">Cool Tools</h2>
<p>Really cool tools like <a href="http://www.phonegap.com">PhoneGap</a> and <a href="http://www.jqtouch.com">jQTouch</a> are out there that help lower the barriers to making apps and, in the case of PhoneGap, multi-platform apps. Both tools have been released under MIT licenses and are free to use.</p>
<h2 id="one-mans-secret-sauce-for-success">One Man’s Secret Sauce for Success</h2>
<p>David Whatley gave a funny presentation that provided a lot of laughs and some great insights into how the iPhone app store works. Even better was his ability to back up what he was saying with some real sales data and numbers. His secret sauce for success: PR, code re-use, and playing to your strengths.</p>
<h2 id="app-stores-are-glorified-catalogs">App stores are glorified catalogs</h2>
<p>Standing out or even being noticed amongst all the other apps is extremely difficult. Relying only on the app catalog to drive sales is effectively a “post and pray” strategy akin to trying to win the lottery. Try to find a niche and solve a problem. Focus on creating value for markets not just an app for it. Mark Murphy described many ways to <a href="http://www.slideshare.net/commonsguy/making-money-at-mobile">make money in mobile</a> other than direct app sales during his talk and in a series of blog posts about <a href="http://www.androidguys.com/2010/03/04/android-business-models/">Android Business Models</a>.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="http://www.phonegap.com">PhoneGap</a></li>
<li><a href="http://www.jqtouch.com">jQTouch</a></li>
<li><a href="http://web.archive.org/web/20100119082158/http://iphonedevdepot.com/tiki-index.php">iPhone Dev Depot</a></li>
<li><a href="http://www.slideshare.net/commonsguy/making-money-at-mobile">Making Money at Mobile (Slides)</a></li>
<li><a href="http://github.com/mikelaurence/CoreResource">CoreResource iPhone Library</a></li>
</ul>
<h2 id="books-recommended-during-the-conference">Books Recommended During the Conference</h2>
<ul>
<li><a href="https://www.goodreads.com/book/show/42362591-aiming-at-amazon">Aiming at Amazon</a></li>
<li><a href="https://www.goodreads.com/book/show/324748.The_Dip">The Dip</a></li>
<li><a href="https://www.goodreads.com/book/show/3251206-the-whuffie-factor">The Whuffie Factor</a></li>
</ul>
<p>Overall, the signal-to-noise ratio at the conference was good but not great. I did manage to glean at least one or two new ideas from most of the sessions I attended, and it was great to see the enthusiasm that the Chicago tech community has for events like this.</p>
Monotouch Binding Gotcha2010-02-15T00:00:00-06:00https://www.kevfoo.com/2010/02/monotouch-binding-gotcha<p>I stumbled across a subtle gotcha while exploring binding Objective-C types in <a href="http://web.archive.org/web/20100214234720/http://monotouch.net/">MonoTouch</a>. As luck would have it, someone else did also and posted a question about it on the <a href="http://web.archive.org/web/20100210154919/http://monotouch.net/Chat">MonoTouch IRC channel</a>. The poster of the question eventually came across the answer and shared it there, and I am going to post it here in case anyone else makes the same mistake and is looking for some answers.</p>
<p>I was following along with the documentation for <a href="https://web.archive.org/web/20100210152449/http://monotouch.net/Documentation/Binding_New_Objective-C_Types">binding new Objective-C types</a> on the MonoTouch site, and as a way to ease into the binding process, I chose a class to define from the CloudMade SDK that I am looking to expose in MonoTouch. The class selected was the BBox class (bbox.h) and I went about creating the following API definition shown below:</p>
<div class="wlWriterEditableSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:32446696-877b-4dce-94d1-62a9f74add99" style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px"><pre style="background-color:#FFFFFF;white-space:-moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; word-wrap: break-word;overflow: auto;"><span style="color: #0000FF;">using</span><span style="color: #000000;"> System;</span><span style="color: #0000FF;">using</span><span style="color: #000000;"> MonoTouch.Foundation;</span><span style="color: #0000FF;">using</span><span style="color: #000000;"> MonoTouch.ObjCRuntime;</span><span style="color: #0000FF;">namespace</span><span style="color: #000000;"> CloudMade{ [BaseType(</span><span style="color: #0000FF;">typeof</span><span style="color: #000000;"> (NSObject))] </span><span style="color: #0000FF;">interface</span><span style="color: #000000;"> BBox { [Export(</span><span style="color: #800000;">"</span><span style="color: #800000;">westernLongitude</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000FF;">float</span><span style="color: #000000;"> WesternLongitude {</span><span style="color: #0000FF;">get</span><span style="color: #000000;">;</span><span style="color: #0000FF;">set</span><span style="color: #000000;">;} [Export(</span><span style="color: #800000;">"</span><span style="color: #800000;">southernLatitude</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000FF;">float</span><span style="color: #000000;"> SouthernLatitude {</span><span style="color: #0000FF;">get</span><span style="color: #000000;">;</span><span style="color: #0000FF;">set</span><span style="color: #000000;">;} [Export(</span><span style="color: #800000;">"</span><span style="color: #800000;">easternLongitude</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000FF;">float</span><span style="color: #000000;"> EasternLongitude {</span><span style="color: #0000FF;">get</span><span style="color: #000000;">;</span><span style="color: #0000FF;">set</span><span style="color: #000000;">;} [Export(</span><span style="color: #800000;">"</span><span style="color: #800000;">northerLatitude</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000FF;">float</span><span style="color: #000000;"> NorthernLatitude {</span><span style="color: #0000FF;">get</span><span style="color: #000000;">;</span><span style="color: #0000FF;">set</span><span style="color: #000000;">;} [Export(</span><span style="color: #800000;">"</span><span style="color: #800000;">asString</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000FF;">string</span><span style="color: #000000;"> AsString(); }}</span></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div>
<div>
Once completed, I saved it as BBox.cs and attempted to generate the bindings by invoking btouch on the file.  You can see below the unsuccessful message I received. </div>
<div> </div>
<div class="wlWriterEditableSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ded3f3e2-df9d-45b8-84f8-1be4953cd47a" style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px"><pre style="background-color:#FFFFFF;white-space:-moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; word-wrap: break-word;overflow: auto;"><span style="color: #000000;">$</span><span style="color: #000000;"> btouch BBox</span><span style="color: #000000;">.</span><span style="color: #000000;">cs </span><span style="color: #000000;">/</span><span style="color: #000000;">var</span><span style="color: #000000;">/</span><span style="color: #000000;">folders</span><span style="color: #000000;">/</span><span style="color: #000000;">E4</span><span style="color: #000000;">/</span><span style="color: #000000;">E44PAZnZGKGpVrmseo2N3</span><span style="color: #000000;">++++</span><span style="color: #000000;">TI</span><span style="color: #000000;">/</span><span style="color: #000000;">-Tmp-</span><span style="color: #000000;">/</span><span style="color: #000000;">9qgrm9nm</span><span style="color: #000000;">.</span><span style="color: #000000;">lnv</span><span style="color: #000000;">/</span><span style="color: #000000;">CloudMade</span><span style="color: #000000;">/</span><span style="color: #000000;">BBox</span><span style="color: #000000;">.</span><span style="color: #000000;">g</span><span style="color: #000000;">.</span><span style="color: #000000;">cs</span><span style="color: #000000;">(</span><span style="color: #000000;">46</span><span style="color: #000000;">,</span><span style="color: #000000;">71</span><span style="color: #000000;">)</span><span style="color: #000000;">: error CS0117: `CloudMade</span><span style="color: #000000;">.</span><span style="color: #000000;">BBox' does </span><span style="color: #0000FF;">not</span><span style="color: #000000;"> contain a definition </span><span style="color: #0000FF;">for</span><span style="color: #000000;"> `Messaging'</span><span style="color: #000000;">/</span><span style="color: #000000;">var</span><span style="color: #000000;">/</span><span style="color: #000000;">folders</span><span style="color: #000000;">/</span><span style="color: #000000;">E4</span><span style="color: #000000;">/</span><span style="color: #000000;">E44PAZnZGKGpVrmseo2N3</span><span style="color: #000000;">++++</span><span style="color: #000000;">TI</span><span style="color: #000000;">/</span><span style="color: #000000;">-Tmp-</span><span style="color: #000000;">/</span><span style="color: #000000;">9qgrm9nm</span><span style="color: #000000;">.</span><span style="color: #000000;">lnv</span><span style="color: #000000;">/</span><span style="color: #000000;">CloudMade</span><span style="color: #000000;">/</span><span style="color: #000000;">BBox</span><span style="color: #000000;">.</span><span style="color: #000000;">g</span><span style="color: #000000;">.</span><span style="color: #000000;">cs</span><span style="color: #000000;">(</span><span style="color: #000000;">28</span><span style="color: #000000;">,</span><span style="color: #000000;">30</span><span style="color: #000000;">)</span><span style="color: #000000;">: </span><span style="color: #000000;">(</span><span style="color: #000000;">Location of the symbol related to previous error</span><span style="color: #000000;">)</span><span style="color: #000000;"></span><span style="color: #000000;">/</span><span style="color: #000000;">var</span><span style="color: #000000;">/</span><span style="color: #000000;">folders</span><span style="color: #000000;">/</span><span style="color: #000000;">E4</span><span style="color: #000000;">/</span><span style="color: #000000;">E44PAZnZGKGpVrmseo2N3</span><span style="color: #000000;">++++</span><span style="color: #000000;">TI</span><span style="color: #000000;">/</span><span style="color: #000000;">-Tmp-</span><span style="color: #000000;">/</span><span style="color: #000000;">9qgrm9nm</span><span style="color: #000000;">.</span><span style="color: #000000;">lnv</span><span style="color: #000000;">/</span><span style="color: #000000;">CloudMade</span><span style="color: #000000;">/</span><span style="color: #000000;">BBox</span><span style="color: #000000;">.</span><span style="color: #000000;">g</span><span style="color: #000000;">.</span><span style="color: #000000;">cs</span><span style="color: #000000;">(</span><span style="color: #000000;">57</span><span style="color: #000000;">,</span><span style="color: #000000;">71</span><span style="color: #000000;">)</span><span style="color: #000000;">: error CS0117: `CloudMade</span><span style="color: #000000;">.</span><span style="color: #000000;">BBox' does </span><span style="color: #0000FF;">not</span><span style="color: #000000;"> contain a definition </span><span style="color: #0000FF;">for</span><span style="color: #000000;"> `Messaging'</span><span style="color: #000000;">/</span><span style="color: #000000;">var</span><span style="color: #000000;">/</span><span style="color: #000000;">folders</span><span style="color: #000000;">/</span><span style="color: #000000;">E4</span><span style="color: #000000;">/</span><span style="color: #000000;">E44PAZnZGKGpVrmseo2N3</span><span style="color: #000000;">++++</span><span style="color: #000000;">TI</span><span style="color: #000000;">/</span><span style="color: #000000;">-Tmp-</span><span style="color: #000000;">/</span><span style="color: #000000;">9qgrm9nm</span><span style="color: #000000;">.</span><span style="color: #000000;">lnv</span><span style="color: #000000;">/</span><span style="color: #000000;">CloudMade</span><span style="color: #000000;">/</span><span style="color: #000000;">BBox</span><span style="color: #000000;">.</span><span style="color: #000000;">g</span><span style="color: #000000;">.</span><span style="color: #000000;">cs</span><span style="color: #000000;">(</span><span style="color: #000000;">28</span><span style="color: #000000;">,</span><span style="color: #000000;">30</span><span style="color: #000000;">)</span><span style="color: #000000;">: </span><span style="color: #000000;">(</span><span style="color: #000000;">Location of the symbol related to previous error</span><span style="color: #000000;">)</span><span style="color: #000000;">Compilation failed: </span><span style="color: #000000;">2</span><span style="color: #000000;"> error</span><span style="color: #000000;">(</span><span style="color: #000000;">s</span><span style="color: #000000;">),</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;"> warningsbtouch: API binding contains errors</span><span style="color: #000000;">.</span><span style="color: #000000;"></span></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div>
<p>After looking at the errors, I saw the following message:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'CloudMade.BBox' does not contain a definition for 'Messaging'
</code></pre></div></div>
<p>It looked as if I was missing a using statement. However, this was not the case. <strong><em>Messaging</em></strong> is a class in the MonoTouch.ObjCRuntime namespace and the using statement declarations already included it. The problem was the interface had the same name as the file. That caused the issue shown above as the temporary classes generated during the process caused conflicts. The solution to this issue was simple. Rename the file something other than what the interface you are defining is named.</p>
<p>So the moral of the story is: <em>Do not give the file that has the API definition the same name as one of the interfaces defined.</em></p>
<p>Steer clear of that, and you’ll be binding Objective-C types with ease.</p>
Nexus One - First Impressions2010-02-09T00:00:00-06:00https://www.kevfoo.com/2010/02/nexus-one-first-impressions<p>My thoughts on the <a href="http://web.archive.org/web/20100208215850/http://www.google.com/phone">Nexus One</a> and the Android OS? I like it. A lot.</p>
<h2 id="likes">Likes</h2>
<ul>
<li>
<p>Speed. I have the older iPhone 3G hardware and from a user experience perspective it was not that bad. When compared to the Nexus One, however, it is down right sluggish. This probably isn’t as big of a factor when comparing a 3GS versus Nexus One, but for someone coming from a 3G iPhone, it is a huge plus.</p>
</li>
<li>
<p>Being able to run apps in the background. Browsing the web while streaming Pandora and downloading an app in the background works flawlessly and shows no noticeable signs of lag or sluggishness.</p>
</li>
<li>
<p>The OLED screen is really nice. Higher resolution and OLED screen looks great in low-light situations and is supposed to save precious battery by not drawing less power than normal LCD screens.</p>
</li>
<li>
<p>The Android OS is pretty sweet. It has a lot of nice features like the status bar, an active “desktop” that you can put interactive widgets on as well as app icons, and an easy way to get music and files on and off the device.</p>
</li>
<li>
<p>Integration with Google Apps is fantastic as expected. I love getting mail and talk updates in the status bar and the overall experience with the apps on the Nexus One is better than on the iPhone.</p>
</li>
</ul>
<h2 id="dislikes">Dislikes</h2>
<ul>
<li>
<p>Cut and Paste is clunky. I can see why Apple needed to spend awhile working out the UX issues before rolling it out.</p>
</li>
<li>
<p>The touch interaction with the device has not been as good as the iPhone in my experience. I am not sure where the blame lies (hardware or software), but I sometimes find it difficult to select elements in the UI. This is not something that occurs frequently, but in the short time I’ve owned the device, it has occurred enough to be noticeable.</p>
</li>
<li>
<p>4GB of storage out of the box is lacking. It is an easy upgrade but given the amount of money that Google is charging for the device, this seems like they are skimping here.</p>
</li>
<li>
<p>The <a href="http://www.android.com/market/">Android Market</a> place experience is integrated nicely but isn’t as tightly integrated as the iTunes App Store. This is arguably a good or a bad thing depending on how you look at it. As a user this is a bad thing since it makes finding and buying apps more difficult than just popping open iTunes. I’ve found the Android Marketplace, both on the phone and the web, hard to browse.</p>
</li>
</ul>
<p>There are a few negatives that I can’t pin exclusively on the device but are still worth noting. I have been using my AT&T SIM with the Nexus One and most likely will do so until my contract is up in a few months. With that caveat out of the way, the talk quality seemed poor and the phone gets really hot when talking for periods greater than 10 minutes. I hesitate to even mention these issues since I am not using the phone on the network it was intended, but I know there probably are others considering doing what I did, and this feedback might help if someone is on the fence.</p>
<p>The Nexus One is exactly the kind of phone and competition that was needed to push Apple. I strongly recommend anyone looking for a smartphone to give it serious consideration before running out and getting an iPhone.</p>
iPhone Wireframes2010-02-01T00:00:00-06:00https://www.kevfoo.com/2010/02/iphone-wireframes<p>I stumbled across a great collection of <a href="http://web.archive.org/web/20100125092914/http://www.geekchix.org/blog/2010/01/03/a-collection-of-printable-sketch-templates-and-sketch-books-for-wireframing/">iPhone wireframe templates</a> tonight while going through my feeds. It got me thinking about the process I’ve been using to layout and design the app I am working on.</p>
<p>I’ve been using regular 3 x 5’ notecards to sketch out different screens for the iPhone app I am working on. On the ruled side I’ll scribble notes and call out must/should/wish components of the screens to help prioritize features. On the blank side I’ve been sketching out wireframes of the screens to help get a feel for the UI and flow of the app.</p>
<p><a href="https://cache.kevfoo.com/2010/02/cards_laid_out.jpg"><img src="https://cache.kevfoo.com/2010/02/cards_laid_out_thumb.jpg" alt="cards_laid_out" title="cards_laid_out" /></a></p>
<p>I’ve played around with <a href="http://www.balsamiq.com/">Balsamiq</a> and, while the tool is fantastic, I find myself still partial to the notecards. With the same rough dimensions of the iPhone and the flexibility of being able to quickly rearrange and reorder the screens, the cards suit my preferences as a visual-spatial learner well.</p>
<p>As with most tools and techniques, personal preference plays a large role in how and when they’re employed. I always enjoy checking out how other people work and try and steal glean ideas from them. I’ve found the following blogs pretty useful for design, usability, and UX. I hope they help inspire you to create something cool.</p>
<p><a href="http://wireframes.tumblr.com/">I (heart) wireframes</a><br />
<a href="http://web.archive.org/web/20100217185801/http://infosthetics.com/">information aesthetics</a>
<a href="http://web.archive.org/web/20100201040543/http://www.everydayux.com/">everydayUX</a>
<a href="http://sender11.typepad.com/sender11/">Sender 11</a></p>
MonoTouch Quickie : UIPasteboard Snippet2010-01-26T00:00:00-06:00https://www.kevfoo.com/2010/01/monotouch-quickie-uipasteboard-snippet<p>Came across the need to plop a string onto the clipboard (aka <a href="http://go-mono.com/docs/monodoc.ashx?link=T%3aMonoTouch.UIKit.UIPasteboard">UIPasteboard</a>) programatically last night and hit up the MonoTouch docs to see what was involved. It wasn’t readily apparent what would work for the string expected to be passed as the SetValue method pasteboardType argument. Turns out, after digging into the official Apple docs, the string you need to provide is the Uniform Type Identifier (UTI) that corresponds to the data type being passed (in my case “public.utf8-plain-text”). For my future reference (always seems to take longer to find things at the iPhone doc site) here is a table of the relevant UTIs that might come in handy in the future.</p>
<table cellspacing="0" border="0" width="400" cellpadding="2"><tbody> <tr> <td valign="top" width="200">string</td> <td valign="top" width="200">public.utf8-plain-text</td> </tr> <tr> <td valign="top" width="200">JPEG</td> <td valign="top" width="200">public.jpeg</td> </tr> <tr> <td valign="top" width="200">PNG</td> <td valign="top" width="200">public.png</td> </tr> <tr> <td valign="top" width="200">Url</td> <td valign="top" width="200">public.url</td> </tr> </tbody></table>
<p>And for good measure here is a snippet that shows putting a string on the general clipboard and reading it off.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">string</span> <span class="n">clipboardText</span> <span class="p">=</span> <span class="s">"Copy and Paste Me!"</span><span class="p">;</span>
<span class="n">UIPasteboard</span><span class="p">.</span><span class="n">General</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="k">new</span> <span class="nf">NSString</span><span class="p">(</span><span class="n">clipboardText</span><span class="p">),</span><span class="s">"public.utf8-plain-text"</span><span class="p">);</span>
<span class="kt">string</span> <span class="n">actualClipboardText</span> <span class="p">=</span> <span class="n">UIPasteboard</span><span class="p">.</span><span class="n">General</span><span class="p">.</span><span class="nf">GetValue</span><span class="p">(</span><span class="s">"public.utf8-plain-text"</span><span class="p">);</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">AreSame</span><span class="p">(</span> <span class="n">clipboardText</span><span class="p">,</span> <span class="n">actualClipboardText</span><span class="p">.</span><span class="nf">ToString</span><span class="p">());</span></code></pre></figure>
<p>Enjoy.</p>
<p><em>Updated 1/30/2010: Replaced NSStrings with strings</em><br />
<em>Updated 6/28/2012: Updated SetValue call to wrap up the C# string into an NSString as the API takes an NSObject now.</em></p>
Chatsworth House Keeping and Update2009-12-12T00:00:00-06:00https://www.kevfoo.com/2009/12/chatsworth-house-keeping-and-update<p>I just moved <a href="https://www.kevfoo.com/2009/01/chatsworth-a-google-talk-group-chat-bot/">Chatsworth</a> from <a href="https://code.google.com/archive/p/chatsworth/">Google code</a> over to its new home on <a href="http://github.com/kevinmcmahon/chatsworth">github</a>. I’ve got a couple more features in mind and figured now was as good a time as any to make the switch. I already host my <a href="https://kevfoo.com/2009/11/fun-with-monotouch-and-multi-level-table-views-without-interface-builder/">iPhone</a> <a href="https://kevfoo.com/2009/11/monotouch-uialertview-uitextfield-crazy-delicious/">samples</a> and some personal projects on github and, given the smoother branching and merging experience of git, I think it will be more conducive to spiking some features and trying some ideas out.</p>
<p>As for the chatbot, I’ve recently added a link logging feature. Any URL sent to the chatroom now gets logged to the sqlite database. Users can query the database right from the chat window by entering <em><strong>/links [number previous of to links return]</strong></em> and the links, timestamp and the person who sent it will be sent in an individual IM to the requestor.</p>
<p>Going forward, the two areas that I think need the most improvement are installation and documentation. The majority of the questions and feedback that I have received are setup and configuration related. Improving the installation and documentation stories at this point make the most sense and would be worth the effort.</p>
<p>On the technical side of things, I am considering making a Chatsworth plugin for Windows Home Server. Currently I am running the service from my home server and, given the always on nature that suits the chatbot and the low/no maintenance needed to running once it is setup, the pairing seems natural and could help provide a little better setup and configuration experience.</p>
<p>I hope to work on this more over the holidays and please feel free to reach out with a question or feedback. Enjoy.</p>
Monotouch: UIAlertView + UITextField = Crazy Delicious2009-11-28T00:00:00-06:00https://www.kevfoo.com/2009/11/monotouch-uialertview-uitextfield-crazy-delicious<p>If you have ever tried to buy or install anything from the App Store or iTunes on your iPhone, then you certainly have been prompted for your password by a screen that looks like this:</p>
<p><a href="https://cache.kevfoo.com/2009/11/itunes_pw_iphone.jpg"><img src="https://cache.kevfoo.com/2009/11/itunes_pw_iphone_thumb.jpg" alt="itunes_pw_iphone" title="itunes_pw_iphone" /></a></p>
<p>The control used by Apple to prompt you for your password isn’t available out-of-the-box in Cocoa Touch, but fortunately it is pretty easy to roll your own. I’ve put together a <a href="http://github.com/kevinmcmahon/CustomUITextFieldAlertView">sample project</a> that shows a couple different techniques that are available for creating and customizing the control, and I’ll go over a few of them here.</p>
<p>The stock <a href="http://www.go-mono.com/docs/index.aspx?link=T%3AMonoTouch.UIKit.UIAlertView%2FP">UIAlertView</a> control consists of a title label, message label, and typically one or more buttons. It is important to note that in Monotouch, a default button setup isn’t configured, and buttons have to be added explicitly. If a UIAlertView is configured without any buttons, then dismissing the control will have to be done programmatically because the user will have no way to dismiss the control. In both of my examples I’ve configured the control to have two buttons (Cancel and Ok), but I could have gotten by with just one. If only one button is used, it is important to also note that the button is considered the Cancel button by the control, and the <a href="http://www.go-mono.com/docs/monodoc.ashx?link=P%3aMonoTouch.UIKit.UIAlertView.CancelButtonIndex">CancelButtonIndex</a> property will refer to that button.</p>
<p><a href="https://cache.kevfoo.com/2009/11/Basic_UIAlertView.png"><img src="https://cache.kevfoo.com/2009/11/Basic_UIAlertView_thumb.png" alt="Basic_UIAlertView" title="Basic_UIAlertView" /></a></p>
<p>The easiest way to go about composing a UIAlertView with a <a href="http://www.go-mono.com/docs/monodoc.ashx?link=T%3aMonoTouch.UIKit.UITextField">UITextField</a> is to create an instance of a UITextField and add it as a sub-view. Since you’re adding a layer on top of UIAlertView’s view, the key will be fitting this new control into the UI. There are two ways of going about this. The first and maybe most obvious way to solve this problem is to make some room between the message label and buttons for the text field ala the iTunes password prompt. Since the UIAlertView control has already been laid out, moving those controls around within the frame is not as straight forward a task as it may seem. Before we tackle that we will explore a simpler method that overlays the text field directly on top of the message label. You sacrifice being able to display an additional message to the user, but the end result looks professional and is very easy to implement. This will also be the basis for getting the control to have the same look and feel as the iTunes password prompt, so it is important to understand this prior to attempting to move the controls around.</p>
<p>The key to overlaying the text field on top of the message label is setting the bounding rectangle for the UITextField. The bounding rectangle is where the text field draws the box in which a user enters text. For our purposes, the rectangle needs to be drawn in between the title label and the buttons. It is important to make sure that it covers the area where the message label is drawn. Thanks to the work of others we know that if we start drawing the text field at the x,y offset of 12,45 that the text field will hide the message label.</p>
<p>The code to overlay the text field is as follows:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="nf">PromptForName</span><span class="p">(</span><span class="n">HandlerToUse</span> <span class="n">handlerType</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">UITextField</span> <span class="n">tf</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">UITextField</span><span class="p">(</span><span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">Drawing</span><span class="p">.</span><span class="nf">RectangleF</span><span class="p">(</span><span class="m">12f</span><span class="p">,</span> <span class="m">45f</span><span class="p">,</span> <span class="m">260f</span><span class="p">,</span> <span class="m">25f</span><span class="p">));</span>
<span class="n">tf</span><span class="p">.</span><span class="n">BackgroundColor</span> <span class="p">=</span> <span class="n">UIColor</span><span class="p">.</span><span class="n">White</span><span class="p">;</span>
<span class="n">tf</span><span class="p">.</span><span class="n">UserInteractionEnabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="n">tf</span><span class="p">.</span><span class="n">AutocorrectionType</span> <span class="p">=</span> <span class="n">UITextAutocorrectionType</span><span class="p">.</span><span class="n">No</span><span class="p">;</span>
<span class="n">tf</span><span class="p">.</span><span class="n">AutocapitalizationType</span> <span class="p">=</span> <span class="n">UITextAutocapitalizationType</span><span class="p">.</span><span class="n">None</span><span class="p">;</span>
<span class="n">tf</span><span class="p">.</span><span class="n">ReturnKeyType</span> <span class="p">=</span> <span class="n">UIReturnKeyType</span><span class="p">.</span><span class="n">Done</span><span class="p">;</span>
<span class="n">tf</span><span class="p">.</span><span class="n">SecureTextEntry</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">UIAlertView</span> <span class="n">myAlertView</span> <span class="p">=</span> <span class="k">new</span> <span class="n">UIAlertView</span>
<span class="p">{</span><span class="n">Title</span> <span class="p">=</span> <span class="s">"Please enter your name"</span><span class="p">,</span> <span class="n">Message</span> <span class="p">=</span> <span class="s">"this line is hidden"</span><span class="p">};</span>
<span class="n">myAlertView</span><span class="p">.</span><span class="nf">AddButton</span><span class="p">(</span><span class="s">"Cancel"</span><span class="p">);</span>
<span class="n">myAlertView</span><span class="p">.</span><span class="nf">AddButton</span><span class="p">(</span><span class="s">"Ok"</span><span class="p">);</span>
<span class="n">myAlertView</span><span class="p">.</span><span class="nf">AddSubview</span><span class="p">(</span><span class="n">tf</span><span class="p">);</span>
<span class="c1">// More Setup Goes Here</span>
<span class="n">myAlertView</span><span class="p">.</span><span class="nf">Show</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The method starts by instantiating a UITextField control with a bounding rectangle passed via its constructor. The rectangle itself is constructed with four parameters via its constructor: the first two parameters set the x and y offset from the origin point of the control, and the last two parameters set the size of the drawing area. After the initial construction of the UITextField, a few additional properties are set to customize the display.</p>
<p>Among the properties set is the <em>SecureTextEntry</em>. Setting this property to true will make the text field act like a password field and hide all the characters that you’ve entered. Once the text field has been created and initialized, we move on to creating a UIAlertView. Via object initialization, the <em>Title</em> and <em>Message</em> properties are set with some text. Finally, two buttons are created, and the text field is added as a sub-view to the UIAlertView. Calling the show on the UIAlertView causes the alert to pop up. Below you can see the rendered control.</p>
<p><a href="https://cache.kevfoo.com/2009/11/TextField_As_Subview_UIAlertView.png"><img src="https://cache.kevfoo.com/2009/11/TextField_As_Subview_UIAlertView_thumb.png" alt="TextField_As_Subview_UIAlertView" title="TextField_As_Subview_UIAlertView" /></a></p>
<p>So now that we have the basic control setup, we can tweak it so that the text field and the message label can both be displayed.</p>
<p>If you recall our custom UIAlertView control is currently composed of two UILabels, two UIButtons and a UITextField that we added. The UILabels are derived from UIView and is not a subclass of UIControl; whereas, the UIButton and the UITextField are both derived from UIControls. Since we want to position the UITextField beneath the labels, we can use this knowledge to selectively shift only the UIControls in the view down leaving the title and message labels in place.</p>
<p>To accomplish this task the following steps need to be performed:</p>
<ol>
<li>The control height needs to be increased to make space for the text field and provide additional space to move the UIControls. This is done by setting the frame to a larger size. The frame will be increased by the height of the text field plus a buffer, so the control has space between it and the others. This is what the frame looks like after being enlarged: <a href="https://cache.kevfoo.com/2009/11/FrameEnlarged_CustomUIAlertView.png"><img src="https://cache.kevfoo.com/2009/11/FrameEnlarged_CustomUIAlertView_thumb.png" alt="FrameEnlarged_CustomUIAlertView" title="FrameEnlarged_CustomUIAlertView" /></a></li>
<li>Once the frame has been enlarged, we’ll loop through the sub-views looking for UIControls and adjust only those types down by the same text field plus buffer offset Here is what the control looks like after each UIControl is found during the iteration through the sub-views.</li>
</ol>
<p><a href="https://cache.kevfoo.com/2009/11/FirstUIControlShifted_CancelButton.png"><img src="https://cache.kevfoo.com/2009/11/FirstUIControlShifted_CancelButton_thumb.png" alt="FirstUIControlShifted_CancelButton" title="FirstUIControlShifted_CancelButton" /></a><a href="https://cache.kevfoo.com/2009/11/SecondUIControlShifted_OkButton.png"> <img src="https://cache.kevfoo.com/2009/11/SecondUIControlShifted_OkButton_thumb.png" alt="SecondUIControlShifted_OkButton" title="SecondUIControlShifted_OkButton" /></a> <a href="https://cache.kevfoo.com/2009/11/FinalUIControlShifted_TextField.png"><img src="https://cache.kevfoo.com/2009/11/FinalUIControlShifted_TextField_thumb.png" alt="FinalUIControlShifted_TextField" title="FinalUIControlShifted_TextField" /></a></p>
<p>Here is the code that does the control adjustment:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">TextFieldAlertView</span> <span class="p">:</span> <span class="n">UIAlertView</span>
<span class="p">{</span>
<span class="k">private</span> <span class="n">UITextField</span> <span class="n">_tf</span><span class="p">;</span>
<span class="c1">// ...</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">AdjustControlSize</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">float</span> <span class="n">tfExtH</span> <span class="p">=</span> <span class="n">_tf</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Size</span><span class="p">.</span><span class="n">Height</span> <span class="p">+</span> <span class="m">16.0f</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">frame</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RectangleF</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">X</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Y</span> <span class="p">-</span> <span class="n">tfExtH</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Size</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span>
<span class="k">this</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Size</span><span class="p">.</span><span class="n">Height</span> <span class="p">+</span> <span class="n">tfExtH</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="n">Frame</span> <span class="p">=</span> <span class="n">frame</span><span class="p">;</span>
<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">view</span> <span class="k">in</span> <span class="k">this</span><span class="p">.</span><span class="n">Subviews</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">view</span> <span class="k">is</span> <span class="n">UIControl</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">view</span><span class="p">.</span><span class="n">Frame</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RectangleF</span><span class="p">(</span><span class="n">view</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">X</span><span class="p">,</span> <span class="n">view</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Y</span> <span class="p">+</span> <span class="n">tfExtH</span><span class="p">,</span>
<span class="n">view</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Size</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span> <span class="n">view</span><span class="p">.</span><span class="n">Frame</span><span class="p">.</span><span class="n">Size</span><span class="p">.</span><span class="n">Height</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So now that the desired look and feel of the control has been established, it is time to wire up the control. There are a couple options for handling events generated by the control: one is more in line with how Cocoa handles events, and another that is similar to how .Net works.</p>
<p>The Cocoa way to wire up delegates involves subclassing a special delegate class associated to the control and selectively overriding the methods for the actions you want to customize. In the case of the UIAlertView control, that delegate class is a <a href="http://www.go-mono.com/docs/index.aspx?link=C%3AMonoTouch.UIKit.UIAlertViewDelegate">UIAlertViewDelegate</a>. The delegate class includes overridable methods like <em>Clicked</em> where you can define the actions to take when a button on the control is clicked or where you can extend behaviors by overriding methods like <em>Preseneted</em> which is called after the control has been displayed to the user.</p>
<p>The .Net way to wire up events is to provide specific delegates for specific events. To make interacting with the Cocoa Touch controls easier, Monotouch provides public events for each one of the methods that can be overridden in the UIAlertViewDelegate class. Having the individual public events makes wiring up controls more intuitive for the .Net developer, but it is good to know about the Cocoa-style approach especially when looking at Objective-C examples and/or ADC docs. I have examples of wiring up the control via both methods in the example source.</p>
<p>The last topic I’d like to cover briefly is how I encapsulated the customization of control by subclassing UIAlertView. The key to getting the control to look right is to override the LayoutSubviews method and build out the text field, add it to the UIAlertView and shift the controls around there.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">LayoutSubviews</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// layout the stock UIAlertView</span>
<span class="k">base</span><span class="p">.</span><span class="nf">LayoutSubviews</span><span class="p">();</span>
<span class="c1">// // build out the text field</span>
<span class="n">_tf</span> <span class="p">=</span> <span class="nf">ComposeTextFieldControl</span><span class="p">(</span><span class="n">_secureTextEntry</span><span class="p">);</span>
<span class="c1">// add the text field to the UIAlertView</span>
<span class="k">this</span><span class="p">.</span><span class="nf">AddSubview</span><span class="p">(</span><span class="n">_tf</span><span class="p">);</span> <span class="c1">// shift the UIControls to fit the text field</span>
<span class="nf">AdjustControlSize</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’ve added some more customizations to my subclassed UIAlertView control that allows you to enable the text field security via a constructor parameter and a tweak to the control’s Transform property to shift the control up so it isn’t hidden by the keyboard. You can see those changes and the entire working example <a href="http://github.com/kevinmcmahon/CustomUITextFieldAlertView">here on github</a>.</p>
<p>Enjoy.</p>
<p>Links to some of the resources used:</p>
<ul>
<li><a href="http://web.archive.org/web/20090305065103/http://iphonedevelopment.blogspot.com/2009/02/alert-view-with-prompt.html">Alert View with Prompt (iPhone Development Blog)</a></li>
<li><a href="http://junecloud.com/journal/code/displaying-a-password-or-text-entry-prompt-on-the-iphone.html">Displaying a password or text entry prompt on the iPhone (Junecode Blog)</a></li>
<li><a href="http://web.archive.org/web/20090801024411/http://codesofa.com/blog/archive/2009/07/15/look-uialertview-is-dating-uitableview.html">Look UIAlertView is Dating UITableView (codesofa)</a></li>
<li><a href="http://web.archive.org/web/20091106121143/http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIAlertViewDelegate_Protocol/UIAlertViewDelegate/UIAlertViewDelegate.html">iPhone Dev Center: UIAlertViewDelegate Protocol Reference (Apple, <em>login required</em>)</a></li>
</ul>
Fun with Monotouch and Multi-Level Table Views without Interface Builder2009-11-22T00:00:00-06:00https://www.kevfoo.com/2009/11/fun-with-monotouch-and-multi-level-table-views-without-interface-builder<p><a href="http://web.archive.org/web/20091123065334/http://monotouch.net/">Monotouch</a> enables development on the iPhone/iPod Touch platform using C# and .Net. Recently Monotouch was officially released and it seemed like the perfect time to have a go at mobile app development. The strong appeal of being able to leverage my C# and .Net experience while scratching an itch that I have had for a few apps was more than enough to entice me to order a brand new Mac mini and start learning the Cocoa framework.</p>
<p>My pet app project requires some pretty basic multi-level tabular data visualization, so that was the first thing I attempted to tackle. I started my learning endeavor by reading the <a href="http://web.archive.org/web/20090901120637/https://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html">view controller programming guide</a> and working through a few of the <a href="http://web.archive.org/web/20091209071104/http://monotouch.net/Tutorials">tutorials</a> listed on the Monotouch site. One of the example tutorials listed was for <a href="http://www.alexyork.net/blog/post/UINavigationController-with-MonoTouch-Building-a-simple-RSS-reader-Part-1.aspx">a basic RSS reader</a>. Armed with his guide, I got up and running with a basic table view allowed me to drill down one level deep and return to the root via navigation button. This was a great introduction and the easy to follow steps combined with the screenshots certainly hit the mark; however, this was not quite the layout I was looking for.</p>
<p>The data that I wanted to display calls for more than just a list-view-to-detail-view relationship that the RSS reader implemented. As a beginner trying to figure out how to wire up my custom table controllers to each other and the navigation controller all via Interface Builder resulted in a lot of thrashing and a lot of ugly code. I eventually was able to roughly approximate the behavior I wanted, but it was pretty obvious to me there had to be a better way.</p>
<p>I set out to see if I could find more Monotouch examples to see if I could pick up some tips and stumbled across <a href="http://conceptdev.blogspot.com/">Craig Dunn’s blog</a>. Craig not only put together apps for the Monospace and PDC conferences (and graciously provided the source) but also posted a <a href="http://conceptdev.blogspot.com/2009/10/monotouch-rogets-1911-thesaurus.html">Roget’s Thesaurus</a> app that was just what I was looking for. It was a simple app, but it had all the elements in play that I was looking for and an <a href="http://3.bp.blogspot.com/_Ba76y6K7kvs/Su66_wEzO3I/AAAAAAAABIQ/9MzCAwYQ-HQ/s1600-h/RogetPlacemat.png">incredible diagram</a> that tied it all together.</p>
<p>One of the key tips and tricks I picked up from Craig’s code was how easy it was to cut out the Interface Builder from the process. I also generally tend to avoid using the designer whenever possible in Visual Studio but here it was a key factor in my understand. This isn’t to say that I’d never use the Interface Builder again or that it doesn’t have its uses because it does but there was <em>too much</em> magic going on in how Monotouch links up the XIBs to their backing C# classes. It wasn’t until I started explicitly creating views and view controllers instead of trying to wire them up after the fact did I start to understand how things were supposed to interact.</p>
<p>To implement a custom <strong>UITableViewController</strong> you need to set two properties with custom classes and override a method which is invoked by the framework when the view controller is loaded. You’ll need to set the TableDelegate and DataSource properties on the controller with your own implementations of <strong>UITableViewDelegate</strong> and <strong>UITableViewDataSource</strong>. The implementation of the table delegate allows you to customize how the table will respond to certain events and the data source implementation provides not only the data the table uses but also how that data is to be displayed within the table. Typically the ViewDidLoad method is overriden to perform the view setup work needed during initialization of the view controller. Within the method the TableDelegate and DataSource properties can be set.</p>
<p>Example setting the TableDelegate and DataSource via the ViewDidLoad method:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">MultiLevelTableView</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">MonoTouch</span><span class="p">.</span><span class="n">Foundation</span><span class="p">.</span><span class="nf">Register</span><span class="p">(</span><span class="s">"RootViewController"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">RootViewController</span> <span class="p">:</span> <span class="n">UITableViewController</span>
<span class="p">{</span>
<span class="k">class</span> <span class="nc">DataSource</span> <span class="p">:</span> <span class="n">UITableViewDataSource</span>
<span class="p">{</span>
<span class="cm">/* Impl Here */</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">TableDelegate</span> <span class="p">:</span> <span class="n">UITableViewDelegate</span>
<span class="p">{</span>
<span class="cm">/* Impl Here */</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">ViewDidLoad</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">base</span><span class="p">.</span><span class="nf">ViewDidLoad</span><span class="p">();</span>
<span class="n">TableView</span><span class="p">.</span><span class="n">Delegate</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TableDelegate</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="n">TableView</span><span class="p">.</span><span class="n">DataSource</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DataSource</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Example setting the properties during <strong>UITableView</strong> construction:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">MultiLevelTableView</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">MonoTouch</span><span class="p">.</span><span class="n">Foundation</span><span class="p">.</span><span class="nf">Register</span><span class="p">(</span><span class="s">"RootViewController"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">RootViewController</span> <span class="p">:</span> <span class="n">UITableViewController</span>
<span class="p">{</span>
<span class="n">UITableView</span> <span class="n">tableView</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">DataSource</span> <span class="p">:</span> <span class="n">UITableViewDataSource</span>
<span class="p">{</span>
<span class="cm">/* Impl Here */</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">TableDelegate</span> <span class="p">:</span> <span class="n">UITableViewDelegate</span>
<span class="p">{</span>
<span class="cm">/* Impl Here */</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">ViewDidLoad</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">base</span><span class="p">.</span><span class="nf">ViewDidLoad</span><span class="p">();</span>
<span class="n">tableView</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">UITableView</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">Delegate</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TableViewDelegate</span><span class="p">(</span><span class="k">this</span><span class="p">),</span> <span class="n">DataSource</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TableViewDataSource</span><span class="p">(</span><span class="k">this</span><span class="p">),</span>
<span class="p">};</span>
<span class="c1">//You can set more properties here like size and location in the frame etc.</span>
<span class="k">this</span><span class="p">.</span><span class="n">View</span><span class="p">.</span><span class="nf">AddSubview</span><span class="p">(</span><span class="n">tableView</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The meat of the table view controller falls in the implementations of the DataSource and TableDelegate.</p>
<p>A basic DataSource setup involves overriding two methods used by parent controller. The RowsInSection method tells how many rows need to be displayed in the table and the GetCell method returns a cell view which was customized for display by setting the text and display properties.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">DataSource</span> <span class="p">:</span> <span class="n">UITableViewDataSource</span>
<span class="p">{</span>
<span class="k">static</span> <span class="n">NSString</span> <span class="n">kCellIdentifier</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">NSString</span><span class="p">(</span><span class="s">"MyIdentifier"</span><span class="p">);</span>
<span class="n">RootViewController</span> <span class="n">tvc</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">DataSource</span><span class="p">(</span><span class="n">RootViewController</span> <span class="n">tvc</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">tvc</span> <span class="p">=</span> <span class="n">tvc</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="kt">int</span> <span class="nf">RowsInSection</span><span class="p">(</span><span class="n">UITableView</span> <span class="n">tableView</span><span class="p">,</span> <span class="kt">int</span> <span class="n">section</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">tvc</span><span class="p">.</span><span class="n">RootData</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="n">UITableViewCell</span> <span class="nf">GetCell</span><span class="p">(</span><span class="n">UITableView</span> <span class="n">tableView</span><span class="p">,</span> <span class="n">NSIndexPath</span> <span class="n">indexPath</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">cell</span> <span class="p">=</span> <span class="n">tableView</span><span class="p">.</span><span class="nf">DequeueReusableCell</span><span class="p">(</span><span class="n">kCellIdentifier</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">cell</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">cell</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">UITableViewCell</span><span class="p">(</span><span class="n">UITableViewCellStyle</span><span class="p">.</span><span class="n">Default</span><span class="p">,</span> <span class="n">kCellIdentifier</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">cell</span><span class="p">.</span><span class="n">TextLabel</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">tvc</span><span class="p">.</span><span class="n">RootData</span><span class="p">.</span><span class="nf">ElementAt</span><span class="p">(</span><span class="n">indexPath</span><span class="p">.</span><span class="n">Row</span><span class="p">);</span>
<span class="n">cell</span><span class="p">.</span><span class="n">Accessory</span> <span class="p">=</span> <span class="n">UITableViewCellAccessory</span><span class="p">.</span><span class="n">DetailDisclosureButton</span><span class="p">;</span>
<span class="k">return</span> <span class="n">cell</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The TableDelegate is where the behavior of the table to various events is defined. It is here in which we load up the next view to display when a user clicks on a cell. A basic implementation of a table view delegate involves overriding the RowSelected method with instructions on what to do with the row now that it has been selected. If the node selected happened to be a leaf node for example we might load up the detail’s view to display the nodes properties or if the node was the parent of more child nodes we would load another list view to continue to drill down into the data. In our case we want to drill down to the next layer of data which is managed by the SubGroupViewController.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">TableDelegate</span> <span class="p">:</span> <span class="n">UITableViewDelegate</span>
<span class="p">{</span>
<span class="n">RootViewController</span> <span class="n">tvc</span><span class="p">;</span>
<span class="n">SubGroupViewController</span> <span class="n">sgvc</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">TableDelegate</span><span class="p">(</span><span class="n">RootViewController</span> <span class="n">tvc</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">tvc</span> <span class="p">=</span> <span class="n">tvc</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">RowSelected</span><span class="p">(</span><span class="n">UITableView</span> <span class="n">tableView</span><span class="p">,</span> <span class="n">NSIndexPath</span> <span class="n">indexPath</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// get info from selected row</span>
<span class="kt">string</span> <span class="n">selectedGroup</span> <span class="p">=</span> <span class="n">tvc</span><span class="p">.</span><span class="n">RootData</span><span class="p">.</span><span class="nf">ElementAt</span><span class="p">(</span><span class="n">indexPath</span><span class="p">.</span><span class="n">Row</span><span class="p">);</span>
<span class="c1">// load up new controller to display</span>
<span class="k">if</span> <span class="p">(</span><span class="n">sgvc</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">sgvc</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SubGroupViewController</span><span class="p">(</span><span class="n">selectedGroup</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// push the controller on the navigation stack</span>
<span class="n">tvc</span><span class="p">.</span><span class="n">NavigationController</span><span class="p">.</span><span class="nf">PushViewController</span><span class="p">(</span><span class="n">sgvc</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The hang up for me with using Interface Builder to try and get the multi-level table views wired together was mostly due to not being familiar with the objects I was dealing with and not understanding what properties are needed to be initialized so the framework can display them correctly. Approaching the problem via the Interface Builder created a lot of noise that made seeing what was really needed to wire these table view controllers with the navigation controller difficult. In this case the the pre-packaged templates and generic layouts provided by Monotouch and Interface Builder were more of a hindrance than help but that is to be expected while learning a new technology.</p>
<p>I’ve packaged up my <a href="http://github.com/kevinmcmahon/MultiLevelTableView">implementation of a multi-level table view</a> app and put it on github. If anyone is looking for straight-forward, simple example of nesting table views hopefully this will help.</p>
<p>I also recommend checking out the following links which helped me immensely:</p>
<ul>
<li>
<p><a href="http://www.alexyork.net/blog/category/MonoTouch.aspx">Alex York’s Monotouch posts including the RSS reader</a> </p>
</li>
<li>
<p><a href="http://sabonrai.wordpress.com/2009/08/28/monotouch-sample-code-uitableview/">Sabon Rai’s Sample Code for UITableView</a></p>
</li>
<li>
<p><a href="http://conceptdev.blogspot.com/">Craig Dunn’s Blog</a></p>
</li>
</ul>
Tree Surgeon: Review and a couple tips2009-03-07T00:00:00-06:00https://www.kevfoo.com/2009/03/tree-surgeon-review-and-a-couple-tips<p>This past week I was playing around with <a href="https://en.wikipedia.org/wiki/Hudson_(software)">Hudson</a> and <a href="http://www.jetbrains.com/teamcity/index.html">Team City</a> and getting some NAnt scripts put together to use. In a happy coincidence I happened to catch a mention about <a href="http://treesurgeon.codeplex.com/">Tree Surgeon</a> from twitter and it had piqued my curiosity. Tree Surgeon is a project on <a href="http://www.codeplex.com/">CodePlex</a> that has been around for a while and helps automate the process of setting up a source tree and assembling build scripts. It was just what I was looking for.</p>
<p>The goal of Tree Surgeon was to make setting up and laying out new projects using best practices dead simple. All the major tools and libraries that typically find their way into your .Net source trees are provided for you. In addition to laying out the directory structure for your source and libraries, a solution file gets created with three main projects to get you started. The basic project setup includes a console app, core library and unit test project. The build scripts come pre-wired to have NCover run your tests and generate reports from the results and also do things like packaging your build artifacts into zip files for deployment.</p>
<p>The whole process, start to project generated, is very easy. Download and run the installer. Launch the program, select the version of Visual Studio (2003, 2005, and 2008) and the flavor of testing framework (NUnit or MbUnit) and then hit generate. The files and directories, built out in your Documents folder, are then created and ready to be used.</p>
<p>There are two slight issues I ran into that I’d like to point out. Both issues had already been noted in the project forums and were easy to fix. When generating a source tree with MbUnit selected as the unit test framework, the build script generated has the unit test assembly referenced is named incorrectly. You will have to change it to match your project’s unit test assembly name. The second issue has to do with running NCover and NUnit on x64 machines. I found that in order for NCover and NUnit to work on x64 machines I had to add two calls to the CorFlags utility to make executables work. This was accomplished by making the modifications to the “˜run-unit-tests’ target below on lines 5-8.</p>
<div>
<div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 1:</span> <span style="color: #0000ff"><</span><span style="color: #800000">target</span> <span style="color: #ff0000">name</span><span style="color: #0000ff">="run-unit-tests"</span><span style="color: #0000ff">></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 2:</span> <span style="color: #0000ff"><</span><span style="color: #800000">mkdir</span> <span style="color: #ff0000">dir</span><span style="color: #0000ff">="${build.dir}\test-reports"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 3:</span> <span style="color: #0000ff"><</span><span style="color: #800000">exec</span> <span style="color: #ff0000">program</span><span style="color: #0000ff">="regsvr32"</span> <span style="color: #ff0000">workingdir</span><span style="color: #0000ff">="tools\NCover"</span> <span style="color: #ff0000">commandline</span><span style="color: #0000ff">="/s CoverLib.dll"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 4:</span> <span style="color: #008000"><!-- Need the CorFlags Commands For x64 --></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 5:</span> <span style="color: #0000ff"><</span><span style="color: #800000">exec</span> <span style="color: #ff0000">program</span><span style="color: #0000ff">="C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\CorFlags"</span> </pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 6:</span> <span style="color: #ff0000">workingdir</span><span style="color: #0000ff">="tools\NCover"</span> <span style="color: #ff0000">commandline</span><span style="color: #0000ff">="NCover.Console.exe /32BIT+"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 7:</span> <span style="color: #0000ff"><</span><span style="color: #800000">exec</span> <span style="color: #ff0000">program</span><span style="color: #0000ff">="c:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\CorFlags"</span> </pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 8:</span> <span style="color: #ff0000">workingdir</span><span style="color: #0000ff">="tools\NUnit"</span> <span style="color: #ff0000">commandline</span><span style="color: #0000ff">="NUnit-Console.Exe /32BIT+"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 9:</span> <span style="color: #0000ff"><</span><span style="color: #800000">exec</span> <span style="color: #ff0000">program</span><span style="color: #0000ff">="tools\ncover\NCover.Console.exe"</span> </pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 10:</span> <span style="color: #ff0000">workingdir</span><span style="color: #0000ff">="${build.dir}\Debug\UnitTests"</span><span style="color: #0000ff">></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 11:</span> <span style="color: #0000ff"><</span><span style="color: #800000">arg</span> <span style="color: #ff0000">value</span><span style="color: #0000ff">="//w &quot;.&quot;"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 12:</span> <span style="color: #0000ff"><</span><span style="color: #800000">arg</span> <span style="color: #ff0000">value</span><span style="color: #0000ff">="//x &quot;..\..\test-reports\Coverage.xml&quot;"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 13:</span> <span style="color: #0000ff"><</span><span style="color: #800000">arg</span> <span style="color: #ff0000">value</span><span style="color: #0000ff">="&quot;..\..\..\tools\nunit\nunit-console.exe&quot;"</span> <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 14:</span> <span style="color: #0000ff"><</span><span style="color: #800000">arg</span> <span style="color: #ff0000">value</span><span style="color: #0000ff">="&quot;MyProjectName.UnitTests.dll&quot; </span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 15:</span> &quot;/xml:..\..\test-reports\UnitTests.xml&quot; &quot;/nologo&quot;" <span style="color: #0000ff">/></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 16:</span> <span style="color: #0000ff"></</span><span style="color: #800000">exec</span><span style="color: #0000ff">></span></pre> <pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"><span style="color: #606060"> 17:</span> <span style="color: #0000ff"></</span><span style="color: #800000">target</span><span style="color: #0000ff">></span></pre> </div></div>
<p>Even though I am pretty late to the party with Tree Surgeon I am very happy to have stumbled across it. The whole source tree and build script setup process is the perfect thing to script out and I am really glad the project creator and contributors have taken the time to do this. It is a big help and I highly recommend you try it out the next time you are spinning up a new source tree for a project.</p>
Alt.Net Seattle Recap2009-03-05T00:00:00-06:00https://www.kevfoo.com/2009/03/altnet-seattle-recap<p>I spent last weekend at the Alt.Net Seattle conference and had some time to reflect on it. This was my first experience with an open spaces style conference, and I have to admit that I am a big fan of the format. While I am not convinced this format would work for every type of gathering, it fit the needs of this type of group very well.</p>
<p>There was a lot made during a couple of sessions about being more accessible, newbie-friendly, and encouraging more new faces and ideas at these events. If you watch some of Scott Hanselman’s <a href="https://www.hanselman.com/blog/ExperiencingALTNETSeattle2009OpenSpaces.aspx">Why So Mean</a> session, you can get the general gist of some of the problems and solutions offered. While I think introspection is generally a good thing, I am not convinced that any progress on this front will be made, and question if these issues are as bad as some may think. As a first-time Alt.Net conference attendee and having met one other person there in real-life before last weekend, I never felt unwelcome or shunned, and everyone that I talked to was cordial and friendly and definitely open to discussion.</p>
<p>However, I didn’t get on a plane to Seattle to talk about feelings. The primary attraction to the event was to discuss building software with some of the most passionate and opinionated developers in the world. By participating in the workshops and sessions, I was able to soak up and join conversations about may topics, including agile and lean, LINQ, DDD, and context/spec-based testing. These are topics that I don’t always have the opportunity to discuss daily and certainly not with people who have the kind of expertise as those at the event. I’ve already been able to apply at work and on my side projects some of the things I’ve picked up, and I don’t think I’m close to truly synthesizing all of the great conversations that I had.</p>
<p>While my overall experience was positive, I did feel like there were some minuses from the trip. Some of the sessions turned out to be mostly one-way presentations that spurred little discussion or participation. I suspect that may be attributed to an overall lack of experience with the topic. Sessions like Miguel de Icaza’s on Mono and the iPhone fell into that category since virtually no one there had any experience with developing for the iPhone and even fewer with Mono. Still, even discussions about more universal topics like DDD or Agile were somewhat “eyes forward” and lacked the interaction and engagement that made other sessions notable.</p>
<p>A cooling factor on some discussions may have been the result of deference to a few of the more forceful personalities or undisputed domain experts in the field. The presence of some of these personalities in the sessions clearly defined or dramatically shifted the discussions. It did not take me long to learn and apply the law of two feet when I found myself at the same sessions as some of these people. I knew that it was a matter of time until the session’s conversation was going to degrade or grind to a halt. My last quibble with the conference resulted from the facilitator-led, new-age ceremony elements. I don’t think the butterfly and bumblebee metaphor stuff or the ringing of the bell really added to the experience and was not needed. I know that the facilitators were trying to make this a very Zen-like experience, but it was not.
In terms of key takeaways, I think I’ve identified a few. I talked with quite a few people about their experiences with agile and scrum. In hearing some other companies’ practices and problems, I was comforted by the knowledge that my current company is not as dysfunctional as I thought. While I believe we can improve, I also realized that I should probably take a moment to appreciate the good things that we do and celebrate the stuff that works well. The other takeaway was that I feel like I am in a much better position to clearly articulate the goals that I had set for myself earlier this year. Before this weekend, I had not done an excellent job articulating some of these goals and had yet to express them in SMART terms. Now I am going to incorporate things like contributions to open source projects, community demos, and presentations into my goals.
The future of these conferences will be in the local or region open spaces starting to be held. Already events are planned for Vancouver and Houston that seem to fit this bill because they are drawing in people from the region versus nationally based on a quick perusal of their attendee list. I speculate that if you reduce the concentration of some of the more well-known personalities in the community, you might be able to make up for any potential loss of expertise with an increase in the amount of active participation from fresh new faces. Additionally, I think some might find the benefits of participating in open discussions with developers in their own communities more meaningful as these types of interactions may yield long-term benefits.</p>
<p>Lastly, I’d like to thank the people of the Alt.Net Seattle group for putting this together, and you should be proud of the success the conference achieved. Job well done.</p>
Chatsworth: A Google talk group chat bot2009-01-26T00:00:00-06:00https://www.kevfoo.com/2009/01/chatsworth-a-google-talk-group-chat-bot<p>For the past year and a half, I’ve been using <a href="http://techwalla.googlepages.com/">PartyChat</a> to participate in multi-user chats with friends. PartyChat essentially provides IRC type functional via <a href="http://talk.google.com">GTalk</a> and has been a great way to have ongoing conversations throughout the day while avoiding 100+ email threads.</p>
<p>The only pitfall PartyChat has to do with stability. PartyChat is a free service used by a large number of people, and it runs off the project creator’s computer in his apartment. I do not know why the service goes down intermittently, but the combination of a home server plus lots of users cannot be helping. While it stinks that PartyChat isn’t always up, I understand I have no right to demand or expect 100% uptime, so I decided to do something about it. Leveraging knowledge acquired on XMPP (the protocol that Jabber and GTalk use) from another project, I wrote a simple group chatbot called <a href="https://code.google.com/archive/p/chatsworth/">Chatsworth</a> to improve the availability of group chat that my friends and I use.</p>
<p>Chatsworth is a windows service written in C# that provides basic chat room functionality. It does not have all the features that PartyChat has, but it does offer people the ability to set up and manage their chatbots. Additionally, if you are concerned about having all your chat logs being available not just to Google but also to the people running the PartyChat servers, then Chatsworth is the group chat provider for you. Chatsworth is fully functional but still immature and under development. I plan to add some additional features, take care of a few loose ends, and provide more unit tests in the immediate future.</p>
<p>Admittedly I could’ve just downloaded the PartyChat java source, compiled it, and ran it on my own set of servers, but where is the fun in that? I figured creating a chatbot would allow me to do more than address some of the availability issues I was having. Chatsworth provides me a non-trivial project that I can use to explore different software development concepts and techniques as well as get some code and design samples out on the web. So if you’re in the market for group chat in GTalk, give <a href="https://code.google.com/archive/p/chatsworth/">Chatsworth</a> a try.</p>
<p>I am looking forward to building this project out further and would love your feedback.</p>
Just Say No (to the Big Redesign)2009-01-11T00:00:00-06:00https://www.kevfoo.com/2009/01/just-say-no-to-the-big-redesign<p>This <a href="http://blog.objectmentor.com/articles/2009/01/09/the-big-redesign-in-the-sky">post</a> by Robert Martin describes a pretty common scenario that companies and teams go through: The Big Redesign(tm). The gist of Uncle Bob’s story was that the only way to clean up a codebase is incrementally. Piece by piece, iteration by iteration, release by release. If I would have read this post six months ago, I might have argued that this is an overly simplistic take. Surely there are situations where a big redesign is not only necessary but the prudent option. However, having just gone through this at work, I’m more than convinced that he is right.</p>
<p>My redesign change of heart did not stem from a spectacular failure or the system ending up in worse shape. I’d like to think the state of our legacy codebase made a compelling case for a redesign (doesn’t everyone?) but rarely is the design itself the problem. It is the conditions that allowed your designs to become unmanagable or problematic.</p>
<p>A codebase does not fall into disrepair over night. The state of the system is the sum of all the decisions and the environment applied to the system over its life. Calling in a “Tiger Team” or your best engineers to execute a grand redesign might provide relief <em>righthissecond</em> but that relief is usually fleeting. None of the problems that got you in this bind will have been addressed. Trying to provide a fix without improving the processes or decisions will only lead to another mess.</p>
<p>The big redesign might be sexy and alluring but it is rarely the right decision.</p>
Crunching some NFL Stats with F#2009-01-10T00:00:00-06:00https://www.kevfoo.com/2009/01/crunching-some-nfl-stats-with-f<p>To explore functional programming, I’ve decided to return to a familiar problem domain, football stats. I used this domain a couple of years ago when I was in the process of making the transition from the Unix-based OS/Java world to the Microsoft/C# world. I am the type of person that learns better by doing than studying, so I’m going to try and jump in and cobble something together to start the learning process. I’ve watched the PDC presentation by <a href="http://blogs.msdn.com/lucabol/">Luca Bolognese</a>, and I’ve read through the first couple chapters of <a href="http://blogs.msdn.com/dsyme/">Don Syme’s</a> <em>Expert F#</em>, so consider me armed with an F# Interactive window and dangerous.</p>
<p>The first stat that I plan to look at is the <a href="http://dberri.wordpress.com/2007/09/09/a-new-qb-score/">QB Score Stat</a> as outlined by Berri, et al. in <a href="https://www.goodreads.com/book/show/321856.The_Wages_of_Wins"><em>Wages of Wins</em></a>. The stat is much easier to calculate than the traditional QB Rating used by the NFL, and if you read the link or book, you’ll see that it correlates much better to wins and points than QB rating. For our purposes, I’ll outline the formula here, but I do recommend checking out the links for more info.</p>
<p>QB Score = Total Yards - (3 * Plays) - (30 * Turnovers)</p>
<p>I got the <a href="http://sports.yahoo.com/nfl/stats/byposition?pos=QB&conference=NFL&year=season_2008&timeframe=All&sort=626&old_category=QB">2008 QB stats</a> from Yahoo, dumped them into Excel, and then saved them off into CSV. This data munging can be done programmatically fairly quickly with HtmlAgilityPack and Linq to XML, but I’ll save that for another post. I’ve provided a copy of the stats in CSV here (<em>Update 8/3/2020: link is dead</em>).</p>
<p>So to get started here is what we have to do in order to calculate the raw QB score and the QB score per play for all the NFL QB’s:</p>
<ol>
<li>Read in the CSV file</li>
<li>Grab the relevant stats for our calculation</li>
<li>Calculate the QB score per play for each QB</li>
<li>Return the QB name and the score.</li>
</ol>
<p>I’ll tackle this step by step and we can verify our results via the F# Interactive window.</p>
<p>To ingest the file, we can leverage the .Net System.IO library. The call pattern to read it into memory is identical to what you would see in C# or VB and is pretty straight forward.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">open</span> <span class="nn">System</span><span class="p">.</span><span class="nc">IO</span>
<span class="k">let</span> <span class="n">filePath</span> <span class="p">=</span> <span class="s2">"D:</span><span class="err">\</span><span class="s2">code</span><span class="err">\</span><span class="s2">data</span><span class="err">\</span><span class="s2">QB_Stats_2008.csv"</span><span class="p">;</span>
<span class="k">let</span> <span class="n">stream</span> <span class="p">=</span> <span class="k">new</span> <span class="nc">FileStream</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="nn">FileMode</span><span class="p">.</span><span class="nc">Open</span><span class="p">)</span>
<span class="k">let</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="nc">StreamReader</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
<span class="k">let</span> <span class="n">csv</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nc">ReadToEnd</span><span class="bp">()</span>
</code></pre></div></div>
<p>Here is the output of the F# interaction window.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>val filePath : string
val stream : System.IO.FileStream
val reader : System.IO.StreamReader
val csv : string
</code></pre></div></div>
<p>As we can see from the output, ‘csv’ is string that holds the contents of the QB stats file. Since we know that the file is is a CSV file, we can break it down into its individual elements like so:</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">stats</span> <span class="p">=</span> <span class="n">csv</span><span class="p">.</span><span class="nc">Split</span><span class="o">([|</span><span class="sc">'\n'</span><span class="o">|])</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">skip</span> <span class="mi">1</span>
<span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">line</span> <span class="p">-></span> <span class="n">line</span><span class="p">.</span><span class="nc">Split</span><span class="o">([|</span><span class="k">'</span><span class="p">,</span><span class="k">'</span><span class="o">|]))</span>
<span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">values</span> <span class="p">-></span> <span class="kt">string</span> <span class="n">values</span><span class="o">.[</span><span class="mi">0</span><span class="o">],</span> <span class="c1">// qb name</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">5</span><span class="o">]),</span> <span class="c1">// att</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">7</span><span class="o">]),</span> <span class="c1">// pass yds</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">11</span><span class="o">]),</span> <span class="c1">// int</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">12</span><span class="o">]),</span> <span class="c1">// rushes</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">13</span><span class="o">]),</span> <span class="c1">// rush yds</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">17</span><span class="o">]),</span> <span class="c1">// sacks</span>
<span class="nn">System</span><span class="p">.</span><span class="nn">Int32</span><span class="p">.</span><span class="nc">Parse</span><span class="p">(</span><span class="n">values</span><span class="o">.[</span><span class="mi">20</span><span class="o">]))</span> <span class="c1">// fumbles lost</span>
</code></pre></div></div>
<p>Since ‘csv’ is a string, we can use the Split method to chunk the string up into individual lines using the ‘\n’ character as our split token. Once split into individual lines, the pipeline operator on line 3 further processes each line. Sequences in F# can be thought of as IEnumerables from C# and come with some nice baked-in methods to help with processing. Our QB stats CSV file has as its first line a key to the data. We’ll need to skip that first line before we get to process the real data, and to do so we’ll use one of those nice baked-in methods (Seq.skip) to do so.</p>
<p>Line 4 further deconstructs the csv file into the individual comma delimited values tokenizing each line. After the lines have been tokenized the individual values can be read. Here I’ve created a tuple to hold each lines values. The tokenized values have been collected in a tuple that holds 8 values. The mapping of the values is specified by the comments.</p>
<p>Here is the output of the F# interaction window after step 2:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>val stats : <span class="nb">seq</span>
</code></pre></div></div>
<p>After step two we have a sequence of tuples that have only the stats and information that we care about. The next step now becomes calculating the QB score. The calculation of the score requires three sub-steps, so let us revise the outline we laid out earlier to include them.</p>
<ol>
<li>Read in the CSV file</li>
<li>Grab the relevant stats for our calculation</li>
<li>Calculate the QB score per play for each QB
<ol>
<li><strong>_Create the formula function _</strong></li>
<li><strong>_Compute the components of the formula _</strong></li>
<li><strong><em>Create the desired output</em></strong></li>
</ol>
</li>
<li>Return the QB name and the score</li>
</ol>
<p>Let’s tackle the first sub-step and codify the formula now and see what we’ll need to provide from the data we just acquired.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">qbcalc</span> <span class="p">(</span><span class="n">plays</span><span class="p">,</span><span class="n">yards</span><span class="p">,</span><span class="n">turnovers</span><span class="p">)</span> <span class="p">=</span> <span class="n">yards</span> <span class="p">-</span> <span class="mi">3</span> <span class="p">*</span> <span class="n">plays</span> <span class="p">-</span> <span class="mi">30</span> <span class="p">*</span> <span class="n">turnovers</span>
</code></pre></div></div>
<p>This line of code creates a function called qbcalc that takes in a tuple composed of the plays, yards, and turnovers components of the formula.</p>
<p>If we run the qbcalc function through the interactive window we get:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>val qbcalc : int <span class="k">*</span> int <span class="k">*</span> int -> int
</code></pre></div></div>
<p>The end result of this is the raw QB score. The arithmetic operations in F# are similar to most languages, so the formula is a straight forward expression without any surprises. Since we know plays, yards and turnovers are all integer values, we could further constrain the types of values that the tuple is composed of, but F#’s type inference already does this for us, so it is not needed. When the compiler analyzed this code, it was able to ascertain from the operations and the integers used that the plays, yards and turnover values were of type int and automatically created the int constraints.</p>
<p>The next step is to compute the individual values of plays, yards, and turnovers. Before we start, I just want to note that I am sure there is a slicker, more concise way to do this, but this is my first go at this, so pardon the mess.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">names</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="n">name</span><span class="o">,_,_,_,_,_,_,_)</span> <span class="p">-></span> <span class="n">name</span><span class="p">)</span>
</code></pre></div></div>
<p>Here we start to perform operations on the stats sequence we captured from the CSV file. The basic structure of what I am doing here is grabbing the specific values of the components I am looking to either aggregate (names) or calculate (plays, yards, and turnovers) from the sequence and mapping them to a new sequence. Here is an example of how to create the plays sequence.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">plays</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="o">(_,</span><span class="n">att</span><span class="o">,_,_,</span><span class="n">rush</span><span class="o">,_,</span><span class="n">sacks</span><span class="o">,_)</span> <span class="p">-></span> <span class="n">att</span><span class="o">+</span><span class="n">rush</span><span class="o">+</span><span class="n">sacks</span><span class="p">)</span>
</code></pre></div></div>
<table>
<tbody>
<tr>
<td>Here the stats sequence is pushed through the pipeline operator (</td>
<td>> ) which allows you to chain functions in a sequence. This is happens because, as pointed out in <a href="https://www.goodreads.com/book/show/2434449.Expert_F_">Expert F#</a>, the pipeline operator is just function application in reverse. This can be expressed like so:</td>
</tr>
</tbody>
</table>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="o">(|>)</span> <span class="n">x</span> <span class="n">f</span> <span class="p">=</span> <span class="n">f</span> <span class="n">x</span>
</code></pre></div></div>
<p>So in our case when we have the following:</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="p">(</span><span class="k">fun</span><span class="o">(_,</span><span class="n">att</span><span class="o">,_,_,</span><span class="n">rush</span><span class="o">,_,</span><span class="n">sacks</span><span class="o">,_)</span> <span class="p">-></span> <span class="n">att</span><span class="o">+</span><span class="n">rush</span><span class="o">+</span><span class="n">sacks</span><span class="p">)</span>
</code></pre></div></div>
<p>Chaining the stats sequence with the the Seq.map function will apply the function we’ve defined in the parenthesis to each element in the stats sequence and return a new sequence with the results of the function. The function we have defined has a signature that matches the 8 value tuples that compose the stats sequence. Since only a few values are needed to be computed for the various values, “˜_’ can be assigned to the values in the parameter definition and more meaningful names can be given to the values we care about. On the right hand side of the -> (a symbol that represents a function), we do the simple adding of the values. Again the results of this function are collected in a new sequence that is returned from the Seq.map call.</p>
<p>After all the individual components of the QB score formula have been computed, we’re left with a bunch of individual sequence values that need to be reconstructed into something that we can pass to the the qbcalc function. The calculation function is defined as taking a tuple composed of a play, yard, and turnover values, so we need to utilize another method that Seq provides called zip.</p>
<p>Here is the code that crunches the individual components.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">getStats</span> <span class="p">=</span>
<span class="k">let</span> <span class="n">stats</span> <span class="p">=</span> <span class="n">loadQBStats</span>
<span class="k">let</span> <span class="n">names</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="n">name</span><span class="o">,_,_,_,_,_,_,_)</span> <span class="p">-></span> <span class="n">name</span><span class="p">)</span>
<span class="k">let</span> <span class="n">plays</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="o">(_,</span><span class="n">att</span><span class="o">,_,_,</span><span class="n">rush</span><span class="o">,_,</span><span class="n">sacks</span><span class="o">,_)</span> <span class="p">-></span> <span class="n">att</span><span class="o">+</span><span class="n">rush</span><span class="o">+</span><span class="n">sacks</span><span class="p">)</span> <span class="k">let</span> <span class="n">yards</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="o">(_,_,</span><span class="n">passyd</span><span class="o">,_,_,</span><span class="n">rushyd</span><span class="o">,_,_)</span> <span class="p">-></span> <span class="n">passyd</span> <span class="o">+</span> <span class="n">rushyd</span><span class="p">)</span>
<span class="k">let</span> <span class="n">turnovers</span> <span class="p">=</span> <span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span> <span class="k">fun</span><span class="o">(_,_,_,</span><span class="kt">int</span><span class="o">,_,_,_,</span><span class="n">fum</span><span class="p">)</span> <span class="p">-></span> <span class="kt">int</span><span class="o">+</span><span class="n">fum</span><span class="p">)</span>
<span class="nn">Seq</span><span class="p">.</span><span class="n">zip3</span> <span class="n">plays</span> <span class="n">yards</span> <span class="n">turnovers</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">zip</span> <span class="n">names</span>
</code></pre></div></div>
<p>The final step to complete is to apply the qbcalc function to each play, yard, and turnover tuple, and zipping up the resulting sequence with the sequence of the names rounds out steps and completes our task. The values were balled up into tuples in previous steps, so a lot of what is left to do is unpacking what we need to do the actual calculation and then reassemble to the output. The unpacking of the tuples is done with the fst and snd functions applied to the sequences. These methods return the fst, and the snd functions return the first and second elements of the tuples, respectively. The last line of the doCalc function divides the raw QB score over the plays completing the calculation and then back pipes that sequence for zipping with the names. The zipped sequence gets returned, and at last, we’ve calculated the QB score per play for the 2008 season. The last thing to note with the calculation is that to get better precision from the final result, the int values being divided need to be converted to a decimal. If the integers aren’t converted, then the results of the division operation will be rounded down, and we’ll lose precision on the calculation.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">doCalc</span> <span class="p">=</span> <span class="k">let</span> <span class="n">stats</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="n">getStats</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">names</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="n">stats</span> <span class="p">|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="n">fst</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">rawScore</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="n">stats</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="n">snd</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="n">qbcalc</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">plays</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="n">stats</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="n">snd</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span> <span class="p">(</span><span class="k">fun</span> <span class="p">(</span><span class="n">plays</span><span class="o">,_,_)</span> <span class="p">-></span> <span class="n">plays</span><span class="p">)</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">components</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="nn">Seq</span><span class="p">.</span><span class="n">zip</span> <span class="n">rawScore</span> <span class="n">plays</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="nn">Seq</span><span class="p">.</span><span class="n">zip</span> <span class="n">names</span> <span class="p">&</span><span class="n">amp</span><span class="p">;</span><span class="n">lt</span><span class="o">;|</span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="n">x</span><span class="p">:</span><span class="kt">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span><span class="kt">int</span><span class="p">)</span> <span class="p">-></span> <span class="nn">System</span><span class="p">.</span><span class="nn">Convert</span><span class="p">.</span><span class="nc">ToDecimal</span><span class="p">(</span><span class="n">x</span><span class="o">)/</span> <span class="nn">System</span><span class="p">.</span><span class="nn">Convert</span><span class="p">.</span><span class="nc">ToDecimal</span><span class="p">(</span><span class="n">y</span><span class="o">))</span> <span class="n">components</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span>
</code></pre></div></div>
<p>Below is the complete source listing of my first crack at doing something useful with F#. There are a couple things (the packing and repacking of the tuples, the CSV parsing) that scream <em><strong>optimize me</strong></em>. In my next F# post, I’ll refactor this code to slim it down and package it up so I can display these results graphically via C#.</p>
<div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">open</span> <span class="nn">System</span><span class="p">.</span><span class="nc">IO</span><span class="p">&</span><span class="n">lt</span><span class="o">;/</span><span class="n">p</span><span class="p">&</span><span class="n">gt</span><span class="p">;</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">p</span><span class="p">&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">loadQBStats</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">filePath</span> <span class="p">=</span> <span class="p">&</span><span class="n">amp</span><span class="p">;</span><span class="n">quot</span><span class="p">;</span><span class="nc">D</span><span class="p">:</span><span class="err">\</span><span class="n">code</span><span class="err">\</span><span class="nc">ProFootballDB</span><span class="err">\</span><span class="nc">Data</span><span class="err">\</span><span class="nn">QB_Stats_2008</span><span class="p">.</span><span class="n">csv</span><span class="p">&</span><span class="n">amp</span><span class="p">;</span><span class="n">quot</span><span class="p">;</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">stream</span> <span class="p">=</span> <span class="k">new</span> <span class="nc">FileStream</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="nn">FileMode</span><span class="p">.</span><span class="nc">Open</span><span class="p">)</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">reader</span> <span class="p">=</span> <span class="k">new</span> <span class="nc">StreamReader</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">csv</span> <span class="p">=</span> <span class="n">reader</span><span class="p">.</span><span class="nc">ReadToEnd</span><span class="bp">()</span><span class="p">&</span><span class="n">lt</span><span class="o">;/</span><span class="n">p</span><span class="p">&</span><span class="n">gt</span><span class="p">;</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">p</span><span class="p">&</span><span class="n">gt</span><span class="p">;</span><span class="k">let</span> <span class="n">stats</span> <span class="p">=</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="n">csv</span><span class="p">.</span><span class="nc">Split</span><span class="o">([|</span><span class="sc">'\n'</span><span class="o">|])</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">skip</span> <span class="mi">1</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">line</span> <span class="p">-></span> <span class="n">line</span><span class="p">.</span><span class="nc">Split</span><span class="o">([|</span><span class="k">'</span><span class="p">,</span><span class="k">'</span><span class="o">|]))</span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="o">;|></span> <span class="nn">Seq</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">values</span> <span class="p">-></span> <span class="p">&</span><span class="n">lt</span><span class="p">;</span><span class="n">br</span> <span class="o">/&</span><span class="n">gt</span><span class="p">;</span><span class="kt">string</span> <span class="n">values</span><span class="o">.[</span><span class="mi">0</span><span class="o">],</span> <span class="c1">// qb name &lt;br /&gt;System.Int32.Parse(values.[5]), // att &lt;br /&gt;System.Int32.Parse(values.[7]), // pass yds &lt;br /&gt;System.Int32.Parse(values.[11]), // int &lt;br /&gt;System.Int32.Parse(values.[12]), // rushes &lt;br /&gt;System.Int32.Parse(values.[13]), // rush yds &lt;br /&gt;System.Int32.Parse(values.[17]), // sacks &lt;br /&gt;System.Int32.Parse(values.[20])) // fumbles lost &lt;br /&gt;stats&lt;/p&gt; &lt;p&gt;let qbcalc (plays,yards,turnovers) = yards - 3 * plays - 30 * turnovers&lt;/p&gt; &lt;p&gt;let getStats = &lt;br /&gt;let stats = loadQBStats &lt;br /&gt;let names = stats |> Seq.map(fun(name,_,_,_,_,_,_,_) -> name) &lt;br /&gt;let plays = stats |> Seq.map(fun(_,att,_,_,rush,_,sacks,_) -> att+rush+sacks) &lt;br /&gt;let yards = stats |> Seq.map(fun(_,_,passyd,_,_,rushyd,_,_) -> passyd + rushyd) &lt;br /&gt;let turnovers = stats |> Seq.map( fun(_,_,_,int,_,_,_,fum) -> int+fum) &lt;br /&gt;Seq.zip3 plays yards turnovers |> Seq.zip names&lt;/p&gt; &lt;p&gt;let doCalc = &lt;br /&gt;let stats = &lt;br /&gt;getStats &lt;br /&gt;let names = &lt;br /&gt;stats |> Seq.map fst &lt;br /&gt;let rawScore = &lt;br /&gt;stats &lt;br /&gt;|> Seq.map snd &lt;br /&gt;|> Seq.map qbcalc &lt;br /&gt;let plays = &lt;br /&gt;stats &lt;br /&gt;|> Seq.map snd &lt;br /&gt;|> Seq.map (fun (plays,_,_) -> plays) &lt;br /&gt;let components = &lt;br /&gt;Seq.zip rawScore plays &lt;br /&gt;Seq.zip names &amp;lt;| Seq.map(fun(x:int, y:int) -> System.Convert.ToDecimal(x)/ System.Convert.ToDecimal(y)) components &lt;br /&gt;</span>
</code></pre></div></div>
<p>Useful links:</p>
<ul>
<li>
<p><a href="https://www.goodreads.com/book/show/2434449.Expert_F_">Expert F# (Expert’s Voice in .Net)</a></p>
</li>
<li>
<p><a href="http://web.archive.org/web/20090109175644/http://msdn.microsoft.com/en-us/fsharp/default.aspx">Microsoft F# Developer Center</a></p>
</li>
<li>
<p><a href="http://channel9.msdn.com/pdc2008/TL11/">PDC Video : Introduction to Microsoft F#</a></p>
</li>
<li>
<p><a href="http://dberri.wordpress.com/2007/09/09/a-new-qb-score/">QB Score Stat</a> from <a href="https://www.goodreads.com/book/show/321856.The_Wages_of_Wins">Wages of Wins</a></p>
</li>
</ul>
Exploring functional programming2009-01-04T00:00:00-06:00https://www.kevfoo.com/2009/01/exploring-functional-programming<p>I am trying to heed the <a href="https://www.goodreads.com/book/show/4099.The_Pragmatic_Programmer">Pragmatic Programmer’s</a> advice to learn a new language each year and this year I figured it would not be a bad idea to look at functional languages. There is a lot of buzz and blog posts about functional programming going on right now so there should be plenty of fresh material and this won’t be my first exposure to functional programming. I’ll have to dust off some of my notes from the programming languages class I took during some of my masters work.</p>
<p>I am planning to follow along with the <a href="https://www.goodreads.com/book/show/3226758-real-world-haskell">Real World Haskell</a> book club (which <a href="http://web.archive.org/web/20090120124706/http://codebetter.com/blogs/matthew.podwysocki/archive/2009/01/04/ann-the-real-world-haskell-book-club-starts-1-5-2009.aspx">starts tomorrow</a> night) and also mess around with F# so I thankfully won’t have to rely on my one semester of <a href="http://web.archive.org/web/20101210080919/http://www.ocaml-tutorial.org/">OCaml</a> to help me get going. As a fan of sports and statistics I’ll probably try and use that as my problem domain while exploring these languages. I am really looking forward to trying to wrap my mind around functional programming as it is so different than thinking about objects and should be a great <a href="http://www.neurobics.com/">“neurobic” activity</a> for the ol’ brain.</p>
Best thing I have read all day2009-01-04T00:00:00-06:00https://www.kevfoo.com/2009/01/best-thing-ive-read-all-day<p>“Most programmers think it is a sin to write code w/o comments, but it is a greater sin to write code that cannot be understood without them” <a href="http://twitter.com/pjb3/status/1095852496">Paul Berry via Twitter</a></p>
F# Support for the SyntaxHighlighter WordPress Plug-in2009-01-02T00:00:00-06:00https://www.kevfoo.com/2009/01/f-support-for-the-syntaxhighlighter-wordpress-plug-in<p>I added the F# keywords to syntaxhighlighter.php and added the <a href="http://webdevdotnet.blogspot.com/2008/11/new-f-syntaxhighlighter-brush.html">F# brush</a> that Elijah Manor put together to the <a href="http://wordpress.org/extend/plugins/syntaxhighlighter/">SyntaxHighlighter WordPress plug-in</a>. I am not sure if anyone is still maintaining that project but you can download the zip here (<em>Update 8/3/2020: link is dead</em>) and install it the same way as the original.</p>
<p>To highlight F# code you can now do [sourcecode language=”‘f#’“]code here[/sourcecode]. You can also swap out “˜f#’ for “˜f-sharp’ or “˜fsharp’.</p>
Mini9 & Vista Quick Review2009-01-01T00:00:00-06:00https://www.kevfoo.com/2009/01/mini9-vista-quick-review<p>I have been playing around with my Dell Mini9 (1Gb RAM, 32GB SSD) netbook that I picked up recently over the holidays and finally got around to swapping the Ubuntu installation that came from the factory with a slimmed down version of Vista Ultimate. For a machine that has the type of resource constraints and intended use (email/web/light office app work) a typical full Vista install wouldn’t be practical but thanks to the <a href="http://www.vlite.net/">vLite</a> configuration tool I was able to shrink my installation footprint down to roughly 3.5Gb. If you have the right tools (a couple gig flash drive, access to a copy of Vista, and some patience) then the conversion isn’t that painful or time consuming. The one recommendation that I would like to emphasize is make sure that you have all the drivers downloaded ahead of time especially the network drivers since neither the wireless or Ethernet drivers got installed by default.. You can dump them on the flash disk as they don’t take up that much space and it will expedite the process of getting your system fully operational.</p>
<p>Here are some of the links that I relied on heavily to get me through the conversion:</p>
<ul>
<li><a href="http://web.archive.org/web/20090225070017/http://rickatnight11.com/?p=71">Rick at Night’s Install Vista on Dell Mini 9 (USB) Guide</a></li>
<li><a href="http://www.codecguide.com/download_mega.htm">K-Lite Mega Codec Pack</a></li>
<li><a href="http://web.archive.org/web/20110810135816/http://support.dell.com/support/downloads/driverslist.aspx?c=us&l=en&s=gen&os=WLH&osl=en&catid=&impid=&SystemID=INSPIRON910">Dell’s Inspiron Mini 9 (910) Driver Downloads</a> (You can use the Windows XP drivers)</li>
</ul>
<p>For a resource constrained machine it certainly does not seem sluggish. The Vista experience, while not in the same league as my tricked out M1530, is not terrible and the solid state drive really makes a world of difference in these netbooks as they are silent, low power, and fast as hell.</p>
<p>In my eyes the one serious drawback of these Mini9’s is the size and layout of the keyboard and touchpad. The keyboard on a 9” laptop obviously has to be compressed and tradeoffs conceded but for someone like me who has stubby fat fingers this becomes more of a problem than I’d thought. The other major issue I have is with the touchpad. The touch sensitive surface that comprises the touchpad runs all the way to the space bar without any buffer or separation. I continuously hit the touchpad surface causing the cursor or focus to jump to where the pointer is. The keyboard issue by itself isn’t a terrible problem, I’m sure I’ll acclimate, but when you compound that with the lack of a buffer between the space bar the whole keyboard/mouse experience becomes extremely frustrating. I bought a bluetooth notebook mouse and can turn off the touchpad so I am not impacted by inadvertent contact but that defeats the purpose of this device for me since I’d much rather have my 15” laptop if I am going to have to be sitting at a table/desk/tray to use the external mouse for any length of time.</p>
<p>Overall the Mini9 isn’t bad for couch surfing and travel but this isn’t a machine that I could use for extended periods of time in the situations where a netbook would be convenient. While I like the concept and the price point that this class of portable addresses I can’t strongly endorse the mini9 due to the layout and design of the keyboard and touchpad. s</p>