Skip to content

An iPhone lover's confession: I switched to the Nexus 4. Completely.

My Nexus 4

+++
Update: Shortly after publishing this article here, it became #1 on Y Combinator’s Hacker News. Subsequently, GIZMODO approached me and asked to re-publish it. Finally, ReadWriteWeb’s Editor-in-Chief (Dan Lyons) wrote a great follow-up. As it is difficult to keep up with responding to all the comments, I mainly focus on responding on Google+.
+++

First things first, I’d love to get in touch on Google+ and Twitter (@ralf).

Over the past few years I’ve invested a lot into Apple products and services.

My Nexus 4If you’d come by my house, you’d find four of the latest Apple TVs, two iMacs, the latest MacBook Air, a MacBook Pro, more than five AirPort Express stations and Apple’s Time Capsule. You could touch every single iPhone, from the first up to the iPhone 5, iPads ranging from first generation to fourth and we’ve lately added two iPad minis.

My iTunes Library comprises well over 8.000 songs – all purchased via the iTunes Store. No matter whom you would ask, everybody will confirm that I’m what some folks call an Apple fanboy.

The reach of Apple’s products goes beyond my personal life.

As the co-founder of Germany’s largest mobile development shop, I’m dealing with apps – predominantly iOS powered – in my daily professional life.

Driven primarily by the business I run, I tried to give Android a chance more than once.

In various self-experiments, I tried to leave my iPhone at home for the Motorola Droid, the Nexus One, the Samsung Galaxy S II and S III – and always switched straight back to the iPhone. None of those Android devices have worked for me – yet.

And then I got the Nexus 4.

When the latest Google flagship Android device shipped, I almost expected it to turn out as yet another “take-a-look-and-sell-it-on-ebay” experience. Little did I know.

It’s now almost two weeks since I switched the Nexus 4 on for the first time – and meanwhile I completely moved to it, leaving my iPhone 5 at home. Do I miss anything? Nope. Except iMessage. More to that later.

In this somewhat lengthy post, I’ll try to explain why.

My motivation is not to bash Platform A over Platform B. On the contrary: I will try to summarize my very personal findings and experience based on years of using iOS. I’ve seen the Apple platform evolve while Android was playing catch-up for so long. When iOS 6 came out, for the first time I complained about the lack of innovation in this major new release. I asked myself, whether we might see Apple beginning to lose its leading position in mobile platforms.

Before you read on, it’s important to emphasize that I’m a pro user.

I’m not the average smartphone owner, who makes just a couple of calls every now and then or runs an app once in a while. By the nature of my job and out of curiosity, I deal a lot with social media outlets, social networks and constantly try new services. With that said, my judgement might not be suitable for everyone. In case you consider yourself being a demanding power user, though, you might find this helpful.

At the time of this writing, I used Android Jelly Bean 4.2.1 on an LG Nexus 4.

Summary

Putting it into a single line: The latest version of Android outshines the latest version of iOS in almost every single aspect.

I find it to be better in terms of the performance, smoothness of the rendering engine, cross-app and OS level integration, innovation across the board, look & feel customizability and variety of the available apps.

In the following paragraphs, I try to explain why.

Performance and Smoothness of the Rendering Engine

I know there are benchmarks which measure all kinds of technical performance on a very detailed level. That’s not what I’ve done and, honestly, I’m not interested into that much. I’m talking about the performance I feel in my daily use.

Using the Nexus 4 with Android 4.2.1 is a pure pleasure when it comes to performance. I don’t exactly know what Google has done with “Project Butter” in Jelly Bean, but the result is astonishing. In the past, Android felt laggy, sometimes even slow and responses to gestures didn’t feel half as immediate as on iOS.

This has changed completely.

I’d say both platform are at least even. In some cases, Android even feels a bit ahead of iOS 6. I especially got this impression when it comes to rapidly switching between apps – which I constantly do now – and scrolling through a huge number of more complex content. (I’m not talking just tables with text here.)

While Android still doesn’t give you bouncing lists and scroll views – primarily, because Apple has a patent for this specific behavior – every transition between views has been reworked, polished and modernized. In most cases, it feels more modern, clean and up-to-date than its iOS counterpart.

Cross-app and OS level integration

One of the biggest advantages I found during my daily use is the level of cross-app and OS level integration.

Cross-app integrationThis also is the area where I got most disappointed when Apple introduced iOS 6.

In fact, I think iOS has reached a point, where usability starts to significantly decrease due to the many workarounds that Apple has introduced. All of these just to prevent exposing a paradigm like a file system or allowing apps to securely talk to each others. There is a better way of doing this. Apples knows about it but simply keeps ignoring the issues.

On Android, it’s quite the opposite. One can see the most obvious example when it comes to handling all sorts of files and sharing.

Let’s assume, I receive an email with a PDF attachment which I’d like to use in some other apps and maybe post to a social network later.

On iOS, the user is forced to think around Apple’s constraints. There is no easy way to just detach the file from the email and subsequently use it in what ever way I want. Instead, all iOS apps that want to expose some sort of sharing feature, do have to completely take care for it themselves. The result is a fairly inconsistent, unsatisfying user experience.

On iOS, you might use the somewhat odd “Open in…” feature – in case the developer was so kind to implement it – to first move the file over to Dropbox, which gives you a virtual, cloud based file system. If you’re lucky, the other app, from which you want to use the file next, offers Dropbox integration, too, so you can re-download it and start from there. All because Apple denies the necessity of basic cross-app local storage.

