The work and times of the app development team at Steamclock.

Archive (25) RSS Feed

Allen Pike • December 6, 2018

DJ Apps: Closing Time

Once upon a time, Steamclock launched two DJ apps for iOS: WeddingDJ and Party Monster. The pitch was simple: a queue-centric flow, smooth crossfades, and an easy interface for amateur DJs.

WeddingDJ was a hit with brides, grooms, and semi-pro DJs alike. Party Monster was well-loved for its opinionatedly queue-based UI, and fans enjoyed its habit of refusing to play Nickelback. Before long, these apps were paying our rent – which is a rare milestone in the indie iOS app world.

Then, the cloud happened.

Man Yells At Cloud

Don’t get me wrong, the cloud is great. I love the cloud, some of my best friends are clouds.

However, Apple’s transition from iTunes downloads to cloud-based streaming gradually hobbled this kind of DJ app. First it was iTunes in the Cloud, and iTunes Match – cool services, but they arrived without any way for apps like ours to trigger a download and make these tracks available to play.

Then came the bomb: Apple Music. Not only can crossfading DJ apps like ours not download Apple Music songs, we can’t even play downloaded tracks, since they they are protected with DRM.

As we wrote in 2015, this has been a pain for our users, and attracts angry and frustrated reviews. Even with app descriptions that started with all-caps “THIS APP DOES NOT WORK WITH APPLE MUSIC“, we’d get users who were upset – and reasonably so – because they’d downloaded our apps but couldn’t use them with their music.

So in 2016, we rounded up the various bug reports we’d made and published a review of the various issues and limitations with the APIs for DJ apps. Essentially, we needed either improvements to the the crossfade-capable but low-level AVFoundation APIs to let them play Apple Music, or additions and fixes to the high-level MPMediaPlayer APIs to support crossfading and queueing.

Livin’ On A Prayer

To their credit, Apple soon made a handful of fixes that changed Apple Music support at the MPMediaPlayer level, but 2 years later there are still multiple showstopper issues. In 2018, it is still not possible to make a nice queueing and crossfading DJ app that can play Apple Music tracks in the background, so this use case is clearly not a priority for them.

In the meantime, our apps’ ratings just have just trended lower and lower. For every delighted customer who’d bought their music, we’d get one or two who had no use for a DJ app that didn’t support streaming.

Even though the apps still make some revenue, we’ve reached a tipping point. Too many users are having a bad experiences, and it’s only getting worse. That’s not the kind of product we want to sell at Steamclock. We’ve tried various avenues – filing bugs, investigating Spotify, and prototyping simple “Baby Party Monster” demos that are possible with Apple Music, but none of these paths lead to what our customers are asking for.

So, the time has come to sing one last song for Party Monster and WeddingDJ. Today, we removed them from sale. The App Store should allow people who own these apps to download them indefinitely, until a future iOS update eventually breaks compatibility. It was sad, but it’s time.

Don’t Stop Believin’

These apps powered more than 100,000 weddings, along with years of parties and road trips. We loved developing them, and we’re so sorry we couldn’t find a path to modernizing them.

That said, we’ve learned a lot in the process, and have put those lessons into building dozens of other apps. We’ve shipped many for our clients, but in the last year we’ve been increasingly prototyping new internal apps, iterating them, and betting on ones worth shipping. This winter we’ll be shipping a Mac app for issues and pull requests, and next year we finally launch our turn-based spy game and v1.0 of our Bluejay iOS library.

Of course, that’s little consolation for folks who wish a nice app for crossfading and queueing Apple Music tracks could exist. We wish it could too. And hey, people are still trying! Maybe one day it will be possible.

We’ll keep an eye open – just in case.

12 6 18 725
Rob MacEachern • November 20, 2018

On the Origin of isMultiple

How isMultiple became part of Swift, and what I learned along the way.

We’ve been using Swift at Steamclock for almost four years, and in that time it’s come a very long way. While Apple has of course done a momentous amount of work on the language in that time, a lot of Swift’s maturation can be attributed to the work of the community, by way of Swift Evolution.

