2009
5/30

App Store Reviewers Are Only Paid to Review Apps







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

"Never ascribe to malice, that which can be explained by incompetence."

--Napoleon Bonaparte

Daring Fireball's John Gruber recently did another bit on the app store review process. While I usually enjoy his satire (brushed metal anyone?), this one didn't seem quite right. The piece is funny enough, but I hate to think that they are being intentionally cruel or evil. Despite the above quote, I'm not even sure that incompetence is involved at the level of the individual reviewers.

I have my own theory on why there are so many rejections and why many highly publicized rejections seem so silly:

  • The large pool of reviewers are likely judged on how many apps they handle over a period of time.

By handle, I mean download, install, evaluate, approve/reject and feedback. If this is the case, then the apparent problems result from the fact that rejections can be done quickly (by finding/exaggerating/creating a violation) and that approval takes time (the entire app must be checked against the iPhone SDK Agreement Section 3.3 and its fifteen subsections).

In the case of Eucalyptus, The reviewer found it took almost no time at all to reject the app by searching for the Kama Sutra. If the intention was to approve the app, then the reviewer would have had to confirm and checkoff every feature against the long list of guidelines.

By quickly rejecting the app, the reviewer can move on to the next item, increasing his number of "handled" apps. Meanwhile, the developer is left feeling bullied and angry. It ultimately ends up a disservice to the millions of App Store users now denied the ability to purchase and benefit from Eucalyptus.

This, again, is just my own speculation but it could explain stories like the Tweetie rejection, the NIN rejection, and even the bizarre green icon rejection. It also explains why the feedback emails are often short.

There are many examples of mis-incentives across every industry. The New York Times has a great piece on the NBA addressing how focusing on the wrong stats can cost you games. In the early days of phone support, operators were graded on how many customers they handled in an hour. This lead to the operator hanging up in the middle of difficult or long support calls, so they could fish for easier, shorter calls to boost their daily numbers. Customers would become more upset and managers didn't understand why their internal metrics didn't match external surveys of customer satisfaction.

You have to incentivize the right metric and apps/hour is not it. The iTunesConnect entity within Apple may or may not believe that the developers it serves are its customers. However, if they don't evolve, refine and improves their service (like Apple evolves, refines and improves its iPods), developers will leave the ecosystem and writers will continue to make fun of them.




2009
5/29

The Secret Life of _cmd







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

An Objective-C class in Cocoa has one hidden object that every knows about. The self object is used extensive within a class to reference the object currently invoking a method.

There is another hidden variable that very few seem to know about: _cmd.

_cmd is the method that is currently being invoked. So if self is the subject, _cmd is the verb.

The most obvious application of _cmd is logging. Here is one of my most used macros:

#define METHOD_LOG (NSLog(@"%@ %s\n%@", \
                    NSStringFromSelector(_cmd), \
                    __FILE__, self))

You would typically use it in a method like follows:

- (void)viewDidLoad
{
    METHOD_LOG;

    //do stuff...
}

This logs a message to the console telling, what method, from what file, invoked with what object.

Tremendously useful. Here's a second version that adds thread information:

#define METHOD_LOG_THREAD (NSLog(@"%@ %@ %s\n%@", \
                        NSStringFromSelector(_cmd), \
                        [NSThread currentThread], \
                        __FILE__, self))

_cmd is not only for logging. We can play neat tricks with it as well.

This method returns an object in a dictionary where the key is the method name,

- (id)color;
{
    //SomeDictionary defined elsewhere in class
    return [SomeDictionary 
            objectForKey:NSStringFromSelector(_cmd)];
}

You could combine this with a property to get easy access inside a dictionary.

@property (readonly) id color;

If you wanted a read-write property, you could implement it this way:

- (id)color;
{
    //SomeDictionary defined elsewhere in class
    return [SomeDictionary objectForKey:
                [NSStringFromSelector(_cmd) capitalize]];
}
- (void)setColor:(id)someObject;
{
    //3 means we start from the C in Color
    NSString *partial_string;
    partial_string = [NSStringFromSelector(_cmd) 
                        substringFromIndex:3];

    //SomeDictionary defined elsewhere in class
    [SomeDictionary setObject:someObject 
                       forKey:partial_string]; 
}

