Category Archives: development

Frameworks Need Better Logging

Over the past couple years, I’ve worked with quite a few third-party frameworks. These experiences have highlighted a significant deficiency common in many frameworks; most frameworks suck at logging. More recently, I’ve had the chance to build a few frameworks from the ground up, so I’ve used those to explore how logging could be done differently.

Bad: NSLog

There are two common ways to handle logging inside a framework, and both of them stink. The first is “good ol’ fashioned” NSLog() or print/f(). As a framework does its work, it merrily peppers the console with diagnostic or error information. These messages are usually helpful during development, but this pattern leaves a lot to be desired when customers report bugs and the console messages are absent.

Also Bad: Another Framework

The second is the utilization of a logging framework like CocoaLumberjack.1 Logging frameworks are great because they offer lots of flexibility, including mechanisms for collecting and sending logs in bug reports. Seems great, right? The framework can push error messages into the log, and my app can bundle those log messages up when it sends in a bug report. Except that this app doesn’t use CocoaLumberjack – it uses NSLogger. What’s worse is that another framework in the app uses GTMLogger. All of the sudden, there are three logging frameworks and they don’t work together.

A Simpler Way

Great frameworks should strive for zero dependencies. Since integrating a logging framework into your framework is contrary to that goal, let’s explore a simple approach that gives your framework’s integrators more control of logging in their apps.

The approach below is pretty simple, but after using it a number of times in frameworks I’ve helped build, it has proven quite effective. The example I’ll give is written in Objective-C, but the pattern translates to Swift nicely.

Public Interface

Here are the public parts: a register and unregister function, along with a callback signature. I also prefer to provide basic levels, but try not to get too carried away – keep it simple.

App Integration

Early in the process lifecycle, the app can register a callback with the framework. This block of code gets called any time the framework produces a logging message. Since a level is passed along, the application can determine how to either map the message to another logging system, or simply discard it.

Framework Internals

The internals of the framework’s logging are even simpler: a basic C function for producing log messages. This, of course, could be improved to use preprocessor macros to include inline file names, line numbers, and compile-time configuration for log levels.

Any time the framework needs to produce a log message, it simply calls the log function:

Logging Implementation

Here’s the basic logging implementation from inside the framework. Obviously, your version may require more safety checking (locks around the callback, etc.) or more sophisticated dispatching to protect certain threads and queues, but this should get you started.

Hopefully this gives you some ideas about how you can improve logging in your own frameworks to make integration even easier. If you have questions or want to share some ideas, you can find me on Twitter.

  1. I’m not picking on CocoaLumberjack at all here – I use it in my apps, and you should consider doing so as well. It’s a great, battle-tested framework.

The Cost of Swift

Before I get started, I need to preface this essay with a few important points:

I really like Swift. I’ve built quite a few apps with it, both at work an on my own. Astra is almost entirely written in Swift, with only a few classes that touch Core Audio written in Objective-C.

My opinions are my own – they don’t reflect those of my employer or my coworkers.

Swift, a language that is less than three years old (publicly), has gained an amazing amount of momentum within the iOS/Mac community, as well as the broader programming populace. It boasts some ambitious goals of speed, safety, and expressiveness. Naturally, Apple’s emphasis on the language has also bolstered its adoption.

After building my own product in Swift and watching other product owners struggle with a common issue, it’s time to address a problem with the language: the cost of using Swift. To demonstrate, I’m going to share a story.

Astra 1.0

Astra was initially written in Swift 2.3. In the interest of releasing version 1.0 of the app, I delayed the Swift 3 migration last fall and shipped with Swift 2.3. As the product owner, I had to make a conscious decision about when I was going to bite off the Swift 3 migration, and getting the app in the market outweighed the debt of an older Swift version.

Once version 1.0 shipped, I began working on the next minor version of the app – a few new features, some enhancements – typical dot-fix things. As anticipated, a patch release needed to be shipped, so I fixed a couple critical bugs in 1.0.1, and then enjoyed the holidays with my family.

Side Projects Cost Time

Astra is a side project; I have a full time job, as well as a family and other “real world” responsibilities. When I get time to work on side projects, it’s usually for 30-60 minutes at a time, and almost always after 10:30pm.

All projects have some sort of constraint. It’s usually budget. Since I work on Astra for “free,” my constraint isn’t budget, but time. Each minute I spend working on it takes me away from my family, sleep, other hobbies, or a whole list of other things I really need to do.

The Swift 3 migration took me four nights, somewhere between 6-8 hours in total. This effort spanned a couple weeks, since I don’t necessarily have consecutive days to work on Astra. This was relatively quick, compared to other stories I’ve heard and witnessed.

Once I had code that compiled, I spent another couple nights getting the app to work again. In the end, I spent well over two weeks migrating the app to Swift 3.

This amount of time has very real consequences, but users will never see those efforts as direct  improvements in the product.

I track Astra bugs and features in Trello, and I try to keep each card small enough to complete in one sitting. I keep the list prioritized, but when I know I have a small amount of time, I hunt for a card I know I can do all at once.

