Friday, December 30, 2011

Speaking at MDevCon

I'm proud to publicly announce my first speaking opportunity of 2012. I'll be speaking at MDevCon in Amsterdam this year on March 10th. This will be my first trip to Amsterdam and I'm incredibly excited to have the opportunity. I'm so excited, actually, that I'll be staying in the city for several days after the conference to hang out in Appsterdam and visit. When I know exactly which days I'll be in the city, I'll let you know.

I have one other conference that I've already agreed to speak at this year, but that one hasn't been publicly announced yet, so I can't spill the beans quite yet, but I'm equally excited about that one.

Thursday, December 29, 2011

Treadmill Desk Update

I'm coming up on the end of my second week using my treadmill desk, albeit two relatively quiet work weeks thanks to the holidays. I haven't put together the plans yet for those who might be interested in building a similar one, but the desk does seem to be holding up well. It's still very sturdy and has been working great for me.

After a really great first day where I put in over nine miles, my daily averages have backed off a bit to more in the 6-7 mile range and I'm averaging about 800 calories a day burned according to the treadmill. It seems that 1.6 MPH is my sweet spot for being able to type and mouse, though I sometimes go a little slower if I'm tired. I also usually start the morning at about 1.3 MPH, but after about 15 minutes, I'm usually up to 1.6.

I'm not finding it difficult to work for the most part. In fact, often I feel more alert and able to deal with complex problems. However, late in the day, I often feel just the opposite. I start to get fatigued, and when I do, then I often find that a difficult problem requires me to go over to my sitting desk or else needs to be pushed off until the next morning when I'm fresh. I suspect that this problem will go away as I continue to get used to it and am able to walk longer.

I currently spend about 4 hours walking, another 1-2 standing at my new desk, and the rest of my workday right now is at my old sitting desk. My goal is to get up to at least 8 hours of walking, but I'm really happy with this so far. It's not hard to do. There's no motivation for me to find. It's just part of my routine. When I go to my office, I start the treadmill. Easy-peazy.

I'm walking slow enough that I'm not experiencing much in the way of muscle soreness. A tiny bit in my calf last week, but none this week. There are some other problems that go along with the treadmill desk, however. First and foremost, my feet start to hurt something fierce by the end of the day. This week was better than last, and I suspect in a few weeks it will be a non-issue. A lot of it, admittedly, was poor shoe choice. More on that later.

Another problem with spending this amount of time moving is the potential for chafing and rashes. It goes away relatively quickly if you're smart enough to wear appropriate clothing, which I wasn't at first. If you experience chafing, here's two words to remember: coconut oil. Yep, the kind you buy for cooking. It's a solid at room temperature, but will turn into liquid from the heat of your hands. It's magic for irritated skin and it's also a lubricant. It's better than any lotion or salve I've ever tried.

I close my office door and open the window while I'm working, even though it's winter and I wear shorts and a t-shirt while working. Even at that, I'm a little warm, but I'm a warm person in general, always have been. I haven't broken out my winter jacket yet this year, so your experience might be different. Nobody I've talked to has mentioned being this warm, so it might just be me.

This one's important. Get some good walking shoes. Don't wear your existing sneakers or shoes, or worse, don't be stupid and go barefoot or wear sandals, crocs or whatever shoes happen to be closest to you. Good shoes, designed for walking, fitted by somebody who knows WTF they are doing. Four or more hours of walking is a lot of steps you're taking each day. Your feet will thank you.

So, the big question people probably want to know is: Have I lost weight?

It's really too early to tell. The scale says I've lost between two and four pounds, but honestly, my weight can fluctuate more than that just from water weight over the course of a single day. I might have lost nothing, or I might have lost more than that. I need more data points before I can form any conclusions. But, I suspect I have lost some. Even with a holiday party last weekend, I've been eating less than normal. I'm not sure if it's psychological or physiological, but I'm hungry less, and when I do eat, I'm tending to eat less and to eat healthier. I also haven't been tempted by my personal demon, the midnight snack. Well, more like the 2:30am-up-late-coding snack. Part of that may simply be that I'm too tired to work until 2:30am these days, though.

Overall, I'm declaring the experiment a success already. I'm able to work just as well walking as I am sitting except when I get fatigued, I'm burning calories while I work, and am just generally feeling better than I have in a long time.

Tuesday, December 20, 2011

Brilliantly Simple Idea: the Treadmill Desk

Software development is not the easiest vocation for getting or staying in shape. For that matter, being part-owner of a relatively young business isn't either.

I've been steadily putting on weight and getting in worse shape since before the dot com boom. Since we started MartianCraft, I've had very few days off, and my average work day has been probably twelve hours, possibly longer. Every time I try and get into a routine of exercising, something happens: a client emergency, a deadline, a build, a new prospective client, a minor family emergency. When there's not enough hours in the day (and there never are), the exercise was always the first thing to get cut.