Ideas move through Swift Evolution in three steps: a pitch, a proposal, and a review. There have been hundreds of reviewed proposals so far, ranging from the addition of the Random API to the removal of C-style for loops, and get as deep as making improvements to the floating point and integer protocols.

These proposals and many more are discussed on the Swift Evolution forums every day, which is where I learned about Chris Eidhof’s proposal to add toggle() to Bool. toggle adds a convenient way to to negate the value of a boolean.

This is particularly useful when a boolean is nested deep in an object graph, like so:

// Before .toggle()
someObject.someProperty.someBoolProperty = !someObject.someProperty.someBoolProperty

// After .toggle()
someObject.someProperty.someBoolProperty.toggle()

Chris’ proposal was easy to understand, the implementation was only a few lines of code, and the changes positively impacted the ergonomics of the language. This proposal showed that improvements don’t necessarily require intimate knowledge of the Swift compiler, or obscure data structures.

At the same time, it exposed some divisions in the Swift Evolution community. Some users argued that such trivial additions weren’t worthy of inclusion in the Swift Standard Library. Accepting toggle, in their eyes, would inspire a horde of trivial additions that would pollute the API surface of the standard library.

Despite these objections, the toggle() proposal was accepted on March 7, 2018, along with a refined set of proposal criteria and an explicit call for “further proposals to fill in gaps like this in the standard library.”

Which was great news, since toggle had planted a seed in my mind.

The Lightbulb

Around that time, I had been reading a book about Swift. Although its code samples were generally quite readable, there were a number of examples that included tests for even and odd numbers using the “remainder” operator, %.

.filter { integer in
    integer % 2 == 0
}

.skipWhile { integer in
    integer % 2 != 0
}

.takeWhile { index, integer in
    integer % 2 == 0 && index < 3
}

Many programmers will be familiar with integer % 2 == 0 as a check for a zero remainder after division by two – implying the number is even – or a non-zero remaninder indicating the number is odd. It’s not rocket science (although admittedly it may be a very tiny simple part of rocket science).

After seeing after seeing integer % 2 == 0 enough times, however, it started to look very strange to me. The strangeness may have been partly due to semantic satiation, but despite having used this pattern dozens of times in various projects – and despite it being standard practice for testing integers for “evenness” – it looked unnecessarily obtuse.

Meanwhile, I had recently been working with Ruby, which is full of developer niceties like odd? and even? . Naturally, I thought it’d be nice to be able to bring something similar to Swift:

.filter { integer in
    integer.isEven
}

.skipWhile { integer in
    integer.isOdd
}

.takeWhile { index, integer in
    integer.isEven && index < 3
}

A small nicety makes this kind of code a lot clearer and less error prone.

The Pitch

The next step was to write a pitch. A pitch is the first part of the Swift Evolution process: you can share a sketch of your proposed changes on the forum, and receive feedback from the community.

While writing a pitch for isEven/isOdd , I discovered someone had already started a pitch thread with the same idea, but progress on it had stalled. I considered starting a fresh forum thread, but decided to try reviving the original one with my own spin on the topic. I added my pitch based on the new post-toggle guidelines, and made some coffee.

Me: This shouldn’t be too controversial!

Narrator: It was.

One by one, responses started popping up. Swift core team members were chiming in. It was definitely exciting, but also surprisingly nerve-wracking. By the end of the pitch phase the thread had over 120 replies and over 5000 views, making it one of the top 10 most active pitch threads all-time on the forum. I don’t think anything I had ever written before had received so much feedback.

The responses were mostly favourable, but there were some concerns. The biggest was that isEven and isOdd didn’t address a big enough problem for inclusion in the standard library. Some folks didn’t seem to appreciate the value in moving away from the % operator – a move that helps avoid subtle bugs when working with negative values, especially due to differences between how % works in other programming languages.

The most common suggestion overall was to include a more general solution to the problem of integer divisibility. I was originally hesitant to expand the pitch because I couldn’t recall many times when I needed check for divisibility other than two (even/odd). I surveyed the source code of a variety of open source projects and found that 20-40% of divisibility testing was done for values other than two, so there was definitely a use case. The next thing you know, isEven/isOdd became the isEven/isOdd/isDivisible(by:) .

The Trouble With Zeros