On Android, it’s really simple.

I can detach the file to a local folder and further work with it from there. Leveraging every single app that handles PDF files. In case I receive a bunch of mp3 files, I can do the same. And every app, that somehow can handle audio playback, can reuse those mp3 files.

Another great example: Sharing stuff on social networks. On iOS, I have to rely on the developers again. Flipboard, as one of the better examples, gives me the ability to directly share with Google+, Twitter and Facebook. On my Nexus 4, I have 20+ options. That is, because every app I install can register as a sharing provider. It’s a core feature of the Android operating system.

But it goes even further: On Android, I can change the default handlers for specific file types – much like I’m used to from desktop operating systems.

If, for example, you’re not happy with the stock Photo Gallery application, that shows up whenever an app wants you to pick an image, you can simply install one from over a hundred alternatives and tell Android to use it as its new default. The next time, you post a photo with the Facebook app – or have to pick an image from within any other app – your favorite gallery picker shows up instead of Android’s own.

All of this is entirely impossible on iOS today. I’ve stopped counting how often I felt annoyed because I clicked a link to a location in Mobile Safari and would have loved the Google Maps app to launch. Instead, Apple’s own Maps app is hardcoded into the system. And there’s no way for me to change it.

The customizability is simply stunning

Let me make this very clear: Gone are the days where home screens on Android phones almost always looked awful.

If you don’t believe me, hop over to MyColorscreen and see for yourself.

Also note that all of those are real Android home screens, not just concepts provided by designers. They are not beautifully photoshopped wallpapers, but fully functional screens with app icons and active widgets.

And all of those can be configured pretty easily just by installing a couple of apps and tweaking settings. Here is an album showing my current configuration, which I was able to achieve after just a couple of days using Android as an absolute newbie.

Getting inspired? Here are some more of my favorites:

Android Home Screen

dots&circles_dark

Android Home Screen

Now, iPhone lovers might argue, that the average Joe doesn’t want to deal with widgets, icons and custom animations.

I’ve used the same argument for years. Well, guess what, you don’t have to. The default Jelly Bean home screen looks beautiful already. But in case you want a somewhat more individual phone, the possibilities are endless.

For years, what you could do with Android, simply yielded awful looking home screens. This has changed. Significantly so.

And believe me or not, but after having configured my Nexus 4 just the way I always wanted – providing me with the fastest access to my most frequently used apps along with the most important information on a single screen – whenever I grab my iPhone for testing purposes, iOS feels pretty old, outdated and less user friendly. For me, there currently is no way of going back. Once you get used to all of these capabilities, it’s hard to live without them.

App quality and variety

Yes, there are still lots of really ugly apps on Google Play.

In my opinion, this has two primary reasons.

First, the obvious one: The lack of a centralized quality control and review. It’s great for encouraging variety, but obviously it also allows for some really cheap productions to be published to the store. Usually, you can spot those immediately from the screenshots on Google Play.

The second reason is more low-level: The way developers declare user interfaces (it’s primarily done in an XML configuration file) allows for rapidly hammering together dirty UIs. That’s what happens a lot and users can see and feel it. iOS developers tend to be more aware to involve designers and iOS UIs cannot be crapped together as easily.

However, gone are the days where the apps I use most greatly fall behind their iOS counterparts.

The Facebook app is identical in terms of the look and feel and its features. As a plus, it has better cross-app integration. The Google+ app is better on Android, but that’s to be expected. Flipboard is fantastic on Android, plus better integration. The same is true for Pulse News. The list goes on: Instagram, Path, LinkedIn, WhatsApp, Quora, Pocket, Amazon Kindle, Spotify, Shazam and Google Talk. They are all great on Android. Plus better integration. Plus home screen widgets. You sense a scheme here?

And if you want to experience some real UI magic – even if you just need an argument when you’re bumping into an iPhone owner the next time – install Zime, a highly addictive calendar for Android which features a smooth 3D animation and really innovative UI.

Talking about variety. This is, where Android’s openness pays off.

On iOS, many things I always wished to see being developed, simply cannot be done because of the strict sandbox Apple enforces around apps. On Android, I use an app to block unwanted calls. To auto-respond to incoming short messages. And to lock some specific apps with an extra passcode, so my customers don’t play with my Facebook profile, when I hand over my Nexus 4 for demos.

I also have apps that give me great insight into the use of mobile data across the device and all apps. Or the battery consumption. Or which apps talk home and how frequently.

None of it is available for iOS. And possibly won’t be at any time in the near future.

What I miss

I said this earlier: The only thing I miss is iMessages. I’m not kidding. Letting go iMessages was difficult, as many of my friends are on iPhones and used to text me via iMessage. While there are perfect alternatives (Facebook Messenger, Google Talk, WhatsApp, to name only a few), from time to time I still find a couple of unread iMessages, when I switch on my iPhone 5.

My most frequently used apps

I’m an Android newbie. During the last couple of days, I had to ask many questions and received hundreds of recommendations for apps. I installed, tried and uninstalled. And kept the great ones. My sincere thanks go out to the great Nexus and Android communities over at Google+.