Now, we have a color property, without ever creating a color instance variable in the class. This has some advantages in that you don't have worry as much about memory management. The object is retained when it's added to SomeDictionary and released when SomeDictionary is released or when the object is replaced.

The Objective-C runtime is full of neat little tricks and _cmd is just one of them.

More advanced techniques can be used to implement some advanced functionality such as higher-order-messaging and even mimic methods as nearly first class objects.

Functional programming aficionados can check out http://www.cocoadev.com/index.pl?HigherOrderMessaging for some discussion on this.




2009
5/10

iPhone Touch Sample Code







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

The iPhone SDK's UIKit allows extremely versatile touch event handling. With only four methods, Apple has exposed nearly limitless potential for programatic reaction. The problem is that these four methods are essentially exposing the hardware. To do anything interesting or useful, one must wrap meat around these bones. Apple's Event Handling documentation covers several simple cases of touch handling:

  1. single, double, triple tap etc.
  2. swipes
  3. location sensitive combo touches, simply described as a "Complex Multi-Touch Sequence", this is merely doing hit-testing on the touches.

Curiously, clear examples for certain basic interactions such as tap-and-hold or pinch-to-zoom are missing.

I'd like to rectify that. This recent inclination is compounded by my new-found passion for the au courant open source repository Github. This illicit dyad has produced the creatively named TouchSampleCode.

Currently, I've coded examples for the following cases (along with the implicit intention to add more):

  • Tap and Hold
  • Tap and Hold with delay
  • DoubleTap and Hold
  • Multi-touch (two fingers) with simple stretch and pinch for zoom and unzoom

The iPhones lack of a keyboard doesn't mean it's not a rich environment for human interaction. The current limitation is not in the hardware, but in the creatively of the developers. Future postings will explore some conjectures on creative uses of the interface.

Please consider leaving a tip if you find this useful. These kinds of small tokens are great energizers and tend to encourage more content.




2009
4/2

iPhone Unit Testing with OCUnit







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

There are a scattering of iPhone based testing frameworks and utilities:

Google's was one of the first. They've apparently built a lot of infrastructure around their solution to fit within existing test systems. With a recent Xcode update, Apple's brought back the original Objective-C test solution.

OCUnit

If you're coming from GTM, then the biggest difference is that OCUnit is the integration into Xcode. This means you get IDE errors instead of having to parse the console output (plus!). It also means you have to go to the console to read your log messages, rather than having them display in the built-in console (minus!).

This one ships with Apple, but the only clear instructions come from Sente (the original author of OCUnit, back in 1998).

Lecture 19 of the Stanford iPhone class had some slides and an example project. Unfortunately, due to the nature of the Stanford website, it's unlikely the slides and sample files will reappear on the website until Lecture 19 is presented for the current semester. Even when the links existed however, the Stanford way didn't give detailed instructions. In the mean time, all I can say is that you trust me when I say that the Stanford way of linking is much easier to do.

The Sente method of having the test run every time you build is far superior to the Stanford method (you have to manually change the target and run the tests). If you aren't continually running your tests, they approach a moldy and lifeless state.