Recently, I came across this brilliantly simple idea from a Mayo Clinic cardiologist. It's called the Treadmill Desk. Essentially, you replace your chair with a treadmill, and instead of trying to get a good strenuous calorie-burning bout of exercise in a few times a week, you just walk while you're working. You don't walk fast; in fact, you go quite slow. They typically recommend that you walk between one and two miles per hour, which is about half of most people's normal walking speed. This speed allows you to function fairly normally. After just a few minutes, you can type and mouse normally. If it gets hard to type, you back off on the speed a little. It's not a race. It's not even exercise in the traditional sense.

I'm typing this blog post while walking on a treadmill.

The goal with this walking is not to break a sweat, but rather to just be in constant motion. You will want your office a touch cooler than normal, though, as even slow walking does raise your body temperature. Despite the low-key nature of the movement, a typical person burns an additional 100 - 130 calories per hour they're at their desk. For a typical person working an eight hour day, that's between 800 and 1040 calories per day. If you work longer hours or weigh more than the average person, it's even more calories. Now, think about that. You have to burn approximately 3500 calories to lose a pound of weight, so assuming you don't increase your caloric intake, that's about a pound every three to four days of work, or two pounds a week. For someone working a forty hour week, that's about five pounds a month, or sixty pounds a year without additional exercise or dieting. There's a lot of health benefits that go along with losing extra weight, also. Lower blood pressure, lower cholesterol, lower incidence of several types of cancer.

If you work more than forty hours a week, the benefits can be even greater, assuming you don't eat more to make up for the extra calories you burn.

I've talked to a number of people who are using treadmill desks, and the feedback was universally positive. Everybody lost weight – several of them a considerable amount – and all were in better health than when they started. And almost everyone said the following word-for-word phrase to me: "I would never go back to a normal desk".

After about the third person gave me the same basic story, I was sold. Sign me up! I can do this.

But there was a problem.

There are lots of plans for treadmill desks around the Internet and you can actually buy treadmill desks now. But, here's the thing: My primary machine is a Mac Pro with two 27" ACD monitors along with a crap-ton of accessories. I couldn't find any desk for under $5,000 that looked sturdy enough to safely and securely hold the weight of all my gear, and all the plans I found for building one seemed to assume the use of a laptop or small computer with small monitor.

Because of that big sheet of glass, Apple's monitors are heavy. Because of that big aluminum case, the Mac Pro is also very heavy. My computer and monitors alone weigh well over a hundred pounds, and I wasn't about to trust that much weight worth that much money to something that looked flimsy or was designed to hold a laptop.

I experimented for a week using a makeshift treadmill desk with my MacBook Air, and I found the walking and working quite easy to do, but also found myself constantly pining to move back to my desk to use my more powerful machine and bigger monitors. I usually abandoned the treadmill after about two hours when I'd start getting into more hardcore coding, not because of the treadmill, but because of the computer.

But, that week was enough to convince me that this was a good way to reverse the slow but persistent upward trend in my weight that's marked the last several years of my life. Unfortunately, it also convinced me that I could only make it work if I could find a way to use my computer while walking.

So, I decided to build my own desk. Building a sturdy desk this tall (over four feet tall in my case, since I'm 6'3" and will be standing on a treadmill desk that's about 6" high) turned out to be an interesting, but not-entirely-trivial engineering exercise. It took a couple of re-designs and a lot of swearing, but my desk is now done and I've been happily using it for a couple of days now.

Here's my rig on my desk (excuse the messy office):

Finished 1