In case you decided to give Android a try before you read this article, or got inspired here, I’d like to save you some of my journey. Here is a list of the apps I found most useful (and beautiful, given the high standards set by years as an iPhone addict):

Note: I always use the paid / pro version of apps, if one is available. Coming from iOS, I simply cannot adjust my eyes to in-app-ads and probably never will. Google Play now offers credit cards, PayPal and some other payment alternatives. Plenty of choice. I encourage everybody to give back to the developer economy and not just go for the free versions.

In case you’re wondering why I took the burden to include all of the links to the apps above, well, here is another advantage over iOS: Google Play allows the complete remote install via the Web. If you’re logged into your account you click the install button after visting one of the links in any browser, and wherever your phone is, the respective app will be installed silently.

My Android Wish List

Let me finish this post with a couple of wishes I’ve got for the next major version of Android, hopefully made available at this year’s Google I/O:

  • More and centralized settings for notifications, or, a notification center.The rich notifications introduced in Jelly Bean and the overall usability of the notification bar and drawer are already far better than those on iOS. (On a side note, I never understood why usability masters like the Apple engineers decided to make the “clear” button so tiny, that you can hardly hit it without using a magnifying glass.)However, the level of customization you get for Android notifications is currently 100% up to the developers.This means, even though Android offers a great variety of possibilities, they are not consistently available in all apps. In fact, some apps barely let you switch notifications on and off, while others allow you to customize every aspect, from notification sound to the color of the notification LED to do-not-disturb times. These should be made available globally and enforced through the APIs.For example, I’d love to be able to receive notifications on Facebook messages, but don’t want them to show the full message preview in the notification bar.There are some apps, which let you chose whether you want a complete preview, or just a standard “you’ve got mail” message, without revealing its content. But it’s up to the developer whether you’ve got the choice or not.Or: Android has support for a notification LED that can flash in different colors. I configured the LED on my Nexus 4 to blink green on new WhatsApp messages. Incoming stuff from Facebook notifies in blue and new business mail causes the LED to flash in white. What sounds like a tiny feature is really valuable: While sitting in a meeting, you can grasp immediately whether you might want to check your phone right away or not. Unfortunately, not all apps let you customize the LED color. Again, it’s up to the developer to provide these settings as part of their application. This belongs into a centralized notification center.Options I’d like to see centralized: LED color, notification sound, content preview. They could also be exposed on app level, but the Android Notification Center should allow for overrides.
  • Support for multiple accounts in Google Now.I’d love to see Google Now taking advantage of multiple configured Google accounts. On my device, I’d like my Google Apps for Businesses account to drive the calendar based cards but my private one for everything else (location and browsing history, etc.). Currently, Google Now can only leverage a single account. I therefore had to switch browsing and location history on for the Google Apps for Business account I use professionally. This should be a no-brainer for Google and I keep wondering, why the folks at Google tend to forget these multiple-account scenarios.
  • Solving the inconsistencies grouped around the back button.I’ve actually found this on many lists and from what I’ve read it has already gotten better in Jelly Bean. However, at times I still get confused about the multiple navigation hierarchies that are caused by the native back button which is part of the OS and a second back button available within apps. Oddly enough, the mostly fantastic Google+ Android app suffers from this issue, too. Sometimes I end up on my home screen just because I “went back to far”. It’s not a big issue, but one which needs to be addressed.As a starter, how about giving the damn back button a different color if the next time you hit it, you’ll be taken out of the app.
  • Indicate whether an app uses Google Cloud Messaging or some other technology  stay connected.I believe this one to be huge: On iOS, there are essentially no long-running background processes, except for VoIP or Navigation apps. This means, all apps that notify users of incoming data while they are inactive, make use of a centralized service operated by Apple, called Push Notifications. It has a great advantage with respect to battery life, as there is only a single process on the OS level, that monitors all incoming messages and distributes them to the targeted apps, instead of potentially many apps doing whatever they want to do to stay connected.Android has a similar service, named Google Cloud Messaging.Unfortunately, there is no obvious way to differentiate apps that leverage this service from those, that constantly poll or even keep a socket connection to their home servers.I’d love to see the ones making use of Google Cloud Messaging identified in Google Play and on the OS level, maybe in the already available App Info screen. That way, I could dramatically increase battery life by stopping those that constantly talk back home and encourage developers to make use of Google’s Cloud Messaging service.

One last word

At the beginning I stated, that I tried Android many times before and it never worked for me. I figured, there are two main reasons for this. First, Android has made a major step forward with Jelly Bean. It just wasn’t on pair with iOS before. Second, and more important, I found the stock Android experience provided by Google the best you can get. After switching to the Nexus 4, I tried my Samsung S III again, and it did not work for me.

What Samsung does with its TouchWiz modifications and many of the other tiny changes – and other non Nexus vendors, too – totally ruins the experience for me. If you’re coming from iOS I highly recommend choosing one of the Nexus devices with guaranteed updates and a clean Android environment the way Google envisioned it.

Closing it off

This was rather lengthy. I figured, switching the mobile OS platform should be worth an in-depth view. Hence this post. I hope you’ve enjoyed it.

Will I sell my iPhone 5? No. No. No. I never sold one. I’ll keep it. Maybe it’ll manage to win me back with iOS 7.

Looking forward to your feedback in the comments. Or on Google+, Facebook and Twitter.

Enable Google Now with Google Apps for Business account

Enable Google Now for Google Apps for Business customers

