"EGO" Xcode Theme for Xcode 4 + EGOv2

Shaun, Feb 4

Almost two years ago now, we posted our Xcode theme, "EGO".  It's been significantly more popular than I ever thought, we've seen a lot tweets about, we've seen it in screen casts, and it even made an appearance in the product launch video for robotcat's Outside app.  There's no real metric for these things, but it seems to be one of the more popular third party Xcode things (we're number one in Google!).

So, with the GM release of Xcode 4, we've gone ahead and ported our original EGO theme to the new Xcode 4 file format.

We're also posting a alternative version of EGO, EGOv2.  It's based on EGO but has some key differences: New background color, new comment colors, themed console, and other minor tweaks and slight HSB adjustments.  Here's a comparison:


Original EGO Theme

New EGOv2 Theme

 

To install the themes, run the following blocks of code in Terminal:

Original EGO

mkdir -p ~/Library/Developer/Xcode/UserData/FontAndColorThemes; cd ~/Library/Developer/Xcode/UserData/FontAndColorThemes; curl -O http://developers.enormego.com/assets/egotheme/EGO.dvtcolortheme

New EGOv2

mkdir -p ~/Library/Developer/Xcode/UserData/FontAndColorThemes; cd ~/Library/Developer/Xcode/UserData/FontAndColorThemes; curl -O http://developers.enormego.com/assets/egotheme/EGOv2.dvtcolortheme

Now just restart Xcode, go to Preferences > Fonts & Colors, and select "EGO" or "EGOv2" from the color theme drop down.

Note: Both themes now use Menlo Bold 13pt.  This might not be for everyone, but I've found it much easier on the eyes and the bold seems to render nicer than regular, to me.  Feel free to change!



CSS Conditionals with LessPHP + Modernizr

Shaun, Jan 31

We've been using LessPHP quite extensively for the last few months, and around the same time we learned about Modernizer (after attempting to build the same thing ourselves).

LessPHP spawns from Less.js, however we like the control/flexibility the PHP variant gave us, which is why we're using it over Less.js.  In a nutshell, LESS is a CSS compiler that allows you to write CSS a bit more naturally, and then compiles down to CSS-complaint syntax.  It offers numerous features such as mix-ins, variables, math, functions, etc..., but the coolest thing LESS gives you is the ability to "nest":

If you're unfamiliar with Modernizer, it's a Javascript library that will test various CSS3 features and append classes to the HTML tag.  You can then target classes by calling html.borderradius a.button { } and html.no-borderradius a.button { } to offer an image free solution of rounded corners to browsers than support it, and then use legacy methods if the browser doesn't.

The problem with using this in combination with less is that less  makes it really hard to do any sort of conditional on a parent class, once you're nested.  Since you're already nested in, there's no easy way to do it other than going to the bottom of the file and creating a new nested block prefixed by html.borderradius.

We've been adding onto LessPHP since we started using it, and I finally grew tired of the issue mentioned above.  I've wanted CSS conditionals for a long time, and the complexity of LessPHP/Modernizer was unnecessary.  I patched less to add a new "prepend selector", any time a selector is prepended with ^, it will automatically move that selector to the top of the scope when compiling.

Here's an example of how it works:

It's definitely a bit of a hack, but it does work.  Just don't try to nest multiple prepend selectors :).



Safari View Source in TextMate

Shaun, Nov 21

While I love developing in Safari over alternatives like Firefox and Chrome, it lacks in one particular area: View Source.  Safari's View Source is practically useless, it doesn't show line numbers, no syntax highlighting, etc.

Safari does offer the Web Inspect, which is nice, but a little annoying for quick look ups since everything is broken down into the DOM tree.

I've seen other Safari Extension's like BetterSource that do this right in the browser, but really, why re-invent the wheel?  TextMate is an awesome editor (albeit with this issues) and would be perfect for this.

I decided to see if I could get a "View Source in TextMate" item in the contextual menu.  After reading over all of the docs, it became apparent to me that opening files in a native app just wasn't going to happen.

I was able to get it to work by writing a TextMate plugin to handle the heavy lifting via it's txmt:// URL scheme.  So it all worked out in the end, you'll just need to install the TextMate plugin as well as the Safari extension.  Enjoy!

Download the ViewSourceInTextMate TextMate Plugin

Download the ViewSourceInTextMate Safari Extension



Facebook Redux: Who doesn't love a happy ending?

Shaun, Aug 28


Facebook's commit properly attributing enormego

Late last week, we had a bit of a misunderstanding with Facebook. We probably jumped the gun and ended up rendering the title of the post a bit hypocritical, as many of you pointed out. We should have reached out to Facebook first to find out how this happened, but we were pissed off and wanted to tell the world. It's not easy to get in touch with Facebook, and we were in no mood to sift through their site to figure it out. As it turns out, we could have emailed them at opensource@facebook.com.

As it turns out, Facebook didn't even know they did anything wrong. EGOTableViewPullRefresh made it's way into Three20 by way of a fork merge from a third party. This obviously doesn't excuse Facebook, since they are responsible for their repository, but it does explain how it happened.