The Astra board didn’t change for over two weeks because of the Swift 3 migration. That was two weeks I didn’t get new features or enhancements built, or customer-reported issues fixed. I was simply agreeing to proposed changes by the tools, and then fixing the problems it created along the way.

Developer Happiness

The language an app is written in does very little to serve the needs of a customer. There are weak arguments that some languages result in fewer crashes or bugs, but that doesn’t play out in reality. A product built with proper testing practices always demonstrates a higher level of quality in my experience, regardless of language.

Fundamentally, a developer’s role in app development is to translate interaction and visual designs into functioning software. That means it’s their job to deal with unexpected errors and failures. It’s risk management. These risks also have to be balanced with product timelines – press events, market changes, and competition.

When the ability to ship a product is affected by a tool or programming language, a product owner has to question if it is the right tool. For software, the programming language is a means to speed up development of a product. For iOS, Swift keeps developers from having to write in C or Assembly, much like using UIKit keeps them from having to develop graphical interfaces from scratch. And while Swift is pleasant to use as a developer, it doesn’t provide tangible benefits to a product owner, much less end users.

Apple’s Timeline

Swift is both an academic venture and a product for Apple. It is mastered by two conflicting objectives – to aggressively evolve and simplify difficult development tasks, while marching along a scheduled release cadence. Caught in the middle, developers who choose to use the language end up with code that breaks regularly.

Last week, we also learned that ABI stability has been deferred from Swift 4.

Apple has been aggressive in deprecating versions of Swift, which means Apple dictates Swift migration timelines, and not the product owner. The next release of Xcode is expected to remove support for Swift 2.3. In the case of Astra, this hasn’t been a problem yet, but easily could be.

Emergency Hotfixes

I had to spin a quick 1.0.2 release to Astra a few weeks ago. If you’re following along, you realize that meant fixing a bug in Swift 2.3. In addition to fixing the bug quickly, I had to decide how to get the the fix in both Swift 2.3 and Swift 3 branches. In the end, I implemented the bug fix twice, due to how divergent the project was after other refactoring.

With a fix in place, I had to build the code. Fortunately, I haven’t jumped to Xcode betas, so Swift 2.3 is still supported on my build machine. What if the timeline had played out differently? Sure, Apple still allows submissions from old Xcode versions. That would have meant lost time setting up an older Xcode version. There’s even more time I’m not building features or fixing bugs.

The choice to use Swift imposes a lot of otherwise unnecessary decisions and time costs.

Some Guidance

Here are a few points to consider as you decide how to embark on a new project, whether it be personal, contract, or a product at work.

If the project is intentionally going to be short-lived (less than a year), use Swift. You’re already in the realm of quick and dirty. Constrain yourself to specific versions of libraries, and make sure you clearly communicate this approach is being used to all stakeholders.

For something medium-term (1-3 years), cautiously use Swift, but avoid third-party code. Projects with Swift frameworks need to match Swift version throughout. This means carefully matching versions of frameworks you’re pulling in, especially during migration periods. This is an easy place to burn a lot of time.

If you’re building a framework (especially a commercial one), use Objective-C. Do not impose unnecessary burden on apps integrating your library. Carefully design your interfaces for elegant Swift integration, but implement your code in Objective-C.

For a projects anticipating long-term development, or that have the scrutiny of investors, or are the primary means of putting food on your table, seriously consider Objective-C. It may not be glamorous or flashy, but it is reliable and proven. Seize the opportunity to demonstrate wisdom and expertise to clients by articulating a case for avoiding new shiny things.

The Future

As a developer, I’m very excited to watch Swift continue to evolve. I’m deeply impressed by Apple’s approach, both in making the language open source, and the level of public collaboration they’ve continued to demonstrate.

As a product owner, I have to be cautious when selecting tools. If it can’t be directly translated into value for my customers, then a flashy new tool or language isn’t the best choice. Today, Swift’s breakneck pace and deprecation cadence make it a risky choice for a product, whether it be a side project or a breadwinner.

Friends and Family Apps

When the App Store first launched, mere presence was enough to ensure at least some small amount of success. Today, things are much different. A great idea with good execution will still struggle to make a splash in the App Store, unless the stars are perfectly aligned. Even then, it’s usually more about who you know and the press you can drum up.

Here’s a conversation I had with Kevin Harwood last week:

Kevin: “How was the [Pactola] launch?”

Me: “Eh… I knew it was going to be light. It’s pretty niche.”

Kevin: “Friends and family?”

Me: “Yep.”

I wasn’t kidding; I knew full-well going into the project that I was going to have a hard time selling an application that provides a rest API for sending push notifications. There are similar free apps, and the target market is pretty specific. To make matters worse, I spent no time marketing or reaching out to press folks who might want to do a review. These are pretty basic things, right? You’re probably asking why I bothered to build the app.

Here’s a secret for anybody interested in iOS development: employers don’t care how many units you’ve sold on the App Store. They care about what you’ve built: how you did it, what you learned, the extra attention to detail you gave, and how all of those things can help their team.