I recently got my brand new Nexus 4 and it totally changed my opinion about Android (for the better).

I couldn’t emphasize more how Samsung ruins the Jelly Bean experience with what it does to it on the S3 – but that’s a totally different story. In short: I start to fall in love with Android since I own the Nexus 4.

One quirk that bothered me: I was unable to use Google Now. When swiping from bottom to top, no Google Now cards showed up. Also the Settings for the Google Search app had no option to turn on Google Now.

Turns out, if you’re setting up your Android phone with a Google Apps for Business account – not a vanilla Gmail one – Google Now has to be enabled by your Google Apps administrator.

Unfortunately, the settings to do so have been burdened in the Google Apps control panel to a level, where I wonder whether Google does not want us to find those or their usability engineer called in sick the day they got added.

If you came here, you might be as desperate as I was, so here is the path to enabling Google Now:

  • Launch your Google Apps control panel by going to http://google.com/a/<your custom domain>.
  • Click Organization & Users.
  • Click Services.
  • Scroll all the way down until you reach Google+.
  • Click on the Configure premium features link.
  • In the left pane select Mobile.
  • Scroll all the way down to Android settings.
  • Check the boxes for Enable Google Now and the optional Enable Lock Screen Widgets.
  • Do not forget to hit Save.

The next time you launch the Google Search app on your Android phone, Google Now will prompt you whether you want to activate the service. In case you’ve got multiple Gmail / Google Apps for Business accounts, you can select the one used for Google Now.

Below are some screenshots in case you’re still lost. If you found this helpful, you might want to add me on Google+ and Twitter.

Enabling Google Now for Google Apps for Business customers Enabling Google Now for Google Apps for Business customers

Bookmark the correct Google+ account with Google multi sign-in

Google Account Chooser

If you are like me, you might have multiple Google / Gmail / Google+ accounts.

Google made things more complicated by initially deciding to force the use of @gmail.com accounts for Google+, leaving all Google Apps customers with custom domains out in the cold. In case your employer is a Google Enterprise Apps customer, you might very well have at least three Google accounts – like me.

While Google supports multi sign-in for quite a while, I ran into a problem trying to bookmark a link to Google+. The way the system is designed, it always uses the account you’ve first signed into as your primary one. That happens to be the one I use professionally, but not the one, my Google+ profile is linked to.

Turns out, there is a simple solution:

Back in August, Google quietly launched Account Chooser, which you find at https://accounts.google.com/AccountChooser. Mine looks like this with the red numbers added by me:

To bookmark a link to a specific Google+ account, you simply use https://plus.google.com/u/<account number>/ – so if I want to bookmark the mail@ Google+ profile, I would use https://plus.google.com/u/2/. Please note, that the trailing slash is important.

For this to work, it is key that in case you ever sign out (or the cookies expire), you sign back in the exact same order.

If you like this, you might want to add me to your circles over on Google+ and start following my Twitter account.

 

 

Running a clean install of Windows 8 Pro on your MacBook Air via Boot Camp including Trackpad support

7a08cd67-9aa9-4070-8a34-dfa710170a19_20

Wondering why I chose such a terribly long headline?

Well, there are many articles out there dealing with installing Windows 8 on MacBooks, however, most of them did not deal with exactly my situation. Before we move forward, you might want to follow me on Twitter for feedback and updates.

Here is a brief checklist. Compare it with what you want to do and find out, whether this article is for you:

  • I am using the Windows 8 Pro version released to market by Microsoft on October 26nd, 2012. Not any of the preview versions.
  • I am installing on the latest MacBook Air running Mac OS X Mountain Lion with all updates applied.
  • I use the downloadable version, without any optical media (DVD or so).
  • I only use an installed version of Windows 7 to get the Windows 8 Pro downloadable, after that, I remove it.
  • I’m doing a clean install, not an upgrade over an existing version of Windows 7.
  • I want to get the MacBook’s Trackpad fully working, including two-finger tap for right-clicks.
  • I want to activate my copy of Windows 8 Pro with the product key I purchased for US$ 39.99 even though I don’t have a previous version installed.

Sounds a little bit exhaustive but I really want to make sure, we’re on the same page as to the prerequisites. As always, you’re proceeding with the following at your own risk.

Getting Windows 8 Pro and creating a bootable USB

I purchased the downloadable version of Windows Pro 8 straight from the Microsoft website and ended up with the product key in my inbox. Note: With previous versions you purchased through the website and after checking out with your credit card got access to downloading the ISO images.

This has changed.

With Windows 8 Pro Microsoft forces you to download the Windows Upgrade Assistant and run it on an existing version of Windows, preferably on Windows 7. The application will prompt you for your credit card details and then allow you to download Windows 8 Pro. Once the Upgrade Assistant has completed, it’ll give you an option to download a Windows 8 Pro ISO image, which you would want to do.

Once the download has finished, move the ISO file to a removable USB memory stick or external drive. Boot into Mac OS X and move the ISO file from your external storage to the desktop. Launch Boot Camp Assistant. Select “Create a Windows 7 install disk” and deselect all other options. Choose the ISO file and confirm that you erasing the USB memory stick is fine for you.

Boot Camp Assistant will create a bootable copy of Windows 8 Pro from the ISO file.

Installing Windows 8 Pro