Within hours of our post going up, Jeff and David from Facebook's Open Source team became aware of the mix up and reached out to us resolved the issue quickly. All in all, we were pretty happy with the resolution. We received credit for our code in three20, and we helped make Facebook's iPhone app just a little bit better.

Facebook didn't stop there though, David reached out to me again this week and send me a Facebook Swag Pack:

At the end of the day, Facebook screwed up, but did everything right to resolve it, and then went beyond that by sending us from free Facebook swag. We're definitely sorry for how we initially handled this situation as well, since it's clear now we could have avoided this by simply getting in touch with Facebook right away.

So thank you Facebook, you guys definitely have common courtesy!



What ever happened to common courtesy?

Shaun, Aug 20


Pull-to-Refresh is powered by Facebook's three20 library, taken directly from our EGOTableViewPullRefresh library, without any attribution.

Updated 8:30PM — See below

Upon updating Facebook's iPhone app last night, the first thing I immediately noticed was Pull to Refresh, the awesome UI Element created by Loren Brichter for Tweetie 2. We fell in love with this element the second Tweetie 2 hit app store, so we re-created it and open sourced it for everyone to use.

We've seen Pull to Refresh used in a few apps before and always wondered whether or not it was the one we created, but never one as popular as Facebook. I immediately started looking to see if our code made it into their app, it would be awesome to see Facebook using something we wrote.

I was a little disappointed when I found out this element was actually apart of their open source iOS library three20. Still, I was curious as to how they did it. Was it similar to what we came up? Was it better? We were never fully happy with our implementation, but never had the time to spend on making it better.

Digging through their source code, I finally found the class: TTTableHeaderDragRefreshView. I started looking over to code to see how they accomplished it, and that's when I realized it: this was our class. You can see a diff on the two classes init methods below:


Note: For the purposes of the diff tool, I normalized the code blocks a bit, but you can compare for yourself by going here and here.

Facebook prefixed some variables, slapped their Three20 branding on it, restructured some code, but it was the same code we wrote. The same code we wrote, with zero mention of us.

Just like all of our open source code (and we've published a lot of it), our intent is always for it to be used to help developers and generally make apps/app store a better place for everyone. We were ecstatic that we might have made Facebook just a little bit better.

To find out that they took our code, re-released it as their own, and take credit for it though? That's not cool Facebook. Not cool at all. It also violates our license, which states they need to retain our copyright notice when republishing it.

Still, we're glad we could help made Facebook, three20, and all of the apps using pull to refresh in three20, better. It just would have been nice to get at least a hat tip from Facebook.

Update 8:30pm: Facebook reached out to us in the comments below and they've updated their headers to attribute the source code to us.



How to Add Those @2x iOS4 Resources to SVN

Shaun, Jun 16

In order for your app to take full advantage of the iPhone 4 Retina Display, you'll need to add 2x resources to your iPhone project.

If you're using SVN to manage your files, you'll be faced with something pretty annoying:

$ svn add Default@2x.png
svn: warning: 'Default' not found

This was incredibly frustrating for me, no matter how I tried to escape it: single quotes, double quotes, backslashes, etc. SVN refused to add.

This is due to internal path recognizers in SVN. It expects the last at symbol to specify a revision. This is easily corrected by adding an at symbol to the end of your file:

$ svn add Default@2x.png@
A (bin) Default@2x.png

You'll still need to manually add each resource, but it's better than nothing. You could also use an IDE like Cornerstone, but I prefer the SVN CLI way of managing SVN.


Before anyone says anything: I know this isn't an issue in GIT. GIT's awesome, I love GIT. Unfortunately I still have some projects that require SVN.



"Enter/Return" Key to Submit Forms in HTML5

Saverio, Jun 14

In a lot of our projects, we use custom buttons built with CSS to really make the form look clean and polished. They look awesome, but a lot of the times, we're not able to have some functionality that the native "button" or "submit" tags will give you. To fix this, we usually just throw a hidden submit button on the page like this:

<input type="submit" value="Submit the form!" style="display: none;" />

This usually works fine, but in HTML5, it seems the spec has changed a bit and the latest Webkit (as seen in Safari 5) have changed the way they do this and actually don't respond to the enter/return key here. Apparently if the element is set to display: none, it won't respond to user events. To fix this, we just need to hide the element while still making it "visible" to user events. The following works just great:

<input type="submit" value="Submit the form!" style="position: absolute; top: 0; left: 0; z-index: 0; width: 1px; height: 1px; visibility: hidden;" />

Simple change, but it works very well.



Setting Up Clang Static Analyzer and Xcode (in three steps)

Shaun, Dec 2

Xcode's Build & Analyze is great, but as far as I've encountered, it doesn't find everything that Clang Static Analyzer does, even if it is based on it.

Here's a quick guide to install Clang:

  1. Download the latest "checker-XX.tar.bz2" from http://clang-analyzer.llvm.org/
  2. Extract, and move the contents to "/Developer/Tools/clang/"
  3. Add this line to your ~/.bash_profile file.
    alias clang_gen='rm -rf /tmp/scan-build*; xcodebuild clean; /Developer/Tools/clang/scan-build --view xcodebuild'

That's pretty much it!  Just go to your project directory in Terminal now, and run "clang_gen".  Once it's done running, it should open up Safari with the results.



AppStore Improvements

Saverio, Nov 24

Joe Hewitt (famous for his work on the Facebook iPhone application as well as the Three20 iPhone library) posted an article a few months back and followed it with his announcement earlier this month that he was handing off the project to someone else because he is "philosophically opposed to the existence of their review process" associated with the Apple AppStore. Joe's a great developer and he's brought a lot of help to the community with his Three20 classes, so it's a shame to see him leave the platform (which goes against his post back in August saying that he wouldn't). I'm sad to see him go but don't agree with Joe that the AppStore should remove the review process all together.