(Yes, there's a beer bottle in the background. That was my reward for finishing the desk.)


After I've been using it for longer and am convinced the design is good, I'll share how I made the desk for anyone who might wish to build one for themselves.

So far, so good, though. The desk seems to be solid, has no perceptible wobble, and the monitors and computer don't seem to be putting any noticeable strain on it. Yesterday, I walked nearly nine miles at an average of 1.6 mile per hour and had a productive work day. Today's about half over and I've burned a little over 600 calories while fixing two pretty gnarly bugs in client apps. These two days have been enough to convince me that this is going to work.

By having my good computer on the treadmill desk and my MacBook Air at my regular desk, there's a built-in incentive for me to walk, or at least stand, rather than sit at my desk. I'm simply more productive when I have more screen real estate and a more powerful computer.

In a week or so, I'll put together a parts list and basic instructions, but the design is pretty basic. 1¼" galvanized pipe makes up the main frame of the desk with ⅛" wire rope and turnbuckles as guy wires to provide additional lateral stability.

This is the treadmill I use:
NewImage


It's designed only for walking, will hold up to 400 pounds, and it's shorter than most treadmills, so it takes up less room. It's not cheap, but it's fairly reasonable for a treadmill at $400, and it can fold up out of the way if you need to. I've been very happy with it so far. While it's not whisper quiet, it's nowhere near as loud as most treadmills I've used, and it has a full set of controls on the handle where you can reach them easily while using it as part of a treadmill desk, which is much nicer than having to awkwardly reach over the desk to the treadmill's console.

The desk plans (when I post them them) are intended to work with this treadmill, you will probably need to make minor adjustments to use it with other models, as well as account for any height difference.

Monday, November 14, 2011

Comments Gone

I regret to inform you that I've had to completely disable comments on the blog. I had to switch to moderation a while back since the spam detectors were failing to catch anything meaningful. Unfortunately, the rate of spam comments has been accelerating recently to the point where I simply don't have the time to search for the few actual and valid comments in the proverbial haystack of spam.

I wish I didn't have to do this. I like comments. I like hearing other peoples' takes on things and love it when somebody catches a mistake or proposes a better solution. But, comment moderation has been sucking away what little time I have for the block lately.

Maybe at some point I'll have the time to transition to software that can handle spam comments better, but I don't right now.

Sorry it had to be this way. It sucks that there are people in the world who do this shit and ruin things for everyone else.

Wednesday, November 9, 2011

Flash is Dead. No, Really this Time

It appears that Adobe is going to cease development of mobile Flash.

I'm not ordinarily one to gloat or dwell on I-told-you-sos, but I'm going to make an exception in this one instance. I took an awful lot of heat for those blog rants backin 2009 for saying things like

I hate to break it to you, but Flash, as it currently exists, is dead. Oh, it's not going to die quickly, it's going to die a slow painful death precisely because there has been such a large investment of time and money into using it by so many large corporations like Disney. Flash's roots run way too deep for it to disappear quickly.

Here's the thing, though: Flash is a product of a different generation of computing. It's a product of a world where 90% of the people used one platform, and the bulk of the remaining used another. There was Windows, and there was the Mac.

[…]

And now, the world is changing. People are increasingly browsing the web from mobile devices, and unlike the computer world of a decade ago, the mobile computing landscape is not anything like a monoculture or monopoly. There are several viable mobile platforms all competing in that space. We have the iPhone, Blackberry, Palm Pre, Windows Mobile, Android, Symbian and probably others that have slipped my mind. All of these are operating systems currently shipping on phones and all come with browsers. None of them, except a solitary model of Android phone, has Flash.

But before I get too smug, you know what? I missed a huge factor in the demise of Flash. I assumed the performance issues they were having back then were simply technical hurdles that would be overcome by Adobe's engineers before long. In the end, the lack of a monoculture was certainly a significant factor in the demise of mobile Flash, but the real nail in the coffin was that Adobe never even got mobile Flash working demonstrably well on a single model on a single platform, let alone working well on the "billions of mobile phones" they were shooting for with the Flash Consortium. I completely overestimated Adobe's ability to deliver, technically.

But all is not lost for Flash developers. Adobe has stated that they are going to "refocus" their efforts on allowing Flash to create HTML5 compliant web apps. What a brilliant idea!

If I may… one last quote from my much-reviled Flash rants:

But, that doesn't mean there's no hope. There are many ways that Adobe could save Flash/Flex for the mobile world. One way would be to create something like Google's GWT - an environment where some or all of the code gets translated into HTML and Javascript to be run on the client, leaving to a VM only those tasks that can't reasonably be handled that way.

I said that back in August 2009 and, frankly, I was kind of stating the obvious. Maybe Adobe should have made me CEO. I doubt I would've done much worse than the current management given the recent layoff announcements.

I know people think of me as an Adobe hater. I'm not. More accurately, I'm a disgruntled fan. In the nineties I held Adobe in higher regard than I held Apple (at least Apple before Jobs' return). I adored Photoshop, which I had started using around version 1.5 and which enabled me to make a living for a couple of years. Somehow, in the span of fifteen years, Adobe went from being a company that made amazing things to being a company that made mediocre things that they marketed as amazing. A company that had no focus. A company that churned out new versions of software in order to generate new income, not to meet honest customer demand. I watched a great company become a company that didn't care about being great as long as quarterly profit sheets looked good.

I, for one, am hoping for a Phoenix-like rise from the ashes on the order of Apple's comeback. Hoping… but not expecting.

Thursday, October 20, 2011

Objective-C 2.0 Compiler Directives

Steffen Itterheim has posted a really nice list of all the Objective-C 2.0 compiler directives along with examples of how to use them.

Nice!

Wednesday, October 19, 2011

GLKit Examples

I've added three new projects to my iOS OpenGL ES repository on GitHub. They are fairly simple examples of how to use GLKit and GLKBaseEffect. You can find them in the GLKit Stuff directory.

They're kind of rough, but they should be helpful to you if you're just getting started with GLKit and trying to figure out how to use it.

Thanks to Julián Oliver for tweeting the solution to a problem I was having getting textures to work with GLKBaseEffect.

Tuesday, October 18, 2011

CGAffineTransform Additions

As you probably know, Apple provides a bunch of functionality for manipulating objects in 2D space using CGAffineTransform. Oddly, Apple doesn't provide you with a way to extract the scale, transform, and rotation information from a CGAffineTransform and they don't provide any shearing functionality at all.

Here are some additional inline functions that I use. This adds the ability to extract component values of the CGAffineTransform and also adds the ability to create and extract shear information.

As always, this code is free to use without restriction or limitation, but has no warranty whatsoever. If you fix a bug, feel free to let me know about the fix so I can incorporate the fix.

#ifndef __MCP_AFFINE_TRANSFORM_ADDITIONS__
#define __MCP_AFFINE_TRANSFORM_ADDITIONS__

#import <CoreGraphics/CoreGraphics.h>

#ifdef __cplusplus
extern "C" {
#endif

#define degreesToRadian(x) (M_PI * x / 180.0)
#define radiansToDegrees(x) (180.0 * x / M_PI)

static inline CGAffineTransform CGAffineTransformMakeShear(CGFloat shearX, CGFloat shearY)
{
return CGAffineTransformMake(1.f, shearY, shearX, 1.f, 0.f, 0.f);
}

static inline CGAffineTransform CGAffineTransformShear(CGAffineTransform transform, CGFloat shearX, CGFloat shearY)
{
CGAffineTransform sheared = CGAffineTransformMakeShear(shearX, shearY);
return CGAffineTransformConcat(transform, sheared);
}

static inline CGFloat CGAffineTransformGetDeltaX(CGAffineTransform transform) {return transform.tx;};
static inline CGFloat CGAffineTransformGetDeltaY(CGAffineTransform transform) {return transform.ty;};
static inline CGFloat CGAffineTransformGetScaleX(CGAffineTransform transform) {return sqrtf( (transform.a * transform.a) + (transform.c * transform.c) );};
static inline CGFloat CGAffineTransformGetScaleY(CGAffineTransform transform) {return sqrtf( (transform.b * transform.b) + (transform.d * transform.d) );};
static inline CGFloat CGAffineTransformGetShearX(CGAffineTransform transform) {return transform.b;};
static inline CGFloat CGAffineTransformGetShearY(CGAffineTransform transform) {return transform.c;};
static inline CGFloat CGPointAngleBetweenPoints(CGPoint first, CGPoint second)
{
CGFloat dy = second.y - first.y;
CGFloat dx = second.x - first.x;
return atan2f(dy, dx);
}

static inline CGFloat CGAffineTransformGetRotation(CGAffineTransform transform)
{
// No exact way to get rotation out without knowing order of all previous operations
// So, we'll cheat. We'll apply the transformation to two points and then determine the
// angle betwen those two points

CGPoint testPoint1 = CGPointMake(-100.f, 0.f);
CGPoint testPoint2 = CGPointMake(100.f, 0.f);
CGPoint transformed1 = CGPointApplyAffineTransform(testPoint1, transform);
CGPoint transformed2 = CGPointApplyAffineTransform(testPoint2, transform);
return CGPointAngleBetweenPoints(transformed1, transformed2);
}


#ifdef __cplusplus
}
#endif

#endif

Saturday, October 15, 2011

Long Live the King

Jeff Atwood of Coding Horror has an interesting post today about being a developer for Apple's ecosystem. We'll leave aside for now the fact that Jeff isn't and has never been an Apple developer and is, in fact, a strong proponent of Microsoft's developer stack. Despite that flaw, I usually enjoy reading Coding Horror. I think Jeff's a smart guy with a lot to offer the world in terms of insight about software development.

But…

Jeff's premise today is that third party Apple developers "serve at the pleasure of the king", which is absolutely true. But, he finishes his post by saying he would never develop for Apple's app stores because of the chance of "being thrown under a bus" by Apple. Fair enough, we all have different tolerance levels for things, and his assumption here is essentially correct. Apple developers run a risk of having Apple compete directly with us if our product is successful enough. It happens, perhaps, to .01% of us, but it does happen.

I want to run with Jeff's metaphor a bit, but first, I need to address a couple of points in his article.

The first point I want to talk about is his claim that if Microsoft were to ever do something as audacious as release a product with the same features as one of their third party developers
…developers would be screaming bloody murder and rioting in the, er, blogs and web forums.

Wait… what? Excuse me? What the hell kind of fantasy world does one have to live in to think that Microsoft doesn't, wouldn't, and hasn't competed with their own third-party developers. Microsoft practically made a sport of it in the nineties, and has continued to do it since then, albeit less obnoxiously. I mean, have we forgotten that Microsoft was convicted by both the US and the EU of abusing their monopoly power to compete unfairly. Microsoft used to routinely use their market power to put smaller software companies out of business in order to gain control of a particular niche of the software industry.

And what were the third party Microsoft developers doing back then? Screaming bloody murder?

Nope.

They were defending Microsoft, and understandably so. The hand that feeds and all that. Those developers knew that being assimilated by Microsoft, or being unfairly competed against by Microsoft, were risks of doing business in that particular market and they deemed it worth the risk, much the way that iOS developers do today with Apple.

The second point that I think needs to be made is Atwood's implied assumption that because Marco Arment isn't up in arms over Safari's Reading List feature (which duplicates many of Instapaper's features), and isn't ranting to anybody who will listen, that it means he's "a loyal subject of the king". The implication being that Marco will put up with any treatment that Apple chooses to dish out, no matter how unfair, because he's a good whipping boy.

Which is a completely inaccurate characterization.

I don't know Marco personally, but he's one of the very few people I follow on the Internet who has consistently come across like a grown-up. Marco knew that getting sherlocked was a risk he faced by participating in Apple's ecosystem and decided that it was worth the risk. When that risk turned into reality, he accepted the consequences like a grown up. Anyone who could condemn a man for acting maturely should be called out for it, and that's exactly what Jeff is doing here. Marco's response to the Reading List feature was nothing less than admirable.

Virtual Geography


Now, back to Jeff's original metaphor of App ecosystems as countries. Let's talk a little bit about the Apple Kingdom and also about the neighboring countries where, as a merchant, you might choose to peddle your wares if the King's terms aren't acceptable to you.

The Apple Kingdom


As Atwood noted, the Apple Kingdom is a monarchy, ruled by a King. By Jeff's own admission, this king is a benign king, making decisions that he feels are best for his citizenry (consumers). He's not perfect, but generally his decisions have served the populace well, and they are happy and prosperous. The Apple Kingdom has beautiful paved roads and a great system of railways, and state-run stores within walking distance of every single citizen in the country. The king has invited even the smallest merchants to use this infrastructure to sell goods in exchange for a small percentage of their income and so long as those merchants follow some guidelines that the King has set (and, admittedly, may periodically change) in order to make sure his citizens have a good experience in his stores.

The Apple Kingdom is large, and its citizens are fairly affluent. They regularly shop at the King's stores and enjoy buying things there. Even tiny merchants are finding ways to make a comfortable living by being able to reach all the citizens of the country. Sure, not all merchants are succeeding. Even the best roads and stores aren't going to make a poorly run business profitable or get people to buy spoiled produce, but the opportunity is there for any one who wishes to sell goods to do so with minimal overhead and startup costs.

The King, however, is also something of a merchant himself, and sometimes he will sell at his own stores. He doesn't sell much, but when he does, sometimes he's selling the same types of products as some of the merchants in his kingdom. The King, of course, doesn't have to pay a percentage of his income to use the trains, roads, or stores, though. In even rarer cases, he'll find a product in the stores he likes so much that he wants to provide it to all of his citizens for free, which basically makes it impossible for the original merchant to sell that product any longer.

Now, the Apple Kingdom is not the only country in the world, and the King doesn't prevent anybody from emigrating or, in fact, even from exporting products to other countries. When deciding whether to pack up and go to another country, you need to make sure that your destination is going to be better than where you are now, so let's look at these other countries.

Androidistan

The largest neighboring country is an enormous country called Androidistan. Androidistan doesn't have a king, but instead has dozens of local and municipal governments of varying types and quality. Their national system of government is probably best described as anarchy. Androidistan's infrastructure varies greatly from province to province, with some roads and trains being free for anybody to use, and others requiring payment. Many provinces have their own stores where anybody can sell, but it's hard to find what you want and the quality of the goods is generally kind of poor, though there are exceptional products if you're willing to look for them. Unfortunately, there are also dangerous products sitting on the shelves as well. Poisoned produce and other deadly items disguised as legitimate goods.

Although there's a lot of people in Androidistan, most of them don't really like to buy goods at the government stores. They like to go in and make a meal of the free samples, but most don't really like to actually buy anything and shoplifting is rampant in these stores. In fact, it's often considerably easier to steal a product than to buy it legitimately because many of the stores aren't very well run.

Most merchants have found they can't make enough money selling their goods in Androidistan's markets, so they've tried placing stickers on their goods advertising other products. Some merchants are making a really good living doing this, but many more aren't even covering their costs.

Windowsland


On the other side of the Apple Kingdom is the relatively large country of Windowsland. Windowsland was once prosperous and has a huge amount of industry, but the country has been stagnating somewhat during the recent move toward smaller merchants. The government has recently started updating their infrastructure and building stores modeled after the ones in the Apple Kingdom. Windowsland has started a heavy advertising campaign trying to get merchants to sell their goods there.

Despite a great infrastructure and clean, brightly-lit stores, Windowsland has been having limited success in moving to the modern age of small merchants. Their large industry is still going strong, though, and in the long run, likely their stores will start to do a good business. Someday.

Oh, did we mention that Windowsland is also run by a King? He charges the same percent of income to use his railways, roads, and stores, but has placed fewer restrictions on his merchants in an attempt to lure them to sell in his country. Windowsland is a pleasant enough place, but there's just not all that many people going into their stores yet.

The Democratic Republic of WebOS


This small country had huge potential, but recent mismanagement and ineptitude have basically shut the government down. There are a few citizens roaming the streets still in a bit of a daze, but most have left for better-run countries.

The Blackberry Oligarchy


The Blackberry Oligarchy, which actually has two kings, has gone in one generation from prosperous world power to third world country due to its inability to change its ways. Citizens have been leaving this country at an alarming rate and the remaining citizens have little faith that their pair of kings will be able to fix the problems given the frequent power outages and decaying infrastructure.

There are several other small outlying countries in MobileWorld, but these five pretty much cover the major markets that merchants would want to sell into.

Epilogue


Metaphors are easy to stretch to a point that they become meaningless, and I probably crossed the line a few paragraphs back, but the point is this: all ecosystems have their benefits and their downsides. In the old days of boxed software, instead of a single benign king, you had to deal with a dozen tyrants (software chains) to get your goods on the shelf, and they took more than half the sale price in exchange. If you didn't want to deal with them, you could instead choose to work through a distributor who took another large chunk of your gross income in exchange for dealing with the tyrants on your behalf.

In every market at every time the risk of competition from a bigger, richer competitor has existed.

Part of Atwood's argument is that even good Monarchies can go bad. Sure they can. So can good democracies. Let's take what Apple might be someday off the table. We can't fault people for not staging a revolution before there's an actual reason to revolt.

Whether you do it explicitly or not, going into business means you're accepting certain risks in order to obtain certain rewards. When bad things happen in business — when risks (anticipated or otherwise) turn into actual problems — acting like a twelve year-old and whining to the Internets doesn't make things better. You buck up, find a way to fix it, or move on to something else.

So, yeah. We Apple developers serve at the pleasure of our King, and we know it. In exchange, we get access to millions of citizens who want to buy our products. A tiny percentage of us will end up competing directly with our king at times, but overall, life is pretty good in the Apple Kingdom for us merchants.

And, while we'd be glad to have you among our ranks, Jeff, you're more than welcome to pack up your gear and move to a neighboring country if you think life would be better for you there.

Monday, October 10, 2011

Disappointing

So, like others, I was tempted to write a defense of the iPhone 4S after all the pundits and naysayers came out last week calling the iPhone 4S "disappointing". I'm glad I never found the time. As it turns out the iPhone 4s doesn't need anybody to stand up for it.

1,000,000 phones sold on the first day of pre-orders. That's 400,000 more than the iPhone 4.

And, that's not counting the number that will sell on October 14th when they arrive at the Apple Stores in 7 countries.

There's also a large batch of countries that don't get the phone until October 28, and a whopping 40 more countries (including China, where new Apple Stores have been shattering sales records lately) that won't get the iPhone 4s until some time in December.

Yeah. Disappointing. You guys in the media sure nailed that one.

Thursday, October 6, 2011

Respect & Shame

It's been fascinating seeing how many people have responded to the death of Steve Jobs exactly the way I did. It's truly amazing how many people who never met the man feel not just that the world has lost somebody significant but that they themselves have lost a friend.

Hearing others express exactly what I'm feeling? It helps. And, for the most part, the media has been right there with us, responding to Steve's death by focusing on his impact and the good he has done.

I really hope seeing how many peoples' lives he has touched is helping Steve's family and friends get through this.

But not all of the media has chosen to focus on the good. I won't link to any of the actual articles, but Gawker and the New York Times top a short list of media outlets that have chosen to focus on Steve's flaws and to stand up and shout out to anybody who will listen that Steve was… well… human. Imperfect. Flawed.

There's a custom in modern society that's often called "respect for the dead". It has nothing whatsoever to do with the dead. The dead don't care what you say about them. It's about the living who cared about the dead, and they do. It's because they care that mourning is such a difficult process. Painful. Sad. It's not a time when you want reminders of the flaws of the person whose absence you are trying to come to terms with.

No human with with a shred of empathy or decency chooses to publicly criticize the recently departed, famous or otherwise, regardless of how they felt about them. Doing so is an act of cruelty. It's hurtful. Little. It's kicking people hard when they are already as low as they can be.

For those, like me, who care, but didn't know Steve Jobs personally, people like this are an annoyance. They're just another crass, classless obnoxious Internet loudmouth that we have to make an effort to ignore.

But for Steve's family, friends, and coworkers… the people who really knew him, it's a hell of a lot more than that. It breaks my heart to think that they might read those articles.

Shame on you, New York Times. You're better than that. Shame on you, Gawker. You should be better than that.

Wednesday, October 5, 2011

Sad Mac

Sad mac

I haven't had much time for blogging lately. I have about a half-dozen unfinished blog posts in my queue, but have been too busy to finish any of them.

One of the things keeping me busy right now is work for Apple through MartianCraft. This work, in many ways, is a dream come true for me, as I wanted to work for Apple for years and never managed it.

I started programming on an Apple ][+ in 1980 and Apple has been a part of my life ever since then. I don't need to look much further than the desk I'm sitting at to realize just how much my life has been impacted by this company called Apple started in a garage by two guys named Steve. I make a living using Apple's products and developing for Apple's products, but more importantly, my life is better because of their products.

I took a few hours away from my desk this evening and returned to the news of Steve Jobs' death. I came back to news that quite literally felt like a kick in the stomach. And I mean literally. My stomach hurts.

It seems odd to feel this way about a man I never met. I've only once been closer to Steve Jobs than the front section of Moscone West or North, and that was when I accidentally knocked into him on the show floor at MacWorld one year. But I feel like an old friend has died. I'm fighting back tears, and maybe I should be ashamed of that.

But I'm not.

Steve died far too young. I think a few tears are in order.

Friday, August 12, 2011

Online Session Code for Big Objects (Plus a Warning)

In Chapter 9 of More iPhone Development, we wrote a set of classes that mimicked the behavior of GameKit's peer-to-peer connectivity, but for regular network connections (GameKit's only works with BlueTooth and local network connections). Basically, we wrote a class that lets you send and receive anything that can be packaged into an instance of NSData. Since it's relatively trivial to implement NSCoding for most classes, this means passing objects between two iOS apps (or an iOS and a Mac app) becomes pretty easy. You don't have to poll for the data, or worry about chunking out the data. You just make a method call and pass an NSData instance to send data, and then implement a delegate method for receiving data back from the other end. Life is good, right?