When Boot Camp Pro comes up with the suggestion for the to-be-created Windows partition, you might want to increase its size. I chose to go for 60 GB for the Windows 8 Pro partition. Let Boot Camp do the partitioning changes and restart. It’ll boot right into the Windows Pro 8 installer. Go through the Windows setup assistant. In case it rejects to install on the newly created BOOTCAMP drive, you might have to select Drive Options and Format before you can move on.

Let the Windows installer complete. This is really just a standard Windows install, so I spare step-by-step instructions. They can be found elsewhere.

Once the install has finished, you should have a fully working copy of Windows Pro 8 with two major issues:

  • The trackpad only works as a mouse, so you can’t do right-clicks.
  • You’re prompted to enter the product key but the one you’ve purchased gets rejected.
  • If you try to open the Boot Camp preference pane, you get an error: “An error occurred while trying to access the startup disk settings.”

Getting the trackpad to work

The next issue to address is the trackpad. Unfortunately, as of writing this post, Apple has not updated Boot Camp to support Windows 8 Pro. You should install the Boot Camp Window support tools, however, you will run into the above mentioned issue when trying to access the Boot Camp control panel.

Fixing it, requires a bit of low-level hacking.

  • You will need a system tool named mt.exe. To get it, install a free version of Microsoft Visual Studio Express for Windows 8 from here.
  • Once the install has completed, launch an elevated command prompt. To do so, go to the Start Screen, type CMD and press Ctrl-Shift-Enter.
  • Change the directory to your desktop. In my case, I had to run cd c:UsersRalfDesktop
  • Copy the AppleControlPanel.exe file from its location to your desktop via copy c:windowssystem32AppleControlPanel.exe .
  • Next run mt.exe -inputresource:AppleControlPanel.exe;#1 -out:exctracted.manifest
  • Open the extracted.manifest file with Notepad and replace highestAvailable with asInvoker
  • Save the modified extracted.manifest file and run mt.exe -outputresource:AppleControlPanel.exe;#1 -manifest extracted.manifest
  • Double-click AppleControlPanel.exe on your desktop and you will be able to adjust all your trackpad settings.
  • You might optionally want to move the modified .exe back to /windows/system32 replacing the AppleControlPanel.exe that’s already there. This will allow you to launch the control panel from the Windows taskbar icon.

Solving the product key issue

This one really drove me nuts and I still cannot believe, Microsoft screwed up on this, essentially ruining the entire experience for me. In essence, the US$ 39.99 product key you can buy online is an upgrade-only one. Microsoft actually does state this in the fine print but I still find the marketing around getting Windows 8 Pro for an introductory price misleading and confusing.

It’s not that I don’t agree with the low price for just the upgradeable version, it’s how they handle it technically what really is an epic fail. You have to have Windows 7 installed and install Windows 8 Pro on top of it for your product key to work. This is just stupid. They should have supported clean installs and just make the Activation Wizard ask for your existing Windows 7 product key and the newly purchased Windows 8 Pro one.

Completely disallowing clean installs is just dumb and a typical Microsoft-does-not-get-it.

However, it turns out there is a hack to use your upgrade product key. Obviously, this will very likely not be supported by Microsoft, but it lets you move forward:

  • Run the registry editor (regedit)
  • Find the following key: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionSetupOOBE 
  • Change the value for MediaBootInstall from 1 to 0
  • Open an elevated command prompt
  • Run the following command: slmgr -rearm 
  • Reboot

(Credits)

That’s it

I know this was a bit of a tough journey.

If all of the above sounds too cryptic to you, you might want to wait until Microsoft allows you to purchase fully working product keys and Apple updates its Boot Camp utilities. On the other hand, the entire process – if strictly followed – takes only about 20 – 30 minutes and you end up having a clean installed, fully working, fully activated copy of Windows 8 on your MacBook.

Hope this helps.

App.net app for your iPhone and other third party services

AppApp iPhone app for App.net

If you are addicted to Social Media and haven’t spent the last couple of weeks behind a rock, you sure have heard about App.net. I’m @ralf there, the same as on Twitter.

In started with a blog post from well-known Dalton Caldwell, promoting the idea of an ads free, developer friendly social platform. While for whatever reason, most of the journalists did not get Dalton’s idea and put App.net in the corner of being a Twitter clone, @sneakyness put it best on App.net:

“Anybody that says #AppNet is a #TwitterClone obviously hasn’t paid any attention to what’s going on. The website is merely a demonstration of what can be. This is a social graph lego kit with built in users!”

Dalton went for crowd funding the new platform and asked people to back the project by paying for a one year membership. If App.net wouldn’t get 500.000 US$ by August, 13th 2012, his vision would not come to life. Well, when I started typing this article, the funding counter was at 542.200 US$.

One of App.net’s promises from the beginning is to make the service extremely developer friendly. If you take a look into their early API specification – which you can actively work on and contribute to – they seem to be serious, it’s a developer’s dream.

Given a great idea, a fantastic alpha and a great API, developers from all over the world jumped onto the bandwagon and started creating amazing things, even when they couldn’t be sure, that App.net would ever reach its funding goal.

I started a page listing mobile apps, web based services, desktop clients, libraries and sample code on the official App.net GitHub Wiki, trying to guide new members to experiences besides App.net’s own alpha site.