Joe's argument that being completely "without barriers to entry" worked for the Web so it should work for the iPhone platform is comparing apples to oranges. The AppStore is more than just a directory of apps, it's a platform that connects developers to customers.

1. The web is "instantly accessible." You don't "download" web sites before you get to view the content. In most cases, there's no install or setup process - Web sites are just there. If you go to a bad site, you can simply hit the back button and click on another ad infinitum. Imagine how different the web experience would be if you had to "pre-load" each site? If a user had to wait up to a minute to load a site and get it setup before looking at it. People would be much more careful about the links they clicked and search engines would be forced to have an entirely different setup for search results (user ranked sites? an approval process like the AppStore?). Throw in needing to pay $1.99 before clicking through to see if the site is what you need and it would be even more aggravating to come across a loser. Certainly, the Internet (and search space) wouldn't be as popular as they are if it worked this way but it is a good analog to the current state of the AppStore. Let's imagine that Apple did remove the review process. There are already a lot of horrible apps on the AppStore. It seems strange that Apple lets some of this stuff get through. As an entirely open platform, anyone can post to the Web - take a minute and look around; this equals a whole lot of junk sites. On the Web, it's ok because of the nature of "browsing" and the simplicity of it all. People expect to deal with it and know how to vet their own search process. The time wasted is so minuscule that it doesn't bother us; but if that same openness was used in the AppStore it would be absolute chaos - even harder to find anything worth downloading, massively time-consuming to wade through the garbage and extremely expensive to buy and trash apps that were not for you.

2. Monetization is not the same on the iPhone as it is on the Web. On the Web, the majority of revenue comes from ad dollars. Other than a few subscription offerings, the user rarely pays when they go to a site. On the iPhone, you're paying for access most of the time. Sure there are a lot of free apps, but there are also a lot of paid apps worth downloading. If you go to a crappy site, you just leave, if you download a crappy paid app, you are really pissed. Despite what those scam-ish ads say, making money on the Web is really hard. Being able to monetize your traffic using ads requires a large audience, and building a large audience requires lead time - read: the time between when you start and when you can actually collect your first check can be very long. With the AppStore, if my app gets released today for $2.99, I instantly get access to an entire community of people who want to buy it. I can start making money the second the app goes live. That "instant gratification" is important when you're trying to pay yourself and your team. Comparatively, Mac OS X developers have historically had a hard time selling their apps because of the difficulty in getting access to customers but the AppStore has fixed that. No-name developers can put out an app and it'll sell on the AppStore as long as it's good.

I hope these points make it clear that there are major differences between the AppStore and the Web, making it unfair to compare them. But I do agree with Joe that the AppStore review process is not perfect. While discarding the review process entirely would be like throwing the baby out with the bathwater, here is what I think Apple needs to fix:

1. Apple needs to make the review process much more transparent. Reasons for apps getting rejected need to be clearer and there needs to be a direct line of communication between reviewers and developers. Right now, if you want to know why your app got rejected or if you want to explain something to the reviewer that they may have missed, you can't. Sure you can email the generic AppStore email address, but getting a response takes forever (timeframe for response is generally between six months and never). When an app gets submitted a "comments" section should show up right in iTunesConnect so developers and reviewers can talk directly about the status.

2. Updates take forever to get approved, making bug fixes hard and drawn out. When I do Web development, if I find a bug, I can fix and deploy instantly. On the AppStore, you can't do that. Sometimes a very large bug appears and it's imperative that you get it fixed, waiting two weeks for a review is not efficient. The reason Apple forces updates to go through the review process at all is because they don't want to review your app, approve it and then have you submit an update that violates their terms. Updates should go through a lighter review process, just a quick check to make sure that the app is not "too different" from the original. The problem is, light reviews still take time and Apple is limited on resources. The best way to "lighten the load" is to give trust levels to developers by building a Karma system. Here's how it would work:

  • The first time a developer submits an app, it takes a while to get approved.
  • Once it's approved, Karma goes up.
  • When the developer issues an update and that update gets approved, Karma goes up again.
  • When they submit another app and it gets approved, the review gets easier and easier since they're establishing a level of trust.
  • In this model, EA wouldn't need to be as heavily reviewed as "some kid from New York." And, if a developer reaches a certain level of Karma, app updates could go through without the need for a review at all.