The next issue at hand was getting correct handling of zeros with isDivisible. A typical mathy definition of divisibility usually looks something like:

If a and b are integers with a≠0, then we say a divides b if there exists an integer k such that b = ka.

Given the definition of divisibility, some argued that someNumber.isDivisible(by: 0) should be a precondition failure that crashes – but few developers would actually want that in practice.

On the other hand, relaxing the definition of divisibility to allow checks for divisibility by zero leads to a different strange situation: what if both values are zero?

let x = 0
let y = 0

// Zero is divisible by zero, according to the relaxed definition of divisibility.
if x.isDivisible(by: y) {
    // Crashes because division by zero traps in Swift. Divisibility != divideability
	x / y 
}

Well, that’s awkward. The code looks safe at first glance, but leads to an unceremonious – and likely unexpected – crash.

Thankfully, a community member proposed a clever workaround: rename isDivisible(by:) to isMultiple(of:). This provided the functionality we wanted, without implying anything about an actual literal mathematical division. This small tweak was so perfect and so simple, but it may not have ever come up without this broad public feedback process.

A month into the pitch phase, and it had evolved to be isEven/isOdd/isMultiple(of:) and all that was missing was implementation. Luckily for me, Apple’s Stephen Canon graciously provided an implementation complete with tests. Stephen is one of the most knowledgeable people on the planet when it comes to computational numerics, so getting an offer to use his implementation as part of the proposal was like an awesome, geeky gift from the programming gods.

Meanwhile, on another pitch thread, Swift core team member Ben Cohen posted a message expressing his concern that recent pitches contained “huge quantities of sugar and not enough protein”. This signalled that the isEven/isOdd/isMultiple(of:) review might be an uphill climb.

Review Begins

The first day of the review started on August 20, which coincided with the first day of my summer vacation. The review phase felt like a rollercoaster ride. A string of positive reviews would be posted and I’d be flying high, but then a scathing negative review would be posted which would leave me questioning why I’d even bothered proposing these changes in the first place. Well-known names in the Swift community like Erica Sadun and even Chris Lattner voted “-1”. My confidence sank, but I still stayed up each evening, responding to and addressing concerns that had been posted that day.

More than anything, the thing that surprised me was how passionate users were on each side of the proposal. This modest proposal, which started as a clear way to test if a number was odd.

Jordan Rose, another Swift core team member, pointed out that the proposal was a victim of Parkinson’s Law of Triviality:

Members of an organization will give disproportionate weight to trivial issues.

I suppose that’s what I get for making my first pitch something small. 😅

At the review’s conclusion a week later, supporters outnumbered opposers by roughly 2:1. It looked like isEven/isOdd/isMultiple had a chance of being accepted – but it was far from certain. I felt relieved that it was out of my hands, and I got back to enjoying my vacation. 🎣

A Decision Is Made

On August 30th, 7 weeks after my initial pitch post, the Swift core team announced new criteria for additions to the standard library:

To be considered for addition to the library, a proposed feature must satisfy two conditions: it must provide functionality that is useful to a substantial population of Swift programmers, and it must provide substantial advantages over the alternative ways of accomplishing that functionality.

Stern, but fair. Some examples of such advantages from the new guideline:

  • It may be complex, challenging, or error-prone for users to implement themselves.
  • It may have substantial performance advantages over a user implementation, either because it has access to library internals or just because the library implementation will likely be more carefully tuned.
  • It may be substantially easier to work with because it composes better with other language or library features.
  • It may be substantially more “fluent”: that is, more natural to discover, use, and read in code. The implementation may involve composing primitives in a subtle or tricky way, or the primitives may be unfamiliar to many programmers. This is a more subjective criterion than the others, and people may reasonably differ about how to apply it.

In the end, the core team felt that isMultiple did meet the “fluency” criteria. It could prevent bugs around negative remainders, and could have performance benefits for large integer types. The addition of isMultiple was accepted, but given the addition of isMultiple, isEven and isOdd did not cross the threshold for inclusion in the standard library.

And so the chapter closed, and with success! Well, partial success.