Here is a quick rundown of the things I’ve tried myself:

  • AppApp is a working iPhone app. The featured article image shows it’s current status. It’s maintained as an open source project on GitHub. While the creators, @sneakyness and @bsneed, ran out of tester slots as of this writing, in case you’re an iOS developer or got a friend, you can grab the code and build it yourself.
  • shrtmsg is a mobile web app, working fantastically on iPhones. Maintained by @matthew, it currently offers the most complete feature set on a mobile, though I ran into some performance issues, which given the enormous increase in traction App.net has gained over the last couple of days, do not make me wonder.
  • quickApp by the omnipresent @q was one of the first third party “experiments” leveraging the alpha API. It still is the best way of experiencing the global stream in real-time on a desktop. Even better than App.net’s own web site. The good thing is, it’s exactly what the App.net team wants to see happening.

These are the ones I personally use all the time. Again, I keep updating the list of the growing number of initiatives.

As many of my readers know, I’m running Germany’s largest iOS and Android development company, so are we planning to build an App.net client? Honestly, I don’t think so. Here is why: I strongly believe, that the third party Twitter ecosystem has brought us some of the most innovative and perfect mobile user experiences grouped around real-time social platforms (think TweetBot).

No offense meant, but much of what we are going to see next will be attempts to clone Tweetbot’s, Tweetie’s or Twitterrific’s best ideas and come up with the next big social mobile client. On the other hand, for the folks at Tapbot, the Iconfactory or even Loren Brichter, it would be just so easy to also support App.net. They already made some of the best Twitter clients, it’s just one more step.

And I seriously hope they hear me and will just do so.

I’m ending this at 549.550 US$. And counting. Exciting times and thanks to @dalton and @berg for bringing us something new to play with and think about.

CakePHP hasAndBelongsToMany relationship queries demystified (HABTM, Contained, Pagination and more)

cake-logo

Introduction

CakePHP is a pretty powerful development php framework. If you are like me, you understand that Ruby on Rails is way cooler these days, but do not feel ashamed to admit you’re coding in PHP because sometimes it just gets the job done faster. And without being less elegant if you’re using the right tools.

I’ve used CakePHP for Google+ Counter‘s front end tier and used it for numerous other professional web projects and frequently share my findings via Twitter (@ralf).

However, my relationship with the framework is a love-hate one. The main reason: CakePHP’s documentation is far from good. At a first glance, it appears to be fairly complete and comprehensive. There are tutorials, an API documentation and even complete online books. Unfortunately, once you start coding real-world projects, you’ll find that many parts of the docs to not be unambiguous and leaving many questions open.

Worst thing: The above is mostly true for stuff related to CakePHP’s powerful object-relational mapper (ORM).

As efficiently querying the model is an extremely important part of any web application, it’s a shame that CakePHP leaves developers guessing and in trial-and-error mode most of the time. Hop over to Stack Overflow and see for yourself. Even on the #CakePHP IRC channel, users are asking the same query related questions over and over again – a clear sign for a lack of documentation.

Recently I ran into what I expected to be a very basic and everyday scenario that ultimately took me days to figure out. Once I got it right, I felt so happy, that I wanted to give something back to the community. Hence this article.

All of the below is valid for CakePHP 2.x.

The Model

I had to model a pretty common scenario: Users belonging to groups. Obviously, any user can belong to many groups and any group can have more than a single user. In CakePHP the relationship the User and the Group model share is called “has and belongs to many”, abbreviated as HABTM.

I started with fairly straightforward model classes (omitting the validation code for brevity):

<?php
class User extends AppModel {
	public $name = "User";
	public $displayField = 'name';
	public $hasAndBelongsToMany = array('Group'=>array('className'=>'Group'));
}
?>
<?php
class Group extends AppModel {
	public $name = 'Group';
	public $displayField = 'name';
	public $hasAndBelongsToMany = array('User'=>array('className'=>'User'));
}
?>

Note that I’ve set the models up having the HABTM relationship with each others. Behind the scenes CakePHP will leverage a join table which – by convention – is named GroupsUsers. Three columns (id, group_id and user_id) will be populated to link m users to n groups.

Challenge I:
How to save a single user belonging to multiple groups and have CakePHP add the correct records to the join table?

Sounds pretty basic, I know. And in fact, it’s not all that complicated. The problem: CakePHP’s documentation! For example, the chapter about saving data for the {php}saveAssociated(){/php} method states: “Method used to save multiple model associations at once.”

Immediate questions that would need clarification:

  • What exactly does saving model associations mean? Does it mean that the related models are saved to the database, too? If so, what else does {php}saveAll(){/php} do in addition?
  • In case neither of the data already exists in the database, does save also mean initially create?
  • Does this only work for hasOnehasMany and belongsTo relationships?
  • Does it work for hasAndBelongsToMany relationships, too?

The docs leave us guessing.

My finding: Whatever I tried to do, neither could I get {php}saveAssociated(){/php} nor {php}saveAll(){/php} to create and save a new user along with a couple of groups to my database. If there is a way, at least nobody in the Cake community seems to be aware of it [1][2].

When reading through the docs, you get the impression that your code should look somewhat like this:

$this->User->set(
    array(
        'id'=>null,
        'name'=>'Dummy User',
        'email'=>'dummy.-mail@example.com',
        'password'=>'dummy',
        'password_confirmation'=>'dummy',
        'role'=>$role,
        'company'=>'Sample GmbH',
        'dealer_number'=>'0000',
        'phone'=>'+49 ',
        'status'=>$status,
        'Group'=> // What should go here?
    )
);
$this->User->save();