Some may argue that this will give long-time AppStore developers an edge, but everyone (regardless of Karma) will still need to go through the initial approval for an app they submit. The only thing changed is the time it takes to get updates approved. The other problem is that developers might get lazy since they can just "deploy again" if they need to; however, if the system is built correctly, it can prevent this. If I submit multiple updates in a 72-hour period for the same app, I'm clearly being lazy and my Karma should go down. By creating a system like this, Apple will be able to trust developers more and alleviate some of the load.

3. Planning for an app release is nearly impossible. There is no way to release an app at the EXACT moment you want. Currently, you submit, wait a few weeks and then Apple approves your app and makes it live. You have no idea when it's going live until you get that "Ready for Sale" email from Apple after you've passed approval. This makes it really hard to plan marketing campaigns to build demand. You're essentially making a "guess" and that's no way to run a business. The current method of "picking a release date" is that after you go live, you can quickly login to iTunesConnect and change the "release date" to sometime in the future. This helps, but you can only pick the date and not the time, so your app could go live anytime on that date. The problem with the current solution is that when your app is approved, it goes live unless you change the release date and if you don't get into iTunesConnect fast enough, your app will be available for a few minutes/hours. On top of that, we've found that using a release date ruins your chances of performing well in the Top Free/Paid charts because Apple uses a velocity score based on the date of approval, not the date of release. The simplest fix here is to:

  • Allow developers to specify if they want the app to go live "as soon as it gets approved" or if they want to put the app on hold and then login later and set their own release date
  • Fix the rankings so that they work off the real release date and not the date of approval

With these changes, developers can plan their app launches better, making a fun business a much more viable one.

4. User ratings/reviews are pointless. While this isn't "review process" specific, it's a huge issue with the AppStore. It's no secret that the angry user speaks a lot louder and more often than the one who’s pretty happy with the product. With this in mind, you're always going to see a lot more bad reviews on apps. There are a few things Apple can do to make the system better.

  • Don’t allow people to rate an application without writing a review. How can a developer fix a problem based just on a ranking?
  • Change when you ask users to rate the app – right now the window pops up when a user deletes the app. Pretty safe to guess they are not happy with it if they are removing it from their iPhone. Ask app users one-month in to rate the app.
  • Probably the largest problem of all is that developers can't respond to reviews. A lot of times, users complain about problems that they are having because they are using the app wrong and there's no way to get in touch with them to help them. The most you can do is post in the app description and hope that they see it. A way to connect customers and developers needs to be established; even if it's as simple as letting developers post a reply to a user's review.

I think tweaks, like those listed above, would greatly improve the Apple AppStore. People will still argue for a completely open platform but it's not fair to use an argument that compares any other platform to the AppStore. None of the open platforms out there are as good as the AppStore and none of them have a central agent that gives you as much as Apple gives you (audience for your apps, billing services, file delivery, etc.). Perhaps Apple should allow developers pining for a wholly open platform to "opt out" of the AppStore and develop apps that they would then need to market, bill and deliver all on their own (pretty much the same way that indie development is handled now on the Mac). If Apple isn't promoting the app or "endorsing it" by listing it on their AppStore, then they don't need to review it. Simple right? Sure it is, but why would they offer this? They stand a chance of losing a monopoly on the 30 percent they collect on every app sold through the AppStore, why would they just "open it up" for everyone. Would an open platform be nice? Sure. Is it necessary? Maybe. But in all reality, it doesn't make business sense for Apple.

For those who argue that all 120,000 app developers should revolt and file a class action lawsuit, I don't get where you're coming from. If it weren't for Apple, we wouldn't have any market at all. They created it, we took advantage of it, we profited and they did too. All in all, I think Apple should show good faith to their developers and make some improvements but I don't think they "owe it to us." The iPhone isn't popular because of the AppStore or any of the developers; it's popular because of Apple. I think Apple realizes the value in their developers – they have always been a company that heavily supports the development community, so I have no doubt that we'll see change. It just may take some time.



Rejected: For Using UITouch Three20. (Updated)

Shaun, Nov 9

Update: Thanks to @SteveStreza for pointing out this is actually a problem with @joehewitt's famed, but flawed, three20 library.

I'll leave the original blog post up, but Apple had every right to reject us. Sorry Apple!

At this point, hating on Apple's app approval process is old and tired. Everyone knows its broken, Apple even acknowledges it's broken. We all get rejected, usually we continue to go about our business and deal with it (maybe a bit of complaining on Twitter).

This one though, this is an entirely different situation. We just had two applications rejected for using UITouch. Here's a copy of the email we received from Apple: (Screenshot)

Please include the line below in follow-up emails for this request.

Follow-up: [REDACTED]

Dear enormego,

