Rethinking Apple Music
When I launch an app like Apple Music, I’ve got just a handful of simple questions in mind:
- What have I already been listening to lately?
- What have my friends and heroes been listening to lately?
- What’s everybody else been listening to lately?
- What’s brand new?
- What’s something out of the blue you think I might like?
Apple Music only tries to answer the last of these questions (and it answers it poorly). The other four questions aren’t answered at all.
It’s often a mistake to think that one’s anecdotal experience is representative of people in general, but in this case I believe my music habits aren’t far off from most people’s. Music appreciation is a learned skill. We learn how to enjoy music by imitating our friends and heroes. Whether it’s thumbing through the sun visor CD case of that cool friend from high school, or flipping through your dad’s box of old vinyl, we try on the tastes of other people to see what fits. It’s how our taste grows, embraces new favorites.
The social nature of taste, and of musical taste in particular, is so fundamental that it should be the organizing principle of Apple Music. Here’s how I think they should do it:
- Five New Tabs - Reorganize the top level of the app into five tabs that answer the fundamental questions: Keep Listening, Friends, Everyone, New Releases, and For You.
- Bye, Bye, iPod - Break out all the legacy iPod features into another app.
- Consistent Visual Grammar - Create a consistent visual grammar that allows albums, playlists, and radio stations to exist as siblings on any screen.
- Let Me Choose My Heroes - Make it easy for me to browse the music habits of my heroes, whether they’re celebrities or the cool kids down the street.
I. Five New Tabs
Reorganizing the app into the following tabs will make it map closely to the common modes in which we look for music.
Keep Listening - This tab would show me a dynamic feed of the albums, playlists, and radio stations that I’ve been listening to a lot lately. It’d be sorted intelligently, taking into account things like most recent play dates, play counts, favorites, etc. In the days of CDs, this would be like the discs you had scattered around on your passenger seat or in your book bag. Since it’s software, it should be solving this problem for me without me having to manage anything. With this tab, I could just launch the app and drop the needle on something I probably want to hear again.
Friends - This tab would be similar to the Keep Listening tab, but drawn from the music habits of people I follow, whether friends or celebrities. It even could provide a means to filter down to just the Keep Listening feed of a given user, so I can try out the music that my heroes are listening to. The Internet has the power to make this scale in a way that that physical media never could. Use it.
Everybody - This would be similar to the Friends tab, but instead of drawing from users I follow it would be drawn from all users of the service in aggregate (or at least users in my country). Like the Keep Listening and Friends tabs, it too would contain albums, playlists, and radio stations on equal footing.
New Releases - This should be fairly self explanatory. I’d be alright if this was a Featured tab instead of New Releases, since (for all I know) deals with publishers may require providing top-billing for mainstream artists. Either way, this tab should be where I go to find brand new music.
For You - This tab would be distantly similar to the existing tab of the same name. In addition to albums and playlists, it could also contain radio stations. The Apple-curated playlists and album suggestions need to be thrown out. The tab is supposedly “for me,” so it should be highly sensitive to my particular tastes. Pandora has proved that this is possible. I could care less about what Apple’s curators think I should listen to. If I’m in the mood for borrowing someone else’s taste, I’d rather use the Friends tab. If I want to hear more of my own taste, then I should be able to get that from the For You tab.
Search
While not a tab per-se, all five tabs need to have a persistent, easily-accessible search button. Searching is the one activity (besides playback controls) that needs to be instantly available regardless of the current context.
II. Bye, Bye, iPod
All the vestigial traits from the iPod need to be lanced and sewn together in some other app, buried on a second home screen. Perhaps it could be served from the App Store only if you really need it, like iBooks used to be. The days of owning music are numbered. Even a once-fundamental concept like a collection is no longer relevant in a world where (theoretically) all music is available to all users at the touch of a button. Removing legacy iPod features would eliminate most of the egregious forms of complexity from Apple Music, such as trying to figure out why an album isn’t showing up in the search results.
III. Consistent Visual Grammar
Since the five tabs described above are organized around answering the fundamental questions, this presupposes that each tab is capable of presenting albums, playlists, stations, or some combination of the three. In order to do this without creating confusion, a system should be developed that makes it easy to tell at a glance whether an item in a list is an album or a playlist or a station. Whether it’s by changes in shape or texture, or by filters that hone the the visible results, or some other form of grouping, the precise grammar doesn’t matter so much as the need for consistency. Once I learn how to distinguish the three kinds of items, I should be able to apply that knowledge no matter what screen I’m using. In the current Apple Music app, the presentation of these items varies dramatically depending on which screen you’re on. This inconsistency makes the app difficult to understand and distracts from the browsing experience.
IV. Let Me Choose My Heroes
No algorithm will ever generate as beloved a surprise as snooping through the music habits of one of my heroes. Back when Rdio was alive, whether it was a “celebrity” like the members of Y La Bamba, or a cool Internet friend like Neven Mrgan, my most satisfying surprise musical finds came from browsing the tastes of people I admire. Algorithms have their place (when I’m in the mood for something familiar), but they shouldn’t be used exclusively. Nor should Apple’s hubris be allowed to go on unchecked, filling my Apple Music app with Carly Simon playlists and Jumpman Jumpan Jumpan Jumpan remixes. I want to listen to what my heroes are listening to, and they’re not listening to what Cupertino listens to.
A Very Narrow Usage of Storyboards
Lately I’ve been struggling to balance the pros and cons of various view controller initialization techniques. There are three, none of which are perfect:
Doing it All In Code - PRO: I have complete control over when and how my dependencies and views get initialized. CON: Some complex view hierarchies are easier to manage using the Interface Builder GUI (primarily conditional edits for different size class combinations).
NIBs - PRO: I’m able to pass in dependencies to parent view controllers (Core Data stack access, delegates, API clients, etc) at initialization time. Since I work in Swift, this helps avoid inconvenient optionals or dangerous force-unwrapped optionals. CON: NIBs lack some useful features that Storyboards have, like layout guides or automatic cell nib registration.
Storyboards - PRO: All the GUI benefits of NIBs, with addtional perks like top/bottom layout guides and automatic cell nib registration. CON: Storyboards are prone to a whole host of other shortcomings which I won’t list here (a few of which are also shared by NIBs). The most problematic shorticoming is that, unlike NIBs, UIViewController has no equivalent
initWithStoryboard()
method. Why does the lack of aninitWithStoryboard()
method matter? It means you cannot provide a custom init method for your class, which means you cannot pass in dependencies at initialization time. This is true even if you don’t use segues and only use one view controller per storyboard. This implies that the view controller’s dependencies are public read/write properties. It’s an antipattern for parents to have that much introspective knowledge about how to configure their children. Public read/write dependencies also expose a surface area for bugs from API misuse. Exposing an otherwiseprivate let
dependency as a publicvar
implies that the property is free to use.
Lately, the projects I’ve been working on have had pretty heavy AutoLayout requirements. As such, it’s been easiest to build the user interface logic using a Storyboard. But the Storyboard approach makes it really hard to pass in dependencies in a simple, reliable fashion.
Until Apple provides a better solution, my current strategy is to provide a static factory method that takes as arguments all the dependencies that need to be passed in:
static func newFromStoryboard(foo: FooDependency, bar: BarDependency) -> MyViewController
In addition, I make the relevant instance variables private, so that they can only be set from inside the file:
private var foo: FooDependency! private var bar: BarDependency!
The factory method instantiates the view controller from the correct storyboard, and then sets all the dependencies. I only use one view controller per storyboard, and never use segues.
This is not the most elegant approach I can imagine, but I believe it’s the least inelegant approach made possible within the current state of the UIKit framework and Swift.
Don DeLillo on Chickenshit White UI Design
We ran into Murray Jay Siskind at the supermarket. His basket held generic food and drink, nonbrand items in plain white packages with simple labeling. There was a white can labeled CANNED PEACHES. There was a white package of backon without a plastic window for viewing a representative slice. A jar of roasted nuts had a white wrapper bearing the words IRREGULAR PEANUTS. Murray kept nodding to Babette as I introduced them.
“This is the new austerity,” he said. “Flavorless packaging. It appeals to me. I feel I’m not only saving money but contributing to some kind of spiritual consensus. It’s like World War III. Everything is white. They’ll take our bright colors away and use them in the war effort.”
From White Noise.
I admit I never understood what Manton found appealing about microblog posts until today, a week...
I admit I never understood what Manton found appealing about microblog posts until today, a week into my Giving Up On Twitter experiment, inspired by Seth. Seth’s feelings about Twitter caught me off guard. I think I’ve been having a similar experience. So I deleted all but Tweetbot, disabled all but the minimal socially-acceptable push notifications, and moved it to a third home screen folder.
Imagining a First-Party Swift KVO Replacement
My last post got me thinking: if Apple were to add a KVO-like service to Swift, what would it look like? I wish I understood more about how programming languages are created, and how compilers work. What follows isn’t really a serious proposal, but just a sketch of what I would personally want to use as a practicing software developer.
What if every Swift class
received an implicit, protected member with a reserved name like observables
which provided a means to register closures as observers for changed values? An example usage might look like this:
let tweet = Tweet(text: “Hi.”) tweet.observables.isLiked.addObserver { (oldValue, newValue) -> Void in // this is otherwise just a standard closure, with identical // memory management rules and variable scope semantics. print(“Value changed.”) } tweet.isLiked = true // Console would log “Value changed.”
The observables
property would be implied, just like self
. It would be an instance of some ad-hoc generated Swift class created by the compiler at compile time. For every observable property of the owning class, this observables
class will have a corresponding observable property. So if you created a class like this:
class Tweet { var isLiked: Bool = false let text: String init(text: String) { self.text = text } }
The Swift compiler would generate a class like this and assign it to the observables
property for Tweet
:
class Tweet_SwiftObservables { let isLiked = Observable<Bool>() }
The Tweet_SwiftObservables
class name would be auto-generated based on the class name of the target class. Notice that only the isLiked
property is carried over, since the text
property of Tweet
is a let
, not a var
.
The isLiked
member of Tweet_SwiftObservables
is an instance of a generic Observable<T>
class, whose implementation would be something like the following (though, of course, more nuanced):
class Observable<T> { typealias Observer = (oldValue: T?, newValue: T?) -> Void private var observers = [UInt: Observer]() func addObserver(observer: Observer) -> Uint { let token: Uint = 0 // generate some unique token self.observers[token] = observer return token } func removeObserverForToken(token: Uint) { self.observers[token] = nil } }
The money shot is the addObserver()
method. This method accepts a single Observer
argument, which is just a simple closure. It would follow all the existing rules of memory management and variable scope as any other closure. addObserver()
returns a unsigned integer token that can be used to remove the observer at a later time.
What I like about my idea is:
- It’s familiar. It resembles the core mechanic of KVO without the antiquated hassle. It uses existing memory management rules. Everything you already understand about closures applies here.
- It’s type-safe. The
Observable<T>
generic class ensures at compile-time that your observers don’t receive an incorrect type. - It’s readable. The syntax is brief without being unclear. Implementing the observation closure at the same call site as
addObserver()
keeps cause and effect as close together as possible. - It’s easy. It abandons a stringly-typed API in favor of a compile-time API. Since the
Foo_SwiftObservables
classes would be auto-generated by the compiler, there’s no need for busywork tasks like keeping redundant manual protocols or keyword constants up to date with the target classes.