Hmm...

Maybe not. There's a pretty big limitation in the book's implementation. That implementation, designed for passing tiny packets of data (TicTacToe game moves), kept everything in memory. If you try to send a good size image to the other connection, likely you'd run out of memory fairly quickly.

A while back, I faced exactly that situation. For a kiosk app that MartianCraft was writing for a client, I needed to send large images shot with a DSLR camera from a Mac Cocoa program to an iPad program and also needed to send pictures taken with the iPad's camera back to the Mac Cocoa app. These images, compressed, ranged from about one to about five megs. I grabbed the OnlineSession class from More, figuring I had the network code basically done, and watched my application go down in a blaze of… well… not glory, that's for sure. Not only did the iPad run out of memory, it ran out of memory FAST… much faster than I expected. Even sending the smaller iPad camera images often caused low-memory crashes.

There were two basic problems with the OnlineSession class when you try to use it for sending larger volumes of data. First, as I said, was that it relied only on physical memory. Given that the physical limitations of the original iPad, this was problematic. But there was another, much bigger problem.

The second issue was that during the process of chunking up the data to send, the code kept making unnecessary copies of the data. Put simply, I made a n00b mistake. The mistake didn't impact the TicTacToe application because the game moves would easily fit into the send buffer, but it's a mistake I've made before and definitely should've known better.