Thank you for submitting [REDACTED] & [REDACTED] Pro to the App Store. Unfortunately it cannot be added to the App Store because it is using a private API. Use of non-public APIs, which as outlined in the iPhone Developer Program License Agreement section 3.3.1 is prohibited:.

"3.3.1 Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs."

The non-public APIs that are included in your application are the following undocumented, private UITouch instance variables:

UITouch._locationInWindow
UITouch._phase
UITouch._previousLocationInWindow
UITouch._tapCount
UITouch._timestamp
UITouch._touchFlags
UITouch._view
UITouch._window

Regards,

iPhone Developer Program
***************************

We develop a lot for the AppStore. We have a lot of applications on AppStore, which means we've definitely experienced the cryptic and "WTF?" rejections from AppStore, that everyone else has to deal with. This one isn't cryptic at all, it's very, very specific.

Apple seems to think we're accessing it's instance variables directly. Two thoughts of this:

  1. As far as I know, you can't access private/protected instance variables directly from outside the class. Objective-C's compiler will not let you.
  2. The only way to access these variables, is to do it via the ObjC runtime methods, and at that point, Apple wouldn't be able to detect it, since it's being called at run time.

As to the second point, we're not modifying these ivars at run time (why would we? they're public methods). For the arguments sake, even if we were, they'd need to actually run the app, and according to our server logs, they never once opened it.

However, all of that is moot. We're not accessing the ivars directly, we're using the public Apple provided methods.

To take this a step further and try to be as transparent as possible, here is every single reference to UITouch in our application:

As you can clearly see, the bulk of the code used is from Apple's own example code. Other than that, we use it to detect the correct row index when a UIControl is tapped in a UITableViewCell. There is nothing remotely questionable here. This is standard use of UITouch, and I'm sure there are thousands of apps on AppStore that use this in the exact same way — in fact, just about all of our apps on AppStore use it this way.

We'd have no problem at all sending Apple the entire Xcode project to let them see for themselves that this isn't happening in the apps. However, there's no communication on Apple's part, as everyone knows. In fact, one of these apps showed up as Rejected on iTunesConnect 5 days ago, and we just received the email an hour ago explaining why.

This doesn't make any sense, and Apple really needs to explain itself.

Unless of course, Apple has decided that detecting touches on the iPhone is no longer allowed.



What if images on the iPhone were as easy as HTML?

Shaun, Oct 20

HTML is great, you want to set an image and you do <img src="http://example.com/image.jpg" />. It does everything else for you, it caches, it even loads in the background. This is something that is taken completely for granted until you actually have to do it yourself.

The work involved in doing this on the iPhone is a pain. It's not so bad if you're viewing a single image, but if you're displaying web-based images in a table view? Ugh.

Enter EGOImageLoading.

We created EGOImageLoading to be as easy to use as HTML. EGOImageLoading is a few classes to make life easier on the developer, and to give the end user a much smoother result, easily.

Here's what EGOImageLoading does:

  • It loads the image in the background
  • It caches the image to the disk for you
  • It allows you to set a placeholder image while it's loading

And here's where it gets even more awesome: It changes loading priorities on the fly. If the user scrolls a bit in the table view, it'll decrease the priority of those images, and load the ones they're looking at right now, first.

So how simple is this? You can jump to our GitHub page and grab the demo, but here's what our UITableViewCell subclass looks like:

Yeah, it really is that simple.

So how does it work?

It's all based on EGOImageLoader and EGOCache.

We published EGOCache a few months ago, and we've since improved it to work better with EGOImageLoader.

EGOImageLoader does all the heavy lifting. It checks to see if the image is cached, if it is, you get it right away. If not, it loads it up in the background, and if it's told to, it decreases the priorities and bumps new loads to the top.

You can use EGOImageLoader without table views, and it'll work great. However, this post is all about table views, so we created UIImageView and UIButton subclasses, EGOImageView and EGOButton respectively, that interact with EGOImageLoader for you. That means all you have to do is initialize them, and set the URL. Seriously, that's it.

They both contain "increaseImageLoadPriority" and "decreaseImageLoadPriority".

You'll probably never need to increase priority, since it does that for you when it loads (or re-sets a loading URL). However, you are going to want to decrease the load priority once the table view cell is moved off screen, we do this in the above example by tracking willMoveToSuperview.

So there you have it, image loading on the iPhone, that's just as easy as throwing in an HTML image tag.

You can grab EGOImageLoading on it's GitHub page: http://github.com/enormego/EGOImageLoading

Also a quick note, EGOImageLoader is powered by the awesome ASIHTTPRequest class by Ben Copsey. I'm sure you could rework it to work with NSURLConnection, but why? ASIHTTPRequest is amazing. If you're not using it, you should be.



Use/Build Tools for Yourself

Saverio, Oct 15

One of the things that we've been focusing on lately has been building tools to better manage our products and services. It's amazing what a well designed tool can tell you about your business. When I say "tool", I'm referring to an application. Whether it be a web app, a desktop app or even a simple spreadsheet, creating a good "tool" that can help you leverage data that you've collected, or even help you collect data to analyze is a great thing to have.