Here's a set of instructions that take the best of both worlds

  1. Add a new Unit Test bundle target (Project -> New Target... -> Mac OS X -> Unit Test Bundle)

    [ScreenShot of New unit test bundle] (/media/images/NewTargetUnitTestBundle.png)

  2. Modify the "Other Linker Flags" setting.

    We need to change the Cocoa value to Foundation. This will allow us to compile for the iPhone Simlulator

    [ScreenShot of other linker flags] (/media/images/UnitTestBundleOtherLinkerFlags.png)

    [ScreenShot of other linker flags edit] (/media/images/UnitTestBundleOtherLinkerFlagsEdit.png)

    Lastly, search for any Cocoa.h references and delete them:

    [Cocoa.h reference in build settings] (/media/images/UnitTestBundleSearchCocoah.png)

    If you prematurely built and got the following Cocoa.h errors:

    /System /Library /Frameworks /Cocoa.framework /Headers /Cocoa.h:13:26: error: AppKit/AppKit.h: No such file or directory /System /Library /Frameworks /Cocoa.framework /Headers /Cocoa.h:14:30: error: CoreData/CoreData.h: No such file or directory

    You may need to do a Clean All Targets to clear them. Somewhat counterintuitively, if your project does not contain any tests, it will register a test failure.
    Another note about test failures:

    /Developer /Tools /RunPlatformUnitTests.include: 384: error: Failed tests for architecture 'i386' (GC OFF)

    The description here about failure has nothing to do with the architecture or garbage collection. It's just information for your reference about the environment in which the test failed. The part you need to pay attention to is Failed tests. Similarly, there is nothing wrong with the RunPlatformUnitTests.include script.

  3. Add a test file with a TestCase subclass in it.

    Using the Mac OS X -> Cocoa -> Unit Test Case Class Template, create and add a new test case file.

    Test Case files MUST end in TestCase (ie. MathTestCase or BackEndTestCase). Similarly, all test methods should start with test. The underlying tech mechanism uses some very nifty runtime magic to determine which classes and methods to run by their names.

    [] (/media/images/XcodeAddUnitTestCase.png)

    Make sure you are only adding to the Unit Test Bundle and not to the main Project

    [] (/media/images/XcodeAddUnitTestCaseAddToProject.png)

  4. Add a test

    Here's a easy one you can use to test the test framework itself. (How very meta.)

    - (void)testTestFramework
    {
        NSString *string1 = @"test";
        NSString *string2 = @"test";
        STAssertEquals(string1, 
                       string2, 
                       @"FAILURE");
        NSUInteger uint_1 = 4;
        NSUInteger uint_2 = 4;
        STAssertEquals(uint_1, 
                       uint_2, 
                       @"FAILURE");
    }
    
  5. Build the test bundle, make sure tests pass

  6. Build the test bundle, make sure tests fail

    Again, just to exercise your test framework, temporarily change one of the strings to force a test failure.

    [Force a failure] (/media/images/UnitTestForceAFailure.png)

  7. Add the unit test bundle as a dependent

    Now, we want the unit tests to run every time we build. We can do this by setting a dependency on the Unit Test Bundle. Switch back to the main project target (away from the unit test bundle) and Edit the Active Target. Click the + button under to add and new Direct Dependancy

    [Add UnitTestBundle dependancy] (/media/images/XcodeAddUnitTestBundleDependancy.png)

  8. Build the app, make sure test pass and fail

Now you have an automated, up to date testing framework in place. Every time you build, you will be immediately notified of any test regressions. The rest is attitude. Xcode will keep the tests running every build, but you have to keep writing tests when appropriate.

Sample project available at 6Tringle's github repository.

An aesthetic note about header files

Test Case files almost always have an nearly useless header file, so I usually get rid of them, creating single file test cases. The resultant .m looks like this:

//only run on the simulator
#include "TargetConditionals.h"
#if !TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <SenTestingKit/SenTestingKit.h>
@interface SampleTestCase : SenTestCase 
{
}
@end
@implementation SampleTestCase
- (void) setUp
{
    // Optional
}
- (void) tearDown
{
    // Optional
}
- (void)testTestFramework
{
    NSString *string1 = @"test";
    NSString *string2 = @"test";
    STAssertEqualObjects(string1, 
                         string2, 
                         @"FAILURE");

    NSUInteger uint_1 = 4;
    NSUInteger uint_2 = 4;
    STAssertEquals(uint_1, 
                   uint_2, 
                   @"FAILURE");
}
@end
#endif



2009
3/18

gasp Marketing!?







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

Given that the iTunes App Store population is rising, developers who launched on a prayer aren't always raking in the millions. News posts about 9 year old iPhone developers earning their college tuition overnight seems to have pushed expectations beyond the ceiling of reality. Even the smartest developers are only relying on luck if they don't, gasp, do marketing.

Three points from Steve Demeter

I had the good fortune to Meetup with Steve Demeter of Demiforce recently. Over the course of about 30 seconds I was able to swindle vital information.