So, what, specifically, was this mistake, you ask?

Using NSData's regular convenience constructor dataWithBytes:length: when creating the new NSData instance to store the portion of the image that won't fit into the send buffer. If you read the description of dataWithBytes:length:, it very clearly says that it makes a copy of the data you provide. So, every time a packet was sent, the code would create a new NSData instance to hold the remainder that wouldn't fit in the buffer, and it would copy all the remaining unsent data for every packet. Ouch.

So, as a simple example, if we were sending a 5 meg image, and the send buffer was set to 128k, the code would make a 4.825 meg copy after the first packet was sent, then a 4.75 meg copy after the second packet was sent, a 4.265 meg copy after the third packet, and so on. After every packet, another slightly smaller copy of the data was made. A descending progression that would eat up memory fast.

After a lot of swearing at myself, I made some modifications to the class to do two things.

First, I switched to using NSData's dataWithBytes:NoCopy:length:, which uses the provided data in place without making a copy. This kept the memory footprint a lot smaller. In some instances, because the DSLR images were so large and our app needed to send so many, I still hit memory problems. So, the second thing I did was to add filesystem caching of the outbound queue so that all the encoded objects waiting to be sent didn't have to fit in memory for the application to function properly.

The new version of the class functions exactly as the one from the book, so you should be able to just drop-in replace the OnlineSession from Chapter 9 with this one without making any changes to your application code.