For example, our F-MyLife application leverages multiple ad networks to fill it's inventory. It currently uses the AdWhirl SDK to do this and it worked great for a while; however, our buddies over at MobClix built a much better ad aggregation platform with far more networks and we've since moved most of our traffic to MobClix so they can manage it. Since the AppStore is a pain in the ass and getting an update out would have taken weeks, we were able to allocate all of our traffic to MobClix via AdWhirl and MobClix's new platform was able to handle everything from there. We ran our ads like this for over a month and then we started noticing a dropoff in our revenue. We scrambled to find out what the problem was, but since we were filtering through data from multiple networks, it was nearly impossible to put two and two together.

We decided that in order to analyze all of this data and really see connections between everything, we were going to need to build something. So Shaun spent a few nights working on a web application that pulled in all of our data from all of our networks so it could be analyzed and displayed in a readable format along with some charts. This was a huge life saver. After reviewing the reports that we built for ourselves, we noticed a huge problem with our click-through rates and began working with everyone to resolve them.

Had we not built this tool, we probably would of never known the real cause of the issue. In fact, had we built this tool a month ago, we would of identified the bug earlier and we could have fixed it.

You don't always need to "roll your own solution" though. There are tons of products and services out there designed to fix problems just like yours, you just need to research them and set them up. A similar scenario occurred with our servers. We run FML/TWI & ProTip on Amazon's EC2 infrastructure and we monitor everything with Pingdom. The other night, we had a 6 hour downtime between 2am EDT and 8am EDT. The problem was remedied by a simple lighttpd restart; however, during that time, we lost a lot of revenue. This wasn't the first time that this happened either! So, yesterday, we setup Scoutapp to keep an eye on our server load and monitor some other metrics (like MySQL). When we woke up, we were greeted with a bunch of alerts from Scoutapp telling us that we had 1 SQL query which was taking an awfully long time to run and bogging down the server. We logged in, identified the query, figured out why it was taking so long to run and remedied the problem by adding an index to one of our tables. Done!

If it weren't for Scoutapp, we would of had to manually log into each server, every morning and manually check the slow query logs. This is painstaking and given that we're a small company with hundreds of things in the works at any given time, we don't have the free time available to check logs. Scoutapp saw a problem, let us know about it, and we fixed it. A tool saved our ass yet again.

This happens over and over in the software development business. After you're done with the product, and it makes you a little bit of money, you NEED to build or setup tools that can help you manage your new business. If you don't, you'll spend all of your time managing your first product instead of working on your next.

Heartbeat was the first tool we built to manage our AppStore products. It's been a great asset to us and after having had over a year of experience on the AppStore, we're working on Heartbeat 3.0 which will incorporate all of the knowledge that we've gained through the past year.

It seems counter-productive when you're a small company, but that's when these little things help the most. You don't have the time to do it all, so it's important to automate as much of your business as possible. Services like Pingdom and Scoutapp have helped us maintain uptime on our servers. Others like Sifter and Tender have helped us keep track of bugs and deal with support issues from our customers.

There are a ton of other products and services out there that can help you run your business. If you can't find one to meet your specific needs, spend some time, and build your own. Trust me, you'll thank yourself in the long run.



Using MapKit to create Map thumbnails

Shaun, Aug 17

The snippet of code below will give you a 64x64px view of a map thumbnail.  This creates a similar effect to the thumbnails used in the Maps app when you click into a location on the map.




UITableView Ads: Inline and Sticky Ads (as seen in our F-MyLife app)

Shaun, Jul 21

Sam from AdWhirl.com asked us to do a write up on how we implemented ads in our F-MyLife app.

When we decided to add ads to FML, we wanted to do in the least obtrusive way to the user, despite what they think in their reviews.

We originally came up with a system where it would insert the ad in the 4th or 5th row of the table view, and then just scroll.  This was great for the users, but it performed horribly for us.  It was pretty much never on the screen.