It's important for many late-comers to the App Store to remember the early success stories basically don't apply anymore. Apps that were put up early had an extraordinary advantage in terms of visibility. Steve mentioned that new apps need to influence their own visibility and provided a handful of examples how recent apps have.

Create Demand Before you release

Steve talked about the importance of creating demand before release through the use of blogs, YouTube, etc. His example here was Zen Bound. Through the use of YouTube and some good press, the developer had customers "salivating" to buy the app.

Zen Bound Buy app through iTunes

Get Good Reviews on Day One

If you app starts off with bad reviews, customers won't wait for your patches and updates to turn it around. You want to start off strong. Good reviews are infectious. If your beta testers love your app, have them positively review it the day it comes out. Resist the urge to cheat. You'll get caught.

Use Review sites

Unless your app is terrible, review sites almost always help you. Steve's key insight is to allow review sites early access to the final version of your app before it's available in the store. You want the reviews to be release as near as possible to the review date such that you can move up on the hot lists. Most review sites have a large backlog and thus would need access to the app a couple of weeks ahead of time.

Here's a short list. Needless to say, stay away from sites that condone pirated apps, and be wary of those that support jailbreaking. Gruber: "Not all jailbreakers are bootleggers, but all bootleggers are jailbreakers."

It's not a review site, but http://iphone.iusethis.com/ is a nifty site as well. If you testers or customers like your app, kindly suggest they give your app a vote.

If you need more help

In addition to being very tall, Steve's company, Demiforce, is a consulting company specializing in iPhone app and game development as well as promotion and marketing. Steve's a good guy and I'm sure he can help you look good.

Disclaimer: 6Tringle is not affiliated with Steve or his company. We have not contracted him to work for us, nor was this post or recommendation solicited by Demiforce or Steve.




2009
2/2

Renascence of 6Tringle et al.







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

Alternative title: You disappear for 2 years and you expect me to take you back?

Sorry for not writing sooner, we've been busy. We can explain. Let's just start over, shall we?

PhotoClay

6Tringle is happy to announce the imminent availability of PhotoClay on the iPhone. It's like turning Photos into Clay, you can push and mold them to your will. We've made it fun and easy to render silly visages of your friends!

The cauldron is still bubbling however, and there are a couple of other projects being slow roasted to perfection. We'll let you know more when we can.




2006
11/28

Looking at Core Data through NSBrowser







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

Layers of Onions

NSBrowser is an excellent way of interfacing with semi-hierarchically organized data. NSBrowser is part of the NextStep legacy, but achieved real fame through its iPod variant.

One reason its effectiveness is that the user can navigate quickly through large sets of data organized by layered attributes. Even more significant, is that the NSBrowser automatically consolidates the user's focus on the current layer. Not only are we navigating large data quickly, but interaction with the current subgroup of data is enhanced by de-emphasizing the remaining data set. This is similar Spotlight's GPU-enhanced screen dimming effect: reduce the user's depth of focus.

The well-known Core Data is the best Apple method of handling large sets of data and NSBrowser is the best method of interacting with large sets of data. Surely, there must exist some illicit IB magic bindings or a hidden NSMagicClass capable of combining these parts into a coherent whole. One could easily imagine that an elegant and flexible method to intertwine the powerful data API with the stunningly efficient presentation would be an obvious and welcome chunk of Cocoa.

Woefully, the answer is no. NSBrowser's effectiveness is inherently linked to its reliance of hierarchical data. Part of Core Data's strength is that it does not require the model to be hierarchical. In order for Core Data to map to NSBrowser, it is up to the programmer to manually map a model graph to a hierarchal one.

In some cases, this is trivial. Certain modal graphs are already hierarchal. Our first example is a simple location database. Countries have States, States have Cities, etc. This is a somewhat trivial example, but further articles will expand on the topic.

It's Like Pulling Ducks Out of a Hat

On behalf of 6Tringle LLC, I present CoreDataBrowser.dmg! Despite the fact that Steve Jobs would not do this on stage during WWDC, this is a simple exercise. All the work is done in two places: the nib file and CDBrowserDelegate.m.