The decision rationale made sense. Still, a small part of me still felt like there was a certain je ne sais quoi about even and oddness that made isEven and isOdd worthy of inclusion too. But it was not to be.

Lessons, Odd and Even

I learned a lot at each phase of the process, and appreciate everybody who took time to help get this proposal into Swift.

For those considering a Swift proposal of their own, here are my key takeaways:

  1. It will take more effort than you expect. Writing the initial pitch is the easy part. Reading and responding to feedback, especially critical feedback, takes a lot of time and mental energy.
  2. Unexpected things can threaten to derail the entire discussion. My initial pitch included offhand comments about how value.isEven is fewer characters than value % 2 == 0 and that operator precedence rules impact the readability of statements that include the % operator. Both came back to haunt me repeatedly during discussions. Many users (rightfully) rejected the idea that API changes should be driven by character count and disputed my operator precedence claim. You should focus on the key selling points in your pitch, and cut everything else to avoid wasting time discussing weak side-points.
  3. You need an implementation. It wasn’t clear to me at the start of the process that an implementation is required in order to be reviewed. I was fortunate to receive a high quality implementation from Stephen Canon but you can’t assume someone will provide it for you. It’s not as scary as it sounds though! Building Swift from source was easier than I thought it’d be, and I don’t think it would have taken me too long to actually make the changes, if I’d needed to.
  4. Don’t take things too personally. Your pitches and proposals may be criticized harshly, but that’s part of the process. Be gracious when receiving feedback, and try to keep an open mind. The Swift forum is full of really smart people who want the same thing as you: a better programming language.

isMultiple(of:) will be part of Swift 5, which is scheduled for release sometime early in 2019. Despite it being more work than I expected, I’m glad to have been a part of the process. If you have an idea for an improvement to Swift – and can avoid running afoul of Parkinsons’ Law – then consider making a pitch of your own. ⚾️

11 20 18 2349
Allen Pike • June 11, 2018

Release Radar: WealthBar

Once upon a time, saving for retirement was daunting. People had to either pay high fees for their bank’s mutual funds, or learn how to manage their investments directly on the stock market.

Luckily for savers everywhere, there are now services like WealthBar that make this easy as pie. WealthBar invests your savings in world-class index funds, and then does the important work of keeping your portfolio balanced and healthy.

While WealthBar has been building steam for years now, they recently decided it was time to Get Serious™ about their mobile apps. While their existing cross-platform React Native apps were functional, they weren’t great. That’s where we came in.

This spring, we designed and built fully native iOS and Android apps for WealthBar. We dug in to what WealthBar’s clients were asking for in an app, then went beyond that to consider what kind of features and presentation could actually help users save more, and be more successful at building wealth. A lot of investment apps focus on the day to day – or even hour to hour – of the stock market. Evidence shows that real wealth is actually built on the long term decisions, so we designed an app with that focus.

Of course, performance and usability are paramount for consumer-facing apps like WealthBar’s, so for this project we went the full-native route. Using Swift and Kotlin, we were able to build high-performance apps that make navigation a breeze. Native development also helped us integrate well with features like Face ID and password managers. These additions make good security hygiene easier than ever.

Meanwhile, a visual refresh brought the apps into 2018, adding a bit of personality to the UI while keeping things clean and clear.

The result? Two darn good apps, according to WealthBar clients:

Well thanks, uniquenickname72526185! Of course, it also matters what the team at WealthBar thought of our work. Luckily for us, WealthBar wrote about our collaboration on their blog, extensively quoted us, and called our team “the best in the west”. So overall, pretty great!

Of course, as with any “.0” release, there is much more in store. We have some great features on the roadmap – time to get back to work. 💰

Download WealthBar Today

Download on the App Store Download on Google Play
Allen Pike • May 14, 2018

Vancouver Xcoders

On May 9 we hosted the first-ever Vancouver Xcoders! Xcoders is a speaker series about development on Apple platforms. If you’re interested in attending or speaking at an Xcoders event, we’d love to see you at our next event. We’re aiming to have one every 2-3 months.

Xcoders is the next iteration of the VanCocoa event we’ve run since 2013. While we’re still keen on providing a platform and gathering place for the city’s experienced Cocoa developers, we’ve taken a page from our namesake group Seattle Xcoders in broadening our mission to reaching out to the wider iOS and Mac development community here in Vancouver.