You can download the new version right here.

Tuesday, July 26, 2011

Working for MartianCraft

In case you missed my tweets earlier this week, MartianCraft is looking to add a few developers. We're looking for a couple of experienced developers, and are also thinking about bringing in one or two entry-level devs without significant experience to be trained up.

Initially, the work would be project-based contracting and would start in late August. Conversion to full-time employment is a possibility, but not right away.

If you're interested in being considered, send an e-mail with relevant work experience and/or résumé/CV to work@martiancraft.com.

Thursday, July 14, 2011

Auto-Incrementing Build Numbers for Release Builds in Xcode

I use the wonderful Test Flight to distribute builds. One thing that Test Flight is a little picky about is build numbers. When you upload a build, it uses the build number to see if you're uploading a replacement or a new build. It will let you create a new build even if you don't remember to increment the build number, but it's an extra manual step, and then you end up with two builds with the same build number.

Because I'm forgetful, I wanted to automated this process. Basically, I wanted to increment the version short string any time we do an Archive and increment the bundle build ID any time we do a Release configuration build, but leave the version numbers alone on Debug builds.

Unfortunately, several of our projects are ones that we inherited or took over, so not every project uses the same version numbering scheme. How we increment 1.0b5 is different from how we increment 1.0.12, or a simple build number like 1058.