If prior to creating the user you create a group and put a reference to it as in line 13 of the following code snippet, CakePHP correctly inserts the appropriate record into the join table. So this works:

$firstGroup = $this->Group->find('first');
$this->User->set(
    array(
        'id'=>null,
        'name'=>'Ralf Rottmann',
        'email'=>'ralf@example.com',
        'password'=>'secure',
        'password_confirmation'=>'secure',
        'role'=>'admin',
        'company'=>'acceleract GmbH ',
        'dealer_number'=>'00001',
        'phone'=>'+49 123 456 789',
        'status'=>'true',
        'Group'=>$firstGroup
    )
);
$this->User->save();

In line 1 we fetch a group object for an existing group from the database and in line 14 we associate it with the to-be-created user. Saving the user does also insert a new record into the join table.

You might be tempted to just hand over an array of groups as in the following snippet (fetching it via {php}find(‘all’){/php} in line 1 and handing it to the new user object in line 14):

$groups = $this->Group->find('all');
$this->User->set(
    array(
        'id'=>null,
        'name'=>'Ralf Rottmann',
        'email'=>'ralf.rottmann@example.com',
        'password'=>'secure',
        'password_confirmation'=>'secure',
        'role'=>'admin',
        'company'=>'ACME GmbH ',
        'dealer_number'=>'00001',
        'phone'=>'+49 123 456 789',
        'status'=>'true',
        'Group'=>$groups
    )
);
$this->User->save();

Turns out, CakePHP does not insert anything into the join table.

The complete association gets lost. To be clear, if you change line 14 from ‘Group’=>$groups to ‘Group’=>$groups[1] a record is inserted into the join table. If you hand over more than a single group, nothing gets inserted. Now, that’s what I call non-intuitive!

The correct solution is to hand over an array of just the group ids:

$groups = $this->Group->find('all');

$groupIds = array();
foreach ($groups as $group) {
    array_push($groupIds, $group['Group']['id']);
}

$this->User->set(
    array(
        'id'=>null,
        'name'=>'Ralf Rottmann',
        'email'=>'ralf.rottmann@example.com',
        'password'=>'secure',
        'password_confirmation'=>'secure',
        'role'=>'admin',
        'company'=>'CakePHP GmbH ',
        'dealer_number'=>'00001',
        'phone'=>'+49 123 456 789',
        'status'=>'true',
        'Group'=>$groupIds
    )
);
$this->User->save();

In lines 3-6 we create an array of group ids which we hand to the to-be-created user in line 20. CakePHP correctly inserts all the right records into the join table. Why the hell this is not clearly stated nor we find any example of such a frequent use case anywhere in the documentation remains the secret of the authors.

Challenge II:
Finding all users belonging to a specific group

Assume we’ve used the above to create a couple of groups and hundreds of users with each user belonging to more than one of these groups. Our join tables contains all records linking user IDs to group IDs. Next we want to get all users belonging to a specific group. Pretty common scenario, one would think. Again, not so in CakePHP.

Without bothering you with my trial-and-error learning curve, here is the second piece of totally non-intuitive code:

$group = $this->Group->find('first',
    array(
        'conditions' => array(
            'id'=>100
        )
    )
);

In order to find all Users belonging to a specific Group you actually do have to perform your find on the Group model and not on the User model. I found absolutely no way doing it the other way around. No matter how complex find parameters I handed over to the User model, I never got the result I was looking for. The response array for the above query looks something like this:

array(
	'Group' => array(
		'id' => '100',
		'name' => 'Handelspartner',
		'created' => '2012-07-29 14:59:14',
		'modified' => '2012-07-29 14:59:14'
	),
	'User' => array(
		(int) 0 => array(
			'password' => '*****',
			'id' => '6041',
			'name' => 'Dummy User 002',
			'email' => 'dummy.002-mail@example.com',
			'role' => 'store',
			'company' => 'Number 002',
			'dealer_number' => '0000002',
			'phone' => '+49 002',
			'status' => 'new',
			'created' => '0000-00-00 00:00:00',
			'modified' => '0000-00-00 00:00:00',
			'GroupsUser' => array(
				'id' => '15639',
				'user_id' => '6041',
				'group_id' => '100'
			)
		),
		(int) 1 => array(
			'password' => '*****',
			'id' => '6042',
			'name' => 'Dummy User 003',
			'email' => 'dummy.003-mail@example.com',
			'role' => 'admin',
			'company' => 'Number 003',
			'dealer_number' => '0000003',
			'phone' => '+49 003',
			'status' => 'new',
			'created' => '0000-00-00 00:00:00',
			'modified' => '0000-00-00 00:00:00',
			'GroupsUser' => array(
				'id' => '15642',
				'user_id' => '6042',
				'group_id' => '100'
			)
		),
		(int) 2 => array(
			'password' => '*****',
			'id' => '6045',
			'name' => 'Dummy User 006',
			'email' => 'dummy.006-mail@example.com',
			'role' => 'store',
			'company' => 'Number 006',
			'dealer_number' => '0000006',
			'phone' => '+49 006',
			'status' => 'false',
			'created' => '0000-00-00 00:00:00',
			'modified' => '0000-00-00 00:00:00',
			'GroupsUser' => array(
				'id' => '15652',
				'user_id' => '6045',
				'group_id' => '100'
			)
		),

Challenge III:
Finding all users belonging to a specific group filtered on a property of the User model

While in written, the headline might sound crazy, there are actually very common use cases demanding for this sort of query:

  • Show me all users belonging to the admin group that have a status of “locked”.
  • Show me all posts with the tag “cakephp” authored by @ralf.

Again, I spare you my days of trial and error. Here is the correct find query:

$result = $this->Group->find('first',
    array(
        'contain' => array(
            'User' => array(
                'conditions' => array(
                    'status' => 'new'
                )
            )
        ),
        'conditions' => array(
            'id'=>100
        )
    )
);

Before this will work though, there is one more thing you have to do and again, I don’t understand why the documentation does not at all emphasize this in the utmost possible way: You have to make your Group model containable!

It’s as simple as putting {php}public $actsAs = array(‘Containable’);{/php} into your model’s class declaration. If you don’t do this, CakePHP will not give you any error, but it will simply ignore the conditions you set for the related model. This is how my Group model ends up looking like:

<?php
class Group extends AppModel {
	public $name = 'Group';
	public $displayField = 'name';
	public $hasAndBelongsToMany = array('User'=>array('className'=>'User'));
    public $actsAs = array('Containable');

    public $validate = array(
		'name'=>array(
			'Valid name'=>array(
				'rule'=>'notEmpty',
				'message'=>'Bitte geben Sie einen Gruppen-Namen ein.'
			)
		)
	);
}
?>

Another one of my favorite non-intuitive CakePHP aspects: In the above example you have to make the Group model containable, while it actually is the User model that is contained. Note, that the User model does not have to act as containable but again, once you detach the containable behavior from the Group model, you will no longer be able to constrain on properties of the User model in this HABTM scenario.

Supporting pagination

If your app requires you to support pagination, you might feel tempted to extend the above query like so:

$group = $this->Group->find('first',
    array(
        'contain' => array(
            'User' => array(
                'conditions' => array(
                    'status' => 'new'
                ),
                'limit' => 5,
                'page' => 3
            )
        ),
        'conditions' => array(
            'id'=>100
        )
    )
);

This will yield a Model “User” is not associated with model “3” error. The limit statement alone works, it’s the page that causes the error. The reason for this: page is syntactic sugar offered by CakePHP without a one-to-one representation in SQL. The framework doesn’t seem to recognize it when attached to contained models.

You can make use of paging by using SQL’s native syntax which is:

limit <start index>, <number of records>

CakePHP’s

'limit' => 5,
'page' => 5

would become

limit' => $page*$limit.', '.$limit

which would evaluate to

'limit' => '25, 5'

That way, you can page through the records of any contained model. Just one of those little CakePHP inconsistencies you should be aware of.

Challenge IV: Adding the user to an additional group

You might guess it by now: No, it’s not straight forward to have CakePHP insert an additional record to the join table.

While I don’t provide a code snippet for this, all you need to know is that CakePHP always deletes all existing records in the join table for a given model when you make a change. Though this is absolutely counter intuitive, you did understand correctly: Prior to inserting any new record for an existing user into the join table, CakePHP deletes all existing entries for this user.

In fact, this has been such a long standing and well known issue in the CakePHP community, that people started creating behaviors to work around this. Unfortunately, I could get none of these to work.

To solve Challenge IV you would have to do the following:

  • Find all groups the user belongs to.
  • Move just their IDs to a new array. (Remember Challenge I?)
  • Add the ID for the additional group the the array.
  • Save the user setting the Group field to the array of IDs.

If you do it in any other way, CakePHP will remove existing join table records without asking.

Closing remarks

I’m not a CakePHP professional.

That means, all of the above are my findings from days of trying and starting to hate the incomplete documentation. I don’t know, whether what I’ve outlined is the only way of doing what I wanted or if I even describe the best approach. I just did not get any better response from the community, nor could anybody point me to the “Cake way” of addressing these common requirements. All I’m sure of is, it works.

And while folks in the #CakePHP IRC channel more often than not accused me to RTFM, I couldn’t find any of the above clearly explained anywhere else.

Should you have any comments, please do make use of the comments feature. And don’t forget to follow me on Twitter. I’m @ralf there and always open for a constructive conversation.

Run Wireshark on Mac OS X Mountain Lion

Wireshark · Go deep.

Apple has removed X11 from the Mountain Lion distribution.

If you were running Wireshark on Lion and upgraded to Mountain Lion, or installed Wireshark on a fresh copy of Mountain Lion, it will no longer run.

You can bring it back by following these steps:

  1. Uninstall Wireshark by trashing the Wireshark.app bundle from your ~/Applications folder.
  2. Download the XQuartz installer from http://xquartz.macosforge.org/landing/ and run it.
  3. Restart.
  4. Download the latest Wireshark version and install it. Some posts on Stack Overflow suggest only older versions would run under Mountain Lion, but I cannot confirm this.
  5. On first install, Wireshark might pop up a dialog asking “Where is X11?”. Browse to ~/Applications/Utilities and select the XQuartz.app bundle.

That’s it. You’re done. If this has helped you, I’d appreciate winning you as a new follower on Twitter:


//

Follow

Get every new post delivered to your Inbox.