There are a lot of folks doing great work on excellent apps right here in town, and we’d like to bring more of that community out of their shells and offices, and into casual communication about how to make great apps.

Also, our new logo features a sweet whale – how could you resist joining now?

5 14 18 168
Allen Pike • March 21, 2018

Saving Money by Paying for Design

When we founded Steamclock, our primary focus was development. While we did do design work when needed, our clients’ teams typically led the design effort while we focused on the software engineering side of things. Since our clients often have in-house design talent, this seemed like a reasonable way to manage costs.

Over time though, we noticed a concerning trend. Projects where a client wanted to save money by doing app design in-house turned out to be more likely to go over budget. On the flip side, projects where we also took on app design were more often on or under budget. What was going on?

Perhaps unsurprisingly, we’d learned a lot in our years of designing and developing mobile apps. Just as designers with a deep background of building web apps get better at designing web apps, designers with a lot of experience in mobile excel at designing mobile apps. This experience makes mobile designers adept at avoiding common pitfalls or complexities on these platforms.

With these lessons learned, we can often eliminate problems at the design stage before they become costly. Usability issues, “scope bombs”, failure to make use of “free” platform features, unnecessarily expensive UI customizations, and insufficient user testing are just a few problems that can be remedied at the design stage by a team that knows mobile development well.

In the same way that a designer can create a week of developer work in an hour of design time, a designer can instead save a week of developer work in just a few minutes by asking the right question, proposing a more elegant solution, or making use of a hard-won lesson from a previous app we’ve shipped and iterated.

Having learned those lessons, I wanted to share some design elements that we’ve learned to recognize as signals – flags that a proposed app design could save a substantial amount of budget from a mobile-centric design and refinement pass.

1. Non-native UI conventions

Native iOS and Android component libraries provide a lot of navigation functionality for free, and adhere to the platform conventions users already understand. If a design uses dropdowns, radio buttons, “hamburger” menus, or other UI elements that are common on the web but not ideal for mobile apps, that’s often a sign the design needs a mobile-centric revision. Even sizing can be a big clue here: when we see designs that have drawn system controls to be slightly different than their standard sizing, there’s often an opportunity to save a lot of development time with a design pass focused on making better use of the platform.

2. Flowing text

Web and print designers are used to a text layout engine that makes it easy for text to flow around images, buttons, and other page elements. While this is possible in native apps, it’s often a lot more work than in web apps, and is a common signal that an app was designed with a web or print mentality.

3. Novel navigation

Some apps, such as Snapchat, distinguish themselves with a novel navigation scheme. Most often, however, it’s best to leverage users’ familiarity with native tab bars and drilldowns. Luckily, there are a lot of engaging ways for an app to distinguish itself visually from the competition without incurring great usability and discoverability costs.

4. Offline sync

Having built apps for eight years, we’ve seen almost every kind of requirement you can imagine. By a significant margin though, offline sync capability is the most frequent feature we see proposed for 1.0 that is best deferred to later. This is sometimes truly needed – for example, an app that is primarily used in the wilderness – but it comes at a substantial cost when you consider the additional development and design work to handle conflict resolution, error handling, sync status reporting, and all the other details that come out of a bullet point as simple as “offline mode”.

5. Welcome tours

A common issue we see in designs proposed by teams that are new to mobile is attempting to mitigate overly complex or unclear functionality by offering a “tour” on first launch. While it may seem like less work to throw up a tour before folks start using your app, text-filled screens are typically glossed over or not retained. Given that, tours don’t tend to fix most usability problems, and the work required to make sure every interaction is clear and understandable will still be necessary.

Shipping better apps

All that said, no matter how new your team is to designing mobile apps, you surely have a huge amount of insight and product knowledge to offer the process. Designing a new app is all about collaboration, especially for teams with a background in print or web work. If your app design work is hitting any of these issues, we’d be happy to help bring them to the next level!

3 21 18 821

The work and times of the app development team at Steamclock.

Archive (25) RSS Feed

Interested in future posts or announcements? Subscribe to our feed.