HackerWeb is one of my most favorite side projects.
It's yet another mobile-friendly minimalistic client for Hacker News. It's a web-only client for the past few years, and now I've built an iOS client, which turns out to be my first ever iOS app publicly available on the App Store! 🚀
The backstory
On June 2011, I made a statement:
Making a web app feels (exactly) like a native app sure takes effort. Still, it's doable.
On October 2011, I was playing around with -webkit-overflow-scrolling: touch
, introduced with the release of iOS 5.
On Feb 2012, I released HackerWeb (known as "HNmobile" at that time). I tried to coin the phrase "Looks and feels like a native iOS app" for the web app. I believe that HackerWeb is the first ever web app that tries to ambitiously cross the boundary between native and web.
On March 2012, I wrote an article on how I built it. It got submitted to Hacker News and to my surprise, appeared on the front page.
Feedback has been remarkable.
Behold @cheeaun 's very native looking Hacker News mobile web app. http://cheeaun.github.com/hnmobile/#/
HN mobile web app http://cheeaun.github.com/hnmobile/landing/ … #hnmobile Now you tell me that a mobile web app can do everything a native app does... as easily!
Holy shit. A webapp that is better than a lot of native apps: http://cheeaun.github.com/hnmobile/#/
Really impressed by @cheeaun's Hacker News mobile web app. Much better than any native clients/the full site on iOS.
This is quite possibly the best iOS web app I've ever used, http://cheeaun.github.com/hnmobile/ it's almost perfect in every way I can think of…
This web app feels snappier than every native HN app I've tried. You should give it a try
This is THE BEST and ONLY useful way to read Hacker News on an iPhone or iPad. Bookmark.
Time goes by as I continue improving the app bit by bit.
Since it's a web app, it auto-updates itself quietly and doesn't need uh... much announcements.
- On 26 March 2012, I wrote a second article with more details and examples.
- On 2 June 2012, I made it work on the iPad.
- Around September 2012, I updated the UI for iOS 6.
- On 12 Dec 2012, I renamed the app to HackerWeb.
- On 21 January 2013, I gave a talk about the things I learnt while building the app.
- On 21 December 2013, I updated the UI again for iOS 7.
Okay, more tweet reviews:
I use HackerWeb every day, best thing ever.
HackerWeb A simply readable Hacker News web app to bookmark on your mobile phone http://buff.ly/1tLaNwR - and it's open source :)
Have you guys seen HackerWeb? HackerNews reading pleasure (mobile included)
just discovered #hackerweb today, best mobile experience I've seen so far http://hackerwebapp.com/
Ever since HackerWeb is launched, there's been a few relevant articles and JavaScript libraries popping up on the Internet:
- March 2012 - Will HTML5 iPhone apps ever cross the Uncanny Valley of Native UI?
- September 2012 - 10 tips for getting that native iOS feel with PhoneGap
- November 2012 - Anatomy of a Native Feeling HTML5 iOS App
- December 2012 - The Making of Fastbook: An HTML5 Love Story
- December 2012 - Junior
- September 2013 - Cloning the UI of iOS 7 with HTML, CSS and JavaScript
- September 2013 - A Beginner's Guide to Perceived Performance: 4 Ways to Make Your Mobile Site Feel Like a Native App
- October 2013 - Ionic Framework
- February 2014 - Framework7
- January 2015 - Native Web Apps
...and probably a few more which I've missed.
Obviously at this point, it's clear to say that I'm quite obsessed with "native".
When React Native is announced on January 2015, I was super excited and a little sceptical at same time. I gave it a quick try and wasn't really satisfied with it. Perhaps it was too new for me and I didn't get it yet.
After attending ReactEurope on July 2015, I become inspired again to try React Native. It took me few weeks and finally, the iOS project comes alive.
Accepting the challenge
Previously, I have to make a web app that looks and feels like a native app. It was really difficult especially the time when I start doing this with the iOS 5 skeuomorphic design. I totally wouldn't recommend trying this for production web sites at all.
This time, I have to make a native app that looks and feels like the original web app. I personally find this ironic because it actually comes full circle. I have a feeling that I need to prove that it can be done the other way around.
I set a few requirements for my "Minimum Viable Product":
New logo. I've never been satisfied with the old logo. This app needs a new one.
No new features. It needs to be exactly the same as the current web app. There were a lot of new ideas during the building phase, but I manage to get a hold of myself and stick to the end goal 😎
Must use Safari View Controller. Technically it's a much better built-in browser or in-app web view. It has all the power of Safari, so it shares the same cookies and session as Safari app itself. It's more secure and convenient for users at the same time. The only caveats are the minimum iOS 9 requirement and users who prefer other 3rd-party browsers like Chrome.
Great performance. With React Native, I find no difficulty at all to make the app feels fast. My concern is a pretty classic problem with every app out there: super long list of items on the screen. The threaded comments interface is pretty challenging especially for Hacker News where the length of a comment text can fill up the whole screen and the nested levels of threads can be quite deep. "Who is hiring?" posts become a different kind of thread where all comments are on the root level and the count can go up to 600 or more.
Designing the logo
The first logo/icon was designed in 2011. The first 3 iterations feature the letter 'Y' because it matches Hacker News's favicon, which is actually the logo of Ycombinator. This 'Y' logo was never meant to be a logo for Hacker News.
Paul Graham mentioned this before:
The YC logo on HN is not the site's logo; it's a very small banner ad.
However, for the sake of familiarity, most Hacker News apps use 'Y' even though there's no letter 'Y' in the name of the site at all.
So in the 4th iteration, I decided to change the letter to 'H' while rebranding the app as 'HackerWeb'. It's a very simple logo until someone mentioned to me that it looked too similar to Harvest's logo. I admit that I didn't put much effort at all in this design iteration and the similarity is purely coincidental.
In the 5th iteration, it's still 'H' with a flatter design, but contains a hidden 'Y' in the orange background (Not sure anyone notice?).
I've done a lot of sketches.
I really like this one as it was inspired by the Monument Valley game.
And then I moved on to this:
This design really caught my eye because I was focusing on combining the letter 'H' and 'W' into one. The 'H' is "folded" to form a hidden 'W'. It's both 2D and 3D at the same time depending on how a person look at it. The blue side of the logo looks like a tilted lowercased 'y', which I think is a nice touch.
The colors somehow look a bit "off" so I played around with colors and gradients.
I choose orange and blue. Orange symbolises the web app while blue is for the native app. Right in the middle, it blends together signifying the boundary between web and native. Finally I got to replace the orange background with a white background, which I think is more complementary to the cleanliness and undistracting nature of the app.
The final winner is this:
Building with React Native
React Native is simply amazing.
Few notes:
- As I'm already familiar with React, it's surprisingly easy to get started with React Native. Everything is still about components, JSX, states and props.
- CSS in JS. Yes, it takes a while to get used to it. In React Native, there's a limited set of CSS properties available for styling. Flexbox is definitely your best friend.
- There's still no one true answer for the navigation system in React Native. There is an experimental implementation coming soon. However I prefer using
NavigatorIOS
because it feels more native on iOS despite its limited API and occasional bugs. - Linking native libraries used to be quite troublesome but it's easier now with rnpm (React Native Package Manager).
- Even though I can
require
orimport
modules, React Native doesn't run in the Node environment. For some modules, I'll have to compile them into standalone modules with Browserify. - I use Alt for managing data and states.
ListView
unfortunately doesn't work like iOS'sUITableView
. It's slightly worse in performance when rendering huge lists and doesn't render the rest of the rows until it's scrolled.- 3D Touch support landed in version 0.20.0 but I haven't figure out how to integrate it with Safari View Controller, for example when 3D-touching a link.
- There's no
display: inline-block
in React Native. But it's possible to simulate it with some flexbox magic. - Not quite relevant to React Native or React, but I got to say, spread operators and JSX spread attributes are pretty darn cool.
LayoutAnimation
is simply magical. It's unbelievable to see how a one-liner code can do so much to animate almost anything on the screen.
Overall, I'm very impressed with React Native. There were a few small issues once in a while but usually they are fixed in just few days.
I built a few of my own custom components, put together in the same repository:
LoadingIndicator
, inspired by react-native-activity-indicator-ios, which is a wrapper forActivityIndicatorIOS
that fades in after about one second.HTMLView
, inspired by react-native-htmlview, react-native-htmltext and react-native-html-render, which converts HTML string into pure React Native components. For example, it converts a<pre>
element into a<ScrollView>
styled with a monospaced font.CacheStore
, inspired by lscache, which is a wrapper forAsyncStorage
that works likelocalStorage
but with key expiration.
When I have the time, I plan to separate them out into individual modules and repositories.
Web and native
On 28 September 2015, I posted a comparison screenshot between the web app and the iOS app:
It's beautiful.
It's a moment when I couldn't even differentiate both of them.
Along the way, I've made a few tiny changes and decisions:
The comment icon used to be vertically centered in each individual row. I moved it to the top mainly because I couldn't make it work on React Native 😜. I gave up and fix the icon placement on the web app instead for consistency sake.
On the web app, if a user long-press on a story link in the list, it will show the iOS Share sheet menu. It's a browser feature. However, I realise that this gesture is not common at all on iOS native apps. I thought of removing this feature but decided to keep it and increase the long-press activation delay instead.
Visited story links on the web app are grayed out. Basically, it's just a styling for
:visited
CSS pseudo-class. As a web developer, I've never thought of it as a "feature" and kind of take it for granted. On the iOS app, I have to manually implement this feature and simulate browser history in a very simplified way. If only I could use a hidden Safari View Controller to check visited links somehow 🤔On the iOS app, I decided to linkify the commentator username pointing to the profile page on Hacker News, for example, my profile page. It simply opens up the Safari View Controller. I've thought of creating a nicer interface for it but feels like over-designing it and perhaps over-engineering it because will need to fetch another API call, etc. After implementing this, I back-ported the change to the web app.
In the comments view of the iOS app, I added a 'Share' button in the navigation bar. This is not needed on the web app because everything shareable can be triggered by long-pressing. Since long-press gesture is not common for native apps, I add the 'Share' button there.
For both web and iOS app, there's no pull-to-refresh. Technically it's not hard to implement it with React Native, yet it's not a natural gesture on web sites. I choose not to implement it because there's no reason to do it. Hacker News is not Twitter or Facebook. There's no real-time stories sliding in from the top. I purposedly make it difficult to reload the stories. Even if the reload button is tapped, it will fetch from the cache most of the time which will expire in few minutes. It's a bit like I'm helping readers to be less obsessive in an unusual way, thus also explains why I limit the number of stories to 60. No more, no less. Done with the stories? Go back to your life 😉
As of now, there is no visible "integration" between the iOS app and the web app. No Universal links. No jumping from the web app to the iOS app. If the user shares a story from the Share sheet, the URL points to Hacker News website, not the web app. From my point of view, there is no need to pull users away from the web app or trying to get more active users on my iOS app. I just don't care about the platform differences.
Launching on the App Store
On 19 February 2016, I gave out a few TestFlight invites to a number of people, so that I could gather feedback and flesh out the bugs. Surprisingly, most of the feedback are positive and there were no serious bugs.
I really need to launch the app on the App Store soon because I've been taking a break since October and stalling the progress for few months. I accidentally diverted my attention to my other side projects and fortunately manage to refocus back on this project on 31 January.
I think hard about the pricing of my app. Usually I would make my apps free, but this time, I need to offset the Apple Developer Program membership cost, not to mention all the money I've spent maintaining my (unofficial) Hacker News API since the 2012 debut. Gratipay (previously known as Gittip) doesn't work for me at all. PayPal.Me doesn't work in Singapore or even any parts of Asia.
I did some research on the pricing of other Hacker News apps and finally decided to just go for minimum pricing (99 cents at this point), judging from the complexity of the app and the fact that it looks exactly the same as the current web app now. I don't have to implement a "trial phase" thing or any similar strategies because users can try it on the web app itself before purchasing.
In the end, this is more like a small donation to me, not really a source of revenue or profit.
On 25 February 2016, I submitted the app to the App Store.
I know that the review process would take roughly a week so while waiting, I focus on revamping the landing page. I took this chance to rethink of everything on the page, like removing all the unnecessary marketing nonsense, removing GitHub links, and just go straight to the point.
Super minimalistic. The preview "image" on the right side is actually the live web app itself contained inside an iframe
. And I've also included an easter egg 😁
On 1 March, the review status changed to "In Review". I was thrilled. Then the status changed to "Pending Contract" and I have to sign some contracts. Stan Chang helped me a lot in figuring out all the confusing documents and legalities. After that, it changed to "Ready for Sale" and my first impression was like "Wait, really?". Honestly I was expecting the app to be rejected after hearing those horror stories from other iOS developers, but I guess I got lucky(?)
Officially I announce the release of my first iOS app on Twitter and Facebook 🚀
So this is it. HackerWeb for iOS. Since 2011.
I am deeply grateful to all my long-time loyal users, beta testers, and my friends who supported me. I really appreciate Paul Irish's compliment and everyone else's because positive feedback is always the reason that kept me going and continue building things. It has always been worth more than any number of downloads or likes or whatever.
I’ll be continuing to work on my project in my spare time and further improve things along the way. Follow me on Twitter or @hackerwebapp for updates.
Update 2 May 2016: Continue to Part 2, Building HackerWeb for Android.