The secret to getting Core Data in an NSBrowser is in the nib file. There are 9 array controllers. 5 of these are auto-generated by Xcode and IB. The remaining 4 are prefixed with "Browsed" and represent the NSArrayControllers used by the NSBrowser to present the slices of data.

Looking at the nib file, layer slicing is done via bindings. Inspecting the bindings pane of any of the Browsed array controllers will quickly show that the content set is bound to the parent's current selection. For example, if you inspect the BrowsedStateArrayCon, the content set is bound to the CountryArrayController's selection's states. BrowsedCityArrayCon is bound to BrowsedStates's selections' cities and so on. This is chaining of selections to sets is how we can massage core data into smaller collections of arrays suitable for display in an NSBrowser.

Even the most casual observers would notice NSBrowsers similarity to the renowned NSTableView. The programming guide is not exactly a deluge of information, but is more than capable of enlightening those who invest timeffort into it. (Making up words is fun. Squishing two words together? Purecstasy!)

Everyday is a Winding Code

The first thing to notice is that we enumerate our NSBrowser columns in CDBrowserDelegate.h. This is simply good programming practice for static columns. enums are tools for better programmers.

The real work of populating an NSBrowser is done through delegate methods.

  • - (BOOL)browser:(NSBrowser *)sender isColumnValid:(int)column

    The column enumerators allow us to implement this quickly and efficiently. An even better implementation would have an addition CDBrowserInvalidColumn in the enumerator for bounds checking. The invalid column is slightly more resistant to refactoring problems.

  • - (NSString *)browser:(NSBrowser *)sender titleOfColumn:(int)column

    This won't work unless you call [some_browser setTakesTitleFromPreviousColumn:NO]. This is typically done in awakeFromNib.

  • - (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column

    Our array controllers contain our data. We use [some_array_controller arrangedObjects] to get the data.

  • - (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(int)row column:(int)column

    Again, the arrangedObjects method is our friend. Combined with objectAtIndex, it becomes trivial to get appropriate Core Data Managed Objects and modify the browser cells.

We make extensive use of switch statements in these delegates. There are more elegant ways, but for a small demo app, it is sufficient.

The remaining code is related to updating the browser and keeping it synced with the array controllers.

There are two cases to account for:

  1. The NSBrowser is clicked and we need to update the array controller's selection.
  2. The underlying Core Data store is modified and we need to update the NSBrowser.

The first case we use - (IBAction)BrowserAction:(id)sender to update the array controllers. The current implementation updates every column. This is potentially a good spot for optimization (but only if Shark says so. POitRoAE.)

The second case is handled through KVO. We set up the delegate object as observers of the array controllers in the awakeFromNib method. Then, in compliance with KVO, we implement the - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; method. If the array controller adds, deletes or modifies one of its items, we will reload the appropriate column.

The First Hit is Free...

Code mercenaries rejoice, more to follow.




2006
9/27

Scratching out an App







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

Drawing a line in the ground

Where do you start? When growing a garden, which seed gets planted first? You have the crazy idea for an app that would win accolades, quiet screaming babies and woo the gender of your preference. Now what?

Starting an app from nothing can be much more daunting that jumping into a partially or fully finished bowl of code. If you find you self staring at an empty project in Xcode, the ultimate-super-secret is to find an starting point with a low barrier of entry.

Do UI love me?

When brewing Cocoa, you will always have a GUI. One way to get started is to mock up an app in an image editor or directly in Interface Builder. Since we are at day one, we should create GUIs that get us started, not GUIs where we fuss over pixel widths. If you create a GUI intending to throw it away, you won't fuss over extraneous details. Premature GUI tweaking is a tuber of evil.

throwaway GUI example image

It's not pretty, but it will take you about 60 seconds to do in IB. The "browse for file" code will be fast, even if you have never done it before. The key is the big button in the middle. You hook up an IBAction, a few IBOutlets and BOOM. IB will generate the template and your LBoE point is waiting to be filled in like an overdue tax form.

Prove it!

Another starting point is writing tests. The low barrier here is that writing a test should be easier than writing the code that passes a test. Writing test code may not be interesting, but that's a personal problem. Recent versions of Xcode have a testing framework built in.

The whole software testing concept is explored in a diverse medley of websites, books, articles and interpretive song and dance. One way to get get basic functionality started is to write a test. If you know you are going to need some string processing functionality, write a simple test with some example inputs and outputs.

I Object

If your app is based on Core Data, you can use Xcode's Data Model Design tool to create your model graph.

coredatamodel

This is a widget based tool that will get you thinking about your objects by visualizing the relationship between them. As if this LBoE wasn't low enough, you can create a simple GUI by dragging and dropping from Xcode to IB. It's a neat party trick and sure to impress innocent bystanders.

Let's just keep things simple

The lowest of low barrier entry points is a simple list of priorities. This can be done with a text editor or fancy outliner. The outline format maps well; the code will eventually be structured into chunks of functionality, each with sub-functionality. Once your priorities are laid out, draw straws and pretend you are sentencing an item to execution by delete key.

  • SuperApp
    • Object Model
    • Need Simple Text Processing
      • Convert // to <!-- --> inside an NSString
      • Convert <,> to html entities inside an NSString
      • Convert tabs to spaces inside an NSString
      • Convert one long NSString (with n's) into an NSArray of NSStrings
    • Basic GUI
      • open dialog
      • display raw HTML in an NSTextView
      • display preview HTML in a WebKitView

What if we just start over from the middle?

It is also important to remember that you don't have to start from scratch. Apple has a trove of example code that can help those new or not so new to Cocoa. The always excellent CocoaDev has an exhaustive list of sample code.

If you do find code in the wild, remember to respect the licensees and don't plagiarize. Give credit when it is due.

Let's take things a step at a time

It should be intuitively obvious to even the most casual of readers that the critical action is to just get started. Million dollar ideas are worth approximately $0.02 if they are never acted upon.




2006
9/25

Proclamation for the benefit of your light sensing organs:







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

Black text on yellow is good for your eyes. Smart people like Micheal Tsai and Wincent Colaiuta even think so. I've been using this set up since the CodeWarrior days (before they went Freescale).

Initially, you won't perceive any immediate or significant change. As you get used to the color scheme, in other editors you will notice the absence of kindness to your eyes. We can thank the physiological characteristics of the eye; earth-bound humans are most sensitive to yellowish-green light. The black text on light-yellow allows for high contrast, eye-friendly display.

If you Xcode or Textmate, consider giving your eyes a rest. Fully saturated yellow (like a smiley face) is not going to help you at all. By now, you are probably asking yourself, "Self? What kind of yellow do I really want to look at for some ridiculous number of hours a day?" Fortuitously, answers are at hand.

yellow color picker

In the Mac OS X color picker, there is a sliders-based option in the toolbar (second from the left on the standard Tiger mini-picker). In the pop-up menu select CMYK Sliders and set everything to 0. Push Yellow up to around 20-25% and you are on your way to being rich and famous.

Everything accounted for, color schemes are a very small part of reducing computer related eyestrain. If your eyes are pestering you for some help (itchy, watery, sore, etc.), do some proper research and improve your environment. Happy rods and cones make happy developers.

Disclaimer: Results may vary for organisms orbiting red or blue stars.




2006
1/1

6Tringle Introduction







6Tringle's featured product: PhotoClay Logo Photo mushing fun with your finger

Buy app through iTunes








We enthusiastically recommend:

There are many ways to start an journey. Common aphorisms would misdirect however, as this particular journey began with a thousand thoughts before a single step. A majority of those thoughts were simply forgotten. A lesser but significant number were pondered and discarded. More than a few were loco like a train yard. The tiny remainder form the basis of this site.

As long as the resolve holds true and the games stay uninstalled, the blog will be a source of information primarily concerning Cocoa development on the Mac OS X platform. Other topics include web development, infrastructure, and the overall Apple ecosystem. During moments of failed restraint, general nonsense may ensue. Further topics include the acquisition of tokens of commercial value to be used in barter of staples such as food, shelter and iPods. The food will mostly consist of sushi.

Our large stable of writers is nonexistent. At present, we have but one stall:

Welcome. I hope you come back often.




Products of unassailable virtue and rectitude

about products blog contact misc xml