The way I deal with this is a Run Script Build Phase in my application's executable target that runs the following Ruby script (make sure you set the "shell" field to /usr/bin/ruby, and make sure the script is the last build phase in the application). Feel free to use this script if you wish and modify it to meet your needs. If you improve it, I'd be glad to incorporate improvements back into it. One item of note: the way that I differentiate between Archive builds and other Release configuration builds might be a bit fragile since I'm relying on an undocumented naming pattern in an environment variable.

Note: I'm aware of agvtool. I avoided it for two reasons. First, I wanted more control over the numbering schemes, and second, I tried using agvtool in a build script a few years back, but at that time, there were issues when you bumped the version numbers of a project that was currently open. Those issues may have been resolved, but I didn't want to fight that battle again.

def get_file_as_string(filename)
data = ''
f = File.open(filename, "r")
f.each_line do |line|
data += line
end
return data
end


def handle_alpha_beta(old_value, letter, infoplist, start_of_value, end_of_value)
parts = old_value.split(letter)
version_num = parts[0]
alpha_num = parts[1].to_i

alpha_num = alpha_num + 1
new_version = version_num.to_s + letter + alpha_num.to_s
print "Assigning new version: " + new_version + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length];
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value - start_of_value + (new_key.length - 1))]

new_info_plist = part_1 + part_2 + part_3
new_info_plist
end