We changed it so it would insert it in the 3rd row, and then stick to the top, similar to how section headers work (that's actually expect how we did it).  We called this implementation "sticky ads", and it's how we'll refer to it in this post.  These performed incredibly well for us, and we thought it was a good mix for the user.  From our stats, most people open FML frequently, so it really wouldn't get too annoying.  We were wrong.  The users HATED it.

The third, and current implementation of FML's ads are called "inline ads".  The idea was to keep the ads on the screen as much as possible, but not permanently reduce the viewport for the user.  What we did here was move the ad as the user scrolls so it appears every 3 FMLs which is approximately 1 screen length.  So far, the users have responded well to these.  They definitely didn't like them being stuck at the top of the screen though.  The "inline ads" seem to perform much better.  Since the ad is always moving on the screen, it's a bit harder for the user to completely block it out.  Our CTR's have increased quite a bit with this implementation.

Due to the poor performance of the original method, we're not going to bother explaining how it works.

"Sticky Ads"

The sticky method was pretty simple, and for the most part, easy.  Once you're notified by AdWhirl that the ad loaded, reload your UITableView.  From there, you simply split your data set into two sections, the top section, and ad section.

From there, you add the UITableView delegate method to return a custom header view for the ad section, and return the AdWhirl ad.  The only catch here is that the touches will get past to the row behind it.  We prevented this by putting the ads onto a UIControl, which blocks touches from being sent to the UITableView.


You can find the full source for this method in the zip file at the bottom of the post.

 

"Inline Ads"

The inline ads were far more complicated.  Since there's no (public) way to make the header view not-float while using UITableViewStylePlain, we divided our content up into sections of 3.  Then we double the sections, to create a new section/row for every ad.


We created a bunch of convenience methods to determine what row/section we were in, but it does get messy.  Explaining this line by line is useless.  We've attached a demo project to the bottom of this post that includes source/examples for both methods.  Look over the code, specifically what's going on in the delegate/dataSource methods.  If you have any questions, shoot me an email.

 

Demo Project / Source Code

Download the source code here: http://tr.im/tnX4

If you have any questions or need any help, shoot me an email at shaun@enormego.com

Enjoy!



UIAlertView Text Fields

Shaun, Jul 6

As I'm sure you already know, UIAlertView supports text fields. It has since the OS 1.0 toolchain, and for whatever reason, Apple has them as private methods. Even when adding a category to support the private methods, things are a little quirky. You'll notice that the keyboard is pretty much black on 3.0, and on 2.0, you actually see multiple keyboards being deployed. That's also the reason it's black, there's two translucent keyboards, giving you a black keyboard.

I've struggled with this on and off through multiple apps for over a year now, but I finally have this working smoothly.

So here's how you do it:

1. Create a category for UIAlertView to enable UITextFields:


We override the - (id)keyboard method, so it doesn't deploy it's own keyboard, which screws things up.


2. Create your UIAlertView and add the text fields


We send becomeFirstResponder on a delay, so the alertView has a chance to start appearing, if we send it before it starts appearing, the animation is choppy or it doesn't animate at all and just appears.


3. Clean up in the delegate methods


This is pretty important, if you don't do it yourself, the keyboard gets stuck on the screen until after the alert has disappeared, which isn't ideal.

Thats it!

Keep in mind, these methods are private, which means they're subject to change. They haven't changed since iPhone OS 1.0 though.



EGOCache: Objective-C Image/String/Data Caching

Shaun, Jul 6

EGOCache is a pretty simple caching class for Objective-C that'll let you cache data for a certain period of time.  It's designed to work on the iPhone, so the paths reflect that.

Pretty simple to implement:


You can grab it via github gist: http://gist.github.com/141427

Also to note, if the cache expired or there is no cache, it simply returns nil.  So all you need to do is check if it returns something, if not, grab the data you're looking for and cache it.



EGOTitledTableViewCell

Shaun, Jun 2

Here's a quick class I wrote to give you a title-table view cell as seen below:

You can grab the code on GitHub: http://gist.github.com/122538

Here's a quick example of how to use it:


Enjoy!

P.S. EGOTitledTableViewCell has properties for titleLabel and textLabel if you want to change the font/text properties.



EGODatabase, Finally Released

Shaun, May 23

We mentioned a few months ago on twitter that we were working on a thread-safe Objective-C wrapper for SQLite after using FMDB extensively in just about all of our apps. We kind of felt like we were doing way too much work using FMDB to avoid thread issues, so we came up with EGODatabase.

EGODatabase uses some code from FMDB, but for the most part, it was completely reworked to use result sets and row objects. One of the biggest differences between FMDB and EGODatabase, aside from EGODatabase being thread-safe, is when selecting data, EGODatabase populates its EGODatabaseRow class with the data from SQLite, as opposed to retaining the SQLite results like FMDB does.

 

EGODatabase is tested to work with with iPhone OS and Mac OS X 10.5

Here's a quick look at how EGODatabase works:


You can grab EGODatabase over at GitHub:

http://github.com/enormego/egodatabase/tree/master



Xcode "EGO" Theme

Shaun, Mar 21

"EGO" is the Xcode theme we created for our own personal use. We started with a copy of the "Dusk" theme and toned down the colors a bit to make it easier on the eyes.

"EGO" uses the font "DejaVu Sans Mono" at 12pt. Some of you may know it as "Panic Sans" from Panic's Coda (which is a repackaged version of it). You can grab the font over at dejavu-fonts.org. If you already have Code installed, feel free to swap in Panic Sans in place of DejaVu Sans Mono.

Here's a screenshot of "EGO" in action:

Xcode

To get EGO run the following code in Terminal:

mkdir -p ~/Library/Application\ Support/Xcode/Color\ Themes; cd ~/Library/Library/Application\ Support/Xcode/Color\ Themes; curl -O http://developers.enormego.com/assets/egotheme/EGO.xccolortheme

Now just restart Xcode, go to Preferences > Fonts & Colors, and select "EGO" from the color theme drop down.

Enjoy!

UPDATE: This will only work for Xcode 3.x. Click here to get EGO for Xcode 4 and EGOv2!



UILabel Wrapping: Updated!

Shaun, Mar 10

We've updated our UILabelWrap class. The update contains a major bug fix and a compatibility fixed for UILabel.

  • Bug Fix: UILabelWrap now redraws when the frame size is changed, instead of stretching the text that was there. This will fix the issues when you rotate orientations.
  • UILabel Compatibility: Renamed highlightedColor to highlightedTextColor to maintain compatibility as a drop in replacement for UILabel
You can grab the updated code at the original post: http://developers.enormego.com/view/uilabel_wrapping.


Security: Blocking SVN/Git files with LightTPD and Apache

Shaun, Mar 4 If you use a source control management solution like SVN or Git for your web applications like we do, you know how awesome it is to deploy changes on a production server.

You can simply run svn up or git pull to update your web server with the latest revision of your app.

There's a huge security risk involved here though. Using the standard Apache or LightTPD setup, all of the files in the .svn or .git directories, are available to download.

SVN Example

You have your app running out of /home/example.com/html/
Let's say, the user wants the source code of your index.php file, all they would need to do is go to this URL:
http://example.com/.svn/text-base/index.php.svn-base That's it, there's your source code.

Git Example

Git can be even more dangerous depending on your setup (specifically, if they can browse directory listings). If they can download your .git directory, they now have a branch of your repository.


So how do I protect myself from this?

Some people like to run svn export instead of svn checkout or for git users, git checkout-index instead of git pull.

Sure this works, but it's not ideal. You can't simply pull the changes and be live.

After you checkout your app, do the follow:

Apache

Put the config items below in the .htaccess file in your root web directory

LightTPD

Put the config items below in your lighttpd config file

Now when you go to those files, you won't be able to access them.

Bonus Tip

Keep the majority of your code below a web accessible directory. All we have in our web accessible directory are our images and index.php file. Everything else sits below it in an app directory.


Line Logging in Objective-C

Shaun, Feb 25

A helpful debugging tool when you run into an issue is to throw an output after each line of code (or using breakpoints, I prefer line outputs).

It can get a bit messy, so I usually throw the following code in my PCH file in each Xcode project:


Now when I run into an issue in a method crashing, I can do this:


Which will then output the following:


The format is the method name and the line number.

Updated: An earlier version of this post used "startLineLogs(@"method name")" thanks to tips from @rob_rix and @iphonedevmag I've updated logLine to use _cmd instead, which made startLineLogs() unneeded. Thanks guys!



UITabBarController Auto-Save Order

Shaun, Feb 16

If you allow your UITabBarController to be customized, you'll notice a huge oversight in the UITabBarController class: there's no way to save the order after they're done customizing.

On OSX, Apple handles issues like these with an 'autosave name' that handles everything for you automatically, it seems to be missing from the iPhone SDK, so you have to figure it out on your own.

We decided to handle the autosaving/loading in the UIApplication delegate, here's what we came up with:


This works by checking the class name of each controller, and reordering it based on that.  This won't work if you use the same class for multiple UITabBarItems,  but we took the assumption that you'll be using a different class for each item. This does however, account for using multiple UINavigationController's by checking the topViewController class instead of storing UINavigationController over and over again.

If you do actually use the same class name in UITabBarItems, you can switch the code above to check for the controller's tabBarItem tag value instead of the class name.  We choose to use the class name so there was no extra work involved in having to set the tag value in IB.



UILabel Wrapping

Shaun, Feb 13

The default behavior of UILabel is not to wrap, and unless you set the number of lines you want it to have, it doesn't.

You can use a UITextView to wrap, but it doesn't have some of the properties that UILabel has, specifically shadows, and there's too much unncessary overhead there since it uses a scroll view.

I took matters into my own hands and wrote a "UILabelWrap" class. It'll wrap to as many lines as it can fit in the frame you set, and after that, it'll truncate the end.

Here's the code:

UILabelWrap.h:


UILabelWrap.m:



Update March 10, 2009: I've updated the code above with one important bug fix and a minor compatibility fix:

  • Major: UILabelWrap now redraws when the frame size is changed, instead of stretching the text that was there. This will fix the issues when you rotate orientations.
  • Compatibility: Renamed highlightedColor to highlightedTextColor to maintain compatibility as a drop in replacement for UILabel


FlickTabControl Released!

Shaun, Feb 11

There's a common UI element popping up in all of our applications lately:

We continuously keep finding great uses for this, and figured other iPhone developers would too. So we've decided to release this as an open source library for everyone.

FlickTabControl was inspired by the tab bar in Facebook's iPhone app and is incredibly easy to use.  You simply add the library, switch your subclass of UITableViewController to FlickTableViewController and implement the FlickTabViewDelegate and FlickTabViewDataSource protocols, and you should be up and running.

You can grab a copy of the source here:

http://github.com/enormego/flicktabcontrol/tree/master



Looking for more? Check out the archives