So why did I build Pactola?

  • The server is built in Go. This was my first experience with the language.
  • Jenkins deploys the server automatically when I push to master. The iOS build is also on Jenkins to ensure I’m building from reproducible sources, and not just a point-in-time on my local workstation.
  • The deployment script relies only on SSH and bash, minimizing VPS setup time and overall dependencies.1
  • The iOS app uses Storyboards and Auto Layout (I’m coming off of two years where I was primarily focused on OS X development targeting 10.6 and up; no ARC, no Auto Layout, and no Storyboards)
  • Screenshots are automated, thanks to ui-screen-shooter. I had to learn about UI Automation and come up with a strategy for creating real data in the screenshots.2
  • I disciplined myself to write out tasks and issues, and then tracked them on a Trello board. When the tickets were all in the Done column, I shipped. I also have a nice backlog of future ideas and nitpicks.
  • I used Apple’s TestFlight system for the beta. This was my first chance to use it, and I got a good feel for how it compares to the legacy TestFlight system.3
  • I’ve now shipped an application that uses APNS and have a strong working knowledge of the system, its capabilities, and have some ideas for leveraging it in other apps.
  • I tried to stick to a Ninety Days rule, though I didn’t account for changing jobs.

So while I may not ship very many copies of Pactola, I’d say there are a few interesting things I could talk about in an interview. I’d highly recommend finding something you’re unfamiliar with and building your own Friends and Family app.

  1. Since Go apps are statically linked, there are no other dependencies on the host system; pactola-server runs on any Linux system once my keys are there to accept the ssh deployment.
  2. The app contains no extra magic for this, except for registering as a special known-device when running on the simulator. The server populates the data for that special device. And yes, there are Go tests for that special behavior.
  3. Given the end of the original TestFlight, that proved to be a good use of time.

Ninety Days for Evening Indies

Justin Williams posted a great essay a few months ago where he shares a good bit of advice:

“Do not spend more than ninety days on your 1.0”

Justin’s reasoning is simple: it’s enough time to get something decent built, but short enough to keep you focused on critical features. Ideally, you ship and follow-up with other great features. Worst case, you regroup and try again.

However, his advice is specifically aimed at folks who are gambling on a full-time project. What about Evening Indies?

Evening Indies?

I’m not an indie developer; I’ve had full-time work since college. The places I’ve worked have all compensated me fairly, and I’ve been blessed to have challenging, enjoyable work. In addition to providing for my family, these jobs have also helped bootstrap my entrepreneurial ventures. I’ve recognized my entrepreneurial spirit for over a decade, and I’ve kept it safely satisfied with side projects and hobbies. Sound familiar? That list of app ideas you keep stashed in Evernote is a pretty good hint, especially if you find yourself thinking about it during work.

I’m not a full-time indie. I’m an Evening Indie.

Even Less Time

Ninety Days is perfectly applicable to the Evening Indie, even though you’re dealing with significantly less time 1. Software markets (especially mobile) can change dramatically in three months. It’s possible you will have built the wrong app by the time you’re done. If you do manage to hold on to a market opportunity longer than that, you still have to compete with your own fatigue and real-life commitments.

It’s incredibly difficult to build a sustainable business independently. Even with his past experience, large user base, and beloved app, Justin couldn’t make Glassboard work.

Realistically, it’s impossible to build something that can sustain an individual (never mind a family) with 3-4 hours of work per day. What’s left is to build something fun and interesting, and to do so pragmatically. With even fewer hours to spend than a full-time indie, you have to be even more diligent to protect your time. Ship something nice and simple. If the idea is solid, your customers will ask for the 1.1 features. Move quickly with them and adapt with the market.

I checked last night – my current side project is 68 days old.

Feels perfect. Beta soon. Ship in November. Keep my day job.

  1. 10-14 hours a day seems pretty normal for the indies I know. I’m lucky to muster 3-4 in an evening

Shipping Is Hard

Almost two months ago, my wife and I were discussing a noise generator app we’d used while our first child was very young. As we recalled the app and remembered how ugly and simple it was, I decided that it wouldn’t be very difficult to improve upon. A few minutes after our discussion, I tweeted:

Wife: “that’s a cool idea.” Me: “CHALLENGE ACCEPTED”

The first proof-of-concept for the app took me just over eight hours. I was able to loop static noises, and had some very basic remote control support in place. It worked, but was hideous. So I spent the next week or so considering some ideas and implementing a few of them. I collected some new beta testers, and started showing people what I was working on.

About a week before I planned to submit the app to Apple, I realized the design was still hideous. I scrapped the UI, and started fresh. The result was something much more satisfying, and I’ve sworn the beta testers who saw the first stuff to never reveal how awful it was.

Static 1.0, connected to a remote device

Static is a noise generator that can be remotely controlled from other instances of the app.

In the end, this weekend app took almost two months to build. Even after writing software professionally for years, it still amazes me how hard the last 20% of a project is.

Static is currently submitted for review with Apple. Once available, I hope you’ll check it out. The app is free to try, with unlimited use of one sound (including full remote control capabilities). Additional sounds and future features can be unlocked with an in-app-purchase.