def find_and_increment_version_number_with_key(key, infoplist)

start_of_key = infoplist.index(key)
start_of_value = infoplist.index("<string>", start_of_key) + "<string>".length
end_of_value = infoplist.index("</string>", start_of_value)
old_value = infoplist[start_of_value, end_of_value - start_of_value]

print "Old version for " + key + ": " + old_value + "\n"
print old_value.class.to_s + "\n"
old_value_int = old_value.to_i
print old_value_int.class.to_s + "\n"
if (old_value.index("a") != nil) # alpha
infoplist = handle_alpha_beta(old_value, "a", infoplist, start_of_value, end_of_value)
elsif (old_value.index("b") != nil) # beta
infoplist = handle_alpha_beta(old_value, "b", infoplist, start_of_value, end_of_value)
elsif (old_value.index(".") != nil) # release dot version
parts = old_value.split(".")
last_part = parts.last.to_i
last_part = last_part + 1
parts.delete(parts.last)

new_version = ""
first = true
parts.each do |one_part|
if (first)
first = false
else
new_version = new_version + "."
end
new_version = new_version + one_part
end
new_version = new_version.to_s + "." + last_part.to_s
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"
infoplist = "#{infoplist[0, start_of_value - '<string>'.length]}#{new_key}#{infoplist[end_of_value + '</string>'.length, infoplist.length - (end_of_value+1)]}"
elsif (old_value.to_i != nil) # straight integer build number
new_version = old_value.to_i + 1
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length]
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value+1)]
infoplist = part_1 + part_2 + part_3
end
infoplist
end



config = ENV['CONFIGURATION'].upcase
config_build_dir = ENV['CONFIGURATION_BUILD_DIR']

archive_action = false
if (config_build_dir.include?("ArchiveIntermediates"))
archive_action = true
end

print "Archive: " + archive_action.to_s + "\n"


print config

if (config == "RELEASE")
print " incrementing build numbers\n"
project_dir = ENV['PROJECT_DIR']
infoplist_file = ENV['INFOPLIST_FILE']
plist_filename = "#{project_dir}/#{infoplist_file}"

infoplist = get_file_as_string(plist_filename)
infoplist = find_and_increment_version_number_with_key("CFBundleVersion", infoplist)
if (archive_action)
infoplist = find_and_increment_version_number_with_key("CFBundleShortVersionString", infoplist)
end
File.open(plist_filename, 'w') {|f| f.write(infoplist) }
else
print " not incrementing build numbers"
end

 
Design by Wordpress Theme | Bloggerized by Free Blogger Templates | coupon codes